jnfrati 4 ani în urmă
părinte
comite
b9fb0265f8

+ 80 - 0
dashboard/src/components/DisplaySwitch.tsx

@@ -0,0 +1,80 @@
+import React from "react";
+import styled from "styled-components";
+
+const DisplaySwitch = (props: {
+  onChange: (option: "table" | "list") => void;
+  value: "table" | "list";
+}): JSX.Element => {
+  return (
+    <DisplaySelectorContainer>
+      <DisplaySelectorLeftOption
+        active={props.value === "table"}
+        onClick={() => props.onChange("table")}
+      >
+        <span className="material-icons-outlined">table_view</span>
+      </DisplaySelectorLeftOption>
+      <DisplaySelectorRightOption
+        active={props.value === "list"}
+        onClick={() => props.onChange("list")}
+      >
+        <span className="material-icons-outlined">toc</span>
+      </DisplaySelectorRightOption>
+    </DisplaySelectorContainer>
+  );
+};
+
+export default DisplaySwitch;
+
+const DisplaySelectorContainer = styled.div`
+  width: fit-content;
+  display: flex;
+  margin-bottom: 15px;
+`;
+
+const DisplaySelectorLeftOption = styled.div`
+  padding: 5px 10px 5px 10px;
+  border-radius: 15px 0 0 15px;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+
+  ${(props: { active: boolean }) => {
+    if (!props.active) {
+      return "";
+    }
+
+    return `
+      background: #2e2e2e;
+      color: #ffffff88;
+    `;
+  }}
+
+  :hover {
+    background: #2e2e2e;
+    color: #ffffff88;
+  }
+`;
+
+const DisplaySelectorRightOption = styled.div`
+  padding: 5px 10px 5px 10px;
+  cursor: pointer;
+  border-radius: 0 15px 15px 0;
+  display: flex;
+  align-items: center;
+
+  ${(props: { active: boolean }) => {
+    if (!props.active) {
+      return "";
+    }
+
+    return `
+      background: #2e2e2e;
+      color: #ffffff88;
+    `;
+  }}
+
+  :hover {
+    background: #2e2e2e;
+    color: #ffffff88;
+  }
+`;

+ 4 - 4
dashboard/src/main/home/cluster-dashboard/chart/JobRunTable.tsx

@@ -15,7 +15,7 @@ type Props = {
   sortType: "Newest" | "Oldest" | "Alphabetical";
 };
 
