import { Box } from '@mui/material'; import { FC, FormEventHandler, ReactElement, useMemo, useRef, useState, } from 'react'; import API_BASE_URL from '../lib/consts/API_BASE_URL'; import AddFenceInputGroup from './AddFenceInputGroup'; import api from '../lib/api'; import { ID_SEPARATOR } from './CommonFenceInputGroup'; import ConfirmDialog from './ConfirmDialog'; import EditFenceInputGroup from './EditFenceInputGroup'; import FlexBox from './FlexBox'; import handleAPIError from '../lib/handleAPIError'; import List from './List'; import { Panel, PanelHeader } from './Panels'; import periodicFetch from '../lib/fetchers/periodicFetch'; import Spinner from './Spinner'; import { BodyText, HeaderText, InlineMonoText, MonoText, SmallText, } from './Text'; import useIsFirstRender from '../hooks/useIsFirstRender'; import useProtectedState from '../hooks/useProtectedState'; import SensitiveText from './Text/SensitiveText'; type FormFenceParameterData = { fenceAgent: string; fenceName: string; parameterInputs: { [parameterInputId: string]: { isParameterSensitive: boolean; parameterId: string; parameterType: string; parameterValue: string; }; }; }; const fenceParameterBooleanToString = (value: boolean) => (value ? '1' : '0'); const getFormFenceParameters = ( fenceTemplate: APIFenceTemplate, ...[{ target }]: Parameters> ) => { const { elements } = target as HTMLFormElement; return Object.values(elements).reduce( (previous, formElement) => { const { id: inputId } = formElement; const reExtract = new RegExp(`^(fence[^-]+)${ID_SEPARATOR}([^\\s]+)$`); const matched = inputId.match(reExtract); if (matched) { const [, fenceId, parameterId] = matched; previous.fenceAgent = fenceId; const inputElement = formElement as HTMLInputElement; const { checked, dataset: { sensitive: rawSensitive }, value, } = inputElement; if (parameterId === 'name') { previous.fenceName = value; } const { [fenceId]: { parameters: { [parameterId]: { content_type: parameterType = 'string' } = {}, }, }, } = fenceTemplate; previous.parameterInputs[inputId] = { isParameterSensitive: rawSensitive === 'true', parameterId, parameterType, parameterValue: parameterType === 'boolean' ? fenceParameterBooleanToString(checked) : value, }; } return previous; }, { fenceAgent: '', fenceName: '', parameterInputs: {} }, ); }; const buildConfirmFenceParameters = ( parameterInputs: FormFenceParameterData['parameterInputs'], ) => ( { let textElement: ReactElement; if (parameterValue) { textElement = isParameterSensitive ? ( {parameterValue} ) : ( {parameterValue} ); } else { textElement = none; } return ( {parameterId} {textElement} ); }} /> ); const ManageFencesPanel: FC = () => { const isFirstRender = useIsFirstRender(); const confirmDialogRef = useRef({}); const formDialogRef = useRef({}); const [confirmDialogProps, setConfirmDialogProps] = useState({ actionProceedText: '', content: '', titleText: '', }); const [formDialogProps, setFormDialogProps] = useState({ actionProceedText: '', content: '', titleText: '', }); const [fenceTemplate, setFenceTemplate] = useProtectedState< APIFenceTemplate | undefined >(undefined); const [isEditFences, setIsEditFences] = useState(false); const [isLoadingFenceTemplate, setIsLoadingFenceTemplate] = useProtectedState(true); const { data: fenceOverviews, isLoading: isFenceOverviewsLoading } = periodicFetch(`${API_BASE_URL}/fence`, { refreshInterval: 60000, }); const listElement = useMemo( () => ( { setFormDialogProps({ actionProceedText: 'Add', content: , onSubmitAppend: (event) => { if (!fenceTemplate) { return; } const addData = getFormFenceParameters(fenceTemplate, event); setConfirmDialogProps({ actionProceedText: 'Add', content: buildConfirmFenceParameters(addData.parameterInputs), titleText: ( Add a{' '} {addData.fenceAgent} {' '} fence device with the following parameters? ), }); confirmDialogRef.current.setOpen?.call(null, true); }, titleText: 'Add a fence device', }); formDialogRef.current.setOpen?.call(null, true); }} onEdit={() => { setIsEditFences((previous) => !previous); }} onItemClick={({ fenceAgent: fenceId, fenceName, fenceParameters }) => { setFormDialogProps({ actionProceedText: 'Update', content: ( ), onSubmitAppend: (event) => { if (!fenceTemplate) { return; } const editData = getFormFenceParameters(fenceTemplate, event); setConfirmDialogProps({ actionProceedText: 'Update', content: buildConfirmFenceParameters(editData.parameterInputs), titleText: ( Update{' '} {editData.fenceName} {' '} fence device with the following parameters? ), }); confirmDialogRef.current.setOpen?.call(null, true); }, titleText: ( Update fence device{' '} {fenceName}{' '} parameters ), }); formDialogRef.current.setOpen?.call(null, true); }} renderListItem={( fenceUUID, { fenceAgent, fenceName, fenceParameters }, ) => ( {fenceName} {Object.entries(fenceParameters).reduce( (previous, [parameterId, parameterValue]) => `${previous} ${parameterId}="${parameterValue}"`, fenceAgent, )} )} /> ), [fenceOverviews, fenceTemplate, isEditFences], ); const panelContent = useMemo( () => isLoadingFenceTemplate || isFenceOverviewsLoading ? ( ) : ( listElement ), [isFenceOverviewsLoading, isLoadingFenceTemplate, listElement], ); if (isFirstRender) { api .get(`/fence/template`) .then(({ data }) => { setFenceTemplate(data); }) .catch((error) => { handleAPIError(error); }) .finally(() => { setIsLoadingFenceTemplate(false); }); } return ( <> Manage fence devices {panelContent} ); }; export default ManageFencesPanel;