diff --git a/apps/spruce/src/gql/generated/types.ts b/apps/spruce/src/gql/generated/types.ts index 05862f178..bb09e52d1 100644 --- a/apps/spruce/src/gql/generated/types.ts +++ b/apps/spruce/src/gql/generated/types.ts @@ -9539,6 +9539,7 @@ export type WaterfallQuery = { id: string; builds: Array<{ __typename?: "WaterfallBuild"; + activated?: boolean | null; displayName: string; id: string; version: string; diff --git a/apps/spruce/src/gql/queries/waterfall.graphql b/apps/spruce/src/gql/queries/waterfall.graphql index f721b158e..7334f51ec 100644 --- a/apps/spruce/src/gql/queries/waterfall.graphql +++ b/apps/spruce/src/gql/queries/waterfall.graphql @@ -4,6 +4,7 @@ query Waterfall($options: WaterfallOptions!) { waterfall(options: $options) { buildVariants { builds { + activated displayName id tasks { diff --git a/apps/spruce/src/pages/waterfall/BuildRow.tsx b/apps/spruce/src/pages/waterfall/BuildRow.tsx index cc6f85392..d2ae1107a 100644 --- a/apps/spruce/src/pages/waterfall/BuildRow.tsx +++ b/apps/spruce/src/pages/waterfall/BuildRow.tsx @@ -2,6 +2,7 @@ import { memo, useCallback } from "react"; import styled from "@emotion/styled"; import { palette } from "@leafygreen-ui/palette"; import { Link } from "react-router-dom"; +import { taskStatusToCopy } from "@evg-ui/lib/constants/task"; import { TaskStatus } from "@evg-ui/lib/types/task"; import { useWaterfallAnalytics } from "analytics"; import { StyledLink } from "components/styles"; @@ -12,6 +13,7 @@ import { WaterfallBuildVariant, WaterfallQuery, } from "gql/generated/types"; +import { statusColorMap, statusIconMap } from "./icons"; import { BuildVariantTitle, columnBasis, @@ -20,7 +22,7 @@ import { Row, } from "./styles"; -const { black, gray, green, white } = palette; +const { black, gray, white } = palette; export const BuildRow: React.FC<{ build: WaterfallBuildVariant; @@ -94,14 +96,18 @@ const BuildGrid: React.FC<{ ); }} > - {build.tasks.map(({ displayName, id, status }) => ( - - ))} + {build.tasks.map(({ displayName, id, status }) => { + // If the entire build is inactive, use inactive status for all tasks + const taskStatus = build.activated ? status : TaskStatus.Inactive; + return ( + + ); + })} ); @@ -128,12 +134,12 @@ const Square = styled(Link)<{ status: string }>` cursor: pointer; position: relative; - /* TODO DEVPROD-11368: Render colors for all statuses. Could use background-image property to render icons. */ - ${({ status }) => - status === TaskStatus.Succeeded - ? `background-color: ${green.dark1};` - : `background-color: ${gray.light2}; - `} + ${({ status }) => { + const icon = statusIconMap?.[status]; + const iconStyle = icon ? `background-image: ${icon};` : ""; + return `${iconStyle} +background-color: ${statusColorMap[status]};`; + }} /* Tooltip */ :before { diff --git a/apps/spruce/src/pages/waterfall/WaterfallGrid.tsx b/apps/spruce/src/pages/waterfall/WaterfallGrid.tsx index 40e42f494..a5f82df52 100644 --- a/apps/spruce/src/pages/waterfall/WaterfallGrid.tsx +++ b/apps/spruce/src/pages/waterfall/WaterfallGrid.tsx @@ -1,6 +1,7 @@ import { useRef } from "react"; import { useSuspenseQuery } from "@apollo/client"; import styled from "@emotion/styled"; +import { DEFAULT_POLL_INTERVAL } from "constants/index"; import { WaterfallQuery, WaterfallQueryVariables } from "gql/generated/types"; import { WATERFALL } from "gql/queries"; import { useDimensions } from "hooks/useDimensions"; @@ -31,6 +32,8 @@ export const WaterfallGrid: React.FC = ({ limit: VERSION_LIMIT, }, }, + // @ts-expect-error pollInterval isn't officially supported by useSuspenseQuery, but it works so let's use it anyway. + pollInterval: DEFAULT_POLL_INTERVAL, }, ); const refEl = useRef(null); diff --git a/apps/spruce/src/pages/waterfall/icons/RedOutlineX.ts b/apps/spruce/src/pages/waterfall/icons/RedOutlineX.ts new file mode 100644 index 000000000..52e1ba75f --- /dev/null +++ b/apps/spruce/src/pages/waterfall/icons/RedOutlineX.ts @@ -0,0 +1 @@ +export default `url('data:image/svg+xml,')`; diff --git a/apps/spruce/src/pages/waterfall/icons/WhiteClockWithArrow.ts b/apps/spruce/src/pages/waterfall/icons/WhiteClockWithArrow.ts new file mode 100644 index 000000000..48aab98d3 --- /dev/null +++ b/apps/spruce/src/pages/waterfall/icons/WhiteClockWithArrow.ts @@ -0,0 +1 @@ +export default `url('data:image/svg+xml,')`; diff --git a/apps/spruce/src/pages/waterfall/icons/WhiteGear.ts b/apps/spruce/src/pages/waterfall/icons/WhiteGear.ts new file mode 100644 index 000000000..78270fe09 --- /dev/null +++ b/apps/spruce/src/pages/waterfall/icons/WhiteGear.ts @@ -0,0 +1 @@ +export default `url('data:image/svg+xml,')`; diff --git a/apps/spruce/src/pages/waterfall/icons/WhiteWrench.ts b/apps/spruce/src/pages/waterfall/icons/WhiteWrench.ts new file mode 100644 index 000000000..1f0e04cd5 --- /dev/null +++ b/apps/spruce/src/pages/waterfall/icons/WhiteWrench.ts @@ -0,0 +1 @@ +export default `url('data:image/svg+xml,')`; diff --git a/apps/spruce/src/pages/waterfall/icons/WhiteX.ts b/apps/spruce/src/pages/waterfall/icons/WhiteX.ts new file mode 100644 index 000000000..e71450eba --- /dev/null +++ b/apps/spruce/src/pages/waterfall/icons/WhiteX.ts @@ -0,0 +1 @@ +export default `url('data:image/svg+xml,')`; diff --git a/apps/spruce/src/pages/waterfall/icons/index.ts b/apps/spruce/src/pages/waterfall/icons/index.ts new file mode 100644 index 000000000..ecec02ebd --- /dev/null +++ b/apps/spruce/src/pages/waterfall/icons/index.ts @@ -0,0 +1,42 @@ +import { palette } from "@leafygreen-ui/palette"; +import { TaskStatus } from "@evg-ui/lib/types/task"; +import redOutlineX from "./RedOutlineX"; +import whiteClockWithArrow from "./WhiteClockWithArrow"; +import whiteGear from "./WhiteGear"; +import whiteWrench from "./WhiteWrench"; +import whiteX from "./WhiteX"; + +const { blue, gray, green, purple, red, yellow } = palette; + +export const statusColorMap: Record = { + [TaskStatus.Succeeded]: green.dark1, + [TaskStatus.Started]: yellow.base, + [TaskStatus.Dispatched]: yellow.base, + [TaskStatus.SystemFailed]: purple.dark2, + [TaskStatus.SystemTimedOut]: purple.dark2, + [TaskStatus.SystemUnresponsive]: purple.dark2, + [TaskStatus.Failed]: red.base, + [TaskStatus.TaskTimedOut]: red.base, + [TaskStatus.TestTimedOut]: red.base, + [TaskStatus.KnownIssue]: red.light3, + [TaskStatus.SetupFailed]: blue.base, + [TaskStatus.Unscheduled]: gray.light1, + [TaskStatus.Aborted]: gray.light1, + [TaskStatus.Blocked]: gray.light1, + [TaskStatus.Inactive]: gray.light1, + [TaskStatus.Undispatched]: gray.dark1, + [TaskStatus.WillRun]: gray.dark1, + [TaskStatus.Pending]: gray.dark1, + [TaskStatus.Unstarted]: gray.dark1, +}; + +export const statusIconMap: Record = { + [TaskStatus.Failed]: whiteX, + [TaskStatus.TaskTimedOut]: whiteClockWithArrow, + [TaskStatus.TestTimedOut]: whiteClockWithArrow, + [TaskStatus.SetupFailed]: whiteWrench, + [TaskStatus.KnownIssue]: redOutlineX, + [TaskStatus.SystemFailed]: whiteGear, + [TaskStatus.SystemTimedOut]: whiteGear, + [TaskStatus.SystemUnresponsive]: whiteGear, +};