diff --git a/src/components/IOC/AdministerUndeployment/AdministerUndeployment.tsx b/src/components/IOC/AdministerUndeployment/AdministerUndeployment.tsx index 573f78003800a03e553b792bbcc7242bbb3e5ddd..7ec264eb7fb65a7d024e1fe14fcc4eedb9ca43de 100644 --- a/src/components/IOC/AdministerUndeployment/AdministerUndeployment.tsx +++ b/src/components/IOC/AdministerUndeployment/AdministerUndeployment.tsx @@ -12,35 +12,39 @@ import { ConfirmationDialog } from "@ess-ics/ce-ui-common"; import { AccessControl } from "../../auth/AccessControl"; import { useUnDeployInDbMutation } from "../../../store/enhancedApi"; import { ApiAlertError } from "../../common/Alerts/ApiAlertError"; +import { IocDetails } from "../../../store/deployApi"; + +interface AdministerUndeploymentProps { + ioc: IocDetails; + buttonDisabled: boolean; + setButtonDisabled: (value: boolean) => void; +} export const AdministerUndeployment = ({ ioc, buttonDisabled, setButtonDisabled -}) => { +}: AdministerUndeploymentProps) => { const navigate = useNavigate(); - const [error, setError] = useState(); const [open, setOpen] = useState(false); const [ undeployInDB, - { value: undeployResponse, error: undeployInDBError, isLoading } + { data: undeployResponse, error: undeployInDBError, isLoading } ] = useUnDeployInDbMutation(); - useEffect(() => { - setError(undeployInDBError); - }, [undeployInDBError]); - const onClose = useCallback(() => { setOpen(false); }, []); const onConfirm = useCallback(() => { - setButtonDisabled(true); - undeployInDB({ - iocId: ioc?.id - }); - setButtonDisabled(false); + if (ioc.id) { + setButtonDisabled(true); + undeployInDB({ + iocId: ioc.id + }); + setButtonDisabled(false); + } }, [undeployInDB, ioc, setButtonDisabled]); useEffect(() => { @@ -110,15 +114,13 @@ export const AdministerUndeployment = ({ container spacing={1} > - {error ? ( + {undeployInDBError && ( <Grid item xs={12} > - <ApiAlertError error={error} /> + <ApiAlertError error={undeployInDBError} /> </Grid> - ) : ( - <></> )} <Grid item diff --git a/src/components/IOC/ChangeHostAdmin/ChangeHostAdmin.tsx b/src/components/IOC/ChangeHostAdmin/ChangeHostAdmin.tsx index 21ff327c131f8a8c1ec3770a304fa988a656ed24..b95635715baa8bb2a509412243f4464582d5f61c 100644 --- a/src/components/IOC/ChangeHostAdmin/ChangeHostAdmin.tsx +++ b/src/components/IOC/ChangeHostAdmin/ChangeHostAdmin.tsx @@ -1,4 +1,11 @@ -import { useState, useEffect, useCallback, useMemo } from "react"; +import { + useState, + useEffect, + useCallback, + useMemo, + type SetStateAction, + type Dispatch +} from "react"; import { ConfirmationDialog } from "@ess-ics/ce-ui-common"; import { Box, @@ -11,23 +18,38 @@ import { Autocomplete } from "@mui/material"; import { AccessControl } from "../../auth/AccessControl"; -import { useTypingTimer } from "../../common/SearchBoxFilter/TypingTimer"; -import { useLazyListHostsQuery } from "../../../store/deployApi"; +import { useTypingTimer } from "../../../hooks/useTypingTimer"; +import { + HostInfoWithId, + IocDetails, + useLazyListHostsQuery +} from "../../../store/deployApi"; import { useUpdateActiveDeploymentHostMutation } from "../../../store/enhancedApi"; import { ApiAlertError } from "../../common/Alerts/ApiAlertError"; -export const ChangeHostAdmin = ({ ioc, buttonDisabled, setButtonDisabled }) => { +interface ChangeHostAdminProps { + ioc: IocDetails; + buttonDisabled: boolean; + setButtonDisabled: Dispatch<SetStateAction<boolean>>; +} + +export const ChangeHostAdmin = ({ + ioc, + buttonDisabled, + setButtonDisabled +}: ChangeHostAdminProps) => { const initHost = useMemo( () => ({ - fqdn: ioc.activeDeployment.host.fqdn, - hostId: ioc.activeDeployment.host.hostId + fqdn: ioc?.activeDeployment?.host?.fqdn, + hostId: ioc?.activeDeployment?.host?.hostId }), - [ioc.activeDeployment.host] + [ioc?.activeDeployment?.host] ); - const [host, setHost] = useState(initHost); - const [error, setError] = useState(); + const [host, setHost] = useState<HostInfoWithId | null>(initHost); const [open, setOpen] = useState(false); - const [query, onHostKeyUp] = useTypingTimer({ interval: 500 }); + const { value: query, onKeyUp: onHostKeyUp } = useTypingTimer({ + interval: 500 + }); const [getHosts, { data: hosts, isLoading: loadingHosts }] = useLazyListHostsQuery(); @@ -36,7 +58,7 @@ export const ChangeHostAdmin = ({ ioc, buttonDisabled, setButtonDisabled }) => { useUpdateActiveDeploymentHostMutation(); const noModification = useCallback( - () => !host || host?.hostId === ioc.activeDeployment.host.hostId, + () => !host || host?.hostId === ioc?.activeDeployment?.host?.hostId, [host, ioc] ); @@ -46,27 +68,29 @@ export const ChangeHostAdmin = ({ ioc, buttonDisabled, setButtonDisabled }) => { }, [setOpen, initHost]); const onConfirm = useCallback(() => { - setButtonDisabled(true); - updateHost({ - iocId: ioc.id, - updateHostRequest: { - hostId: host?.hostId - } - }); + if (ioc.id) { + setButtonDisabled(true); + updateHost({ + iocId: ioc.id, + updateHostRequest: { + hostId: host?.hostId + } + }); + } }, [updateHost, ioc, host?.hostId, setButtonDisabled]); useEffect(() => { if (updateHostError) { setButtonDisabled(false); - setError(updateHostError); } - }, [updateHostError, setError, setButtonDisabled]); + }, [updateHostError, setButtonDisabled]); useEffect(() => { if (updatedIoc) { setHost({ - fqdn: updatedIoc.activeDeployment.host.fqdn, - hostId: updatedIoc.activeDeployment.host.hostId + // Schema change related. Wait until all files have been migrated + fqdn: updatedIoc?.activeDeployment?.host?.fqdn, + hostId: updatedIoc?.activeDeployment?.host?.hostId }); setButtonDisabled(false); } @@ -170,12 +194,12 @@ export const ChangeHostAdmin = ({ ioc, buttonDisabled, setButtonDisabled }) => { }} /> )} - onChange={(_event, value) => { + onChange={(_event, value: HostInfoWithId | null) => { setHost(value); }} onInputChange={(event) => { if (event) { - onHostKeyUp(event.nativeEvent); + onHostKeyUp(event as React.KeyboardEvent<HTMLInputElement>); } }} autoSelect @@ -186,9 +210,9 @@ export const ChangeHostAdmin = ({ ioc, buttonDisabled, setButtonDisabled }) => { item xs={12} > - {error && ( + {updateHostError && ( <Box sx={{ marginBottom: 1 }}> - <ApiAlertError error={error} /> + <ApiAlertError error={updateHostError} /> </Box> )} <Tooltip title={disabledButtonTitle}> diff --git a/src/components/IOC/CreateIOC/CreateIOC.tsx b/src/components/IOC/CreateIOC/CreateIOC.tsx index 16514a7032db10fabc9102278367b3fa605616dd..5a96e6fc94d3b4d0f9d1759a0eeaa5665a204a57 100644 --- a/src/components/IOC/CreateIOC/CreateIOC.tsx +++ b/src/components/IOC/CreateIOC/CreateIOC.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useEffect, useState, type KeyboardEvent } from "react"; import { useNavigate } from "react-router-dom"; import { RootPaper } from "@ess-ics/ce-ui-common"; import { @@ -13,9 +13,11 @@ import { import { RepositoryOptions } from "./RepositoryOptions"; import { WITHOUT_REPO } from "./RepositoryType"; import { RepositoryName } from "./RepositoryName"; -import { useTypingTimer } from "../../common/SearchBoxFilter/TypingTimer"; +import { useTypingTimer } from "../../../hooks/useTypingTimer"; import { useCustomSnackbar } from "../../common/snackbar"; import { + type GitProject, + type NameResponse, useCreateIocMutation, useLazyFetchIocByNameQuery, useLazyListProjectsQuery @@ -24,11 +26,15 @@ import { ApiAlertError } from "../../common/Alerts/ApiAlertError"; export function CreateIOC() { const navigate = useNavigate(); - const showSnackBar = useCustomSnackbar(); - const [namingEntity, setNamingEntity] = useState({}); - const [gitProject, setGitProject] = useState({}); - const [nameQuery, onNameKeyUp] = useTypingTimer({ interval: 500 }); - const [repoQuery, onRepoKeyUp] = useTypingTimer({ interval: 500 }); + const { showSuccess } = useCustomSnackbar(); + const [namingEntity, setNamingEntity] = useState<NameResponse | null>(null); + const [gitProject, setGitProject] = useState<GitProject | null>(null); + const { value: nameQuery, onKeyUp: onNameKeyUp } = useTypingTimer({ + interval: 500 + }); + const { value: repoQuery, onKeyUp: onRepoKeyUp } = useTypingTimer({ + interval: 500 + }); const [selectedRepoOption, setSelectedRepoOption] = useState(WITHOUT_REPO); const [repoName, setRepoName] = useState(""); @@ -43,18 +49,19 @@ export function CreateIOC() { useLazyFetchIocByNameQuery(); // create the ioc on form submit - const onSubmit = (event) => { + const onSubmit = (event: React.FormEvent<HTMLFormElement>) => { event.preventDefault(); - createIoc({ + const params = { createIoc: { - gitProjectId: gitProject.id, + gitProjectId: gitProject?.id, namingUuid: namingEntity ? namingEntity.uuid : undefined, repository_name: repoName ? repoName : undefined } - }); + }; + createIoc(params); }; - const handleSelectRepoOption = (option) => { + const handleSelectRepoOption = (option: string) => { setSelectedRepoOption(option); setGitProject({}); setRepoName(""); @@ -63,7 +70,7 @@ export function CreateIOC() { // fetch new names when name query changes useEffect(() => { if (nameQuery) { - getNames(nameQuery); + getNames({ iocName: nameQuery }); } }, [nameQuery, getNames]); @@ -76,15 +83,14 @@ export function CreateIOC() { // navigate home once ioc created useEffect(() => { if (ioc) { - showSnackBar( + showSuccess( selectedRepoOption === WITHOUT_REPO ? "IOC created" - : "IOC created with a repository", - "success" + : "IOC created with a repository" ); navigate(`/iocs/${ioc.id}?&tab=Management`); } - }, [ioc, showSnackBar, selectedRepoOption, navigate]); + }, [ioc, showSuccess, selectedRepoOption, navigate]); return ( <RootPaper @@ -135,7 +141,11 @@ export function CreateIOC() { /> )} onChange={(_, value) => setNamingEntity(value)} - onInputChange={(event) => event && onNameKeyUp(event.nativeEvent)} + onInputChange={(event) => { + if (event) { + onNameKeyUp(event as KeyboardEvent<HTMLInputElement>); + } + }} autoSelect /> {selectedRepoOption === WITHOUT_REPO ? ( @@ -150,7 +160,11 @@ export function CreateIOC() { return option?.url ?? ""; }} onChange={(_, value) => setGitProject(value)} - onInputChange={(event) => event && onRepoKeyUp(event.nativeEvent)} + onInputChange={(event) => { + if (event) { + onRepoKeyUp(event as KeyboardEvent<HTMLInputElement>); + } + }} renderInput={(params) => ( <TextField {...params} diff --git a/src/components/IOC/CreateIOC/RepositoryName.tsx b/src/components/IOC/CreateIOC/RepositoryName.tsx index 7bfaabaccb7070652ecbed038d9b3cc2108ef821..9e66d142218f702b8301b2b5ea941f738c78936c 100644 --- a/src/components/IOC/CreateIOC/RepositoryName.tsx +++ b/src/components/IOC/CreateIOC/RepositoryName.tsx @@ -1,4 +1,4 @@ -import { useState, useCallback } from "react"; +import { useState, useCallback, type ChangeEvent } from "react"; import { Box, Stack, TextField, Typography } from "@mui/material"; import { string, func } from "prop-types"; @@ -13,11 +13,19 @@ const propTypes = { // ends with an alpha numeric character (a-Z 0-9) const REPO_NAME_REGEX = "^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?$"; -export const RepositoryName = ({ repoName, onRepoNameChange }) => { +interface RepositoryNameProps { + repoName: string; + onRepoNameChange: (name: string) => void; +} + +export const RepositoryName = ({ + repoName, + onRepoNameChange +}: RepositoryNameProps) => { const [valid, setValid] = useState(repoName || repoName.length === 0); const handleNameChange = useCallback( - (e) => { + (e: ChangeEvent<HTMLInputElement>) => { const reg = new RegExp(REPO_NAME_REGEX); const hasValidCharacters = reg.test(e.target.value); const hasValidLength = diff --git a/src/components/IOC/CreateIOC/RepositoryOptions.tsx b/src/components/IOC/CreateIOC/RepositoryOptions.tsx index b5ea8a37f2036edfed1d2119645fb35d4b481fc1..f707c018ce0698196988d745bb1b641835b716c0 100644 --- a/src/components/IOC/CreateIOC/RepositoryOptions.tsx +++ b/src/components/IOC/CreateIOC/RepositoryOptions.tsx @@ -12,10 +12,15 @@ const propTypes = { onSelectRepoOption: func }; +interface RepositoryOptionsProps { + selectedRepoOption: string; + onSelectRepoOption: (option: string) => void; +} + export const RepositoryOptions = ({ selectedRepoOption, onSelectRepoOption -}) => ( +}: RepositoryOptionsProps) => ( <FormControl> <RadioGroup row diff --git a/src/components/IOC/DeployIOC/DeployIOC.tsx b/src/components/IOC/DeployIOC/DeployIOC.tsx index ab71e1b26aaa01b8f0c3ecf24dac76a421e4547a..8ad65e3b91591c56140c4a53e96c58a97e819501 100644 --- a/src/components/IOC/DeployIOC/DeployIOC.tsx +++ b/src/components/IOC/DeployIOC/DeployIOC.tsx @@ -1,36 +1,38 @@ -import { useState, useEffect } from "react"; import { Navigate } from "react-router-dom"; import { IOCDeployDialog } from "../IOCDeployDialog"; import { useStartJobMutation } from "../../../store/enhancedApi"; +import { IocDetails } from "../../../store/deployApi"; +import { DeployIocFormDefaults } from "../../../types/common"; + +interface DeployIOCProps { + open: boolean; + setOpen: (value: boolean) => void; + ioc: IocDetails; + hasActiveDeployment: boolean; + deployIocFormDefaults?: DeployIocFormDefaults; +} export function DeployIOC({ open, setOpen, ioc, hasActiveDeployment, - deployIocFormDefaults = {} -}) { - const [error, setError] = useState(null); - const [deploy, { data: deployment, error: deployError, isLoading }] = + deployIocFormDefaults +}: DeployIOCProps) { + const [deploy, { data: deployment, error: deployError, isLoading, reset }] = useStartJobMutation(); - useEffect(() => { - if (deployError) { - setError(deployError); - } - }, [deployError]); - if (!deployment) { return ( <IOCDeployDialog open={open} setOpen={setOpen} - iocId={ioc.id} + iocId={ioc?.id} submitCallback={deploy} deployIocFormDefaults={deployIocFormDefaults} hasActiveDeployment={hasActiveDeployment} - error={error} - resetError={() => setError(null)} + error={deployError} + resetError={() => reset()} buttonDisabled={ioc.operationInProgress || isLoading} /> ); @@ -38,7 +40,7 @@ export function DeployIOC({ return ( <Navigate to={`/jobs/${deployment.id}`} - push + replace /> ); } diff --git a/src/components/IOC/IOCAdmin/IOCAdmin.tsx b/src/components/IOC/IOCAdmin/IOCAdmin.tsx index c739550dc43fd11beb1fd68245d6178215d784a0..b50f46e36df696123ed1aa3fca823f2c490cbf22 100644 --- a/src/components/IOC/IOCAdmin/IOCAdmin.tsx +++ b/src/components/IOC/IOCAdmin/IOCAdmin.tsx @@ -3,15 +3,18 @@ import { AdministerUndeployment } from "../AdministerUndeployment"; import { IOCDelete } from "../IOCDelete"; import { IOCDetailAdmin } from "../IOCDetailAdmin"; import { ChangeHostAdmin } from "../ChangeHostAdmin"; +import { IocDetails } from "../../../store/deployApi"; -export const IOCAdmin = ({ ioc }) => { - const [buttonDisabled, setButtonDisabled] = useState(ioc.operationInProgress); +export const IOCAdmin = ({ ioc }: { ioc: IocDetails }) => { + const [buttonDisabled, setButtonDisabled] = useState( + ioc.operationInProgress || false + ); return ( <> <IOCDetailAdmin ioc={ioc} buttonDisabled={buttonDisabled} - setButtonDisabled={(value) => setButtonDisabled(value)} + setButtonDisabled={(value: boolean) => setButtonDisabled(value)} /> {ioc.activeDeployment ? ( <ChangeHostAdmin @@ -28,7 +31,7 @@ export const IOCAdmin = ({ ioc }) => { <IOCDelete ioc={ioc} buttonDisabled={buttonDisabled} - setButtonDisabled={(value) => setButtonDisabled(value)} + setButtonDisabled={(value: boolean) => setButtonDisabled(value)} /> </> ); diff --git a/src/components/IOC/IOCDelete/IOCDelete.tsx b/src/components/IOC/IOCDelete/IOCDelete.tsx index ff5f991a7a5957519726e5b4d00231a5b10a402a..c351ead594c2e8bfe91526e1e46f32b67424a39c 100644 --- a/src/components/IOC/IOCDelete/IOCDelete.tsx +++ b/src/components/IOC/IOCDelete/IOCDelete.tsx @@ -5,11 +5,22 @@ import { ConfirmDangerActionDialog } from "@ess-ics/ce-ui-common"; import { useCustomSnackbar } from "../../common/snackbar"; import { AccessControl } from "../../auth/AccessControl"; import { useDeleteIocMutation } from "../../../store/enhancedApi"; -import { getErrorMessage } from "../../common/Alerts/AlertsData"; +import { IocDetails } from "../../../store/deployApi"; +import { getErrorState } from "../../common/Alerts/AlertsData"; -export const IOCDelete = ({ ioc, buttonDisabled, setButtonDisabled }) => { +interface IOCDeleteProps { + ioc: IocDetails; + buttonDisabled: boolean; + setButtonDisabled: (value: boolean) => void; +} + +export const IOCDelete = ({ + ioc, + buttonDisabled, + setButtonDisabled +}: IOCDeleteProps) => { const navigate = useNavigate(); - const showSnackBar = useCustomSnackbar(); + const { showSuccess } = useCustomSnackbar(); const [open, setOpen] = useState(false); const [deleteIOC, { isLoading, error, reset }] = useDeleteIocMutation(); @@ -32,22 +43,24 @@ export const IOCDelete = ({ ioc, buttonDisabled, setButtonDisabled }) => { }, [error, setButtonDisabled]); const onConfirm = useCallback(() => { - reset(); - setButtonDisabled(true); - deleteIOC({ iocId: ioc.id }) - .unwrap() - .then(() => { - showSnackBar(`IOC ${ioc.namingName} deleted`, "success"); - navigate("/iocs", { replace: true }); - }) - .catch(() => setButtonDisabled(false)); + if (ioc.id) { + reset(); + setButtonDisabled(true); + deleteIOC({ iocId: ioc.id }) + .unwrap() + .then(() => { + showSuccess(`IOC ${ioc.namingName} deleted`); + navigate("/iocs", { replace: true }); + }) + .catch(() => setButtonDisabled(false)); + } }, [ reset, setButtonDisabled, deleteIOC, ioc.id, ioc.namingName, - showSnackBar, + showSuccess, navigate ]); @@ -62,7 +75,7 @@ export const IOCDelete = ({ ioc, buttonDisabled, setButtonDisabled }) => { onClose={onClose} onConfirm={onConfirm} isLoading={isLoading} - error={error && getErrorMessage(error)} + error={error && getErrorState(error).message} /> <Box sx={{ pt: 2 }}> diff --git a/src/components/IOC/IOCDeployDialog/IOCDeployDialog.tsx b/src/components/IOC/IOCDeployDialog/IOCDeployDialog.tsx index e010c963fccc3f79f1bcf0cd65b71cb20e6bf91f..456c653d30955adc0ae9dc69a93088a5671abe26 100644 --- a/src/components/IOC/IOCDeployDialog/IOCDeployDialog.tsx +++ b/src/components/IOC/IOCDeployDialog/IOCDeployDialog.tsx @@ -21,30 +21,19 @@ import { useLazyListHostsQuery, useLazyListTagsAndCommitIdsQuery } from "../../../store/deployApi"; -import { getErrorMessage } from "../../common/Helper"; -import { ApiError } from "../../../types/common"; +import { getErrorState } from "../../common/Alerts/AlertsData"; import { ApiAlertError } from "../../common/Alerts/ApiAlertError"; +import { useStartJobMutation } from "../../../store/enhancedApi"; +import type { ApiError, DeployIocFormDefaults } from "../../../types/common"; -interface DeployIocFormDefaults { - name: string; - description: string; - git: string; - gitProjectId: number; - reference?: string; - short_reference?: string; - netBoxHost?: { - fqdn: string; - hostId: string; - }; -} - +export type StartJobMutationTrigger = ReturnType<typeof useStartJobMutation>[0]; interface IOCDeployDialogProps { + iocId?: number; open: boolean; setOpen: (open: boolean) => void; - iocId: string; - submitCallback: (arg: object) => void; + submitCallback: StartJobMutationTrigger; hasActiveDeployment: boolean; - deployIocFormDefaults: DeployIocFormDefaults; + deployIocFormDefaults?: DeployIocFormDefaults; error: ApiError; resetError: () => void; buttonDisabled: boolean; @@ -91,17 +80,17 @@ export function IOCDeployDialog({ const initHost = useMemo( () => ({ - fqdn: deployIocFormDefaults.netBoxHost?.fqdn, - hostId: deployIocFormDefaults.netBoxHost?.hostId + fqdn: deployIocFormDefaults?.netBoxHost?.fqdn, + hostId: deployIocFormDefaults?.netBoxHost?.hostId }), - [deployIocFormDefaults.netBoxHost] + [deployIocFormDefaults?.netBoxHost] ); const [host, setHost] = useState(initHost); const [query, setQuery] = useState(""); - const [gitRepo, setGitRepo] = useState(deployIocFormDefaults.git); - const [revision, setRevision] = useState(deployIocFormDefaults.reference); - const gitProjectId = deployIocFormDefaults.gitProjectId; + const [gitRepo, setGitRepo] = useState(deployIocFormDefaults?.git); + const [revision, setRevision] = useState(deployIocFormDefaults?.reference); + const gitProjectId = deployIocFormDefaults?.gitProjectId; const hasHostData = host?.hostId || host?.fqdn; const handleClose = () => { @@ -117,20 +106,24 @@ export function IOCDeployDialog({ }, [query, getHosts, hasHostData]); useEffect(() => { - getTagOrCommitIds(gitProjectId, "", "CONTAINS"); + if (gitProjectId) { + getTagOrCommitIds(gitProjectId, "", "CONTAINS"); + } }, [gitProjectId, getTagOrCommitIds]); const onSubmit = (event: FormEvent<HTMLFormElement>) => { event.preventDefault(); - submitCallback({ - iocId: iocId, - createJobRequest: { - sourceVersion: revision, - hostId: host.hostId, - type: "DEPLOY" - } - }); + if (iocId) { + submitCallback({ + iocId: iocId, + createJobRequest: { + sourceVersion: revision, + hostId: host.hostId, + type: "DEPLOY" + } + }); + } }; return ( @@ -156,7 +149,7 @@ export function IOCDeployDialog({ id="git" label="Git repository" variant="standard" - defaultValue={deployIocFormDefaults.git || ""} + defaultValue={deployIocFormDefaults?.git || ""} fullWidth onChange={(event) => { setGitRepo(event.target.value); @@ -175,7 +168,7 @@ export function IOCDeployDialog({ autoSelect: false, defaultValue: tagOrCommitIds.find( (tagOrCommit) => - tagOrCommit.reference === deployIocFormDefaults.reference + tagOrCommit.reference === deployIocFormDefaults?.reference ) }} onGitQueryValueSelect={( @@ -187,7 +180,8 @@ export function IOCDeployDialog({ }} textFieldProps={{ helperText: - tagOrCommitIdsError && getErrorMessage(tagOrCommitIdsError) + tagOrCommitIdsError && + getErrorState(tagOrCommitIdsError).message }} /> diff --git a/src/components/IOC/IOCDetailAdmin/IOCDetailAdmin.tsx b/src/components/IOC/IOCDetailAdmin/IOCDetailAdmin.tsx index 9a982ce96b41e885cb8951106c090d3c0d8b8430..83f86029e360ad2d0dff9ffa4ff180915b9b5cba 100644 --- a/src/components/IOC/IOCDetailAdmin/IOCDetailAdmin.tsx +++ b/src/components/IOC/IOCDetailAdmin/IOCDetailAdmin.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useCallback } from "react"; +import { useState, useEffect, useCallback, KeyboardEvent } from "react"; import { ConfirmationDialog } from "@ess-ics/ce-ui-common"; import { Box, @@ -9,26 +9,41 @@ import { Typography, Autocomplete } from "@mui/material"; -import { useTypingTimer } from "../../common/SearchBoxFilter/TypingTimer"; +import { useTypingTimer } from "../../../hooks/useTypingTimer"; import { AccessControl } from "../../auth/AccessControl"; import { useLazyListProjectsQuery, - useLazyFetchIocByNameQuery + useLazyFetchIocByNameQuery, + type IocDetails, + NameResponse } from "../../../store/deployApi"; import { useUpdateIocMutation } from "../../../store/enhancedApi"; import { ApiAlertError } from "../../common/Alerts/ApiAlertError"; -export const IOCDetailAdmin = ({ ioc, buttonDisabled, setButtonDisabled }) => { +interface IOCDetailAdminProps { + ioc: IocDetails; + buttonDisabled: boolean; + setButtonDisabled: (value: boolean) => void; +} + +export const IOCDetailAdmin = ({ + ioc, + buttonDisabled, + setButtonDisabled +}: IOCDetailAdminProps) => { const iocIsDeployed = Boolean(ioc.activeDeployment); const [open, setOpen] = useState(false); - const [error, setError] = useState(null); const [gitId, setGitId] = useState(ioc.gitProjectId); - const [nameQuery, onNameKeyUp] = useTypingTimer({ interval: 500 }); - const [repoQuery, onRepoKeyUp] = useTypingTimer({ interval: 500 }); - const [name, setName] = useState({ - uuid: ioc.namingUuid, - description: ioc.description, - name: ioc.namingName + const { value: nameQuery, onKeyUp: onNameKeyUp } = useTypingTimer({ + interval: 500 + }); + const { value: repoQuery, onKeyUp: onRepoKeyUp } = useTypingTimer({ + interval: 500 + }); + const [name, setName] = useState<NameResponse | null>({ + uuid: ioc?.namingUuid, + description: ioc?.description, + name: ioc?.namingName }); const [ @@ -39,15 +54,14 @@ export const IOCDetailAdmin = ({ ioc, buttonDisabled, setButtonDisabled }) => { const [getNames, { data: names, isLoading: loadingNames }] = useLazyFetchIocByNameQuery(); - const [updateIOC, { data: updatedIOC, error: updateError }] = + const [updateIOC, { data: updatedIOC, error: updateError, reset }] = useUpdateIocMutation(); useEffect(() => { if (updateError) { setButtonDisabled(false); - setError(updateError); } - }, [updateError, setError, setButtonDisabled]); + }, [updateError, setButtonDisabled]); const requiredDataMissing = useCallback(() => !gitId || !name, [gitId, name]); @@ -61,20 +75,22 @@ export const IOCDetailAdmin = ({ ioc, buttonDisabled, setButtonDisabled }) => { ); // when user clicks Submit button a dialog should open - const onSubmit = (event) => { + const onSubmit = (event: React.FormEvent<HTMLFormElement>) => { event.preventDefault(); setOpen(true); }; const onConfirm = useCallback(() => { - setButtonDisabled(true); - updateIOC({ - iocId: ioc?.id, - iocUpdateRequest: { - gitProjectId: gitId, - namingUuid: name && name.uuid - } - }); + if (ioc?.id) { + setButtonDisabled(true); + updateIOC({ + iocId: ioc.id, + iocUpdateRequest: { + gitProjectId: gitId, + namingUuid: name?.uuid + } + }); + } }, [updateIOC, ioc, name, gitId, setButtonDisabled]); useEffect(() => { @@ -92,7 +108,7 @@ export const IOCDetailAdmin = ({ ioc, buttonDisabled, setButtonDisabled }) => { useEffect(() => { if (nameQuery) { - getNames({ query: nameQuery }); + getNames({ iocName: nameQuery }); } }, [nameQuery, getNames]); @@ -102,7 +118,7 @@ export const IOCDetailAdmin = ({ ioc, buttonDisabled, setButtonDisabled }) => { "IOC cannot be renamed while deployed - please undeploy first"; } - function nameAutocomplete(disabled) { + function nameAutocomplete(disabled: boolean) { const isDisabled = disabled || iocIsDeployed; const loading = loadingNames && !isDisabled; return ( @@ -139,13 +155,13 @@ export const IOCDetailAdmin = ({ ioc, buttonDisabled, setButtonDisabled }) => { }} /> )} - onChange={(_event, value) => { + onChange={(_event, value: NameResponse | null) => { setName(value); - setError(null); + reset(); }} onInputChange={(event) => { if (event) { - onNameKeyUp(event.nativeEvent); + onNameKeyUp(event as KeyboardEvent<HTMLInputElement>); } }} disabled={isDisabled} @@ -155,7 +171,7 @@ export const IOCDetailAdmin = ({ ioc, buttonDisabled, setButtonDisabled }) => { ); } - function gitRepoAutocomplete(disabled) { + function gitRepoAutocomplete(disabled: boolean) { const isDisabled = disabled || iocIsDeployed; const loading = loadingAllowedGitProjects && !isDisabled; return ( @@ -170,15 +186,15 @@ export const IOCDetailAdmin = ({ ioc, buttonDisabled, setButtonDisabled }) => { url: ioc.sourceUrl }} getOptionLabel={(option) => { - return option.url; + return option?.url ?? ""; }} - onChange={(event, value) => { + onChange={(_, value) => { setGitId(value?.id); - setError(null); + reset(); }} onInputChange={(event) => { if (event) { - onRepoKeyUp(event.nativeEvent); + onRepoKeyUp(event as KeyboardEvent<HTMLInputElement>); } }} renderInput={(params) => ( @@ -274,14 +290,14 @@ export const IOCDetailAdmin = ({ ioc, buttonDisabled, setButtonDisabled }) => { <AccessControl allowedRoles={["DeploymentToolAdmin"]} allowedUsersWithRoles={[ - { user: ioc.createdBy, role: "DeploymentToolIntegrator" } + { user: ioc.createdBy ?? "", role: "DeploymentToolIntegrator" } ]} renderNoAccess={() => gitRepoAutocomplete(true)} > {gitRepoAutocomplete(false)} </AccessControl> - {error && <ApiAlertError error={error} />} + {updateError && <ApiAlertError error={updateError} />} <Tooltip title={disabledButtonTitle}> <span> diff --git a/src/components/IOC/IOCDetails/IOCDetails.tsx b/src/components/IOC/IOCDetails/IOCDetails.tsx index e972ffa5d39e617f62be4046d0992e8d885bac19..9edf79c0108e8296c6b02168682f83ca8512bd89 100644 --- a/src/components/IOC/IOCDetails/IOCDetails.tsx +++ b/src/components/IOC/IOCDetails/IOCDetails.tsx @@ -2,22 +2,37 @@ import { Grid, Box, Stack, Typography } from "@mui/material"; import { KeyValueTable, AlertBannerList } from "@ess-ics/ce-ui-common"; import { IOCStatus } from "../IOCStatus"; import { AccessControl } from "../../auth/AccessControl"; -import { useFetchIocAlertsQuery } from "../../../store/deployApi"; +import { IocDetails, useFetchIocAlertsQuery } from "../../../store/deployApi"; -const defaultSubset = ({ - name, - description, - host, - active, - status, - git, - version -}) => ({ name, description, host, active, status, git, version }); +interface IOCDetailsProps { + ioc: IocDetails; + getSubset: ( + ioc: IocDetails + ) => Record<string, string | JSX.Element | undefined> | undefined; + // getSubset: (ioc: IocDetails) => { + // Description: string | undefined; + // Revision: JSX.Element; + // "Deployed on": JSX.Element; + // }; -export function IOCDetails({ ioc, getSubset = defaultSubset, buttons }) { + // getSubset: (ioc: IocDetails) => + // | { + // "Naming service record": JSX.Element; + // Repository: JSX.Element; + // "Created by": JSX.Element; + // "IOC Service Controls": JSX.Element; + // } + // | undefined; + buttons?: JSX.Element; +} + +export function IOCDetails({ ioc, getSubset, buttons }: IOCDetailsProps) { const subset = getSubset(ioc); - const { data: alert } = useFetchIocAlertsQuery({ iocId: ioc.id }); + const { data: alert } = useFetchIocAlertsQuery( + { iocId: ioc?.id ?? 0 }, + { skip: !ioc.id } + ); return ( <> @@ -25,7 +40,7 @@ export function IOCDetails({ ioc, getSubset = defaultSubset, buttons }) { container spacing={1} > - {alert?.alerts?.length > 0 && ( + {alert?.alerts && alert.alerts.length > 0 && ( <Box sx={{ mt: (theme) => theme.spacing(2), width: "100%" }}> <AlertBannerList alerts={alert.alerts} /> </Box> diff --git a/src/components/IOC/IOCLiveStatus/IOCLiveStatus.spec.tsx b/src/components/IOC/IOCLiveStatus/IOCLiveStatus.spec.tsx index 76e3422430ae4de75a3d12aeb34ec2e5aa600338..bd5e578b0cf773b2478ddd15ed74e1b6522bdd9b 100644 --- a/src/components/IOC/IOCLiveStatus/IOCLiveStatus.spec.tsx +++ b/src/components/IOC/IOCLiveStatus/IOCLiveStatus.spec.tsx @@ -1,7 +1,10 @@ import { composeStories } from "@storybook/react"; import * as stories from "../../../stories/components/common/IOC/IOCLiveStatus.stories"; -const { Default, LoggedIn } = composeStories(stories); +const composeStory = composeStories(stories); + +// @ts-expect-error TS2339 +const { Default, LoggedIn } = composeStory; function commonTests() { // Expect IOC name in badge diff --git a/src/components/IOC/IOCLiveStatus/IOCLiveStatus.tsx b/src/components/IOC/IOCLiveStatus/IOCLiveStatus.tsx index 7f07d112fa8b450857337efe44d3a1feb52cc1db..ceae8e058232a6320fb4d8bdf916b3bce4bea178 100644 --- a/src/components/IOC/IOCLiveStatus/IOCLiveStatus.tsx +++ b/src/components/IOC/IOCLiveStatus/IOCLiveStatus.tsx @@ -1,4 +1,4 @@ -import { useCallback, useState } from "react"; +import { useCallback, useState, type ChangeEvent } from "react"; import { Typography } from "@mui/material"; import { SimpleAccordion, @@ -11,8 +11,9 @@ import { LokiPanel } from "../../common/Loki/LokiPanel"; import { RecordSearch } from "../../records/RecordSearch"; import { GitRefLink } from "../../common/Git/GitRefLink"; import { AccessControl } from "../../auth/AccessControl"; +import { IocDetails } from "../../../store/deployApi"; -export function IOCLiveStatus({ ioc }) { +export function IOCLiveStatus({ ioc }: { ioc: IocDetails }) { const [searchParams] = useSearchParams(); const [accordionState, setAccordionState] = useState({ logStreamOpen: false, @@ -22,20 +23,21 @@ export function IOCLiveStatus({ ioc }) { const hostName = ioc.activeDeployment?.host?.hostName; const externalIdValid = ioc.activeDeployment?.host?.externalIdValid; - const getSubset = useCallback((ioc) => { + const getSubset = useCallback((ioc: IocDetails) => { const subset = { Description: ioc.description, Revision: ioc.activeDeployment ? ( <GitRefLink url={ioc.activeDeployment?.sourceUrl} - displayReference={ioc.activeDeployment?.sourceVersion} - revision={ioc.activeDeployment?.sourceVersion} + displayReference={ioc?.activeDeployment?.sourceVersion} + revision={ioc?.activeDeployment?.sourceVersion} /> ) : ( <EmptyValue /> ), "Deployed on": - ioc.activeDeployment && ioc.activeDeployment?.host.externalIdValid ? ( + ioc?.activeDeployment?.host && + ioc?.activeDeployment?.host.externalIdValid ? ( <Typography> {ioc.activeDeployment?.host.hostId ? ( <InternalLink @@ -78,7 +80,7 @@ export function IOCLiveStatus({ ioc }) { </Typography> } expanded={accordionState.logStreamOpen} - onChange={(_, expanded) => + onChange={(_: ChangeEvent, expanded: boolean) => setAccordionState({ ...accordionState, logStreamOpen: expanded }) } > @@ -101,7 +103,7 @@ export function IOCLiveStatus({ ioc }) { </Typography> } expanded={accordionState.recordsOpen} - onChange={(_, expanded) => + onChange={(_: ChangeEvent, expanded: boolean) => setAccordionState({ ...accordionState, recordsOpen: expanded }) } > diff --git a/src/components/IOC/IOCManage/IOCManage.tsx b/src/components/IOC/IOCManage/IOCManage.tsx index b6f00dfc8848e7838f42a3424d7ce3f58d018fe0..715c293fdacecd4ca2913bcc26a8efca638475de 100644 --- a/src/components/IOC/IOCManage/IOCManage.tsx +++ b/src/components/IOC/IOCManage/IOCManage.tsx @@ -8,11 +8,15 @@ import { DeployIOC } from "../DeployIOC"; import { UndeployIOC } from "../UndeployIOC"; import { AccessControl } from "../../auth/AccessControl"; import { DeploymentStatus } from "../../../api/DataTypes"; -import { useLazyFetchJobStatusQuery } from "../../../store/deployApi"; +import { + IocDetails, + useLazyFetchJobStatusQuery +} from "../../../store/deployApi"; import env from "../../../config/env"; +import { UserContext } from "../../../types/common"; -export const IOCManage = ({ ioc }) => { - const { user } = useContext(userContext); +export const IOCManage = ({ ioc }: { ioc: IocDetails }) => { + const { user } = useContext<UserContext>(userContext); const [deployDialogOpen, setDeployDialogOpen] = useState(false); const [undeployDialogOpen, setUndeployDialogOpen] = useState(false); const [getJobStatus, { data: jobStatus }] = useLazyFetchJobStatusQuery(); @@ -24,53 +28,57 @@ export const IOCManage = ({ ioc }) => { }, [getJobStatus, ioc.activeDeployment?.jobId]); const getSubset = useCallback( - (ioc) => { + (ioc: IocDetails) => { ioc = { ...ioc }; const { sourceUrl: git } = ioc; // Show START/STOP components only when IOC was deployed SUCCESSFULLY - const deploymentStatus = new DeploymentStatus( - ioc.activeDeployment, - jobStatus - ); - const showControls = deploymentStatus.wasSuccessful(); + if (ioc?.activeDeployment && jobStatus) { + const deploymentStatus = new DeploymentStatus( + ioc?.activeDeployment, + jobStatus + ); - const subset = { - "Naming service record": ( - <ExternalLink - href={`${env.NAMING_ADDRESS}/devices.xhtml?i=2&deviceName=${ioc.namingName}`} - label="Naming Service Record" - > - {ioc.namingName} - </ExternalLink> - ), - Repository: ( - <ExternalLink - href={git} - label="Git Repository" - > - {git} - </ExternalLink> - ), - "Created by": ( - <InternalLink - to={`/user/${ioc.createdBy}`} - label={`Created by, user profile ${ioc.createdBy}`} - > - {ioc.createdBy} - </InternalLink> - ) - }; + const showControls = deploymentStatus.wasSuccessful(); - if (user) { - subset["IOC Service Controls"] = showControls ? ( - <IOCService {...{ ioc }} /> - ) : ( - "IOC is not currently deployed" - ); - } + const subset = { + "Naming service record": ( + <ExternalLink + href={`${env.NAMING_ADDRESS}/devices.xhtml?i=2&deviceName=${ioc.namingName}`} + label="Naming Service Record" + > + {ioc.namingName} + </ExternalLink> + ), + Repository: ( + <ExternalLink + href={git} + label="Git Repository" + > + {git} + </ExternalLink> + ), + "Created by": ( + <InternalLink + to={`/user/${ioc.createdBy}`} + label={`Created by, user profile ${ioc.createdBy}`} + > + {ioc.createdBy} + </InternalLink> + ), + "IOC Service Controls": <></> + }; + + if (user) { + subset["IOC Service Controls"] = showControls ? ( + <IOCService {...{ ioc }} /> + ) : ( + <>IOC is not currently deployed</> + ); + } - return subset; + return subset; + } }, [jobStatus, user] ); @@ -79,19 +87,23 @@ export const IOCManage = ({ ioc }) => { const managedIOC = { ...ioc }; const deployIocFormDefaults = { - name: ioc.namingName, - description: ioc.description, - git: ioc.sourceUrl, - gitProjectId: ioc.gitProjectId + name: ioc.namingName || "", + description: ioc.description || "", + git: ioc.sourceUrl || "", + gitProjectId: ioc.gitProjectId, + reference: "", + short_reference: "", + netBoxHost: { fqdn: "", hostId: "" } }; if (ioc.activeDeployment) { - deployIocFormDefaults.reference = ioc.activeDeployment.sourceVersion; + deployIocFormDefaults.reference = + ioc.activeDeployment.sourceVersion || ""; deployIocFormDefaults.short_reference = - ioc.activeDeployment.sourceVersionShort; + ioc.activeDeployment.sourceVersionShort || ""; deployIocFormDefaults.netBoxHost = { - fqdn: ioc.activeDeployment.host.fqdn, - hostId: ioc.activeDeployment.host.hostId + fqdn: ioc?.activeDeployment?.host?.fqdn || "", + hostId: ioc?.activeDeployment?.host?.hostId || "" }; } diff --git a/src/components/IOC/IOCManage/IOCService.tsx b/src/components/IOC/IOCManage/IOCService.tsx index 16af2a40e6945f0c8ec2becf9be4e3d2473c2345..409427e0f0f946599fa6142a1855a15626665da3 100644 --- a/src/components/IOC/IOCManage/IOCService.tsx +++ b/src/components/IOC/IOCManage/IOCService.tsx @@ -11,55 +11,50 @@ import { ConfirmationDialog } from "@ess-ics/ce-ui-common"; import { AccessControl } from "../../auth/AccessControl"; import { useStartJobMutation } from "../../../store/enhancedApi"; import { ApiAlertError } from "../../common/Alerts/ApiAlertError"; +import type { IocDetails, Job } from "../../../store/deployApi"; -export function IOCService({ ioc, currentCommand }) { +export function IOCService({ ioc }: { ioc: IocDetails }) { const navigate = useNavigate(); - const [error, setError] = useState(); const [startModalOpen, setStartModalOpen] = useState(false); const [stopModalOpen, setStopModalOpen] = useState(false); - const [command, setCommand] = useState(null); + const [command, setCommand] = useState<Job | null>(null); const [startJob, { error: jobError, data: jobData, isLoading }] = useStartJobMutation(); useEffect(() => { - if (jobError) { - setError(jobError); - } - }, [jobError]); - - useEffect(() => { - if (jobData && (!command || command.id !== jobData.id)) { + if (jobData && (!command || command?.id !== jobData.id)) { navigate(`/jobs/${jobData.id}`); setCommand(jobData); - } else if (currentCommand) { - setCommand(currentCommand); } - }, [jobData, command, currentCommand, navigate]); + }, [jobData, command, navigate]); const resetUI = useCallback(() => { setCommand(null); - setError(null); }, []); const start = useCallback(() => { - resetUI(); - startJob({ - iocId: ioc.id, - createJobRequest: { - type: "START" - } - }); + if (ioc.id) { + resetUI(); + startJob({ + iocId: ioc.id, + createJobRequest: { + type: "START" + } + }); + } }, [resetUI, startJob, ioc.id]); const stop = useCallback(() => { - resetUI(); - startJob({ - iocId: ioc.id, - createJobRequest: { - type: "STOP" - } - }); + if (ioc.id) { + resetUI(); + startJob({ + iocId: ioc.id, + createJobRequest: { + type: "STOP" + } + }); + } }, [resetUI, startJob, ioc.id]); const onStartModalClose = useCallback(() => { @@ -180,7 +175,7 @@ export function IOCService({ ioc, currentCommand }) { <Tooltip title={disabledStartButtonTitle}> <span> <Button - color="essGrass" + color="primary" variant="contained" disabled={ioc.operationInProgress || isLoading} sx={{ color: "essWhite.main" }} @@ -196,7 +191,7 @@ export function IOCService({ ioc, currentCommand }) { xs={12} md={12} > - {error && <ApiAlertError error={error} />} + {jobError && <ApiAlertError error={jobError} />} {isLoading && <LinearProgress color="primary" />} </Grid> </Grid> diff --git a/src/components/IOC/IOCStatus/IOCStatus.tsx b/src/components/IOC/IOCStatus/IOCStatus.tsx index d7515c7c5e07d1d35104529dfa90f38ea76586c5..c611c5184b159fdbdcbaec0f27a24b095e169529 100644 --- a/src/components/IOC/IOCStatus/IOCStatus.tsx +++ b/src/components/IOC/IOCStatus/IOCStatus.tsx @@ -7,13 +7,21 @@ import { useLazyFetchIocAlertsQuery } from "../../../store/deployApi"; -export const IOCStatus = ({ id, hideAlerts }) => { +interface IOCStatusProps { + id?: number; + hideAlerts?: boolean; +} + +export const IOCStatus = ({ id, hideAlerts }: IOCStatusProps) => { const [callFetchIocAlerts, { data: iocAlert }] = useLazyFetchIocAlertsQuery(); - const { data: iocStateStatus } = useFetchIocStatusQuery({ iocId: id }); + const { data: iocStateStatus } = useFetchIocStatusQuery( + { iocId: id || 0 }, + { skip: !id } + ); useEffect(() => { - if (!hideAlerts) { + if (!hideAlerts && id) { callFetchIocAlerts({ iocId: id }); } }, [callFetchIocAlerts, hideAlerts, id]); @@ -27,7 +35,7 @@ export const IOCStatus = ({ id, hideAlerts }) => { > {iocStateStatus && ( <Status - id={iocStateStatus.id} + id={iocStateStatus.iocId} state={iocStateStatus} alert={iocAlert} hideAlerts={hideAlerts} diff --git a/src/components/IOC/IOCStatus/IOCStatusData.ts b/src/components/IOC/IOCStatus/IOCStatusData.ts index 6fcc729b1a72419c3ba6914c0d6250bc782762a1..15cf716c27c040d067ed1fe1df4c7d06f1e6227f 100644 --- a/src/components/IOC/IOCStatus/IOCStatusData.ts +++ b/src/components/IOC/IOCStatus/IOCStatusData.ts @@ -1,7 +1,11 @@ +import { IocAlertResponse, IocStatusResponse } from "../../../store/deployApi"; import { STATUS, SEVERITY } from "../../common/Status"; -export const getIOCStatus = (state, alert) => { - let { isActive } = state; +export const getIOCStatus = ( + state: IocStatusResponse, + alert: IocAlertResponse | undefined +) => { + const { isActive } = state; const alertSeverity = alert?.alertSeverity?.toLowerCase(); if ( diff --git a/src/components/IOC/IOCTable/IOCDescription.tsx b/src/components/IOC/IOCTable/IOCDescription.tsx index 89716d695b4efc7f5fdef7511337b7546660cf47..8d0d40a115eb2f50e8b3f90a3d014e954e31e2c1 100644 --- a/src/components/IOC/IOCTable/IOCDescription.tsx +++ b/src/components/IOC/IOCTable/IOCDescription.tsx @@ -2,9 +2,10 @@ import { Skeleton } from "@mui/material"; import { EllipsisText, EmptyValue } from "@ess-ics/ce-ui-common"; import { useGetIocDescriptionQuery } from "../../../store/deployApi"; -export const IOCDescription = ({ id }) => { +export const IOCDescription = ({ id }: { id?: number }) => { const { data: iocDescriptionResponse, isLoading } = useGetIocDescriptionQuery( - { iocId: id } + { iocId: id || 0 }, + { skip: !id } ); const description = iocDescriptionResponse?.description; diff --git a/src/components/IOC/IOCTable/IOCTable.tsx b/src/components/IOC/IOCTable/IOCTable.tsx index 82b8fce139c5f9064078c74581b22849d8e5d74c..2c651998a3f865d8f7dd8ba71c5ce435ded37331 100644 --- a/src/components/IOC/IOCTable/IOCTable.tsx +++ b/src/components/IOC/IOCTable/IOCTable.tsx @@ -4,8 +4,14 @@ import { EllipsisText, EmptyValue } from "@ess-ics/ce-ui-common"; +import { + GridRenderCellParams, + GridValueGetterParams +} from "@mui/x-data-grid-pro"; import { IOCDescription } from "./IOCDescription"; import { IOCStatus } from "../IOCStatus"; +import { Host, Ioc, IocDetails } from "../../../store/deployApi"; +import { OnPageParams, Pagination } from "../../../types/common"; const exploreIocsColumns = [ { @@ -34,14 +40,14 @@ const exploreIocsColumns = [ field: "network", headerName: "Network", width: "10ch", - renderCell: (params) => { + renderCell: (params: GridRenderCellParams) => { return params.value ? ( <EllipsisText>{params.value}</EllipsisText> ) : ( <EmptyValue /> ); }, - valueGetter: (params) => { + valueGetter: (params: GridValueGetterParams) => { return params.value; } } @@ -68,7 +74,7 @@ const hostDetailsColumns = [ } ]; -function createHostLink(host) { +function createHostLink(host?: Host) { if (host?.hostId) { return ( <InternalLink @@ -88,7 +94,7 @@ function createHostLink(host) { return <EmptyValue />; } -function createIocLink(id, name) { +function createIocLink(id?: number, name?: string) { return ( <InternalLink to={`/iocs/${id}`} @@ -100,33 +106,23 @@ function createIocLink(id, name) { ); } -function createTableRowForHostDetails(ioc) { - const { id, name, namingName, activeDeployment } = ioc; +function createTableRowForHostDetails(ioc: IocDetails) { + const { id, namingName } = ioc; return { id: id, - status: ( - <IOCStatus - id={ioc.id} - activeDeployment={activeDeployment} - /> - ), - namingName: createIocLink(id, namingName ?? name), + status: <IOCStatus id={ioc.id} />, + namingName: createIocLink(id, namingName), description: <IOCDescription id={ioc.id} /> }; } -function createTableRowForExploreIocs(ioc) { +function createTableRowForExploreIocs(ioc: IocDetails) { const { id, namingName, activeDeployment } = ioc; return { id: id, - status: ( - <IOCStatus - id={ioc.id} - activeDeployment={ioc.activeDeployment} - /> - ), + status: <IOCStatus id={id} />, namingName: createIocLink(id, namingName), description: <IOCDescription id={ioc.id} />, host: createHostLink(activeDeployment?.host), @@ -134,18 +130,33 @@ function createTableRowForExploreIocs(ioc) { }; } +interface IOCTableProps { + iocs: Ioc[]; + rowType?: "host" | "explore"; + loading: boolean; + pagination: Pagination; + onPage: (page: OnPageParams) => void; +} + +type TableTypeSpecificsType = Record< + "host" | "explore", + [ + typeof hostDetailsColumns | typeof exploreIocsColumns, + typeof createTableRowForHostDetails | typeof createTableRowForExploreIocs + ] +>; + export const IOCTable = ({ iocs, rowType = "explore", loading, pagination, onPage -}) => { - const tableTypeSpecifics = { +}: IOCTableProps) => { + const tableTypeSpecifics: TableTypeSpecificsType = { host: [hostDetailsColumns, createTableRowForHostDetails], explore: [exploreIocsColumns, createTableRowForExploreIocs] }; - const [columns, createRow] = tableTypeSpecifics[rowType]; const rows = iocs?.map((ioc) => createRow(ioc)); diff --git a/src/components/IOC/IOCUndeployDialog/IOCUndeployDialog.tsx b/src/components/IOC/IOCUndeployDialog/IOCUndeployDialog.tsx index c11a799a6902b9697172ee1679deef3c48e6df23..327ea1baabf58d9159dcfe35450bf338e8e1a64c 100644 --- a/src/components/IOC/IOCUndeployDialog/IOCUndeployDialog.tsx +++ b/src/components/IOC/IOCUndeployDialog/IOCUndeployDialog.tsx @@ -1,6 +1,19 @@ +import { type SyntheticEvent } from "react"; import { Stack, Typography, Button } from "@mui/material"; import { Dialog } from "@ess-ics/ce-ui-common"; import { ApiAlertError } from "../../common/Alerts/ApiAlertError"; +import { IocDetails, StartJobApiArg } from "../../../store/deployApi"; +import { ApiError } from "../../../types/common"; + +interface IOCUndeployDialogProps { + open: boolean; + setOpen: (open: boolean) => void; + submitCallback: (data: StartJobApiArg) => void; + ioc: IocDetails; + error: ApiError; + resetError: () => void; + buttonDisabled: boolean; +} export function IOCUndeployDialog({ open, @@ -10,21 +23,22 @@ export function IOCUndeployDialog({ error, resetError, buttonDisabled -}) { +}: IOCUndeployDialogProps) { const handleClose = () => { setOpen(false); resetError(); }; - const onSubmit = (event) => { + const onSubmit = (event: SyntheticEvent) => { event.preventDefault(); - - submitCallback({ - iocId: ioc.id, - createJobRequest: { - type: "UNDEPLOY" - } - }); + if (ioc.id) { + submitCallback({ + iocId: ioc.id, + createJobRequest: { + type: "UNDEPLOY" + } + }); + } }; return ( @@ -73,7 +87,7 @@ export function IOCUndeployDialog({ color="primary" variant="contained" type="submit" - disabled={!ioc || error || buttonDisabled} + disabled={!ioc || !!error || buttonDisabled} > Undeploy </Button> diff --git a/src/components/IOC/UndeployIOC/UndeployIOC.tsx b/src/components/IOC/UndeployIOC/UndeployIOC.tsx index abe1641158387c0e4db9f0359eae7b5a3074df58..27a97003edcd08123e5838640ec4988a3effdd4f 100644 --- a/src/components/IOC/UndeployIOC/UndeployIOC.tsx +++ b/src/components/IOC/UndeployIOC/UndeployIOC.tsx @@ -1,22 +1,20 @@ -import { useState, useEffect } from "react"; import { Navigate } from "react-router-dom"; import { IOCUndeployDialog } from "../IOCUndeployDialog"; import { useStartJobMutation } from "../../../store/enhancedApi"; +import { IocDetails } from "../../../store/deployApi"; -export const UndeployIOC = ({ open, setOpen, ioc }) => { - const [error, setError] = useState(); +interface UndeployIOCProps { + open: boolean; + setOpen: (value: boolean) => void; + ioc: IocDetails; +} +export const UndeployIOC = ({ open, setOpen, ioc }: UndeployIOCProps) => { const [ undeploy, - { data: undeployment, error: unDeploymentError, isLoading } + { data: undeployment, error: unDeploymentError, isLoading, reset } ] = useStartJobMutation(); - useEffect(() => { - if (unDeploymentError) { - setError(unDeploymentError); - } - }, [unDeploymentError]); - if (!undeployment) { return ( <IOCUndeployDialog @@ -24,8 +22,8 @@ export const UndeployIOC = ({ open, setOpen, ioc }) => { setOpen={setOpen} submitCallback={undeploy} ioc={ioc} - error={error} - resetError={() => setError(null)} + error={unDeploymentError} + resetError={() => reset()} buttonDisabled={ioc.operationInProgress || isLoading} /> ); @@ -33,7 +31,7 @@ export const UndeployIOC = ({ open, setOpen, ioc }) => { return ( <Navigate to={`/jobs/${undeployment.id}`} - push + replace /> ); }