From 703e015efb258dbf1271e578d88faa19985416d3 Mon Sep 17 00:00:00 2001 From: "Andrew D. Murray" Date: Thu, 26 Sep 2024 16:02:26 -0400 Subject: [PATCH] Project Workflow Events List (#1573) Co-authored-by: Carson Full --- src/components/TextChip.tsx | 11 ++ .../Projects/Overview/ProjectOverview.graphql | 6 +- .../Projects/Overview/ProjectOverview.tsx | 14 +++ .../WorkflowEvents/WorkflowEventsDrawer.tsx | 92 +++++++++++++++ .../WorkflowEvents/WorkflowEventsIcon.tsx | 14 +++ .../WorkflowEvents/WorkflowEventsList.tsx | 110 ++++++++++++++++++ src/scenes/Projects/WorkflowEvents/index.ts | 3 + .../projectWorkflowEvent.graphql | 16 +++ 8 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 src/components/TextChip.tsx create mode 100644 src/scenes/Projects/WorkflowEvents/WorkflowEventsDrawer.tsx create mode 100644 src/scenes/Projects/WorkflowEvents/WorkflowEventsIcon.tsx create mode 100644 src/scenes/Projects/WorkflowEvents/WorkflowEventsList.tsx create mode 100644 src/scenes/Projects/WorkflowEvents/index.ts create mode 100644 src/scenes/Projects/WorkflowEvents/projectWorkflowEvent.graphql diff --git a/src/components/TextChip.tsx b/src/components/TextChip.tsx new file mode 100644 index 0000000000..ee8ada3df2 --- /dev/null +++ b/src/components/TextChip.tsx @@ -0,0 +1,11 @@ +import { Chip } from '@mui/material'; +import { ChildrenProp, extendSx, StyleProps } from '~/common'; + +export const TextChip = (props: ChildrenProp & StyleProps) => { + return ( + + ); +}; diff --git a/src/scenes/Projects/Overview/ProjectOverview.graphql b/src/scenes/Projects/Overview/ProjectOverview.graphql index 73e31ba28b..17c190d550 100644 --- a/src/scenes/Projects/Overview/ProjectOverview.graphql +++ b/src/scenes/Projects/Overview/ProjectOverview.graphql @@ -94,7 +94,11 @@ fragment ProjectOverview on Project { ...ProjectChangeRequestSummary } - # We're including this in order to update the engagament status + workflowEvents { + ...projectWorkflowEvent + } + + # We're including this in order to update the engagement status # when the project step changes. engagements { items { diff --git a/src/scenes/Projects/Overview/ProjectOverview.tsx b/src/scenes/Projects/Overview/ProjectOverview.tsx index 29fe22f23a..315c412d5f 100644 --- a/src/scenes/Projects/Overview/ProjectOverview.tsx +++ b/src/scenes/Projects/Overview/ProjectOverview.tsx @@ -55,6 +55,7 @@ import { ProjectListQueryVariables } from '../List/ProjectList.graphql'; import { EditableProjectField, UpdateProjectDialog } from '../Update'; import { ProjectWorkflowDialog } from '../Update/ProjectWorkflowDialog'; import { useProjectId } from '../useProjectId'; +import { WorkflowEventsDrawer, WorkflowEventsIcon } from '../WorkflowEvents'; import { ProjectEngagementListOverviewDocument as EngagementList, ProjectOverviewDocument, @@ -117,6 +118,7 @@ export const ProjectOverview = () => { const { projectId, changesetId } = useProjectId(); const beta = useBetaFeatures(); const formatNumber = useNumberFormatter(); + const [workflowDrawerState, openWorkflowEvents] = useDialog(); const [editState, editField, fieldsBeingEdited] = useDialog>(); @@ -243,6 +245,12 @@ export const ProjectOverview = () => { } /> {project && } + {project && ( + + )}
@@ -573,6 +581,12 @@ export const ProjectOverview = () => { {project && ( )} + {project && ( + + )} ); }; diff --git a/src/scenes/Projects/WorkflowEvents/WorkflowEventsDrawer.tsx b/src/scenes/Projects/WorkflowEvents/WorkflowEventsDrawer.tsx new file mode 100644 index 0000000000..1eced2dbc9 --- /dev/null +++ b/src/scenes/Projects/WorkflowEvents/WorkflowEventsDrawer.tsx @@ -0,0 +1,92 @@ +import { Close } from '@mui/icons-material'; +import { Box, Divider, Drawer, DrawerProps, Typography } from '@mui/material'; +import { useSize } from 'ahooks'; +import { useEffect, useRef, useState } from 'react'; +import { extendSx } from '~/common'; +import { IconButton } from '~/components/IconButton'; +import { ProjectWorkflowEventFragment as WorkflowEvent } from './projectWorkflowEvent.graphql'; +import { WorkflowEventsList } from './WorkflowEventsList'; + +type WorkflowEventsDrawerProps = DrawerProps & { + TransitionProps?: DrawerProps['SlideProps']; + events: readonly WorkflowEvent[]; +}; +export const WorkflowEventsDrawer = ({ + events, + TransitionProps, + ...props +}: WorkflowEventsDrawerProps) => { + const listRef = useRef(); + const listSize = useSize(listRef); + const windowSize = useSize(() => document.querySelector('body')); + const [needsWidthCalc, setNeedsWidthCalc] = useState(false); + + const isFullWidth = + !!windowSize?.width && + !!listSize?.width && + windowSize.width <= listSize.width; + + useEffect(() => setNeedsWidthCalc(true), [events]); + useEffect(() => { + listSize?.width && setNeedsWidthCalc(false); + }, [listSize?.width]); + + return ( + <> + + + Status History Log + props.onClose?.(e, 'backdropClick')}> + + + + + + {/* Actual list shown in UI */} + + + + {/* + Hidden list to track intrinsic size to calculate full width. + Outside the drawer so it is in DOM when needed regardless of drawer state. + */} + {needsWidthCalc && ( + + )} + + ); +}; diff --git a/src/scenes/Projects/WorkflowEvents/WorkflowEventsIcon.tsx b/src/scenes/Projects/WorkflowEvents/WorkflowEventsIcon.tsx new file mode 100644 index 0000000000..4b8dfa1f97 --- /dev/null +++ b/src/scenes/Projects/WorkflowEvents/WorkflowEventsIcon.tsx @@ -0,0 +1,14 @@ +import { History as HistoryIcon } from '@mui/icons-material'; +import { Tooltip } from '@mui/material'; +import { IconButton, IconButtonProps } from '~/components/IconButton'; +import { Feature } from '../../../components/Feature'; + +export const WorkflowEventsIcon = (props: IconButtonProps) => ( + + + + + + + +); diff --git a/src/scenes/Projects/WorkflowEvents/WorkflowEventsList.tsx b/src/scenes/Projects/WorkflowEvents/WorkflowEventsList.tsx new file mode 100644 index 0000000000..7e8ec50292 --- /dev/null +++ b/src/scenes/Projects/WorkflowEvents/WorkflowEventsList.tsx @@ -0,0 +1,110 @@ +import { ChevronRight } from '@mui/icons-material'; +import { Box, Divider, List, ListProps, Typography } from '@mui/material'; +import { forwardRef } from 'react'; +import { ProjectStepLabels, ProjectStepList } from '~/api/schema/enumLists'; +import { extendSx } from '~/common'; +import { RelativeDateTime } from '~/components/Formatters'; +import { Link } from '~/components/Routing'; +import { TextChip } from '~/components/TextChip'; +import { ProjectWorkflowEventFragment as WorkflowEvent } from './projectWorkflowEvent.graphql'; + +type WorkflowEventsListProps = { + events: readonly WorkflowEvent[]; + fullWidth?: boolean; +} & ListProps; + +export const WorkflowEventsList = forwardRef( + function WorkflowEventsList({ events, fullWidth = false, ...props }, ref) { + return ( + + {events.toReversed().map((event, index, array) => { + const prev = index >= 1 ? array[index - 1] : null; + const prevStatus = prev ? prev.to : ProjectStepList[0]!; + + return ( + ({ + display: 'flex', + flexWrap: 'wrap', + alignItems: 'center', + gap: 1, + padding: 1, + borderBottom: `thin solid ${theme.palette.divider}`, + })), + ]} + > + + {event.who.value?.__typename === 'User' && ( + + {event.who.value.fullName} + + )} + + + + + + + {ProjectStepLabels[prevStatus]} + + + + {ProjectStepLabels[event.to]} + + + + + ); + })} + + ); + } +); diff --git a/src/scenes/Projects/WorkflowEvents/index.ts b/src/scenes/Projects/WorkflowEvents/index.ts new file mode 100644 index 0000000000..d217f58244 --- /dev/null +++ b/src/scenes/Projects/WorkflowEvents/index.ts @@ -0,0 +1,3 @@ +export * from './WorkflowEventsDrawer'; +export * from './WorkflowEventsList'; +export * from './WorkflowEventsIcon'; diff --git a/src/scenes/Projects/WorkflowEvents/projectWorkflowEvent.graphql b/src/scenes/Projects/WorkflowEvents/projectWorkflowEvent.graphql new file mode 100644 index 0000000000..ae9fb80a47 --- /dev/null +++ b/src/scenes/Projects/WorkflowEvents/projectWorkflowEvent.graphql @@ -0,0 +1,16 @@ +fragment projectWorkflowEvent on ProjectWorkflowEvent { + id + at + to + who { + value { + ... on User { + id + fullName + } + } + } + transition { + to + } +}