diff --git a/src/components/IOC/IOCLiveStatus/IOCLiveStatus.js b/src/components/IOC/IOCLiveStatus/IOCLiveStatus.js index 398b95f15d3e4398aea9087114dcd43d0cdcc417..e463d316b8a1f5f3149ee44f2e33267e2d912437 100644 --- a/src/components/IOC/IOCLiveStatus/IOCLiveStatus.js +++ b/src/components/IOC/IOCLiveStatus/IOCLiveStatus.js @@ -77,7 +77,15 @@ export function IOCLiveStatus({ ioc }) { ) : ( grayOutNoText ), - "Created by": ioc.createdBy + "Created by": ( + <MuiLink + component={ReactRouterLink} + to={`/user/${ioc.createdBy}`} + underline="hover" + > + {ioc.createdBy} + </MuiLink> + ) }; // show comment only for deployments and where comment field is filled diff --git a/src/components/IOC/IOCService/IOCService.js b/src/components/IOC/IOCService/IOCService.js index a37a4cdca182449bc92581833162ff7634b25286..8263bffb083643e418e75bee1285ea766811addf 100644 --- a/src/components/IOC/IOCService/IOCService.js +++ b/src/components/IOC/IOCService/IOCService.js @@ -194,38 +194,26 @@ export function IOCService({ container spacing={1} > - <Grid - item - xs={1} - md={1} - > + <Grid item> <Tooltip title={disabledStopButtonTitle}> - <span> - <Button - className={classes.stopButton} - onClick={openStopModal} - disabled={buttonDisabled} - > - Stop - </Button> - </span> + <Button + className={classes.stopButton} + onClick={openStopModal} + disabled={buttonDisabled} + > + Stop + </Button> </Tooltip> </Grid> - <Grid - item - xs={1} - md={1} - > + <Grid item> <Tooltip title={disabledStartButtonTitle}> - <span> - <Button - className={classes.startButton} - onClick={openStartModal} - disabled={buttonDisabled} - > - Start - </Button> - </span> + <Button + className={classes.startButton} + onClick={openStartModal} + disabled={buttonDisabled} + > + Start + </Button> </Tooltip> </Grid> <Grid diff --git a/src/components/IOC/IOCTable/IOCTable.js b/src/components/IOC/IOCTable/IOCTable.js index 8121eab94de19565f118a55d53f2da01d4251234..c6bda80b4337dafc823db50fc702ee6d3c554ffd 100644 --- a/src/components/IOC/IOCTable/IOCTable.js +++ b/src/components/IOC/IOCTable/IOCTable.js @@ -1,9 +1,8 @@ import React from "react"; import { Table } from "@ess-ics/ce-ui-common"; -import { Grid, Tooltip, Typography } from "@mui/material"; +import { Grid, Tooltip, Typography, Link } from "@mui/material"; import { IOCStatusIcon } from "../IOCIcons"; import { noWrapText } from "../../common/Helper"; -import { useRedirect } from "../../../hooks/Redirect"; const ownIocsColumns = [ { @@ -75,6 +74,18 @@ const hostDetailsColumns = [ } ]; +function createHostLink(host) { + if (host?.externalHostId) { + return ( + <Link href={`/hosts/${host?.externalHostId}`}> + {noWrapText(host.hostName || "---")} + </Link> + ); + } + + return "---"; +} + function createIocDescription(description) { return ( <> @@ -116,7 +127,9 @@ function createTableRowForHostDetails(ioc) { <IOCStatusIcon ioc={ioc} /> </Grid> ), - namingName: noWrapText(namingName ?? name), + namingName: ( + <Link href={`/iocs/${id}`}>{noWrapText(namingName ?? name)}</Link> + ), discrepancy: alertSeverity === "WARNING", inconsistentState: alertSeverity === "ERROR", description: createIocDescription(description) @@ -138,8 +151,10 @@ function createTableRowForOwnIocs(ioc) { <IOCStatusIcon ioc={ioc} /> </Grid> ), - namingName: noWrapText(namingName ?? "---"), - host: noWrapText(activeDeployment?.host.hostName || "---"), + namingName: ( + <Link href={`/iocs/${id}`}>{noWrapText(namingName ?? "---")}</Link> + ), + host: createHostLink(activeDeployment?.host), network: noWrapText(activeDeployment?.host.network || "---"), discrepancy: alertSeverity === "WARNING", inconsistentState: alertSeverity === "ERROR", @@ -163,8 +178,10 @@ function createTableRowForExploreIocs(ioc) { <IOCStatusIcon ioc={ioc} /> </Grid> ), - namingName: noWrapText(namingName ?? "---"), - host: noWrapText(host?.hostName || "---"), + namingName: ( + <Link href={`/iocs/${id}`}>{noWrapText(namingName ?? "---")}</Link> + ), + host: createHostLink(host), network: noWrapText(host?.network || "---"), createdBy: noWrapText(createdBy || "---"), discrepancy: alertSeverity === "WARNING", @@ -187,19 +204,13 @@ export function IOCTable({ explore: [exploreIocsColumns, createTableRowForExploreIocs] }; - const redirect = useRedirect(); const [columns, createRow] = tableTypeSpecifics[rowType]; const rows = iocs.map((ioc) => createRow(ioc)); - const onRowClick = (params) => { - redirect(`/iocs/${params.row?.id}`); - }; - return ( <Table columns={columns} rows={rows} - onRowClick={onRowClick} pagination={pagination} onPage={onPage} loading={loading} diff --git a/src/components/IOC/IOCTable/IOCTable.spec.js b/src/components/IOC/IOCTable/IOCTable.spec.js index 45a3ccd5f3b84eff3d4386987b0a621c6acd814b..1cbaeefbaca6b3cc8f33e256d7eff7025f170bee 100644 --- a/src/components/IOC/IOCTable/IOCTable.spec.js +++ b/src/components/IOC/IOCTable/IOCTable.spec.js @@ -31,7 +31,7 @@ describe("HostTable", () => { cy.findAllByRole("row") .eq(1) // first row is headers, so get next index .find(".MuiTypography-noWrap") - .should("have.length", textColumns.length - 1); + .should("have.length", textColumns.length - 2); }); it("Displays correct content in first row", () => { diff --git a/src/components/Job/JobDetails.js b/src/components/Job/JobDetails.js index 6c9c8b3a8c6b1c2c075db98f29d5e3a506edb636..388ff8c443d0b2fb1bf448bda3c8b8d8eff6241b 100644 --- a/src/components/Job/JobDetails.js +++ b/src/components/Job/JobDetails.js @@ -94,7 +94,15 @@ export function JobDetails({ operation, job }) { ), comment: operation.comment ? operation.comment : "", host: calculateHostText(), - user: operation.createdBy, + user: ( + <MuiLink + component={ReactRouterLink} + to={`/user/${operation.createdBy}`} + underline="hover" + > + {operation.createdBy} + </MuiLink> + ), repository: ( <Typography> <MuiLink diff --git a/src/components/Job/JobTable.js b/src/components/Job/JobTable.js index 70193c8001109e224a7c6a669ceeaa96248bfa5e..9f4b8912640da9fb304efccaf31ff66926915d1f 100644 --- a/src/components/Job/JobTable.js +++ b/src/components/Job/JobTable.js @@ -1,8 +1,7 @@ import React from "react"; import { Table } from "@ess-ics/ce-ui-common"; -import { Grid, Tooltip } from "@mui/material"; +import { Grid, Link } from "@mui/material"; import { noWrapText } from "../common/Helper"; -import { useRedirect } from "../../hooks/Redirect"; import { JobStatusIcon, JobTypeIcon } from "./JobIcons"; import { formatDate } from "../common/Helper"; @@ -28,18 +27,6 @@ const jobLogColumns = [ } ]; -function createGitVersionField(version, shortVersion) { - return ( - <Tooltip - title={version} - arrow - enterDelay={400} - > - <label>{noWrapText(shortVersion ?? version)}</label> - </Tooltip> - ); -} - function createTableRow(job, colorStyle) { return { id: job.id, @@ -66,11 +53,21 @@ function createTableRow(job, colorStyle) { <JobStatusIcon status={job.status} /> </Grid> ), - ioc: noWrapText(job.iocName), - version: createGitVersionField(job.gitReference, job.sourceVersionShort), - host: noWrapText(job.host?.hostName ?? null), + ioc: <Link href={`/iocs/${job.iocId}`}>{noWrapText(job.iocName)}</Link>, + version: ( + <Link href={`/jobs/${job?.id}`}> + {noWrapText(job.sourceVersionShort ?? job.gitReference)} + </Link> + ), + host: ( + <Link href={`/hosts/${job?.host?.externalHostId}`}> + {noWrapText(job.host?.hostName ?? null)} + </Link> + ), network: noWrapText(job.host?.network ?? null), - user: noWrapText(job.createdBy), + user: ( + <Link href={`/user/${job.createdBy}`}>{noWrapText(job.createdBy)}</Link> + ), start: noWrapText(formatDate(job.createdAt)), deploymentFailed: job.status === "FAILED" || job.status === "ERROR", jobType: job.type, @@ -94,21 +91,12 @@ export function JobTable({ jobLog: [jobLogColumns, createTableRowJobLog] }; - const redirect = useRedirect(); const [columns, createRow] = tableTypeSpecifics[rowType]; - const onRowClick = (params) => { - const job = params.row; - if (["DEPLOY", "UNDEPLOY", "START", "STOP"].includes(job?.jobType)) { - redirect(`/jobs/${job?.id}`); - } - }; - return ( <Table columns={columns} rows={jobs?.map((job) => createRow(job))} - onRowClick={onRowClick} pagination={pagination} onPage={onPage} loading={loading} diff --git a/src/components/host/HostTable.js b/src/components/host/HostTable.js index 5236a2e78a0d69fcf91be7444a18a18bdc5b7925..df92a8c3674dc90c8efa78227a892e723ababe6f 100644 --- a/src/components/host/HostTable.js +++ b/src/components/host/HostTable.js @@ -1,9 +1,8 @@ import React from "react"; -import { Box, Tooltip } from "@mui/material"; +import { Box, Tooltip, Link } from "@mui/material"; import { Table } from "@ess-ics/ce-ui-common"; import { HostStatusIcon } from "./HostIcons"; import { noWrapText } from "../common/Helper"; -import { useNavigate } from "react-router-dom"; const columns = [ { @@ -43,7 +42,7 @@ export function createRow(hostContainer) { return { id: host.id, bulb: <HostStatusIcon host={hostContainer} />, - hostName: noWrapText(host.name), + hostName: <Link href={`/hosts/${host.id}`}> {noWrapText(host.name)} </Link>, host: host, description: rowDescription(host.description), network: noWrapText(host.network), @@ -55,17 +54,10 @@ export function createRow(hostContainer) { } export function HostTable({ hosts, pagination, onPage, loading }) { - const navigate = useNavigate(); - - const onRowClick = (params) => { - navigate(`/hosts/${params.row?.host?.id}`); - }; - return ( <Table columns={columns} rows={hosts.map((host) => createRow(host))} - onRowClick={onRowClick} pagination={pagination} onPage={onPage} loading={loading} diff --git a/src/components/records/RecordTable.js b/src/components/records/RecordTable.js index 59a083a3e5c9229156687ddc0b06023c4f991469..6e49bff5a5038b6192fab1411da2722489107aa4 100644 --- a/src/components/records/RecordTable.js +++ b/src/components/records/RecordTable.js @@ -1,9 +1,7 @@ import React from "react"; import { Table } from "@ess-ics/ce-ui-common"; import { RecordStatusIcon } from "./RecordIcons"; -import { Grid, Tooltip, Typography } from "@mui/material"; -import { useRedirect } from "../../hooks/Redirect"; -import { noWrapText } from "../common/Helper"; +import { Grid, Tooltip, Typography, Link } from "@mui/material"; const recordsColumns = [ { @@ -62,6 +60,26 @@ function createRecordDescription(description) { ); } +function createIocLink(record) { + // IOC ID only exists if it has been created in the DB -> avoid linking if it hasn't been created + if (record.iocId) { + return <Link href={`/iocs/${record.iocId}`}>{record.iocName}</Link>; + } + + return record.iocName; +} + +function createHostLink(record) { + // create host link only if the host exists in CSEntry + if (record.hostCSentryId) { + return ( + <Link href={`/hosts/${record.hostCSentryId}`}>{record.hostName}</Link> + ); + } + + return record.hostName; +} + export function createRecordsRow(record) { return { id: record.name, @@ -75,10 +93,14 @@ export function createRecordsRow(record) { <RecordStatusIcon record={record} /> </Grid> ), - name: noWrapText(record.name), + name: ( + <Link href={`/records/${encodeURIComponent(record.name)}`}> + {record.name} + </Link> + ), description: createRecordDescription(record.description), - iocName: record.iocName, - hostName: record.hostName + iocName: createIocLink(record), + hostName: createHostLink(record) }; } @@ -95,7 +117,11 @@ export function createIocDetailsRow(record) { <RecordStatusIcon record={record} /> </Grid> ), - name: record.name, + name: ( + <Link href={`/records/${encodeURIComponent(record.name)}`}> + {record.name} + </Link> + ), iocVersion: record.iocVersion }; } @@ -112,20 +138,13 @@ export function RecordTable({ iocDetails: [iocDetailsColumns, createIocDetailsRow] }; - const redirect = useRedirect(); const [columns, createRow] = tableTypeSpecifics[rowType]; const rows = (records?.recordList ?? []).map((record) => createRow(record)); - const itemLink = (name) => `/records/${encodeURIComponent(name)}`; - const onRowClick = (record) => { - redirect(itemLink(record?.id)); - }; - return ( <Table columns={columns} rows={rows} - onRowClick={onRowClick} pagination={pagination} onPage={onPage} loading={loading} diff --git a/src/views/host/HostDetailsView.js b/src/views/host/HostDetailsView.js index d573a4926ef776c0f4ae99cfe15ad138d7690842..a61069f63bbc2d0f9202dd9d848e2755295fcfb7 100644 --- a/src/views/host/HostDetailsView.js +++ b/src/views/host/HostDetailsView.js @@ -11,7 +11,9 @@ import { IconButton, Card, CardContent, - Typography + Typography, + Link as MuiLink, + Stack } from "@mui/material"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import { useHostIOCList } from "../../api/SwaggerApi"; @@ -20,7 +22,7 @@ import { SimpleAccordion } from "../../components/common/Accordion/SimpleAccordi import { IOCAsyncList } from "../../components/IOC/IOCAsyncList"; import { KeyValueTable } from "@ess-ics/ce-ui-common/dist/components/common/KeyValueTable"; import { LokiPanel } from "../../components/common/Loki/LokiPanel"; -import { useNavigate } from "react-router-dom"; +import { useNavigate, Link as ReactRouterLink } from "react-router-dom"; import { applicationTitle, initRequestParams @@ -35,24 +37,7 @@ import { } from "../../components/common/URLState/URLState"; import { GlobalAppBarContext } from "@ess-ics/ce-ui-common"; import { usePagination } from "../../hooks/pagination"; -import { RootPaper } from "@ess-ics/ce-ui-common/dist/components/common/container/RootPaper"; - -// const PREFIX = "HostDetailsView"; - -// const classes = { -// secondItem: `${PREFIX}-secondItem`, -// hostHeader: `${PREFIX}-hostHeader` -// }; - -// const StyledPaper = styled(Paper)(({ theme }) => ({ -// [`& .${classes.secondItem}`]: { -// marginTop: theme.spacing(2) -// }, - -// [`& .${classes.hostHeader}`]: { -// marginTop: theme.spacing(2) -// } -// })); +import { RootPaper } from "@ess-ics/ce-ui-common"; export function HostDetailsView({ id }) { const [host] = useHost(id); @@ -148,12 +133,45 @@ export function HostDetailsView({ id }) { return ""; } + function renderVmOwners(owners) { + if (owners && owners.length > 0) { + return ( + <Stack + direction="row" + flexWrap="wrap" + divider={<Typography component="span">, </Typography>} + > + {owners.map((username) => ( + <MuiLink + component={ReactRouterLink} + to={`/user/${username}`} + underline="hover" + key={username} + > + {username} + </MuiLink> + ))} + </Stack> + ); + } else { + return "---"; + } + } + let renderHost = { - "registered by": host?.csEntryHost?.user, + "registered by": ( + <MuiLink + component={ReactRouterLink} + to={`/user/${host?.csEntryHost?.user}`} + underline="hover" + > + {host?.csEntryHost?.user} + </MuiLink> + ), "device type": host?.csEntryHost?.device_type, description: host?.csEntryHost?.description, scope: host?.csEntryHost?.scope, - "vm owner": listToString(host?.csEntryHost?.ansible_vars?.vm_owner), + "vm owner": renderVmOwners(host?.csEntryHost?.ansible_vars?.vm_owner), "ansible groups": listToString(host?.csEntryHost?.ansible_groups) };