Skip to content

Commit 5de2192

Browse files
committed
fix: executions list layout
1 parent 81634fe commit 5de2192

File tree

11 files changed

+353
-320
lines changed

11 files changed

+353
-320
lines changed

packages/backend/src/graphql/queries/get-executions.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@ const getExecutions: QueryResolvers['getExecutions'] = async (
2323

2424
const executionsQuery = context.currentUser
2525
.$relatedQuery('executions')
26-
.withGraphFetched({
27-
flow: {
28-
steps: true,
29-
},
30-
})
3126
.where(filterBuilder)
3227
.orderBy('created_at', 'desc')
3328

packages/backend/src/helpers/generate-error-email.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ function truncateFlowName(flowName: string) {
1616

1717
export function createBodyErrorMessage(
1818
flowName: string,
19-
flowId: string,
19+
pipeId: string,
2020
): string {
2121
const currDateTime = DateTime.now().toFormat('MMM dd yyyy, hh:mm a')
2222
const searchParams = new URLSearchParams()
2323
searchParams.set('status', 'failure')
24-
searchParams.set('input', flowId)
24+
searchParams.set('pipeId', pipeId)
2525

2626
const appPrefixUrl = appConfig.isDev ? appConfig.webAppUrl : appConfig.baseUrl
2727
const redirectUrl = `/executions?${searchParams.toString()}`

packages/frontend/src/components/FlowRow/index.tsx

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { IFlow } from '@plumber/types'
22

33
import { ReactElement } from 'react'
4+
import { BiChevronRight } from 'react-icons/bi'
45
import { Link } from 'react-router-dom'
56
import {
67
Card,
@@ -9,6 +10,7 @@ import {
910
Grid,
1011
GridItem,
1112
HStack,
13+
Icon,
1214
Text,
1315
VStack,
1416
} from '@chakra-ui/react'
@@ -22,10 +24,18 @@ import FlowContextMenu from './FlowContextMenu'
2224

2325
type FlowRowProps = {
2426
flow: IFlow
27+
isExecution?: boolean
28+
showMenu?: boolean
29+
showTimestamp?: boolean
2530
}
2631

2732
export default function FlowRow(props: FlowRowProps): ReactElement {
28-
const { flow } = props
33+
const {
34+
flow,
35+
showMenu = true,
36+
showTimestamp = true,
37+
isExecution = false,
38+
} = props
2939

3040
const createdAt = DateTime.fromMillis(parseInt(flow.createdAt, 10))
3141
const updatedAt = DateTime.fromMillis(parseInt(flow.updatedAt, 10))
@@ -42,12 +52,16 @@ export default function FlowRow(props: FlowRowProps): ReactElement {
4252
borderRadius={0}
4353
borderBottom="1px solid"
4454
borderBottomColor="base.divider.medium"
55+
minH={100}
4556
>
4657
<CardBody
4758
sx={{ cursor: 'pointer' }}
4859
p={0}
4960
as={Link}
50-
to={URLS.FLOW(flow.id)}
61+
to={isExecution ? URLS.EXECUTION_FLOW(flow.id) : URLS.FLOW(flow.id)}
62+
display="flex"
63+
alignItems="center"
64+
justifyContent="stretch"
5165
>
5266
<Grid
5367
templateAreas={{
@@ -63,6 +77,7 @@ export default function FlowRow(props: FlowRowProps): ReactElement {
6377
md: 'calc(30px * 3 + 8px * 2) minmax(0, auto) min-content',
6478
}}
6579
gap={6}
80+
flex={1}
6681
alignItems="center"
6782
py={6}
6883
px={{ base: 3, md: 8 }}
@@ -90,16 +105,18 @@ export default function FlowRow(props: FlowRowProps): ReactElement {
90105
>
91106
{flow?.name}
92107
</Text>
93-
<Text
94-
display="inline-block"
95-
w="100%"
96-
maxW="85%"
97-
color="base.content.medium"
98-
textStyle="body-2"
99-
>
100-
{isUpdated && `updated ${relativeUpdatedAt}`}
101-
{!isUpdated && `created ${relativeCreatedAt}`}
102-
</Text>
108+
{showTimestamp && (
109+
<Text
110+
display="inline-block"
111+
w="100%"
112+
maxW="85%"
113+
color="base.content.medium"
114+
textStyle="body-2"
115+
>
116+
{isUpdated && `updated ${relativeUpdatedAt}`}
117+
{!isUpdated && `created ${relativeCreatedAt}`}
118+
</Text>
119+
)}
103120
</VStack>
104121
</GridItem>
105122
<GridItem area="menu">
@@ -111,7 +128,11 @@ export default function FlowRow(props: FlowRowProps): ReactElement {
111128
<Text>{flow?.active ? 'Published' : 'Draft'}</Text>
112129
</Badge>
113130

114-
<FlowContextMenu flow={flow} />
131+
{showMenu ? (
132+
<FlowContextMenu flow={flow} />
133+
) : (
134+
<Icon boxSize={5} as={BiChevronRight} />
135+
)}
115136
</Flex>
116137
</GridItem>
117138
</Grid>

packages/frontend/src/config/urls.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
export const CONNECTIONS = '/connections'
22
export const EXECUTIONS = '/executions'
33
export const EXECUTION_PATTERN = '/executions/:executionId'
4+
export const EXECUTION_FLOW = (pipeId: string): string =>
5+
`/executions?pipeId=${pipeId}`
46
export const EXECUTION = (executionId: string): string =>
57
`/executions/${executionId}`
68
export const ROOT = '/'

packages/frontend/src/graphql/queries/get-executions.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,6 @@ export const GET_EXECUTIONS = gql`
2424
createdAt
2525
updatedAt
2626
status
27-
flow {
28-
id
29-
name
30-
active
31-
steps {
32-
iconUrl
33-
}
34-
}
3527
}
3628
}
3729
}

packages/frontend/src/hooks/usePaginationAndFilter.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export function usePaginationAndFilter() {
5151
page,
5252
input,
5353
status,
54+
searchParams,
5455
setSearchParams: setFormattedSearchParams,
5556
isSearching: input.trim() !== '' || page !== 1 || status !== '',
5657
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import type { IExecution, IFlow } from '@plumber/types'
2+
3+
import { useQuery } from '@apollo/client'
4+
import { Center, Container, Flex } from '@chakra-ui/react'
5+
import { Pagination } from '@opengovsg/design-system-react'
6+
7+
import ExecutionRow from '@/components/ExecutionRow'
8+
import NoResultFound from '@/components/NoResultFound'
9+
import PageTitle from '@/components/PageTitle'
10+
import PrimarySpinner from '@/components/PrimarySpinner'
11+
import { GET_EXECUTIONS } from '@/graphql/queries/get-executions'
12+
import { usePaginationAndFilter } from '@/hooks/usePaginationAndFilter'
13+
14+
import StatusInput from './StatusInput'
15+
16+
const RESULTS_PER_PAGE = 10
17+
18+
interface ExecutionsListProps {
19+
executions: IExecution[]
20+
isSearching: boolean
21+
isLoading: boolean
22+
}
23+
24+
const getLimitAndOffset = (page: number) => ({
25+
limit: RESULTS_PER_PAGE,
26+
offset: (page - 1) * RESULTS_PER_PAGE,
27+
})
28+
29+
function ExecutionsList({
30+
executions,
31+
isLoading,
32+
isSearching,
33+
}: ExecutionsListProps) {
34+
const hasExecutions = executions.length > 0
35+
36+
if (isLoading) {
37+
return (
38+
<Center mt={8}>
39+
<PrimarySpinner fontSize="4xl" />
40+
</Center>
41+
)
42+
}
43+
44+
if (!hasExecutions) {
45+
return (
46+
<NoResultFound
47+
description={
48+
isSearching ? 'No executions matching status' : 'No executions yet'
49+
}
50+
action={
51+
isSearching
52+
? 'Select a different status.'
53+
: 'Executions will appear here when the pipe runs.'
54+
}
55+
/>
56+
)
57+
}
58+
59+
return (
60+
<>
61+
{executions.map((execution) => (
62+
<ExecutionRow key={execution.id} execution={execution} />
63+
))}
64+
</>
65+
)
66+
}
67+
68+
export default function ExecutionList({ flow }: { flow: IFlow }) {
69+
const { page, setSearchParams, status, isSearching } =
70+
usePaginationAndFilter()
71+
const { id: flowId, name: flowName } = flow
72+
const { data: executionsData, loading } = useQuery(GET_EXECUTIONS, {
73+
variables: {
74+
...getLimitAndOffset(page),
75+
status,
76+
flowId,
77+
},
78+
fetchPolicy: 'cache-and-network',
79+
skip: !flowId,
80+
})
81+
82+
const { pageInfo, edges } = executionsData?.getExecutions || {}
83+
const executions: IExecution[] =
84+
edges?.map(({ node }: { node: IExecution }) => {
85+
return {
86+
...node,
87+
flow,
88+
}
89+
}) ?? []
90+
const totalCount: number = pageInfo?.totalCount ?? 0
91+
const hasPagination = !loading && totalCount > RESULTS_PER_PAGE
92+
return (
93+
<Container py={9}>
94+
<PageTitle
95+
title={`Executions for ${flowName}`}
96+
searchComponent={
97+
<StatusInput
98+
status={status}
99+
onStatusChange={(newStatus) => {
100+
setSearchParams({ status: newStatus })
101+
}}
102+
/>
103+
}
104+
/>
105+
<ExecutionsList
106+
executions={executions}
107+
isLoading={loading}
108+
isSearching={isSearching}
109+
/>
110+
{hasPagination && (
111+
<Flex justifyContent="center" mt={6}>
112+
<Pagination
113+
currentPage={pageInfo?.currentPage}
114+
onPageChange={(page) => setSearchParams({ page })}
115+
pageSize={RESULTS_PER_PAGE}
116+
totalCount={totalCount}
117+
/>
118+
</Flex>
119+
)}
120+
</Container>
121+
)
122+
}

0 commit comments

Comments
 (0)