import { useCallback, useState } from 'react'; import type { FC } from 'react'; import { useBlocker } from 'react-router-dom'; import { toast } from 'react-toastify'; import CancelIcon from '@mui/icons-material/Cancel'; import CircleIcon from '@mui/icons-material/Circle'; import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew'; import WarningIcon from '@mui/icons-material/Warning'; import { Box, Button, Typography } from '@mui/material'; import * as SystemApi from 'api/system'; import { Body, Cell, Header, HeaderCell, HeaderRow, Row, Table } from '@table-library/react-table-library/table'; import { useTheme } from '@table-library/react-table-library/theme'; import { updateState, useRequest } from 'alova'; import { BlockNavigation, ButtonRow, FormLoader, MessageBox, SectionContent, useLayoutTitle } from 'components'; import RestartMonitor from 'framework/system/RestartMonitor'; import { useI18nContext } from 'i18n/i18n-react'; import * as EMSESP from './api'; import ModulesDialog from './ModulesDialog'; import type { ModuleItem, Modules } from './types'; const Modules: FC = () => { const { LL } = useI18nContext(); const [numChanges, setNumChanges] = useState(0); const [licenseChanges, setLicenseChanges] = useState(0); const blocker = useBlocker(numChanges !== 0); const [selectedModuleItem, setSelectedModuleItem] = useState(); const [dialogOpen, setDialogOpen] = useState(false); const [restarting, setRestarting] = useState(false); const [restartNeeded, setRestartNeeded] = useState(false); const { data: modules, send: fetchModules, error } = useRequest(EMSESP.readModules, { initialData: [] }); const { send: restartCommand } = useRequest(SystemApi.restart(), { immediate: false }); const restart = async () => { await restartCommand().catch((error: Error) => { toast.error(error.message); }); setRestarting(true); }; const { send: writeModules } = useRequest( (data: Modules) => EMSESP.writeModules(data), { immediate: false } ); const modules_theme = useTheme({ Table: ` --data-table-library_grid-template-columns: 48px 180px 120px 100px repeat(1, minmax(160px, 1fr)) 180px; `, BaseRow: ` font-size: 14px; .td { height: 32px; } `, BaseCell: ` &:nth-of-type(1) { text-align: center; } `, HeaderRow: ` text-transform: uppercase; background-color: black; color: #90CAF9; .th { border-bottom: 1px solid #565656; height: 36px; } `, Row: ` background-color: #1e1e1e; position: relative; cursor: pointer; .td { border-top: 1px solid #565656; border-bottom: 1px solid #565656; } &:hover .td { border-top: 1px solid #177ac9; border-bottom: 1px solid #177ac9; } &:nth-of-type(odd) .td { background-color: #303030; } ` }); const onDialogClose = () => { setDialogOpen(false); }; const onDialogSave = (updatedItem: ModuleItem) => { setDialogOpen(false); updateModuleItem(updatedItem); }; const editModuleItem = useCallback((mi: ModuleItem) => { setSelectedModuleItem(mi); setDialogOpen(true); }, []); const onCancel = async () => { await fetchModules().then(() => { setNumChanges(0); }); }; function hasModulesChanged(mi: ModuleItem) { return mi.enabled !== mi.o_enabled || mi.license !== mi.o_license; } function hasModuleLicenseChanged(mi: ModuleItem) { return mi.license !== mi.o_license; } const updateModuleItem = (updatedItem: ModuleItem) => { updateState('modules', (data: ModuleItem[]) => { const new_data = data.map((mi) => mi.id === updatedItem.id ? { ...mi, ...updatedItem } : mi ); setNumChanges(new_data.filter((mi) => hasModulesChanged(mi)).length); setLicenseChanges(new_data.filter((mi) => hasModuleLicenseChanged(mi)).length); return new_data; }); }; const saveModules = async () => { await writeModules({ modules: modules.map((condensed_mi) => ({ key: condensed_mi.key, enabled: condensed_mi.enabled, license: condensed_mi.license })) }) .then(() => { toast.success(LL.MODULES_UPDATED()); }) .catch((error: Error) => { toast.error(error.message); }) .finally(() => { setNumChanges(0); }); setRestartNeeded(licenseChanges > 0); }; const renderContent = () => { if (!modules) { return ; } useLayoutTitle(LL.MODULES()); if (modules.length === 0) { return ( {LL.MODULES_NONE()} ); } const colorStatus = (status: number) => { if (status === 1) { return
Pending Activation
; } return
Activated
; }; return ( <> {LL.MODULES_DESCRIPTION()} {(tableList: ModuleItem[]) => ( <>
{LL.NAME(0)} Author {LL.VERSION()} Message {LL.STATUS_OF('')}
{tableList.map((mi: ModuleItem) => ( editModuleItem(mi)}> {mi.enabled ? ( ) : ( )} {mi.name} {mi.author} {mi.version} {mi.message} {colorStatus(mi.status)} ))} )}
{restartNeeded ? ( ) : ( {numChanges !== 0 && ( )} )} ); }; return ( {blocker ? : null} {restarting ? : renderContent()} {selectedModuleItem && ( )} ); }; export default Modules;