diff --git a/package-lock.json b/package-lock.json index 959f3babc75bb54466f1eb6517621385bf4520c9..d9b9a0dee0100b4245ebbb42acb60583259772df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "@ahooksjs/use-url-state": "^3.5.0", "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", - "@ess-ics/ce-ui-common": "^0.4.2", + "@ess-ics/ce-ui-common": "^0.5.0", "@fontsource/roboto": "^4.1.0", "@mui/icons-material": "^5.14.1", "@mui/material": "^5.14.1", @@ -2655,9 +2655,9 @@ } }, "node_modules/@ess-ics/ce-ui-common": { - "version": "0.4.2", - "resolved": "https://artifactory.esss.lu.se/artifactory/api/npm/ics-npm/@ess-ics/ce-ui-common/-/ce-ui-common-0.4.2.tgz", - "integrity": "sha1-bHkj1S9gtfMc3yF9gn1tSzwPJ6E=", + "version": "0.5.0", + "resolved": "https://artifactory.esss.lu.se/artifactory/api/npm/ics-npm/@ess-ics/ce-ui-common/-/ce-ui-common-0.5.0.tgz", + "integrity": "sha1-inPHY3jh1frYoaBCh2qTJAaoZYQ=", "dependencies": { "@fontsource/titillium-web": "^4.5.9", "@mui/x-data-grid-pro": "^6.5.0", @@ -41722,9 +41722,9 @@ "dev": true }, "@ess-ics/ce-ui-common": { - "version": "0.4.2", - "resolved": "https://artifactory.esss.lu.se/artifactory/api/npm/ics-npm/@ess-ics/ce-ui-common/-/ce-ui-common-0.4.2.tgz", - "integrity": "sha1-bHkj1S9gtfMc3yF9gn1tSzwPJ6E=", + "version": "0.5.0", + "resolved": "https://artifactory.esss.lu.se/artifactory/api/npm/ics-npm/@ess-ics/ce-ui-common/-/ce-ui-common-0.5.0.tgz", + "integrity": "sha1-inPHY3jh1frYoaBCh2qTJAaoZYQ=", "requires": { "@fontsource/titillium-web": "^4.5.9", "@mui/x-data-grid-pro": "^6.5.0", diff --git a/package.json b/package.json index 3eedaea6e324fcebac6d891b7c0f5bec54d30d81..ad53ac618cb135c5f76ec7f2f21c326e53ad8403 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "@ahooksjs/use-url-state": "^3.5.0", "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", - "@ess-ics/ce-ui-common": "^0.4.2", + "@ess-ics/ce-ui-common": "^0.5.0", "@fontsource/roboto": "^4.1.0", "@mui/icons-material": "^5.14.1", "@mui/material": "^5.14.1", diff --git a/src/components/IOC/ChangeHostAdmin/ChangeHostAdmin.js b/src/components/IOC/ChangeHostAdmin/ChangeHostAdmin.js index 2d3cdc3ec986fd4b5e910c0ce99839a10a5179d4..2ee8c6653d9479f8433257ee51db268f80758937 100644 --- a/src/components/IOC/ChangeHostAdmin/ChangeHostAdmin.js +++ b/src/components/IOC/ChangeHostAdmin/ChangeHostAdmin.js @@ -62,7 +62,7 @@ export default function ChangeHostAdmin({ // for the dialog const [error, setError] = useState(); - const [open, setOpen] = useState(); + const [open, setOpen] = useState(false); const { value: updatedIoc, diff --git a/src/components/IOC/IOCDelete/IOCDelete.js b/src/components/IOC/IOCDelete/IOCDelete.js index d5b4f3c6bc0a42a4202c248565a43a19ff30a047..47908811574b596fa25a68530228e73a8a7050df 100644 --- a/src/components/IOC/IOCDelete/IOCDelete.js +++ b/src/components/IOC/IOCDelete/IOCDelete.js @@ -137,7 +137,7 @@ export default function IOCDelete({ ioc, buttonDisabled }) { disabled={ buttonDisabled || ioc.operationInProgress || - ioc.activeDeployment + Boolean(ioc.activeDeployment) } color="error" variant="contained" diff --git a/src/components/IOC/IOCDeployDialog/IOCDeployDialog.js b/src/components/IOC/IOCDeployDialog/IOCDeployDialog.js index 7cc9380459b19854a44e0f801e0f00d6f78fa491..681f0e164788e4bd9d8bfbbbb63054ec58f1e8d1 100644 --- a/src/components/IOC/IOCDeployDialog/IOCDeployDialog.js +++ b/src/components/IOC/IOCDeployDialog/IOCDeployDialog.js @@ -44,7 +44,8 @@ export function IOCDeployDialog({ error: tagOrCommitIdError } = useAPIMethod({ fcn: client.apis.Git.listTagsAndCommitIds, - call: false + call: false, + init: [] }); const getTagOrCommitId = useCallback( (gitProjectId, reference, includeAllReference, searchMethod) => { diff --git a/src/components/IOC/IOCDetailAdmin/IOCDetailAdmin.js b/src/components/IOC/IOCDetailAdmin/IOCDetailAdmin.js index 3efa1bf0185d96febfc21b71b5ba5287166eb9ad..515dbec7772f1bfc91ed7f4482bc16400efac83f 100644 --- a/src/components/IOC/IOCDetailAdmin/IOCDetailAdmin.js +++ b/src/components/IOC/IOCDetailAdmin/IOCDetailAdmin.js @@ -125,7 +125,7 @@ export default function IOCDetailAdmin({ } }, [uioc, getIOC, resetTab]); - const iocIsDeployed = ioc.activeDeployment; + const iocIsDeployed = Boolean(ioc.activeDeployment); let nameDisabledTitle = ""; if (iocIsDeployed) { diff --git a/src/components/IOC/IOCManage/IOCManage.js b/src/components/IOC/IOCManage/IOCManage.js index ed29204df52baa1deb51a162d1eb27452c8ab828..f9387bc30638ab3939cff7afd909c8dcb6ac87a5 100644 --- a/src/components/IOC/IOCManage/IOCManage.js +++ b/src/components/IOC/IOCManage/IOCManage.js @@ -244,7 +244,7 @@ export function IOCManage({ submitCallback={closeDeployModal} init={formInit} iocId={ioc.id} - hasActiveDeployment={ioc.activeDeployment} + hasActiveDeployment={Boolean(ioc.activeDeployment)} /> <UndeployIOC open={undeployDialogOpen} diff --git a/src/components/common/Loki/LokiPanel.js b/src/components/common/Loki/LokiPanel.js index 95dde124c65e29fe3266e8f2aa063f52979520f7..9661b00eee1398d19e4278b09cd9b725d24d2cfd 100644 --- a/src/components/common/Loki/LokiPanel.js +++ b/src/components/common/Loki/LokiPanel.js @@ -84,6 +84,77 @@ export function LokiPanel({ host, iocName, isSyslog, isDeployed }) { const [timeRangeText, setTimeRangeText] = React.useState("12 hours"); const [periodChange, setPeriodChange] = useState(false); const [alertIds, setAlertIds] = useState([]); + const [html, setHtml] = useState(""); + + const preprocessLog = useCallback( + ( + logData, + isDeployed, + showWarning, + timeRangeText, + alertIds, + setAlertIds + ) => { + if (logData?.warning && alertIds?.length === 0) { + const warningId = showWarning(logData.warning, "warning"); + setAlertIds((alertIds) => [...alertIds, warningId]); + } + + if (logData && logData.lines?.length > 0) { + const logHtml = logData.lines.map((line) => formatLogLine(line)); + return `<html> + <head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + <style type="text/css">.logdate { color: #5cb85c; } body.pre{font-family: Monaco, Menlo, Consolas, "Courier New", monospace;} + </style> + </head> + <body> + <pre>${logHtml.join("")}</pre> + </body> + </html>`; + } else { + if (!(isDeployed === true)) { + return "<html><body> NOT DEPLOYED YET! </body></html>"; + } + } + + return `<html><body> - No messages found for ${timeRangeText} period - </body></html>`; + }, + [] + ); + + const logsToPreprocess = useCallback( + ( + isSysLog, + logData, + procServLog, + isDeployed, + showWarning, + timeRangeText, + alertIds, + setAlertIds + ) => { + if (isSysLog === true) { + return preprocessLog( + logData, + true, + showWarning, + timeRangeText, + alertIds, + setAlertIds + ); + } + + return preprocessLog( + procServLog, + isDeployed, + showWarning, + timeRangeText, + alertIds, + setAlertIds + ); + }, + [preprocessLog] + ); const params = useMemo( () => ({ @@ -171,6 +242,31 @@ export function LokiPanel({ host, iocName, isSyslog, isDeployed }) { // return <div style={{ width: "100%" }}><LinearProgress color="primary" /></div>; // } + useEffect(() => { + setHtml( + logsToPreprocess( + isSyslog, + sysLogData, + procServLog, + isDeployed, + showWarning, + timeRangeText, + alertIds, + setAlertIds + ) + ); + }, [ + setHtml, + alertIds, + isDeployed, + isSyslog, + logsToPreprocess, + procServLog, + showWarning, + sysLogData, + timeRangeText + ]); + const dataReady = () => { return sysLogData || procServLog; }; @@ -208,16 +304,7 @@ export function LokiPanel({ host, iocName, isSyslog, isDeployed }) { md={12} > <Console - html={logsToPreprocess( - isSyslog, - sysLogData, - procServLog, - isDeployed, - showWarning, - timeRangeText, - alertIds, - setAlertIds - )} + html={html} dataReady={dataReady} extraClass={isDeployed ? classes.deployed : classes.undeployed} title={isSyslog ? "Host log stream" : "IOC log stream"} @@ -236,37 +323,6 @@ export function LokiPanel({ host, iocName, isSyslog, isDeployed }) { ); } -function logsToPreprocess( - isSysLog, - logData, - procServLog, - isDeployed, - showWarning, - timeRangeText, - alertIds, - setAlertIds -) { - if (isSysLog === true) { - return preprocessLog( - logData, - true, - showWarning, - timeRangeText, - alertIds, - setAlertIds - ); - } - - return preprocessLog( - procServLog, - isDeployed, - showWarning, - timeRangeText, - alertIds, - setAlertIds - ); -} - function formatLogLine(logLine) { var Convert = require("ansi-to-html"); var convert = new Convert(); @@ -279,36 +335,3 @@ function formatLogLine(logLine) { "<br/></span>" ); } - -function preprocessLog( - logData, - isDeployed, - showWarning, - timeRangeText, - alertIds, - setAlertIds -) { - if (logData.warning && alertIds.length === 0) { - const warningId = showWarning(logData.warning, "warning"); - setAlertIds((alertIds) => [...alertIds, warningId]); - } - - if (logData && logData.lines?.length > 0) { - var logHtml = logData.lines.map((line) => formatLogLine(line)); - return `<html> - <head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"> - <style type="text/css">.logdate { color: #5cb85c; } body.pre{font-family: Monaco, Menlo, Consolas, "Courier New", monospace;} - </style> - </head> - <body> - <pre>${logHtml.join("")}</pre> - </body> - </html>`; - } else { - if (!(isDeployed === true)) { - return "<html><body> NOT DEPLOYED YET! </body></html>"; - } - } - - return `<html><body> - No messages found for ${timeRangeText} period - </body></html>`; -} diff --git a/src/views/IOC/IOCDetailsView.js b/src/views/IOC/IOCDetailsView.js index f8be1d64fae8764f7018fce954460bb40d59a706..32839522266f8db02a5358cc58e4e8e4dc9a0e49 100644 --- a/src/views/IOC/IOCDetailsView.js +++ b/src/views/IOC/IOCDetailsView.js @@ -1,4 +1,4 @@ -import { Grid, Tab, Tabs, IconButton } from "@mui/material"; +import { Grid, IconButton, Stack } from "@mui/material"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import React, { useCallback, @@ -11,12 +11,16 @@ import { IOCLiveStatus } from "../../components/IOC/IOCLiveStatus"; import { IOCManage } from "../../components/IOC/IOCManage"; import { useNavigate } from "react-router-dom"; import IOCAdmin from "../../components/IOC/IOCAdmin"; -import AccessControl from "../../components/auth/AccessControl"; import { applicationTitle, initRequestParams } from "../../components/common/Helper"; -import { GlobalAppBarContext, useAPIMethod } from "@ess-ics/ce-ui-common"; +import { + GlobalAppBarContext, + useAPIMethod, + useIsCurrentUserPermitted, + TabPanel +} from "@ess-ics/ce-ui-common"; import { useSafePolling } from "../../hooks/Polling"; import useUrlState from "@ahooksjs/use-url-state"; import { @@ -123,10 +127,13 @@ export function IOCDetailsView({ ioc, getIOC, abortGetIOC, loading }) { }, [jobPagination, setJobUrlPagination]); // Invoked by Table on change to pagination - const onPage = (params) => { - setJobPagination(params); - abortGetOperations(); - }; + const onPage = useCallback( + (params) => { + setJobPagination(params); + abortGetOperations(); + }, + [abortGetOperations, setJobPagination] + ); useSafePolling(getIOC, loading, IOC_POLL_INTERVAL, true, abortGetIOC); useSafePolling( @@ -141,8 +148,8 @@ export function IOCDetailsView({ ioc, getIOC, abortGetIOC, loading }) { setButtonDisabled(Boolean(ioc?.operationInProgress)); }, [ioc?.operationInProgress]); - const handleTabChange = (event, tab) => { - setUrlState({ tab: serialize(tab) }); + const onTabChange = (index, label) => { + setUrlState({ tab: serialize(label) }); }; // Submit new search whenever pagination or ioc changes @@ -176,118 +183,90 @@ export function IOCDetailsView({ ioc, getIOC, abortGetIOC, loading }) { [getIOC] ); - const statusTab = ( - <Tab - key="Status" - label="Status" - value="Status" - /> - ); - const manageDeploymentTab = ( - <Tab - key="Management" - label="Management" - value="Management" - /> - ); - const iocAdminTab = ( - <Tab - key="Admin" - label="Admin" - value="Admin" - /> - ); + const tabs = [ + { + label: "Status", + content: <IOCLiveStatus ioc={ioc} /> + } + ]; - const CustomTabs = ({ children }) => { - return ( - <Tabs - value={deserialize(urlState.tab)} - onChange={handleTabChange} - > - {children} - </Tabs> - ); - }; + const isPermittedManagement = useIsCurrentUserPermitted({ + allowedRoles: ["DeploymentToolAdmin", "DeploymentToolIntegrator"] + }); + + const isPermittedAdmin = useIsCurrentUserPermitted({ + allowedRoles: ["DeploymentToolAdmin"] + }); + + if (isPermittedManagement) { + tabs.push({ + label: "Management", + content: ( + <IOCManage + ioc={ioc} + getIOC={getIOC} + buttonDisabled={buttonDisabled} + currentCommand={ + ongoingCommand?.operations?.length > 0 + ? ongoingCommand.operations[0] + : null + } + operations={operations?.operations} + operationsLoading={operationsLoading || !operationsDataReady} + getOperations={getOperations} + setButtonDisabled={setButtonDisabledAndUpdate} + pagination={jobPagination} + onPage={onPage} + /> + ) + }); + } + if (isPermittedAdmin) { + tabs.push({ + label: "Admin", + content: ( + <IOCAdmin + ioc={ioc} + getIOC={getIOC} + resetTab={resetTab} + buttonDisabled={buttonDisabled} + /> + ) + }); + } + const initialIndex = tabs.map((it) => it.label).indexOf(urlState?.tab); return ( <Grid container spacing={1} > - <Grid - item - xs={1} - > - <IconButton - color="inherit" - onClick={handleClick} - size="large" - > - <ArrowBackIcon /> - </IconButton> - </Grid> - <Grid - item - xs={11} - > - <Grid - container - justifyContent="center" - > - {ioc && ( - <CustomTabs> - {statusTab} - <AccessControl - allowedRoles={[ - "DeploymentToolAdmin", - "DeploymentToolIntegrator" - ]} - renderNoAccess={() => <></>} - > - <CustomTabs>{manageDeploymentTab}</CustomTabs> - </AccessControl> - <AccessControl - allowedRoles={["DeploymentToolAdmin"]} - renderNoAccess={() => <></>} - > - <CustomTabs>{iocAdminTab}</CustomTabs> - </AccessControl> - </CustomTabs> - )} - </Grid> - </Grid> <Grid item xs={12} style={{ paddingBottom: 0 }} > - {deserialize(urlState.tab) === "Status" && <IOCLiveStatus ioc={ioc} />} - {deserialize(urlState.tab) === "Management" && ( - <IOCManage - ioc={ioc} - getIOC={getIOC} - buttonDisabled={buttonDisabled} - currentCommand={ - ongoingCommand?.operations?.length > 0 - ? ongoingCommand.operations[0] - : null - } - operations={operations?.operations} - operationsLoading={operationsLoading || !operationsDataReady} - getOperations={getOperations} - setButtonDisabled={setButtonDisabledAndUpdate} - pagination={jobPagination} - onPage={onPage} - /> - )} - {deserialize(urlState.tab) === "Admin" && ( - <IOCAdmin - ioc={ioc} - getIOC={getIOC} - resetTab={resetTab} - buttonDisabled={buttonDisabled} - /> - )} + <TabPanel + tabs={tabs} + initialIndex={initialIndex} + onTabChange={onTabChange} + TabsProps={{ centered: true, sx: { flex: 1 } }} + renderTabs={(tabs) => ( + <Stack + flexDirection="row" + justifyContent="space-between" + > + <IconButton + color="inherit" + onClick={handleClick} + size="large" + > + <ArrowBackIcon /> + </IconButton> + {tabs} + </Stack> + )} + /> </Grid> </Grid> );