From a53056ff120320b0b231bc1131af3074437e8a8b Mon Sep 17 00:00:00 2001 From: Tsu-ba-me Date: Fri, 10 Feb 2023 17:45:02 -0500 Subject: [PATCH] fix(striker-ui): add /manage-element page --- striker-ui/pages/manage-element/index.tsx | 181 ++++++++++++++++++++++ striker-ui/types/APIHost.d.ts | 11 +- 2 files changed, 190 insertions(+), 2 deletions(-) create mode 100644 striker-ui/pages/manage-element/index.tsx diff --git a/striker-ui/pages/manage-element/index.tsx b/striker-ui/pages/manage-element/index.tsx new file mode 100644 index 00000000..5add77d6 --- /dev/null +++ b/striker-ui/pages/manage-element/index.tsx @@ -0,0 +1,181 @@ +import Head from 'next/head'; +import { useRouter } from 'next/router'; +import { FC, ReactElement, useEffect, useMemo, useState } from 'react'; + +import api from '../../lib/api'; +import getQueryParam from '../../lib/getQueryParam'; +import Grid from '../../components/Grid'; +import handleAPIError from '../../lib/handleAPIError'; +import Header from '../../components/Header'; +import { Panel } from '../../components/Panels'; +import PrepareHostForm from '../../components/PrepareHostForm'; +import PrepareNetworkForm from '../../components/PrepareNetworkForm'; +import Spinner from '../../components/Spinner'; +import Tab from '../../components/Tab'; +import TabContent from '../../components/TabContent'; +import Tabs from '../../components/Tabs'; +import useIsFirstRender from '../../hooks/useIsFirstRender'; +import useProtect from '../../hooks/useProtect'; +import useProtectedState from '../../hooks/useProtectedState'; + +const MAP_TO_PAGE_TITLE: Record = { + 'prepare-host': 'Prepare Host', + 'prepare-network': 'Prepare Network', + 'manage-fence-devices': 'Manage Fence Devices', + 'manage-upses': 'Manage UPSes', + 'manage-manifests': 'Manage Manifests', +}; +const PAGE_TITLE_LOADING = 'Loading'; +const STEP_CONTENT_GRID_COLUMNS = { md: 8, sm: 6, xs: 1 }; +const STEP_CONTENT_GRID_CENTER_COLUMN = { md: 6, sm: 4, xs: 1 }; + +const PrepareHostTabContent: FC = () => ( + , + ...STEP_CONTENT_GRID_CENTER_COLUMN, + }, + }} + /> +); + +const PrepareNetworkTabContent: FC = () => { + const isFirstRender = useIsFirstRender(); + + const { protect } = useProtect(); + + const [hostOverviewList, setHostOverviewList] = useProtectedState< + APIHostOverviewList | undefined + >(undefined, protect); + const [hostSubTabId, setHostSubTabId] = useState(false); + + const hostSubTabs = useMemo(() => { + let result: ReactElement | undefined; + + if (hostOverviewList) { + const hostOverviewPairs = Object.entries(hostOverviewList); + + result = ( + { + setHostSubTabId(newSubTabId); + }} + orientation="vertical" + value={hostSubTabId} + > + {hostOverviewPairs.map(([hostUUID, { shortHostName }]) => ( + + ))} + + ); + } else { + result = ; + } + + return result; + }, [hostOverviewList, hostSubTabId]); + + if (isFirstRender) { + api + .get('/host', { params: { types: 'node,dr' } }) + .then(({ data }) => { + setHostOverviewList(data); + setHostSubTabId(Object.keys(data)[0]); + }) + .catch((error) => { + handleAPIError(error); + }); + } + + return ( + {hostSubTabs}, + sm: 2, + }, + 'preparenetwork-center-column': { + children: ( + + ), + ...STEP_CONTENT_GRID_CENTER_COLUMN, + }, + }} + /> + ); +}; + +const ManageElement: FC = () => { + const { + isReady, + query: { step: rawStep }, + } = useRouter(); + + const [pageTabId, setPageTabId] = useState(false); + const [pageTitle, setPageTitle] = useState(PAGE_TITLE_LOADING); + + useEffect(() => { + if (isReady) { + let step = getQueryParam(rawStep, { + fallbackValue: 'prepare-host', + }); + + if (!MAP_TO_PAGE_TITLE[step]) { + step = 'prepare-host'; + } + + if (pageTitle === PAGE_TITLE_LOADING) { + setPageTitle(MAP_TO_PAGE_TITLE[step]); + } + + if (!pageTabId) { + setPageTabId(step); + } + } + }, [isReady, pageTabId, pageTitle, rawStep]); + + return ( + <> + + {pageTitle} + +
+ + { + setPageTabId(newTabId); + setPageTitle(MAP_TO_PAGE_TITLE[newTabId]); + }} + orientation={{ xs: 'vertical', sm: 'horizontal' }} + value={pageTabId} + > + + + + + + + + + + + + + {} + + + ); +}; + +export default ManageElement; diff --git a/striker-ui/types/APIHost.d.ts b/striker-ui/types/APIHost.d.ts index c9c87811..efe96f74 100644 --- a/striker-ui/types/APIHost.d.ts +++ b/striker-ui/types/APIHost.d.ts @@ -28,12 +28,19 @@ type APIHostConnectionOverviewList = { type APIHostInstallTarget = 'enabled' | 'disabled'; -type APIHostDetail = { +type APIHostOverview = { hostName: string; hostType: string; hostUUID: string; - installTarget: APIHostInstallTarget; shortHostName: string; }; +type APIHostOverviewList = { + [hostUUID: string]: APIHostOverview; +}; + +type APIHostDetail = APIHostOverview & { + installTarget: APIHostInstallTarget; +}; + type APIDeleteHostConnectionRequestBody = { [key: 'local' | string]: string[] };