@evenicanpm/admin-integrate 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,13 @@
1
+ import { gql } from "graphql-request";
2
+
3
+ export const PROCESS_FRAGMENT_SM = gql`
4
+ fragment e4ProcessFragmentSM on Process {
5
+ name
6
+ code
7
+ id
8
+ modified
9
+ isScheduled
10
+ isTriggered
11
+ processIcon
12
+ }
13
+ `;
@@ -0,0 +1,27 @@
1
+ import { gql } from "graphql-request";
2
+
3
+ import { PROCESS_FRAGMENT_SM } from "./fragments";
4
+
5
+ const GET_PROCESS = gql`
6
+ ${PROCESS_FRAGMENT_SM}
7
+ query GetProcesses($input: ProcessInput) {
8
+ processes(input: $input) {
9
+ nodes {
10
+ ...e4ProcessFragmentSM
11
+ }
12
+ pageInfo {
13
+ resultsReturned
14
+ currentPage
15
+ hasNextPage
16
+ hasPreviousPage
17
+ skip
18
+ top
19
+ sort
20
+ sortDesc
21
+ search
22
+ integrationTags
23
+ }
24
+ recordCount
25
+ }
26
+ }
27
+ `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evenicanpm/admin-integrate",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "e4Integrate Admin Panel",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -19,5 +19,5 @@
19
19
  "@xyflow/react": "^12.8.4",
20
20
  "elkjs": "^0.10.0"
21
21
  },
22
- "gitHead": "6a95e9f84f48a7e1e96bfdf4ee1a766a7d8808b6"
22
+ "gitHead": "608903e897229fead2b663cd82c019451ed65a45"
23
23
  }
