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 useChecklist from '../../hooks/useChecklist'; import useConfirmDialogProps from '../../hooks/useConfirmDialogProps'; import useFormUtils from '../../hooks/useFormUtils'; import useIsFirstRender from '../../hooks/useIsFirstRender'; import useProtectedState from '../../hooks/useProtectedState'; type UpsFormData = { agent: string; brand: string; ipAddress: string; name: string; typeId: string; uuid: string; }; const getFormData = ( upsTemplate: APIUpsTemplate, ...[{ target }]: Parameters> ): UpsFormData => { const { elements } = target as HTMLFormElement; const { value: name } = elements.namedItem( INPUT_ID_UPS_NAME, ) as HTMLInputElement; const { value: ipAddress } = elements.namedItem( INPUT_ID_UPS_IP, ) as HTMLInputElement; const inputUpsTypeId = elements.namedItem(INPUT_ID_UPS_TYPE); let agent = ''; let brand = ''; let typeId = ''; if (inputUpsTypeId) { ({ value: typeId } = inputUpsTypeId as HTMLInputElement); ({ agent, brand } = upsTemplate[typeId]); } const inputUpsUUID = elements.namedItem(INPUT_ID_UPS_UUID); let uuid = ''; if (inputUpsUUID) { ({ value: uuid } = inputUpsUUID as HTMLInputElement); } return { agent, brand, ipAddress, name, typeId, uuid, }; }; const buildConfirmUpsFormData = ({ brand, ipAddress, name, uuid, }: UpsFormData) => { const listItems: Record = { 'ups-brand': { label: 'Brand', value: brand }, 'ups-name': { label: 'Host name', value: name }, 'ups-ip-address': { label: 'IP address', value: ipAddress }, }; 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, isFormSubmitting, submitForm } = formUtils; const { buildDeleteDialogProps, checks, getCheck, hasChecks, setCheck } = useChecklist({ list: upsOverviews, }); 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 = getFormData(upsTemplate, event); const { name: newUpsName } = editData; setConfirmDialogProps({ actionProceedText: 'Update', content: buildConfirmUpsFormData(editData), onProceedAppend: () => { submitForm({ body: editData, getErrorMsg: (parentMsg) => ( <>Failed to update UPS. {parentMsg} ), method: 'put', successMsg: `Successfully updated UPS ${upsName}`, url: `/ups/${upsUUID}`, }); }, titleText: ( Update{' '} {newUpsName}{' '} with the following data? ), }); confirmDialogRef.current.setOpen?.call(null, true); }, titleText: ( Update UPS{' '} {upsName} ), }; }, [formUtils, setConfirmDialogProps, submitForm, upsTemplate], ); const addUpsFormDialogProps = useMemo( () => ({ actionProceedText: 'Add', content: ( ), onSubmitAppend: (event) => { if (!upsTemplate) { return; } const addData = getFormData(upsTemplate, event); const { brand: upsBrand, name: upsName } = addData; setConfirmDialogProps({ actionProceedText: 'Add', content: buildConfirmUpsFormData(addData), onProceedAppend: () => { submitForm({ body: addData, getErrorMsg: (parentMsg) => <>Failed to add UPS. {parentMsg}, method: 'post', successMsg: `Successfully added UPS ${upsName}`, url: '/ups', }); }, titleText: ( Add a{' '} {upsBrand} UPS with the following data? ), }); confirmDialogRef.current.setOpen?.call(null, true); }, titleText: 'Add a UPS', }), [formUtils, setConfirmDialogProps, submitForm, upsTemplate], ); const listElement = useMemo( () => ( { setFormDialogProps(addUpsFormDialogProps); formDialogRef.current.setOpen?.call(null, true); }} onDelete={() => { setConfirmDialogProps( buildDeleteDialogProps({ getConfirmDialogTitle: (count) => `Delete ${count} UPSes?`, onProceedAppend: () => { submitForm({ body: { uuids: checks }, getErrorMsg: (parentMsg) => ( <>Failed to delete UPS(es). {parentMsg} ), method: 'delete', url: '/ups', }); }, renderEntry: ({ key }) => ( {upsOverviews?.[key].upsName} ), }), ); confirmDialogRef.current.setOpen?.call(null, true); }} onEdit={() => { setIsEditUpses((previous) => !previous); }} onItemCheckboxChange={(key, event, checked) => { setCheck(key, checked); }} onItemClick={(value) => { setFormDialogProps(buildEditUpsFormDialogProps(value)); formDialogRef.current.setOpen?.call(null, true); }} renderListItemCheckboxState={(key) => getCheck(key)} renderListItem={(upsUUID, { upsAgent, upsIPAddress, upsName }) => ( {upsName} agent="{upsAgent}" ip="{upsIPAddress}" )} /> ), [ addUpsFormDialogProps, buildDeleteDialogProps, buildEditUpsFormDialogProps, checks, getCheck, hasChecks, isEditUpses, setCheck, setConfirmDialogProps, setFormDialogProps, submitForm, upsOverviews, ], ); const panelContent = useMemo( () => isLoadingUpsTemplate || isUpsOverviewLoading ? : listElement, [isLoadingUpsTemplate, isUpsOverviewLoading, listElement], ); const messageArea = useMemo( () => ( ), [], ); if (isFirstRender) { api .get('/ups/template') .then(({ data }) => { setUpsTemplate(data); }) .catch((error) => { handleAPIError(error); }) .finally(() => { setIsLoadingUpsTemplate(false); }); } return ( <> Manage UPSes {panelContent} ); }; export default ManageUpsPanel;