-const dateFormatter = (date: string) => {
+export const dateFormatter = (date: string) => {
   if (!date) {
     return "N/A";
   }
@@ -35,7 +35,7 @@ const dateFormatter = (date: string) => {
   return rtf.format(-time.time, time.unitOfTime);
 };
 
-const runnedFor = (start: string | number, end?: string | number) => {
+export const runnedFor = (start: string | number, end?: string | number) => {
   const duration = timeFrom(start, end);
 
   const unit =
@@ -46,7 +46,7 @@ const runnedFor = (start: string | number, end?: string | number) => {
   return `${duration.time} ${unit}`;
 };
 
-function timeFrom(time: string | number, secondTime?: string | number) {
+export function timeFrom(time: string | number, secondTime?: string | number) {
   // Get timestamps
   let unixTime = new Date(time).getTime();
   if (!unixTime) return;
@@ -442,7 +442,7 @@ const RedirectButton = styled(DynamicLink)`
   }
 `;
 
-type JobRun = {
+export type JobRun = {
   metadata: {
     name: string;
     namespace: string;

+ 181 - 1
dashboard/src/main/home/cluster-dashboard/expanded-chart/jobs/JobList.tsx

@@ -1,4 +1,4 @@
-import React, { useContext, useState } from "react";
+import React, { useContext, useMemo, useState } from "react";
 import styled from "styled-components";
 
 import api from "shared/api";
@@ -7,6 +7,9 @@ import JobResource from "./JobResource";
 import useAuth from "shared/auth/useAuth";
 import usePagination from "shared/hooks/usePagination";
 import Selector from "components/Selector";
+import DisplaySwitch from "components/DisplaySwitch";
+import { CellProps, Column } from "react-table";
+import { dateFormatter, JobRun, runnedFor } from "../../chart/JobRunTable";
 
 type PropsType = {
   jobs: any[];
@@ -28,6 +31,7 @@ const JobListFC = (props: PropsType): JSX.Element => {
   } = useContext(Context);
   const [deletionCandidate, setDeletionCandidate] = useState(null);
   const [deletionJob, setDeletionJob] = useState(null);
+  const [view, setView] = useState<"table" | "list">("list");
 
   const {
     firstContentIndex,
@@ -85,6 +89,12 @@ const JobListFC = (props: PropsType): JSX.Element => {
 
   return (
     <>
+      <DisplaySwitch
+        onChange={(option) => {
+          setView(option);
+        }}
+        value={view}
+      />
       <JobListWrapper>
         {props.jobs
           .slice(firstContentIndex, lastContentIndex)
@@ -161,6 +171,176 @@ const JobListFC = (props: PropsType): JSX.Element => {
 
 export default JobListFC;
 
+type JobTableRendererType = {
+  jobs: JobRun[];
+  handleDelete: () => void;
+  deletionJob: JobRun;
+  currentChartVersion: number;
+  latestChartVersion: number;
+  isDeployedFromGithub: boolean;
+  repositoryUrl?: string;
+};
+
+const JobTableRenderer = (props: JobTableRendererType): JSX.Element => {
+  const { currentProject, currentCluster } = useContext(Context);
+
+  const columns = useMemo<Column<JobRun>[]>(
+    () => [
+      {
+        Header: "Namespace / Name",
+        accessor: (originalRow) => {
+          const owners = originalRow.metadata.ownerReferences;
+          let name = "N/A";
+          if (Array.isArray(owners)) {
+            name = owners[0]?.name;
+          }
+          if (originalRow?.metadata?.labels["meta.helm.sh/release-name"]) {
+            name = originalRow.metadata.labels["meta.helm.sh/release-name"];
+          }
+
+          if (name !== "N/A") {
+            return originalRow.metadata?.namespace + "/" + name;
+          }
+
+          return name;
+        },
+        width: "max-content",
+      },
+      {
+        Header: "Run at",
+        accessor: (originalRow) => dateFormatter(originalRow.status.startTime),
+      },
+      {
+        Header: "Run for",
+        accessor: (originalRow) => {
+          if (originalRow.status?.completionTime) {
+            return originalRow.status?.completionTime;
+          } else if (
+            Array.isArray(originalRow.status?.conditions) &&
+            originalRow.status?.conditions[0]?.lastTransitionTime
+          ) {
+            return originalRow.status?.conditions[0]?.lastTransitionTime;
+          } else {
+            return "Still running...";
+          }
+        },
+        Cell: ({ row }: CellProps<JobRun>) => {
+          if (row.original.status?.completionTime) {
+            return runnedFor(
+              row.original.status?.startTime,
+              row.original.status?.completionTime
+            );
+          } else if (
+            Array.isArray(row.original.status?.conditions) &&
+            row.original.status?.conditions[0]?.lastTransitionTime
+          ) {
+            return runnedFor(
+              row.original.status?.startTime,
+              row.original.status?.conditions[0]?.lastTransitionTime
+            );
+          } else {
+            return "Still running...";
+          }
+        },
+        styles: {
+          padding: "10px",
+        },
+      },
+      {
+        Header: "Status",
+        id: "status",
+        Cell: ({ row }: CellProps<JobRun>) => {
+          if (row.original.status?.succeeded >= 1) {
+            return <Status color="#38a88a">Succeeded</Status>;
+          }
+
+          if (row.original.status?.failed >= 1) {
+            return <Status color="#cc3d42">Failed</Status>;
+          }
+
+          return <Status color="#ffffff11">Running</Status>;
+        },
+      },
+      {
+        Header: "Commit/Image tag",
+        id: "commit_or_image_tag",
+        accessor: (originalRow) => {
+          const container = originalRow.spec?.template?.spec?.containers[0];
+          return container?.image?.split(":")[1] || "N/A";
+        },
+        Cell: ({ row }: CellProps<JobRun>) => {
+          const container = row.original.spec?.template?.spec?.containers[0];
+
+          const tag = container?.image?.split(":")[1];
+          return tag;
+        },
+      },
+      {
+        Header: "Command",
+        id: "command",
+        accessor: (originalRow) => {
+          const container = originalRow.spec?.template?.spec?.containers[0];
+          return container?.command?.join(" ") || "N/A";
+        },
+        Cell: ({ row }: CellProps<JobRun>) => {
+          const container = row.original.spec?.template?.spec?.containers[0];
+
+          return (
+            <CommandString>
+              {container?.command?.join(" ") || "N/A"}
+            </CommandString>
+          );
+        },
+      },
+      {
+        id: "delete",
+        Cell: ({ row }: CellProps<JobRun>) => {
+          return (
+            <i
+              className="material-icons"
+              onClick={(e) => {
+                e.stopPropagation();
+                props.handleDelete();
+              }}
+            >
+              delete
+            </i>
+          );
+        },
+        maxWidth: 40,
+      },
+    ],
+    []
+  );
+
+  const data = useMemo(() => {}, [props.jobs]);
+
+  return <></>;
+};
+
+const CommandString = styled.div`
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  max-width: 300px;
+  color: #ffffff55;
+  margin-right: 27px;
+  font-family: monospace;
+`;
+
+const Status = styled.div<{ color: string }>`
+  padding: 5px 10px;
+  background: ${(props) => props.color};
+  font-size: 13px;
+  border-radius: 3px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: min-content;
+  height: 25px;
+  min-width: 90px;
+`;
+
 const FlexEnd = styled.div`
   display: flex;
   justify-content: flex-end;