@@ -0,0 +1,48 @@
1
+ import { useQuery, QueryClient, UseQueryOptions } from "@tanstack/react-query";
2
+ import { queryFn } from "./get-process.server";
3
+
4
+ import {
5
+ GetProcessesQuery,
6
+ GetProcessesQueryVariables,
7
+ } from "@evenicanpm/admin-core/api/e4/graphqlRequestSdk";
8
+
9
+ export const buildProcessExecutionsQueryKey = {
10
+ all: () => ["getProcesses"] as const,
11
+ byInput: (variables?: GetProcessesQueryVariables) => [
12
+ "getProcesses",
13
+ variables,
14
+ ],
15
+ };
16
+
17
+ export const getProcesses = {
18
+ queryKey: buildProcessExecutionsQueryKey.all(),
19
+
20
+ prefetch: (
21
+ queryClient: QueryClient,
22
+ variables?: GetProcessesQueryVariables,
23
+ ) =>
24
+ queryClient.prefetchQuery({
25
+ queryKey: buildProcessExecutionsQueryKey.byInput(variables),
26
+ queryFn: () => queryFn(variables),
27
+ }),
28
+
29
+ fetchData: async (
30
+ queryClient: QueryClient,
31
+ variables?: GetProcessesQueryVariables,
32
+ ) => {
33
+ return await queryClient.fetchQuery({
34
+ queryKey: buildProcessExecutionsQueryKey.byInput(variables),
35
+ queryFn: () => queryFn(variables),
36
+ });
37
+ },
38
+
39
+ useData: (
40
+ variables?: GetProcessesQueryVariables,
41
+ options?: UseQueryOptions,
42
+ ) =>
43
+ useQuery<GetProcessesQuery>({
44
+ queryKey: buildProcessExecutionsQueryKey.byInput(variables),
45
+ queryFn: () => queryFn(variables),
46
+ ...options,
47
+ }),
48
+ };
@@ -0,0 +1,9 @@
1
+ "use server";
2
+ import createGraphqlclient from "@evenicanpm/admin-core/api/sdk/graphqlClient.client";
3
+ import { GetProcessesQueryVariables } from "@evenicanpm/admin-core/api/e4/graphqlRequestSdk";
4
+
5
+ export const queryFn = async (variables?: GetProcessesQueryVariables) => {
6
+ const sdk = await createGraphqlclient();
7
+ const { data } = await sdk.GetProcesses(variables);
8
+ return data;
9
+ };
@@ -0,0 +1 @@
1
+ export { getProcesses } from "./get-process.query";
@@ -0,0 +1,2 @@
1
+ export { default as IntegrationTag } from "./integration-tag";
2
+ export { default as IntegrationStatusIcon } from "./integration-status-icon";
@@ -0,0 +1,21 @@
1
+ import {
2
+ Filters,
3
+ MultiSelectFilter,
4
+ } from "@evenicanpm/admin-integrate/components/list-filter/list-filter-types";
5
+
6
+ export interface IntegrationListFilters extends Filters {
7
+ multiSelectFilter: MultiSelectFilter;
8
+ }
9
+
10
+ export const integrationListFilters: IntegrationListFilters = {
11
+ multiSelectFilter: {
12
+ type: "multiSelect",
13
+ label: "IntegrationList.tags",
14
+ value: null,
15
+ options: [
16
+ { id: "Schedule Pending", value: "IntegrationList.scheduledPending" },
17
+ { id: "Scheduled", value: "IntegrationList.scheduled" },
18
+ { id: "Triggered", value: "IntegrationList.triggered" },
19
+ ],
20
+ },
21
+ };
@@ -0,0 +1,33 @@
1
+ import React from "react";
2
+
3
+ import { SxProps } from "@mui/material";
4
+ import StorageIcon from "@mui/icons-material/Storage";
5
+ import ElectricBoltIcon from "@mui/icons-material/ElectricBolt";
6
+ import CodeIcon from "@mui/icons-material/Code";
7
+ import TableChartIcon from "@mui/icons-material/TableChart";
8
+ import EmailIcon from "@mui/icons-material/Email";
9
+ import IosShareIcon from "@mui/icons-material/IosShare";
10
+ import QuestionMarkIcon from "@mui/icons-material/QuestionMark";
11
+ import { purple } from "@mui/material/colors";
12
+
13
+ interface Props {
14
+ processIcon: string;
15
+ sx?: SxProps;
16
+ }
17
+
18
+ const IntegrationStatusIcon: React.FC<Props> = (props: Props) => {
19
+ const color = purple[500];
20
+ const statusIconMap = {
21
+ DB: <StorageIcon color="success" />,
22
+ Webhook: <ElectricBoltIcon color="error" />,
23
+ Restful: <CodeIcon color="info" />,
24
+ Table: <TableChartIcon sx={{ color: color }} />,
25
+ Email: <EmailIcon color="warning" />,
26
+ Export: <IosShareIcon color="primary" />,
27
+ noIcon: <QuestionMarkIcon color="info" />,
28
+ };
29
+
30
+ return statusIconMap[props.processIcon] || statusIconMap["noIcon"];
31
+ };
32
+
33
+ export default IntegrationStatusIcon;
@@ -0,0 +1,77 @@
1
+ import React from "react";
2
+ import { styled } from "@mui/material/styles";
3
+ import { Box, Typography, SxProps } from "@mui/material";
4
+ import { useTranslations } from "next-intl";
5
+
6
+ const StyledTagBox = styled(Box)(({ theme }) => ({
7
+ padding: theme.spacing(0, 1.5),
8
+ border: "solid 1px",
9
+ borderRadius: 4,
10
+ alignContent: "center",
11
+ maxWidth: "fit-content",
12
+ }));
13
+
14
+ const StyledTag = styled(Typography)(({ theme }) => ({
15
+ fontWeight: "bold",
16
+ }));
17
+
18
+ interface Props {
19
+ process: {
20
+ isScheduled: number;
21
+ isTriggered: number;
22
+ };
23
+ sx?: SxProps;
24
+ }
25
+
26
+ const IntegrationTag: React.FC<Props> = ({ process, sx }) => {
27
+ const t = useTranslations("Integrate.IntegrationList");
28
+
29
+ return (
30
+ <Box display="flex" gap={1} justifyContent="center">
31
+ {process.isTriggered === 1 && (
32
+ <StyledTagBox
33
+ sx={{
34
+ backgroundColor: "info.100",
35
+ borderColor: "transparent",
36
+ ...sx,
37
+ }}
38
+ >
39
+ <StyledTag color="info" variant="caption">
40
+ {t("triggered")}
41
+ </StyledTag>
42
+ </StyledTagBox>
43
+ )}
44
+
45
+ {process.isScheduled === 1 && (
46
+ <StyledTagBox
47
+ sx={{
48
+ backgroundColor: "success.100",
49
+ borderColor: "transparent",
50
+ ...sx,
51
+ }}
52
+ >
53
+ <StyledTag color="success" variant="caption">
54
+ {t("scheduled")}
55
+ </StyledTag>
56
+ </StyledTagBox>
57
+ )}
58
+
59
+ {process.isScheduled === 0 && process.isTriggered === 0 && (
60
+ <StyledTagBox
61
+ sx={{
62
+ backgroundColor: "warning.100",
63
+ borderColor: "transparent",
64
+ color: "orange.700",
65
+ ...sx,
66
+ }}
67
+ >
68
+ <StyledTag color="inherit" variant="caption">
69
+ {t("scheduledPending")}
70
+ </StyledTag>
71
+ </StyledTagBox>
72
+ )}
73
+ </Box>
74
+ );
75
+ };
76
+
77
+ export default IntegrationTag;
@@ -306,7 +306,7 @@ const ListFilter = function ListFilter<FilterShape extends Filters>(
306
306
 
307
307
  <Button
308
308
  variant="contained"
309
- color="primary"
309
+ color="info"
310
310
  startIcon={<ArrowForwardIos />}
311
311
  disabled={Object.values(filterErrors).some((v) => v)}
312
312
  onClick={handleFilterSet}
@@ -0,0 +1 @@
1
+ export { default } from "./integration-list";
@@ -0,0 +1,147 @@
1
+ import React, { useState } from "react";
2
+ import { redirect, RedirectType } from "next/navigation";
3
+
4
+ // MUI
5
+ import { Theme, useTheme } from "@mui/material";
6
+ import { Box, Stack, Typography, Popover } from "@mui/material";
7
+ import { RemoveRedEye } from "@mui/icons-material";
8
+ import { SxProps } from "@mui/material";
9
+
10
+ // Custom components
11
+ import {
12
+ StyledTableCell,
13
+ StyledTableRow,
14
+ StyledIconButton,
15
+ } from "@evenicanpm/admin-integrate/components/data-table/table-row";
16
+ import {
17
+ IntegrationTag,
18
+ IntegrationStatusIcon,
19
+ } from "@evenicanpm/admin-integrate/components/integration-list";
20
+
21
+ // Hooks
22
+ import useTimestamp from "@evenicanpm/admin-integrate/hooks/use-timestamp";
23
+
24
+ // Types
25
+ import { executionItem } from "./integration-list";
26
+ import { Head } from "@evenicanpm/admin-integrate/components/data-table/table-header";
27
+ import { TableCellProps } from "@mui/material";
28
+
29
+ interface Props {
30
+ item: executionItem;
31
+ heading: Head[]; // for alignment
32
+ sx?: SxProps;
33
+ }
34
+
35
+ const IntegrationListRow: React.FC<Props> = (props: Props) => {
36
+ const theme = useTheme();
37
+ const styles = getStyles(theme);
38
+
39
+ // e.g. "xx days ago"
40
+ const time = useTimestamp(props.item.modified);
41
+ const localTime = new Date(props.item.modified).toString();
42
+
43
+ const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
44
+ const open = Boolean(anchorEl);
45
+
46
+ const handlePopoverOpen = (event: React.MouseEvent<HTMLElement>) => {
47
+ setAnchorEl(event.currentTarget);
48
+ };
49
+
50
+ const handlePopoverClose = () => {
51
+ setAnchorEl(null);
52
+ };
53
+
54
+ const handleRedirect = () => {
55
+ redirect(
56
+ `/e4integrate/integration/${props.item.id}?IntegrationId=${props.item.id}`,
57
+ RedirectType.push,
58
+ );
59
+ };
60
+
61
+ return (
62
+ <StyledTableRow sx={props.sx}>
63
+ <StyledTableCell
64
+ align={props.heading[0].align as TableCellProps["align"]}
65
+ width={props.heading[0].width}
66
+ >
67
+ <Stack spacing={1} direction="row">
68
+ <Box display="flex" justifyContent="flex-end" alignItems="center">
69
+ <IntegrationStatusIcon processIcon={props.item.processIcon} />
70
+ </Box>
71
+ <Box display="flex" justifyContent="flex-end" alignItems="center">
72
+ <Typography>{props.item.name}</Typography>
73
+ </Box>
74
+ </Stack>
75
+ </StyledTableCell>
76
+ <StyledTableCell
77
+ align={props.heading[1].align as TableCellProps["align"]}
78
+ width={props.heading[1].width}
79
+ >
80
+ <Typography>{props.item.id}</Typography>
81
+ </StyledTableCell>
82
+ <StyledTableCell
83
+ align={props.heading[1].align as TableCellProps["align"]}
84
+ width={props.heading[1].width}
85
+ >
86
+ <IntegrationTag
87
+ process={{
88
+ isScheduled: props.item.isScheduled,
89
+ isTriggered: props.item.isTriggered,
90
+ }}
91
+ />
92
+ </StyledTableCell>
93
+ <StyledTableCell
94
+ align={props.heading[2].align as TableCellProps["align"]}
95
+ width={props.heading[2].width}
96
+ sx={styles.timeCell}
97
+ >
98
+ <Typography
99
+ onMouseEnter={handlePopoverOpen}
100
+ onMouseLeave={handlePopoverClose}
101
+ >
102
+ {time}
103
+ </Typography>
104
+ <Popover
105
+ sx={{ pointerEvents: "none" }}
106
+ open={open}
107
+ anchorEl={anchorEl}
108
+ anchorOrigin={{
109
+ vertical: "bottom",
110
+ horizontal: "center",
111
+ }}
112
+ transformOrigin={{
113
+ vertical: "top",
114
+ horizontal: "center",
115
+ }}
116
+ onClose={handlePopoverClose}
117
+ disableRestoreFocus
118
+ >
119
+ <Box sx={styles.popover}>
120
+ <Typography>{localTime}</Typography>
121
+ </Box>
122
+ </Popover>
123
+ </StyledTableCell>
124
+ <StyledTableCell
125
+ align={props.heading[3].align as TableCellProps["align"]}
126
+ width={props.heading[3].width}
127
+ >
128
+ <StyledIconButton onClick={handleRedirect}>
129
+ <RemoveRedEye />
130
+ </StyledIconButton>
131
+ </StyledTableCell>
132
+ </StyledTableRow>
133
+ );
134
+ };
135
+
136
+ const getStyles = (theme: Theme) => {
137
+ return {
138
+ timeCell: {
139
+ cursor: "default",
140
+ },
141
+ popover: {
142
+ padding: theme.spacing(1),
143
+ },
144
+ };
145
+ };
146
+
147
+ export default IntegrationListRow;
@@ -0,0 +1,78 @@
1
+ import React from "react";
2
+
3
+ // Types
4
+ import { Head } from "@evenicanpm/admin-integrate/components/data-table/table-header";
5
+ import { Item } from "./integration-list";
6
+
7
+ // API
8
+ import { ProcessExecutionInput } from "@evenicanpm/admin-core/api/e4/graphqlRequestSdk";
9
+
10
+ // MUI
11
+ import { Theme, useTheme } from "@mui/material";
12
+ import { TableContainer, Table, TableBody } from "@mui/material";
13
+ import { SxProps } from "@mui/material";
14
+
15
+ // Custom components
16
+ import { TableHeader } from "@evenicanpm/admin-integrate/components/data-table";
17
+
18
+ // Local components
19
+ import IntegrationListRow from "./integration-list-row";
20
+
21
+ interface Props {
22
+ heading: Head[];
23
+ items: Item[];
24
+ input: ProcessExecutionInput;
25
+ setInput: (newInput: ProcessExecutionInput) => void;
26
+ rowSx?: SxProps;
27
+ }
28
+
29
+ export default function IntegrationListTable(props: Props) {
30
+ const theme = useTheme();
31
+ const styles = getStyles(theme);
32
+
33
+ const handleRequestSort = (property: string) => {
34
+ const current = props.input.sort;
35
+ if (property === current) {
36
+ props.setInput({
37
+ ...props.input,
38
+ sortDesc: !props.input.sortDesc,
39
+ });
40
+ } else {
41
+ props.setInput({
42
+ ...props.input,
43
+ sort: property,
44
+ sortDesc: false,
45
+ });
46
+ }
47
+ };
48
+
49
+ return (
50
+ <>
51
+ <TableContainer>
52
+ <Table>
53
+ <TableHeader
54
+ heading={props.heading}
55
+ orderBy={props.input.sort}
56
+ order={props.input.sortDesc ? "desc" : "asc"}
57
+ onRequestSort={handleRequestSort}
58
+ rowSx={props.rowSx}
59
+ />
60
+ <TableBody>
61
+ {props.items.map((item, index) => (
62
+ <IntegrationListRow
63
+ key={index}
64
+ item={item}
65
+ heading={props.heading}
66
+ sx={props.rowSx}
67
+ />
68
+ ))}
69
+ </TableBody>
70
+ </Table>
71
+ </TableContainer>
72
+ </>
73
+ );
74
+ }
75
+
76
+ const getStyles = (theme: Theme) => {
77
+ return {};
78
+ };
@@ -0,0 +1,262 @@
1
+ import React, { useState, useEffect } from "react";
2
+ import { useTranslations } from "next-intl";
3
+
4
+ // API
5
+ import {
6
+ GetProcessesQuery,
7
+ ProcessInput,
8
+ } from "@evenicanpm/admin-core/api/e4/graphqlRequestSdk";
9
+ import { ProcessExecutionInput } from "@evenicanpm/admin-core/api/e4/graphqlRequestSdk";
10
+
11
+ // MUI
12
+ import { Theme, useTheme } from "@mui/material";
13
+ import {
14
+ Card,
15
+ Box,
16
+ Stack,
17
+ Typography,
18
+ Grid2 as Grid,
19
+ Skeleton,
20
+ } from "@mui/material";
21
+
22
+ // Custom components
23
+ import { FlexBox } from "@evenicanpm/admin-core/components/flex-box";
24
+ import {
25
+ TablePagination,
26
+ TableSkeleton,
27
+ } from "@evenicanpm/admin-integrate/components/data-table";
28
+ import { ListFilter } from "@evenicanpm/admin-integrate/components/list-filter";
29
+
30
+ // Local components
31
+ import IntegrationListTable from "./integration-list-table";
32
+
33
+ // Types
34
+ import { Head } from "@evenicanpm/admin-integrate/components/data-table/table-header";
35
+ import { ListFilterOutput } from "@evenicanpm/admin-integrate/components/list-filter/list-filter-types";
36
+ import {
37
+ IntegrationListFilters,
38
+ integrationListFilters,
39
+ } from "@evenicanpm/admin-integrate/components/integration-list/integration-filter";
40
+ import { log } from "console";
41
+
42
+ // Returned from processExecutions query
43
+ export interface executionItem {
44
+ isScheduled: 0 | 1;
45
+ isTriggered: 0 | 1;
46
+ id: string;
47
+ name: string;
48
+ modified: string;
49
+ processIcon: string;
50
+ }
51
+
52
+ // Row height in terms of multiples of theme.spacing(1) (e.g. theme.spacing(7.5 & rows per page))
53
+ const rowHeight = 7.5;
54
+
55
+ interface Props {
56
+ heading: Head[];
57
+ data: GetProcessesQuery;
58
+ loading: boolean;
59
+ input: ProcessInput;
60
+ setInput: (newInput: ProcessInput) => void;
61
+ }
62
+
63
+ const IntegrationList: React.FC<Props> = (props: Props) => {
64
+ const t = useTranslations("Integrate.IntegrationList");
65
+
66
+ const theme = useTheme();
67
+ const styles = getStyles(theme);
68
+
69
+ const [items, setItems] = useState<Item[]>([]);
70
+ const [fetched, setFetched] = useState<boolean>(false); // prevents "empty list" msg from showing for split second
71
+ const [page, setPage] = useState(props.input.skip / props.input.top + 1);
72
+ const [perPage, setPerPage] = useState(props.input.top);
73
+
74
+ const handleChangePage = (
75
+ event: React.ChangeEvent<unknown>,
76
+ value: number,
77
+ ) => {
78
+ if (page !== value) {
79
+ setPage(value);
80
+ props.setInput({
81
+ ...props.input,
82
+ skip: props.input.top * (value - 1),
83
+ });
84
+ setFetched(false);
85
+ }
86
+ };
87
+
88
+ const handleSetFilters = (
89
+ filterUpdate: ListFilterOutput<IntegrationListFilters>,
90
+ ) => {
91
+ const { itemsPerPage, search, filters } = filterUpdate;
92
+ console.log("filters", filters?.multiSelectFilter);
93
+
94
+ props.setInput({
95
+ ...props.input,
96
+ top: itemsPerPage,
97
+ skip: 0,
98
+ search: search,
99
+ integrationTags:
100
+ filters?.multiSelectFilter?.map((f) => `${f.id}`) || null,
101
+ sortDesc: true,
102
+ });
103
+ setFetched(false);
104
+ setPerPage(itemsPerPage);
105
+ setPage(1);
106
+ };
107
+
108
+ const handleClearFilters = () => {
109
+ props.setInput({
110
+ ...props.input,
111
+ skip: 0,
112
+ integrationTags: null,
113
+ });
114
+ setFetched(false);
115
+ };
116
+
117
+ useEffect(() => {
118
+ if (props.data) {
119
+ setItems(props.data.processes.nodes as Item[]);
120
+ setFetched(true);
121
+ }
122
+ }, [props.data]);
123
+
124
+ return (
125
+ <>
126
+ <Box sx={styles.list}>
127
+ <ListFilter<IntegrationListFilters>
128
+ setFilters={handleSetFilters}
129
+ clearFilters={handleClearFilters}
130
+ filters={integrationListFilters}
131
+ config={{
132
+ pagination: {
133
+ pageDefault: 10,
134
+ pageOptions: [10, 20, 50],
135
+ },
136
+ }}
137
+ />
138
+ </Box>
139
+
140
+ {props.loading && !props.data && (
141
+ <Card>
142
+ <TableSkeleton
143
+ heading={props.heading}
144
+ perPage={perPage}
145
+ rowSx={styles.rowSx}
146
+ />
147
+ </Card>
148
+ )}
149
+
150
+ {!props.loading && props.data && fetched && items.length > 0 ? (
151
+ <Box
152
+ sx={{
153
+ minHeight:
154
+ props.data.processes.pageInfo.resultsReturned < perPage &&
155
+ page === 1
156
+ ? theme.spacing(
157
+ rowHeight *
158
+ (props.data.processes.pageInfo.resultsReturned + 1),
159
+ )
160
+ : theme.spacing(rowHeight * (perPage + 1)),
161
+ }}
162
+ >
163
+ <Card>
164
+ <IntegrationListTable
165
+ heading={props.heading}
166
+ items={items}
167
+ input={props.input}
168
+ setInput={props.setInput}
169
+ rowSx={styles.rowSx}
170
+ />
171
+ </Card>
172
+ </Box>
173
+ ) : null}
174
+
175
+ {!props.loading && fetched && items.length === 0 ? (
176
+ <Card>
177
+ <FlexBox
178
+ justifyContent="center"
179
+ alignItems="center"
180
+ sx={{
181
+ minHeight: "335px",
182
+ marginBottom: theme.spacing(2),
183
+ }}
184
+ >
185
+ <Stack spacing={1} justifyContent="center" alignItems="center">
186
+ <Typography variant="h6" color="info">
187
+ {t("ListEmpty.title")}
188
+ </Typography>
189
+ <Typography variant="body1">{t("ListEmpty.body")}</Typography>
190
+ </Stack>
191
+ </FlexBox>
192
+ </Card>
193
+ ) : null}
194
+
195
+ {!props.loading && props.data && fetched && items.length > 0 ? (
196
+ <Grid container spacing={1} sx={styles.pagination}>
197
+ <Grid size={{ xs: 10.5 }}>
198
+ <FlexBox
199
+ justifyContent="center"
200
+ alignItems="center"
201
+ sx={styles.paginationBox}
202
+ >
203
+ <TablePagination
204
+ page={page}
205
+ onChange={handleChangePage}
206
+ count={Math.ceil(
207
+ props.data.processes.recordCount / props.input.top,
208
+ )}
209
+ />
210
+ </FlexBox>
211
+ </Grid>
212
+ <Grid size={{ xs: 1.5 }}>
213
+ <FlexBox
214
+ justifyContent="flex-end"
215
+ alignItems="center"
216
+ sx={{ height: "100%" }}
217
+ >
218
+ <Typography color="grey">
219
+ {`${props.data.processes.pageInfo.resultsReturned} ${t("Pagination.of")} ${props.data.processes.recordCount} ${t("Pagination.items")}`}
220
+ </Typography>
221
+ </FlexBox>
222
+ </Grid>
223
+ </Grid>
224
+ ) : null}
225
+ </>
226
+ );
227
+ };
228
+
229
+ const getStyles = (theme: Theme) => {
230
+ return {
231
+ title: {
232
+ display: "flex",
233
+ alignItems: "center",
234
+ height: "100%",
235
+ },
236
+ typography: {
237
+ fontWeight: "medium",
238
+ },
239
+ button: {
240
+ borderWidth: 2,
241
+ borderRadius: 1,
242
+ marginRight: theme.spacing(1),
243
+ },
244
+ iconButton: {
245
+ borderRadius: 1,
246
+ },
247
+ pagination: {
248
+ marginTop: theme.spacing(2),
249
+ },
250
+ list: {
251
+ margin: theme.spacing(2, 0),
252
+ },
253
+ paginationBox: {
254
+ paddingLeft: theme.spacing(15),
255
+ },
256
+ rowSx: {
257
+ height: theme.spacing(rowHeight),
258
+ },
259
+ };
260
+ };
261
+
262
+ export default IntegrationList;
@@ -1,17 +1,24 @@
1
- import React from "react";
1
+ import React, { useState } from "react";
2
2
  import { useTranslations } from "next-intl";
3
3
 
4
4
  // MUI
5
5
  import { Box } from "@mui/material";
6
6
 
7
+ // API
8
+ import { getProcesses } from "@evenicanpm/admin-integrate/api/Integrations/queries";
9
+ import { ProcessInput } from "@evenicanpm/admin-core/api/e4/graphqlRequestSdk";
10
+
7
11
  // Custom components
8
12
  import {
9
13
  IntegrateBreadcrumbs,
10
14
  Crumb,
11
15
  } from "@evenicanpm/admin-integrate/components/breadcrumbs";
16
+ import { Head } from "@evenicanpm/admin-integrate/components/data-table/table-header";
17
+ import IntegrationList from "./integration-list";
12
18
 
13
19
  export default function Integrations() {
14
20
  const t = useTranslations("Integrate");
21
+ const l = useTranslations("Integrate.IntegrationList.ListHeader");
15
22
 
16
23
  const breadcrumbs: Crumb[] = [
17
24
  {
@@ -23,9 +30,65 @@ export default function Integrations() {
23
30
  },
24
31
  ];
25
32
 
33
+ const heading: Head[] = [
34
+ {
35
+ id: "name",
36
+ label: l("integrationName"),
37
+ align: "left",
38
+ width: "30%",
39
+ sortable: true,
40
+ },
41
+ {
42
+ id: "id",
43
+ label: l("id"),
44
+ align: "left",
45
+ width: "15%",
46
+ },
47
+ {
48
+ id: "isScheduled",
49
+ label: l("status"),
50
+ align: "center",
51
+ width: "30%",
52
+ },
53
+ {
54
+ id: "modified",
55
+ label: l("lastModified"),
56
+ align: "center",
57
+ width: "15%",
58
+ sortable: true,
59
+ },
60
+ {
61
+ id: "view",
62
+ label: l("actions"),
63
+ align: "center",
64
+ width: "5%",
65
+ },
66
+ ];
67
+
68
+ // TODO: initialize input based on url query params
69
+ const [input, setInput] = useState<ProcessInput>({
70
+ skip: 0,
71
+ sort: "modified",
72
+ sortDesc: true,
73
+ top: 10,
74
+ integrationTags: null,
75
+ search: null,
76
+ });
77
+
78
+ const { data, isLoading } = getProcesses.useData({
79
+ input: input,
80
+ });
81
+
26
82
  return (
27
83
  <Box>
28
84
  <IntegrateBreadcrumbs breadcrumbs={breadcrumbs} />
85
+ <IntegrationList
86
+ heading={heading}
87
+ data={data}
88
+ loading={isLoading}
89
+ input={input}
90
+ setInput={setInput}
91
+ />
29
92
  </Box>
30
93
  );
31
94
  }