-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Project Workflow Events List (#1573)
Co-authored-by: Carson Full <[email protected]>
- Loading branch information
1 parent
83de79f
commit 703e015
Showing
8 changed files
with
265 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { Chip } from '@mui/material'; | ||
import { ChildrenProp, extendSx, StyleProps } from '~/common'; | ||
|
||
export const TextChip = (props: ChildrenProp & StyleProps) => { | ||
return ( | ||
<Chip | ||
label={props.children} | ||
sx={[{ borderRadius: 1 }, ...extendSx(props.sx)]} | ||
/> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
92 changes: 92 additions & 0 deletions
92
src/scenes/Projects/WorkflowEvents/WorkflowEventsDrawer.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ( | ||
<> | ||
<Drawer | ||
SlideProps={TransitionProps} // normalize with Dialog | ||
{...props} | ||
anchor="right" | ||
sx={[ | ||
{ | ||
'.MuiPaper-root': { | ||
p: 2, | ||
maxWidth: '100vw', | ||
// Even if the smaller "full width" event list layout | ||
// is narrower than the larger desktop layout, keep the drawer | ||
// full width so the width doesn't snap around. | ||
width: isFullWidth ? '100vw' : undefined, | ||
}, | ||
}, | ||
...extendSx(props.sx), | ||
]} | ||
> | ||
<Box | ||
sx={{ | ||
display: 'flex', | ||
alignItems: 'center', | ||
justifyContent: 'space-between', | ||
}} | ||
> | ||
<Typography variant="h3">Status History Log</Typography> | ||
<IconButton onClick={(e) => props.onClose?.(e, 'backdropClick')}> | ||
<Close /> | ||
</IconButton> | ||
</Box> | ||
<Divider sx={{ pt: 1 }} /> | ||
|
||
{/* Actual list shown in UI */} | ||
<WorkflowEventsList events={events} fullWidth={isFullWidth} /> | ||
</Drawer> | ||
|
||
{/* | ||
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 && ( | ||
<WorkflowEventsList | ||
events={events} | ||
ref={listRef} | ||
sx={{ | ||
p: 2, // matches above | ||
visibility: 'hidden', | ||
position: 'absolute', | ||
top: 0, | ||
right: 0, | ||
width: 'fit-content', | ||
}} | ||
/> | ||
)} | ||
</> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) => ( | ||
<Feature flag="project-workflow-history" match={true}> | ||
<Tooltip title="View Status History Log"> | ||
<IconButton {...props}> | ||
<HistoryIcon /> | ||
</IconButton> | ||
</Tooltip> | ||
</Feature> | ||
); |
110 changes: 110 additions & 0 deletions
110
src/scenes/Projects/WorkflowEvents/WorkflowEventsList.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<any, WorkflowEventsListProps>( | ||
function WorkflowEventsList({ events, fullWidth = false, ...props }, ref) { | ||
return ( | ||
<List | ||
{...props} | ||
ref={ref} | ||
sx={[ | ||
{ | ||
display: 'flex', | ||
flexDirection: 'column', | ||
}, | ||
!fullWidth && { | ||
display: 'grid', | ||
rowGap: 1, | ||
columnGap: 2, | ||
alignItems: 'center', | ||
gridTemplateColumns: | ||
'[at] min-content [from] minmax(min-content, 1fr) [arrow] min-content [to] minmax(min-content, 1fr)', | ||
}, | ||
...extendSx(props.sx), | ||
]} | ||
> | ||
{events.toReversed().map((event, index, array) => { | ||
const prev = index >= 1 ? array[index - 1] : null; | ||
const prevStatus = prev ? prev.to : ProjectStepList[0]!; | ||
|
||
return ( | ||
<Box | ||
key={event.id} | ||
sx={[ | ||
{ | ||
display: 'contents', | ||
}, | ||
fullWidth && | ||
((theme) => ({ | ||
display: 'flex', | ||
flexWrap: 'wrap', | ||
alignItems: 'center', | ||
gap: 1, | ||
padding: 1, | ||
borderBottom: `thin solid ${theme.palette.divider}`, | ||
})), | ||
]} | ||
> | ||
<Box | ||
sx={{ | ||
gridColumn: 'at', | ||
// add a bit more row padding between the two | ||
mr: fullWidth ? 1 : 0, | ||
}} | ||
> | ||
{event.who.value?.__typename === 'User' && ( | ||
<Link to={`/users/${event.who.value.id}`} color="inherit"> | ||
{event.who.value.fullName} | ||
</Link> | ||
)} | ||
<Typography variant="subtitle2" color="text.secondary" noWrap> | ||
<RelativeDateTime date={event.at} /> | ||
</Typography> | ||
</Box> | ||
<Box | ||
sx={[ | ||
{ display: 'contents' }, | ||
fullWidth && { | ||
display: 'flex', | ||
flexWrap: 'wrap', | ||
alignItems: 'center', | ||
gap: 1, | ||
}, | ||
]} | ||
> | ||
<TextChip sx={{ gridColumn: 'from' }}> | ||
{ProjectStepLabels[prevStatus]} | ||
</TextChip> | ||
<ChevronRight | ||
sx={{ gridColumn: 'arrow', flexGrow: 0 }} | ||
aria-label="transitioned to" | ||
/> | ||
<TextChip sx={{ gridColumn: 'to' }}> | ||
{ProjectStepLabels[event.to]} | ||
</TextChip> | ||
</Box> | ||
<Divider | ||
sx={{ | ||
gridColumn: '1/-1', | ||
display: fullWidth ? 'none' : undefined, | ||
}} | ||
/> | ||
</Box> | ||
); | ||
})} | ||
</List> | ||
); | ||
} | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export * from './WorkflowEventsDrawer'; | ||
export * from './WorkflowEventsList'; | ||
export * from './WorkflowEventsIcon'; |
16 changes: 16 additions & 0 deletions
16
src/scenes/Projects/WorkflowEvents/projectWorkflowEvent.graphql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
fragment projectWorkflowEvent on ProjectWorkflowEvent { | ||
id | ||
at | ||
to | ||
who { | ||
value { | ||
... on User { | ||
id | ||
fullName | ||
} | ||
} | ||
} | ||
transition { | ||
to | ||
} | ||
} |