import { FC, FormEventHandler, useCallback, useMemo, useRef, useState, } from 'react'; import API_BASE_URL from '../../lib/consts/API_BASE_URL'; import AddUpsInputGroup, { INPUT_ID_UPS_TYPE } from './AddUpsInputGroup'; import api from '../../lib/api'; import { INPUT_ID_UPS_IP, INPUT_ID_UPS_NAME } from './CommonUpsInputGroup'; import ConfirmDialog from '../ConfirmDialog'; import EditUpsInputGroup, { INPUT_ID_UPS_UUID } from './EditUpsInputGroup'; import FlexBox from '../FlexBox'; import FormDialog from '../FormDialog'; import handleAPIError from '../../lib/handleAPIError'; import List from '../List'; import MessageGroup, { MessageGroupForwardedRefContent } from '../MessageGroup'; import { Panel, PanelHeader } from '../Panels'; import periodicFetch from '../../lib/fetchers/periodicFetch'; import Spinner from '../Spinner'; import { BodyText, HeaderText, InlineMonoText, MonoText } from '../Text'; import useConfirmDialogProps from '../../hooks/useConfirmDialogProps'; import useFormUtils from '../../hooks/useFormUtils'; import useIsFirstRender from '../../hooks/useIsFirstRender'; import useProtectedState from '../../hooks/useProtectedState'; type UpsFormData = { upsAgent: string; upsBrand: string; upsIPAddress: string; upsName: string; upsTypeId: string; upsUUID: string; }; const getUpsFormData = ( upsTemplate: APIUpsTemplate, ...[{ target }]: Parameters> ): UpsFormData => { const { elements } = target as HTMLFormElement; const { value: upsName } = elements.namedItem( INPUT_ID_UPS_NAME, ) as HTMLInputElement; const { value: upsIPAddress } = elements.namedItem( INPUT_ID_UPS_IP, ) as HTMLInputElement; const inputUpsTypeId = elements.namedItem(INPUT_ID_UPS_TYPE); let upsAgent = ''; let upsBrand = ''; let upsTypeId = ''; if (inputUpsTypeId) { ({ value: upsTypeId } = inputUpsTypeId as HTMLInputElement); ({ agent: upsAgent, brand: upsBrand } = upsTemplate[upsTypeId]); } const inputUpsUUID = elements.namedItem(INPUT_ID_UPS_UUID); let upsUUID = ''; if (inputUpsUUID) { ({ value: upsUUID } = inputUpsUUID as HTMLInputElement); } return { upsAgent, upsBrand, upsIPAddress, upsName, upsTypeId, upsUUID }; }; const buildConfirmUpsFormData = ({ upsBrand, upsIPAddress, upsName, upsUUID, }: UpsFormData) => { const listItems: Record = { 'ups-brand': { label: 'Brand', value: upsBrand }, 'ups-name': { label: 'Host name', value: upsName }, 'ups-ip-address': { label: 'IP address', value: upsIPAddress }, }; return ( ( {label} {value} )} /> ); }; const ManageUpsPanel: FC = () => { const isFirstRender = useIsFirstRender(); const confirmDialogRef = useRef({}); const formDialogRef = useRef({}); const messageGroupRef = useRef({}); const [confirmDialogProps, setConfirmDialogProps] = useConfirmDialogProps(); const [formDialogProps, setFormDialogProps] = useConfirmDialogProps(); const [isEditUpses, setIsEditUpses] = useState(false); const [isLoadingUpsTemplate, setIsLoadingUpsTemplate] = useProtectedState(true); const [upsTemplate, setUpsTemplate] = useProtectedState< APIUpsTemplate | undefined >(undefined); const { data: upsOverviews, isLoading: isUpsOverviewLoading } = periodicFetch(`${API_BASE_URL}/ups`, { refreshInterval: 60000, }); const formUtils = useFormUtils( [INPUT_ID_UPS_IP, INPUT_ID_UPS_NAME, INPUT_ID_UPS_TYPE], messageGroupRef, ); const { isFormInvalid } = formUtils; const buildEditUpsFormDialogProps = useCallback< (args: APIUpsOverview[string]) => ConfirmDialogProps >( ({ upsAgent, upsIPAddress, upsName, upsUUID }) => { // Determine the type of existing UPS based on its scan agent. // TODO: should identity an existing UPS's type in the DB. const upsTypeId: string = Object.entries(upsTemplate ?? {}).find( ([, { agent }]) => upsAgent === agent, )?.[0] ?? ''; return { actionProceedText: 'Update', content: ( ), onSubmitAppend: (event) => { if (!upsTemplate) { return; } const editData = getUpsFormData(upsTemplate, event); const { upsName: newUpsName } = editData; setConfirmDialogProps({ actionProceedText: 'Update', content: buildConfirmUpsFormData(editData), titleText: ( Update{' '} {newUpsName}{' '} with the following data? ), }); confirmDialogRef.current.setOpen?.call(null, true); }, titleText: ( Update UPS{' '} {upsName} ), }; }, [formUtils, setConfirmDialogProps, upsTemplate], ); const addUpsFormDialogProps = useMemo( () => ({ actionProceedText: 'Add', content: ( ), onSubmitAppend: (event) => { if (!upsTemplate) { return; } const addData = getUpsFormData(upsTemplate, event); const { upsBrand } = addData; setConfirmDialogProps({ actionProceedText: 'Add', content: buildConfirmUpsFormData(addData), titleText: ( Add a{' '} {upsBrand} UPS with the following data? ), }); confirmDialogRef.current.setOpen?.call(null, true); }, titleText: 'Add a UPS', }), [formUtils, setConfirmDialogProps, upsTemplate], ); const listElement = useMemo( () => ( { setFormDialogProps(addUpsFormDialogProps); formDialogRef.current.setOpen?.call(null, true); }} onEdit={() => { setIsEditUpses((previous) => !previous); }} onItemClick={(value) => { setFormDialogProps(buildEditUpsFormDialogProps(value)); formDialogRef.current.setOpen?.call(null, true); }} renderListItem={(upsUUID, { upsAgent, upsIPAddress, upsName }) => ( {upsName} agent="{upsAgent}" ip="{upsIPAddress}" )} /> ), [ addUpsFormDialogProps, buildEditUpsFormDialogProps, isEditUpses, setFormDialogProps, upsOverviews, ], ); const panelContent = useMemo( () => isLoadingUpsTemplate || isUpsOverviewLoading ? : listElement, [isLoadingUpsTemplate, isUpsOverviewLoading, listElement], ); if (isFirstRender) { api .get('/ups/template') .then(({ data }) => { setUpsTemplate(data); }) .catch((error) => { handleAPIError(error); }) .finally(() => { setIsLoadingUpsTemplate(false); }); } return ( <> Manage UPSes {panelContent} } proceedButtonProps={{ disabled: isFormInvalid }} /> ); }; export default ManageUpsPanel;