diff --git a/striker-ui/components/ManageUps/AddUpsInputGroup.tsx b/striker-ui/components/ManageUps/AddUpsInputGroup.tsx new file mode 100644 index 00000000..43e4c749 --- /dev/null +++ b/striker-ui/components/ManageUps/AddUpsInputGroup.tsx @@ -0,0 +1,78 @@ +import { FC, ReactElement, useMemo, useState } from 'react'; + +import CommonUpsInputGroup from './CommonUpsInputGroup'; +import FlexBox from '../FlexBox'; +import SelectWithLabel from '../SelectWithLabel'; +import Spinner from '../Spinner'; +import { BodyText } from '../Text'; + +const AddUpsInputGroup: FC = ({ + loading: isExternalLoading, + upsTemplate, +}) => { + const [inputUpsAgentValue, setInputUpsAgentValue] = useState(''); + + const upsAgentOptions = useMemo( + () => + upsTemplate + ? Object.entries(upsTemplate).map( + ([upsTypeId, { brand, description }]) => ({ + displayValue: ( + + {brand} + {description} + + ), + value: upsTypeId, + }), + ) + : [], + [upsTemplate], + ); + + const pickUpsAgentElement = useMemo( + () => + upsTemplate && ( + { + const newValue = String(rawNewValue); + + setInputUpsAgentValue(newValue); + }} + selectItems={upsAgentOptions} + selectProps={{ + onClearIndicatorClick: () => { + setInputUpsAgentValue(''); + }, + renderValue: (rawValue) => { + const upsTypeId = String(rawValue); + const { brand } = upsTemplate[upsTypeId]; + + return brand; + }, + }} + value={inputUpsAgentValue} + /> + ), + [inputUpsAgentValue, upsAgentOptions, upsTemplate], + ); + const content = useMemo( + () => + isExternalLoading ? ( + + ) : ( + + {pickUpsAgentElement} + {inputUpsAgentValue && } + + ), + [inputUpsAgentValue, isExternalLoading, pickUpsAgentElement], + ); + + return content; +}; + +export default AddUpsInputGroup; diff --git a/striker-ui/components/ManageUps/CommonUpsInputGroup.tsx b/striker-ui/components/ManageUps/CommonUpsInputGroup.tsx new file mode 100644 index 00000000..15477906 --- /dev/null +++ b/striker-ui/components/ManageUps/CommonUpsInputGroup.tsx @@ -0,0 +1,58 @@ +import { FC } from 'react'; + +import Grid from '../Grid'; +import InputWithRef from '../InputWithRef'; +import OutlinedInputWithLabel from '../OutlinedInputWithLabel'; + +const CommonUpsInputGroup: FC = ({ + previous: { + hostName: previousHostName, + ipAddress: previousIpAddress, + upsName: previousUpsName, + } = {}, +}) => ( + <> + + } + required + /> + ), + }, + 'common-ups-input-cell-ip-address': { + children: ( + + } + required + /> + ), + }, + }} + spacing="1em" + /> + + +); + +export default CommonUpsInputGroup; diff --git a/striker-ui/components/ManageUps/ManageUpsPanel.tsx b/striker-ui/components/ManageUps/ManageUpsPanel.tsx new file mode 100644 index 00000000..99b2799a --- /dev/null +++ b/striker-ui/components/ManageUps/ManageUpsPanel.tsx @@ -0,0 +1,88 @@ +import { FC, useMemo, useRef, useState } from 'react'; + +import AddUpsInputGroup from './AddUpsInputGroup'; +import api from '../../lib/api'; +import ConfirmDialog from '../ConfirmDialog'; +import FormDialog from '../FormDialog'; +import handleAPIError from '../../lib/handleAPIError'; +import List from '../List'; +import { Panel, PanelHeader } from '../Panels'; +import Spinner from '../Spinner'; +import { HeaderText } from '../Text'; +import useConfirmDialogProps from '../../hooks/useConfirmDialogProps'; +import useIsFirstRender from '../../hooks/useIsFirstRender'; +import useProtectedState from '../../hooks/useProtectedState'; + +const ManageUpsPanel: FC = () => { + const isFirstRender = useIsFirstRender(); + + const confirmDialogRef = useRef({}); + const formDialogRef = useRef({}); + + const [confirmDialogProps] = useConfirmDialogProps(); + const [formDialogProps, setFormDialogProps] = useConfirmDialogProps(); + const [isEditUpses, setIsEditUpses] = useState(false); + const [isLoadingUpsTemplate, setIsLoadingUpsTemplate] = + useProtectedState(true); + const [upsTemplate, setUpsTemplate] = useProtectedState< + APIUpsTemplate | undefined + >(undefined); + + const listElement = useMemo( + () => ( + { + setFormDialogProps({ + actionProceedText: 'Add', + content: , + titleText: 'Add a UPS', + }); + + formDialogRef.current.setOpen?.call(null, true); + }} + onEdit={() => { + setIsEditUpses((previous) => !previous); + }} + /> + ), + [isEditUpses, setFormDialogProps, upsTemplate], + ); + const panelContent = useMemo( + () => (isLoadingUpsTemplate ? : listElement), + [isLoadingUpsTemplate, listElement], + ); + + if (isFirstRender) { + api + .get('/ups/template') + .then(({ data }) => { + setUpsTemplate(data); + }) + .catch((error) => { + handleAPIError(error); + }) + .finally(() => { + setIsLoadingUpsTemplate(false); + }); + } + + return ( + <> + + + Manage Upses + + {panelContent} + + + + + ); +}; + +export default ManageUpsPanel; diff --git a/striker-ui/components/ManageUps/index.tsx b/striker-ui/components/ManageUps/index.tsx new file mode 100644 index 00000000..b99eceb8 --- /dev/null +++ b/striker-ui/components/ManageUps/index.tsx @@ -0,0 +1,3 @@ +import ManageUpsPanel from './ManageUpsPanel'; + +export default ManageUpsPanel; diff --git a/striker-ui/types/APIUps.d.ts b/striker-ui/types/APIUps.d.ts new file mode 100644 index 00000000..c73d68d4 --- /dev/null +++ b/striker-ui/types/APIUps.d.ts @@ -0,0 +1,7 @@ +type APIUpsTemplate = { + [upsTypeId: string]: { + agent: string; + brand: string; + description: string; + }; +}; diff --git a/striker-ui/types/AddUpsInputGroup.d.ts b/striker-ui/types/AddUpsInputGroup.d.ts new file mode 100644 index 00000000..03a891ec --- /dev/null +++ b/striker-ui/types/AddUpsInputGroup.d.ts @@ -0,0 +1,6 @@ +type AddUpsInputGroupOptionalProps = { + loading?: boolean; + upsTemplate?: APIUpsTemplate; +}; + +type AddUpsInputGroupProps = AddUpsInputGroupOptionalProps; diff --git a/striker-ui/types/CommonUpsInputGroup.d.ts b/striker-ui/types/CommonUpsInputGroup.d.ts new file mode 100644 index 00000000..f8af2908 --- /dev/null +++ b/striker-ui/types/CommonUpsInputGroup.d.ts @@ -0,0 +1,9 @@ +type CommonUpsInputGroupOptionalProps = { + previous?: { + hostName?: string; + ipAddress?: string; + upsName?: string; + }; +}; + +type CommonUpsInputGroupProps = CommonUpsInputGroupOptionalProps;