From 06367e07b9f867a5d24aa5e2253357e08575e5cc Mon Sep 17 00:00:00 2001
From: Max Frederiksen <maxfrederiksen@Maxs-MacBook-Air.local>
Date: Mon, 13 Jan 2025 17:10:37 +0100
Subject: [PATCH] Add typing /components/Job/* + JobTable story

---
 src/components/Job/JobDetails.tsx             |  3 +-
 src/components/Job/JobDetailsTable.tsx        | 28 ++++++++-------
 .../Job/JobTable/JobDetailsColumn.tsx         | 26 ++++++++------
 src/components/Job/JobTable/JobTable.tsx      | 35 +++++++++++++------
 src/components/Job/JobUtils.ts                | 11 +++---
 .../common/job/JobTable.stories.tsx           |  4 +--
 6 files changed, 64 insertions(+), 43 deletions(-)

diff --git a/src/components/Job/JobDetails.tsx b/src/components/Job/JobDetails.tsx
index 244da05e..d03c90c5 100644
--- a/src/components/Job/JobDetails.tsx
+++ b/src/components/Job/JobDetails.tsx
@@ -17,11 +17,10 @@ import { ActionTypeIconText } from "./JobIcons";
 import { JobDetailsTable } from "./JobDetailsTable";
 import { DeploymentJobOutput } from "../deployments/DeploymentJobOutput";
 import { AWXJobDetails, Status } from "../../api/DataTypes";
-import { JobDetails, AwxJobDetails } from "../../store/deployApi";
+import { JobDetails } from "../../store/deployApi";
 
 interface JobDetailsProps {
   jobDetail: JobDetails;
-  awxJob: AwxJobDetails;
 }
 
 const createAlert = (type: Status, message: string) => {
diff --git a/src/components/Job/JobDetailsTable.tsx b/src/components/Job/JobDetailsTable.tsx
index 4fd9dca0..ef15b140 100644
--- a/src/components/Job/JobDetailsTable.tsx
+++ b/src/components/Job/JobDetailsTable.tsx
@@ -8,6 +8,7 @@ import {
 import { Typography, Stack } from "@mui/material";
 import { getNoOfIOCs, getNoOfHosts, isBatchJob } from "./JobUtils";
 import { JobRevisionChip } from "./JobRevisionChip";
+import { JobDetails, JobDetailsEntry } from "../../store/deployApi";
 
 const columns = [
   {
@@ -27,7 +28,7 @@ const columns = [
   }
 ];
 
-const calculateHostText = (job) => {
+const calculateHostText = (job: JobDetailsEntry) => {
   // host is resolvable => show link for users
   if (job?.host?.hostId && job?.host?.externalIdValid) {
     return (
@@ -48,29 +49,30 @@ const calculateHostText = (job) => {
   );
 };
 
-const createRow = (job) => {
+const createRow = (job: JobDetailsEntry) => {
+  const { iocId, iocName, host, gitReference, gitProjectId } = job;
   return {
-    id: `${job.iocId}${job.host.hostId}`,
+    id: `${iocId}${host?.hostId}`,
     ioc: (
       <InternalLink
-        to={`/iocs/${job.iocId}`}
-        label={`Ioc details, ${job.iocName}`}
+        to={`/iocs/${iocId}`}
+        label={`Ioc details, ${iocName}`}
         width="100%"
       >
-        <EllipsisText>{job.iocName}</EllipsisText>
+        <EllipsisText>{iocName}</EllipsisText>
       </InternalLink>
     ),
     host: calculateHostText(job),
-    revision: (
+    revision: gitReference && gitProjectId && (
       <JobRevisionChip
-        gitReference={job.gitReference}
-        gitProjectId={job.gitProjectId}
+        gitReference={gitReference}
+        gitProjectId={gitProjectId}
       />
     )
   };
 };
 
-export const JobDetailsTable = ({ operation }) => {
+export const JobDetailsTable = ({ operation }: { operation: JobDetails }) => {
   const isBatchOperation = isBatchJob(operation.action);
 
   const noOfIOCs = useMemo(() => {
@@ -107,9 +109,9 @@ export const JobDetailsTable = ({ operation }) => {
                 variant="body2"
                 sx={{ fontWeight: "600", marginRight: "10px" }}
               >
-                {noOfIOCs} {noOfIOCs > 1 ? "IOCs" : "IOC"}
+                {noOfIOCs} {noOfIOCs && noOfIOCs > 1 ? "IOCs" : "IOC"}
                 {", "}
-                {noOfHosts} {noOfHosts > 1 ? "Hosts" : "Host"}
+                {noOfHosts} {noOfHosts && noOfHosts > 1 ? "Hosts" : "Host"}
               </Typography>
             </Stack>
           }
@@ -117,7 +119,7 @@ export const JobDetailsTable = ({ operation }) => {
           <Table
             columns={columns}
             disableColumnSorting
-            rows={operation.jobs.map((job) => createRow(job))}
+            rows={operation?.jobs?.map((job) => createRow(job))}
           />
         </SimpleAccordion>
       ) : (
diff --git a/src/components/Job/JobTable/JobDetailsColumn.tsx b/src/components/Job/JobTable/JobDetailsColumn.tsx
index 3cdc3111..11aa15ea 100644
--- a/src/components/Job/JobTable/JobDetailsColumn.tsx
+++ b/src/components/Job/JobTable/JobDetailsColumn.tsx
@@ -5,8 +5,10 @@ import { getNoOfIOCs, getNoOfHosts, isBatchJob } from "../JobUtils";
 import { ActionTypeIconText } from "../JobIcons";
 import { ACTION_TYPES } from "../JobData";
 import { JobRevisionChip } from "../JobRevisionChip";
+import { Job } from "../../../store/deployApi";
 
-export const JobDetailsColumn = ({ job }) => {
+// Ask johanna regarding batch jobs
+export const JobDetailsColumn = ({ job }: { job: Job }) => {
   const isBatchOperation = isBatchJob(job.action);
 
   const noOfIOCs = useMemo(() => {
@@ -38,12 +40,14 @@ export const JobDetailsColumn = ({ job }) => {
             >
               {job.iocName}
             </InternalLink>
-            {job.type === ACTION_TYPES.DEPLOY && (
-              <JobRevisionChip
-                gitReference={job.gitReference}
-                gitProjectId={job.gitProjectId}
-              />
-            )}
+            {job?.action === ACTION_TYPES.DEPLOY &&
+              job?.gitReference &&
+              job?.gitProjectId && (
+                <JobRevisionChip
+                  gitReference={job.gitReference}
+                  gitProjectId={job.gitProjectId}
+                />
+              )}
           </Stack>
           <Stack
             flexDirection="row"
@@ -55,18 +59,18 @@ export const JobDetailsColumn = ({ job }) => {
               to={`/hosts/${job?.host?.hostId}`}
               label={`Host details, ${job.host?.hostName}`}
             >
-              {job.host.hostName}
+              {job?.host?.hostName}
             </InternalLink>
-            <Typography variant="body2">{job.host.network}</Typography>
+            <Typography variant="body2">{job?.host?.network}</Typography>
           </Stack>
         </Stack>
       ) : (
         <Stack>
           <Typography variant="body2">
-            {noOfIOCs} {noOfIOCs > 1 ? "IOCs" : "IOC"}
+            {noOfIOCs} {noOfIOCs && noOfIOCs > 1 ? "IOCs" : "IOC"}
           </Typography>
           <Typography variant="body2">
-            {noOfHosts} {noOfHosts > 1 ? "Hosts" : "Host"}
+            {noOfHosts} {noOfHosts && noOfHosts > 1 ? "Hosts" : "Host"}
           </Typography>
         </Stack>
       )}
diff --git a/src/components/Job/JobTable/JobTable.tsx b/src/components/Job/JobTable/JobTable.tsx
index e05170b4..bc7d261c 100644
--- a/src/components/Job/JobTable/JobTable.tsx
+++ b/src/components/Job/JobTable/JobTable.tsx
@@ -1,10 +1,13 @@
 import { useState, useEffect } from "react";
 import { Table, InternalLink } from "@ess-ics/ce-ui-common";
+import { type GridColDef } from "@mui/x-data-grid";
 import { JobStatusColumn } from "./JobStatusColumn";
 import { JobDetailsColumn } from "./JobDetailsColumn";
 import { UserAvatar } from "../../common/User/UserAvatar";
+import { Job } from "../../../store/deployApi";
+import { Pagination } from "../../../types/common";
 
-const defaultColumns = [
+const defaultColumns: GridColDef[] = [
   {
     field: "status",
     headerName: "Status",
@@ -29,14 +32,18 @@ const defaultColumns = [
   }
 ];
 
-const shapeColumns = (customColumns) => {
-  return customColumns.map((column) => {
-    const mappedCol = defaultColumns.find((col) => col.field === column.field);
-    return mappedCol ? { ...mappedCol, ...column } : null;
-  });
+const shapeColumns = (customColumns: typeof defaultColumns) => {
+  return customColumns
+    .map((column) => {
+      const mappedCol = defaultColumns.find(
+        (col) => col.field === column.field
+      );
+      return mappedCol ? { ...mappedCol, ...column } : null;
+    })
+    .filter((column) => column !== null);
 };
 
-const createTableRow = (job) => ({
+const createTableRow = (job: Job) => ({
   id: job.id,
   status: <JobStatusColumn {...{ job }} />,
   jobId: (
@@ -51,14 +58,22 @@ const createTableRow = (job) => ({
   user: <UserAvatar username={job.createdBy} />
 });
 
+interface JobTableProps {
+  jobs?: Job[];
+  customColumns?: GridColDef[];
+  pagination: Pagination;
+  onPage: (params: Pagination) => void;
+  loading: boolean;
+}
+
 export const JobTable = ({
   jobs,
-  customColumns = null,
+  customColumns,
   pagination,
   onPage,
   loading
-}) => {
-  const [columns, setColumns] = useState(null);
+}: JobTableProps) => {
+  const [columns, setColumns] = useState<GridColDef[] | null>(null);
 
   useEffect(() => {
     if (customColumns) {
diff --git a/src/components/Job/JobUtils.ts b/src/components/Job/JobUtils.ts
index 986a7dcf..157e23bf 100644
--- a/src/components/Job/JobUtils.ts
+++ b/src/components/Job/JobUtils.ts
@@ -1,10 +1,11 @@
 import { ACTION_TYPES } from "./JobData";
+import { Job, JobEntry } from "../../store/deployApi";
 
-export const isBatchJob = (type) =>
+export const isBatchJob = (type: Job["action"]) =>
   type === ACTION_TYPES.BATCH_DEPLOY || type === ACTION_TYPES.BATCH_UNDEPLOY;
 
-export const getNoOfIOCs = (jobs) =>
-  [...new Set(jobs.map((job) => job.iocId))].length;
+export const getNoOfIOCs = (jobs?: JobEntry[]) =>
+  [...new Set(jobs?.map((job) => job.iocId))].length;
 
-export const getNoOfHosts = (jobs) =>
-  [...new Set(jobs.map((job) => job.host.hostId))].length;
+export const getNoOfHosts = (jobs?: JobEntry[]) =>
+  [...new Set(jobs?.map((job) => job?.host?.hostId))].length;
diff --git a/src/stories/components/common/job/JobTable.stories.tsx b/src/stories/components/common/job/JobTable.stories.tsx
index cb601ac9..98f2a2e1 100644
--- a/src/stories/components/common/job/JobTable.stories.tsx
+++ b/src/stories/components/common/job/JobTable.stories.tsx
@@ -1,12 +1,12 @@
 import { Box } from "@mui/material";
 import { RouterHarness } from "../../../../mocks/AppHarness";
 import { JobTable } from "../../../../components/Job/JobTable";
-import { type BaseJob } from "../../../../store/deployApi";
 import operationList from "../../../../mocks/fixtures/Jobs.json";
 import {
   hideStorybookControls,
   paginationNoResults
 } from "../../../utils/common-args";
+import { Job } from "../../../../store/deployApi";
 import type { Meta, StoryFn } from "@storybook/react";
 
 export default {
@@ -52,7 +52,7 @@ export const Populated: JobTableStory = (args) => <Template {...args} />;
 
 Populated.args = {
   ...Empty.args,
-  jobs: operationList?.jobs as BaseJob[],
+  jobs: operationList?.jobs as Job[],
   pagination: { ...paginationNoResults, totalCount: operationList.totalCount }
 };
 
-- 
GitLab