@evenicanpm/admin-integrate 1.1.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.
Files changed (114) hide show
  1. package/documents/Process/fragments.ts +13 -0
  2. package/documents/Process/list.ts +27 -0
  3. package/documents/ProcessSchedule/fragments.ts +24 -0
  4. package/documents/ProcessSchedule/read.ts +12 -0
  5. package/documents/ProcessScheduleGroup/fragments.ts +29 -0
  6. package/documents/ProcessScheduleGroup/read.ts +12 -0
  7. package/documents/ScheduledProcess/fragments.ts +24 -0
  8. package/documents/ScheduledProcess/list.ts +27 -0
  9. package/package.json +21 -17
  10. package/src/api/Integrations/queries/get-process.query.ts +48 -0
  11. package/src/api/Integrations/queries/get-process.server.ts +9 -0
  12. package/src/api/Integrations/queries/index.ts +1 -0
  13. package/src/api/dashboard/queries/get-process-executions.query.ts +33 -36
  14. package/src/api/dashboard/queries/get-process-executions.server.ts +4 -4
  15. package/src/api/scheduler/queries/get-process-schedule-group-details.query.ts +45 -0
  16. package/src/api/scheduler/queries/get-process-schedule-group-details.server.ts +14 -0
  17. package/src/api/scheduler/queries/get-process-schedule.query.ts +45 -0
  18. package/src/api/scheduler/queries/get-process-schedule.server.ts +14 -0
  19. package/src/api/scheduler/queries/get-scheduled-processes.query.ts +45 -0
  20. package/src/api/scheduler/queries/get-scheduled-processes.server.ts +11 -0
  21. package/src/api/scheduler/queries/index.ts +3 -0
  22. package/src/components/breadcrumbs/breadcrumbs.tsx +23 -26
  23. package/src/components/breadcrumbs/index.ts +1 -1
  24. package/src/components/breadcrumbs/types.tsx +4 -0
  25. package/src/components/button/outlined-icon-button.tsx +26 -38
  26. package/src/components/core/date-time-popover.tsx +32 -0
  27. package/src/components/core/edited-typography.tsx +20 -0
  28. package/src/components/core/index.ts +3 -0
  29. package/src/components/core/loading.tsx +6 -6
  30. package/src/components/core/title-edit.tsx +122 -0
  31. package/src/components/dashboard-list/dashboard-list-row.tsx +114 -115
  32. package/src/components/dashboard-list/dashboard-list.tsx +119 -119
  33. package/src/components/data-table/table-header.tsx +85 -81
  34. package/src/components/data-table/table-pagination.tsx +27 -27
  35. package/src/components/data-table/table-row.tsx +14 -14
  36. package/src/components/data-table/table-skeleton.tsx +34 -34
  37. package/src/components/execution/execution-filter.ts +22 -22
  38. package/src/components/execution/execution-status-icon.tsx +16 -17
  39. package/src/components/execution/execution-tag.tsx +81 -80
  40. package/src/components/footer/footer.tsx +34 -34
  41. package/src/components/header/dashboard-list-header.tsx +64 -63
  42. package/src/components/header/execution-details-header.tsx +102 -129
  43. package/src/components/header/header.tsx +27 -27
  44. package/src/components/header/index.ts +1 -0
  45. package/src/components/header/integration-details-header.tsx +99 -0
  46. package/src/components/icons/up-down.tsx +15 -15
  47. package/src/components/integration-list/index.ts +2 -0
  48. package/src/components/integration-list/integration-filter.ts +21 -0
  49. package/src/components/integration-list/integration-status-icon.tsx +33 -0
  50. package/src/components/integration-list/integration-tag.tsx +77 -0
  51. package/src/components/integration-view/edges/default-edge.tsx +18 -17
  52. package/src/components/integration-view/elk-layout-options.ts +26 -26
  53. package/src/components/integration-view/elk-types.ts +16 -12
  54. package/src/components/integration-view/flow-types.tsx +5 -5
  55. package/src/components/integration-view/integration-view.tsx +132 -130
  56. package/src/components/integration-view/nodes/base-node.tsx +50 -0
  57. package/src/components/integration-view/nodes/connection-node.tsx +8 -8
  58. package/src/components/integration-view/nodes/entry-node.tsx +8 -8
  59. package/src/components/integration-view/nodes/group-node.tsx +47 -6
  60. package/src/components/integration-view/nodes/task-node.tsx +58 -13
  61. package/src/components/integration-view/temp-data/initialElements.tsx +224 -0
  62. package/src/components/integration-view/types.ts +6 -6
  63. package/src/components/layouts/main-layout.tsx +13 -13
  64. package/src/components/layouts/root-container.tsx +25 -25
  65. package/src/components/link-cards/link-card.tsx +23 -23
  66. package/src/components/link-cards/styles.ts +15 -15
  67. package/src/components/list-filter/date-filter.tsx +41 -45
  68. package/src/components/list-filter/date-range-filter.tsx +78 -77
  69. package/src/components/list-filter/list-filter-types.ts +38 -38
  70. package/src/components/list-filter/list-filter.tsx +363 -354
  71. package/src/components/list-filter/multi-select-filter.tsx +94 -95
  72. package/src/components/list-filter/select-filter.tsx +67 -64
  73. package/src/components/scheduler/day-selector.tsx +174 -0
  74. package/src/components/scheduler/day-tag.tsx +73 -0
  75. package/src/components/scheduler/index.ts +4 -0
  76. package/src/components/scheduler/schedule-filter.ts +25 -0
  77. package/src/components/scheduler/schedule-icon.tsx +87 -0
  78. package/src/components/scheduler/scheduler-types.ts +40 -0
  79. package/src/hooks/use-breadcrumbs.ts +31 -31
  80. package/src/hooks/use-recurrence.tsx +13 -0
  81. package/src/hooks/use-timestamp.tsx +21 -21
  82. package/src/pages/dashboard/dashboard-link-cards.tsx +99 -99
  83. package/src/pages/dashboard/dashboard-list-sections.tsx +130 -125
  84. package/src/pages/dashboard/dashboard.tsx +6 -6
  85. package/src/pages/execution-details/execution-details.tsx +23 -29
  86. package/src/pages/executions/executions-list/executions-list-row.tsx +101 -104
  87. package/src/pages/executions/executions-list/executions-list-table.tsx +45 -53
  88. package/src/pages/executions/executions-list/executions-list.tsx +219 -212
  89. package/src/pages/executions/executions.tsx +67 -72
  90. package/src/pages/integrations/integration-list/index.ts +1 -0
  91. package/src/pages/integrations/integration-list/integration-list-row.tsx +147 -0
  92. package/src/pages/integrations/integration-list/integration-list-table.tsx +78 -0
  93. package/src/pages/integrations/integration-list/integration-list.tsx +262 -0
  94. package/src/pages/integrations/integrations.tsx +83 -28
  95. package/src/pages/scheduler/schedule-drawer/edited/edited-types.ts +17 -0
  96. package/src/pages/scheduler/schedule-drawer/edited/index.ts +1 -0
  97. package/src/pages/scheduler/schedule-drawer/index.ts +1 -0
  98. package/src/pages/scheduler/schedule-drawer/schedule-details-integrations.tsx +227 -0
  99. package/src/pages/scheduler/schedule-drawer/schedule-details-main.tsx +292 -0
  100. package/src/pages/scheduler/schedule-drawer/schedule-details-name.tsx +77 -0
  101. package/src/pages/scheduler/schedule-drawer/schedule-details-skeleton.tsx +64 -0
  102. package/src/pages/scheduler/schedule-drawer/schedule-details.tsx +42 -0
  103. package/src/pages/scheduler/schedule-drawer/schedule-drawer-buttons.tsx +72 -0
  104. package/src/pages/scheduler/schedule-drawer/schedule-drawer-group.tsx +103 -0
  105. package/src/pages/scheduler/schedule-drawer/schedule-drawer-legacy.tsx +94 -0
  106. package/src/pages/scheduler/schedule-drawer/schedule-drawer.tsx +101 -0
  107. package/src/pages/scheduler/schedule-drawer/sortable-integration-row.tsx +93 -0
  108. package/src/pages/scheduler/scheduler.tsx +106 -30
  109. package/src/pages/scheduler/schedules-list/index.ts +1 -0
  110. package/src/pages/scheduler/schedules-list/schedules-list-row.tsx +235 -0
  111. package/src/pages/scheduler/schedules-list/schedules-list-table.tsx +74 -0
  112. package/src/pages/scheduler/schedules-list/schedules-list.tsx +280 -0
  113. package/tsconfig.json +29 -26
  114. package/src/components/integration-view/temp-data/initialElements.ts +0 -238
@@ -4,32 +4,32 @@ import { useTranslations } from "next-intl";
4
4
  import React, { useEffect, useState } from "react";
5
5
 
6
6
  import {
7
- Search,
8
- FilterAltOutlined,
9
- CloseOutlined,
10
- ArrowForwardIos,
11
- Delete,
7
+ Search,
8
+ FilterAltOutlined,
9
+ CloseOutlined,
10
+ ArrowForwardIos,
11
+ Delete,
12
12
  } from "@mui/icons-material";
13
13
  import {
14
- Box,
15
- Button,
16
- IconButton,
17
- TextField,
18
- InputAdornment,
19
- Select,
20
- MenuItem,
21
- Theme,
22
- useTheme,
23
- Paper,
24
- Popper,
25
- Badge,
26
- ClickAwayListener,
14
+ Box,
15
+ Button,
16
+ IconButton,
17
+ TextField,
18
+ InputAdornment,
19
+ Select,
20
+ MenuItem,
21
+ Theme,
22
+ useTheme,
23
+ Paper,
24
+ Popper,
25
+ Badge,
26
+ ClickAwayListener,
27
27
  } from "@mui/material";
28
28
  import {
29
- Filters,
30
- FilterResult,
31
- ListFilterOutput,
32
- FilterConfig,
29
+ Filters,
30
+ FilterResult,
31
+ ListFilterOutput,
32
+ FilterConfig,
33
33
  } from "./list-filter-types";
34
34
 
35
35
  import { OutlinedIconButton } from "../button";
@@ -39,371 +39,380 @@ import DateFilterComponent from "./date-filter";
39
39
  import DateRangeFilterComponent from "./date-range-filter";
40
40
 
41
41
  interface FilterProps<FilterShape extends Filters> {
42
- setFilters: (filterUpdate: ListFilterOutput<FilterShape>) => void;
43
- clearFilters: () => void;
44
- filters?: FilterShape;
45
- config?: FilterConfig;
46
- manualClose?: boolean;
42
+ setFilters: (filterUpdate: ListFilterOutput<FilterShape>) => void;
43
+ clearFilters: () => void;
44
+ filters?: FilterShape;
45
+ config?: FilterConfig;
46
+ manualClose?: boolean;
47
47
  }
48
48
 
49
49
  type FilterErrors<FilterShape extends Filters> = {
50
- [Property in keyof FilterShape]: boolean;
50
+ [Property in keyof FilterShape]: boolean;
51
51
  };
52
52
 
53
53
  const ListFilter = function ListFilter<FilterShape extends Filters>(
54
- props: FilterProps<FilterShape>,
54
+ props: FilterProps<FilterShape>,
55
55
  ) {
56
- const t = useTranslations("Integrate.Filter");
57
- const theme = useTheme();
58
- const styles = getStyles(theme);
56
+ const t = useTranslations("Integrate.Filter");
57
+ const theme = useTheme();
58
+ const styles = getStyles(theme);
59
59
 
60
- const {
61
- setFilters,
62
- clearFilters,
63
- filters = {} as FilterShape,
64
- manualClose = false,
65
- config: { pagination = {}, search = {} } = {},
66
- } = props;
60
+ const {
61
+ setFilters,
62
+ clearFilters,
63
+ filters = {} as FilterShape,
64
+ manualClose = false,
65
+ config: { pagination = {}, search = {} } = {},
66
+ } = props;
67
67
 
68
- const { pageOptions = [10, 20, 30], pageDefault = 10 } = pagination || {};
68
+ const { pageOptions = [10, 20, 30], pageDefault = 10 } = pagination || {};
69
69
 
70
- const { label: searchLabel = t("defaultSearchLabel") } = search || {};
70
+ const { label: searchLabel = t("defaultSearchLabel") } = search || {};
71
71
 
72
- const defaultValues = Object.entries(filters).reduce(
73
- (values, [key, filter]) => {
74
- if (filter.type === "select" && filter.value)
75
- return {
76
- ...values,
77
- [key]: filter.options.find((o) => o.id === filter.value[0].id),
78
- };
79
- if (filter.type === "multiSelect" && filter.value)
80
- return {
81
- ...values,
82
- [key]: filter.options.filter((o) =>
83
- filter.value.find((v) => v.id === o.id),
84
- ),
85
- };
72
+ const defaultValues = Object.entries(filters).reduce(
73
+ (values, [key, filter]) => {
74
+ if (filter.type === "select" && filter.value)
75
+ return {
76
+ ...values,
77
+ // @ts-ignore
78
+ [key]: filter.options.find((o) => o.id === filter.value[0].id),
79
+ };
80
+ if (filter.type === "multiSelect" && filter.value)
81
+ return {
82
+ ...values,
83
+ [key]: filter.options.filter((o) =>
84
+ // @ts-ignore
85
+ filter.value.find((v) => v.id === o.id),
86
+ ),
87
+ };
86
88
 
87
- return { ...values, [key]: filter.value };
88
- },
89
- {},
90
- ) as FilterResult<FilterShape>;
89
+ return { ...values, [key]: filter.value };
90
+ },
91
+ {},
92
+ ) as FilterResult<FilterShape>;
91
93
 
92
- const filterErrorsBase: FilterErrors<FilterShape> = Object.keys(
93
- filters,
94
- ).reduce(
95
- (values, key) => ({
96
- ...values,
97
- [key]: false,
98
- }),
99
- {},
100
- ) as FilterErrors<FilterShape>;
94
+ const filterErrorsBase: FilterErrors<FilterShape> = Object.keys(
95
+ filters,
96
+ ).reduce(
97
+ (values, key) => ({
98
+ ...values,
99
+ [key]: false,
100
+ }),
101
+ {},
102
+ ) as FilterErrors<FilterShape>;
101
103
 
102
- const [perPage, setPerPage] = useState(pageDefault);
103
- const [searchTerm, setSearchTerm] = useState("");
104
- const [open, setOpen] = useState(false);
105
- const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
106
- const [filterErrors, setFilterErrors] = useState(filterErrorsBase);
107
- const [publishedFilterValues, setPublishedFilterValues] =
108
- useState<FilterResult<FilterShape>>(defaultValues);
109
- const [filterValues, setFilterValues] = useState<FilterResult<FilterShape>>(
110
- publishedFilterValues,
111
- );
104
+ const [perPage, setPerPage] = useState(pageDefault);
105
+ const [searchTerm, setSearchTerm] = useState("");
106
+ const [open, setOpen] = useState(false);
107
+ const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
108
+ const [filterErrors, setFilterErrors] = useState(filterErrorsBase);
109
+ const [publishedFilterValues, setPublishedFilterValues] =
110
+ useState<FilterResult<FilterShape>>(defaultValues);
111
+ const [filterValues, setFilterValues] = useState<FilterResult<FilterShape>>(
112
+ publishedFilterValues,
113
+ );
112
114
 
113
- const activeFilterCount = Object.entries(publishedFilterValues).reduce(
114
- (sum, [key, value]) => {
115
- if (!value) return sum;
115
+ const activeFilterCount = Object.entries(publishedFilterValues).reduce(
116
+ (sum, [key, value]) => {
117
+ if (!value) return sum;
116
118
 
117
- const activeFilters =
118
- filters[key].type === "multiSelect" ? value.length : 1;
119
+ const activeFilters =
120
+ filters[key].type === "multiSelect" ? value.length : 1;
119
121
 
120
- return sum + activeFilters;
121
- },
122
- 0,
123
- );
122
+ return sum + activeFilters;
123
+ },
124
+ 0,
125
+ );
124
126
 
125
- const updateFilterValue = (name, value) => {
126
- if (Array.isArray(value) && !value.length) value = null;
127
- setFilterValues({
128
- ...filterValues,
129
- [name]: value || null,
130
- });
131
- };
127
+ const updateFilterValue = (name: string, value: any) => {
128
+ if (Array.isArray(value) && !value.length) value = null;
129
+ setFilterValues({
130
+ ...filterValues,
131
+ [name]: value || null,
132
+ });
133
+ };
132
134
 
133
- const updateFilterErrors = (name: keyof FilterShape, error: boolean) => {
134
- setFilterErrors({
135
- ...filterErrors,
136
- [name]: error,
137
- });
138
- };
135
+ const updateFilterErrors = (name: keyof FilterShape, error: boolean) => {
136
+ setFilterErrors({
137
+ ...filterErrors,
138
+ [name]: error,
139
+ });
140
+ };
139
141
 
140
- const sendFilterOutput = (
141
- update: {
142
- search?: string;
143
- itemsPerPage?: number;
144
- filters?: FilterResult<FilterShape>;
145
- } = {},
146
- ) => {
147
- setFilters({
148
- search: searchTerm,
149
- itemsPerPage: perPage,
150
- filters: publishedFilterValues,
151
- ...update,
152
- });
153
- };
142
+ const sendFilterOutput = (
143
+ update: {
144
+ search?: string;
145
+ itemsPerPage?: number;
146
+ filters?: FilterResult<FilterShape>;
147
+ } = {},
148
+ ) => {
149
+ setFilters({
150
+ search: searchTerm,
151
+ itemsPerPage: perPage,
152
+ filters: publishedFilterValues,
153
+ ...update,
154
+ });
155
+ };
154
156
 
155
- const handleFilterMenuClick = (event: React.MouseEvent<HTMLElement>) => {
156
- if (!open) {
157
- setFilterValues(publishedFilterValues);
158
- setAnchorEl(event.currentTarget);
159
- }
160
- setOpen(!open);
161
- };
157
+ const handleFilterMenuClick = (event: React.MouseEvent<HTMLElement>) => {
158
+ if (!open) {
159
+ setFilterValues(publishedFilterValues);
160
+ setAnchorEl(event.currentTarget);
161
+ }
162
+ setOpen(!open);
163
+ };
162
164
 
163
- const handleFilterSet = () => {
164
- setPublishedFilterValues(filterValues);
165
- sendFilterOutput({ filters: filterValues });
166
- setOpen(false);
167
- };
165
+ const handleFilterSet = () => {
166
+ if (Object.values(filterValues).some((value) => value !== null)) {
167
+ setPublishedFilterValues(filterValues);
168
+ sendFilterOutput({ filters: filterValues });
169
+ setOpen(false);
170
+ } else {
171
+ handleFilterClear();
172
+ }
173
+ };
168
174
 
169
- const handleFilterClear = () => {
170
- const emptyFilterValues = Object.keys(filterValues).reduce(
171
- (values, key) => ({ ...values, [key]: null }),
172
- {},
173
- ) as FilterResult<FilterShape>;
174
- setPublishedFilterValues(emptyFilterValues);
175
- setOpen(false);
176
- clearFilters();
177
- };
175
+ const handleFilterClear = () => {
176
+ const emptyFilterValues = Object.keys(filterValues).reduce(
177
+ (values, key) => ({ ...values, [key]: null }),
178
+ {},
179
+ ) as FilterResult<FilterShape>;
180
+ setPublishedFilterValues(emptyFilterValues);
181
+ setOpen(false);
182
+ clearFilters();
183
+ };
178
184
 
179
- const debounceSendSearchOutput = debounce(
180
- (s) => sendFilterOutput({ search: s }),
181
- 400,
182
- );
185
+ const debounceSendSearchOutput = debounce(
186
+ (s) => sendFilterOutput({ search: s }),
187
+ 400,
188
+ );
183
189
 
184
- useEffect(() => {
185
- debounceSendSearchOutput(searchTerm);
186
- return debounceSendSearchOutput.cancel;
187
- }, [searchTerm]);
190
+ useEffect(() => {
191
+ debounceSendSearchOutput(searchTerm);
192
+ return debounceSendSearchOutput.cancel;
193
+ }, [searchTerm]);
188
194
 
189
- const handlePerPageUpdate = (itemsPerPage: number) => {
190
- setPerPage(itemsPerPage);
191
- sendFilterOutput({ itemsPerPage });
192
- };
195
+ const handlePerPageUpdate = (itemsPerPage: number) => {
196
+ setPerPage(itemsPerPage);
197
+ sendFilterOutput({ itemsPerPage });
198
+ };
193
199
 
194
- return (
195
- <Box sx={styles.filter}>
196
- {Object.keys(filters).length ? (
197
- <ClickAwayListener
198
- onClickAway={() => !manualClose && setOpen(false)}
199
- mouseEvent="onMouseUp"
200
- >
201
- <Box>
202
- <Badge
203
- badgeContent={activeFilterCount}
204
- color="error"
205
- invisible={!activeFilterCount}
206
- >
207
- <OutlinedIconButton
208
- color="info"
209
- onClick={(e) => handleFilterMenuClick(e)}
210
- >
211
- <FilterAltOutlined />
212
- </OutlinedIconButton>
213
- </Badge>
214
- <Popper open={open} anchorEl={anchorEl} placement="bottom-start">
215
- <Paper sx={styles.popOut} elevation={3}>
216
- <Box sx={styles.closeRow}>
217
- {manualClose ? (
218
- <IconButton
219
- centerRipple={false}
220
- onClick={() => setOpen(false)}
221
- >
222
- <CloseOutlined />
223
- </IconButton>
224
- ) : (
225
- <Box sx={styles.bufferRow} />
226
- )}
227
- </Box>
200
+ return (
201
+ <Box sx={styles.filter}>
202
+ {Object.keys(filters).length ? (
203
+ <ClickAwayListener
204
+ onClickAway={() => !manualClose && setOpen(false)}
205
+ mouseEvent="onMouseUp"
206
+ >
207
+ <Box>
208
+ <Badge
209
+ badgeContent={activeFilterCount}
210
+ color="error"
211
+ invisible={!activeFilterCount}
212
+ >
213
+ <OutlinedIconButton
214
+ color="info"
215
+ onClick={(e) => handleFilterMenuClick(e)}
216
+ >
217
+ <FilterAltOutlined />
218
+ </OutlinedIconButton>
219
+ </Badge>
220
+ <Popper open={open} anchorEl={anchorEl} placement="bottom-start">
221
+ <Paper sx={styles.popOut} elevation={3}>
222
+ <Box sx={styles.closeRow}>
223
+ {manualClose ? (
224
+ <IconButton
225
+ centerRipple={false}
226
+ onClick={() => setOpen(false)}
227
+ >
228
+ <CloseOutlined />
229
+ </IconButton>
230
+ ) : (
231
+ <Box sx={styles.bufferRow} />
232
+ )}
233
+ </Box>
228
234
 
229
- <Box sx={styles.filterRows}>
230
- {Object.entries(filters)
231
- .sort(([keyA, { sort: a }], [keyB, { sort: b }]) => a - b)
232
- .map(([name, filter]) => {
233
- switch (filter.type) {
234
- case "select":
235
- return (
236
- <SelectFilterComponent
237
- key={name}
238
- filter={filter}
239
- current={
240
- filterValues[name] as (typeof filter)["value"]
241
- }
242
- onSet={(v) => updateFilterValue(name, v)}
243
- onError={(e) => updateFilterErrors(name, e)}
244
- />
245
- );
246
- case "multiSelect":
247
- return (
248
- <MultiSelectFilterComponent
249
- key={name}
250
- filter={filter}
251
- current={
252
- filterValues[name] as (typeof filter)["value"]
253
- }
254
- onSet={(v) => updateFilterValue(name, v)}
255
- onError={(e) => updateFilterErrors(name, e)}
256
- />
257
- );
258
- case "date":
259
- return (
260
- <DateFilterComponent
261
- key={name}
262
- filter={filter}
263
- current={
264
- filterValues[name] as (typeof filter)["value"]
265
- }
266
- onSet={(v) => updateFilterValue(name, v)}
267
- onError={(e) => updateFilterErrors(name, e)}
268
- />
269
- );
270
- case "dateRange":
271
- return (
272
- <DateRangeFilterComponent
273
- key={name}
274
- filter={filter}
275
- current={
276
- filterValues[name] as (typeof filter)["value"]
277
- }
278
- onSet={(v) => updateFilterValue(name, v)}
279
- onError={(e) => updateFilterErrors(name, e)}
280
- />
281
- );
282
- }
283
- })}
284
- <Box sx={styles.buttonRow}>
285
- {activeFilterCount ? (
286
- <Button
287
- variant="contained"
288
- color="error"
289
- startIcon={<Delete />}
290
- onClick={handleFilterClear}
291
- >
292
- Clear
293
- </Button>
294
- ) : (
295
- ""
296
- )}
235
+ <Box sx={styles.filterRows}>
236
+ {Object.entries(filters)
237
+ .sort(
238
+ ([, { sort: a }], [, { sort: b }]) =>
239
+ (a ?? Infinity) - (b ?? Infinity),
240
+ )
241
+ .map(([name, filter]) => {
242
+ switch (filter.type) {
243
+ case "select":
244
+ return (
245
+ <SelectFilterComponent
246
+ key={name}
247
+ filter={filter}
248
+ current={
249
+ filterValues[name] as (typeof filter)["value"]
250
+ }
251
+ onSet={(v) => updateFilterValue(name, v)}
252
+ onError={(e) => updateFilterErrors(name, e)}
253
+ />
254
+ );
255
+ case "multiSelect":
256
+ return (
257
+ <MultiSelectFilterComponent
258
+ key={name}
259
+ filter={filter}
260
+ current={
261
+ filterValues[name] as (typeof filter)["value"]
262
+ }
263
+ onSet={(v) => updateFilterValue(name, v)}
264
+ onError={(e) => updateFilterErrors(name, e)}
265
+ />
266
+ );
267
+ case "date":
268
+ return (
269
+ <DateFilterComponent
270
+ key={name}
271
+ filter={filter}
272
+ current={
273
+ filterValues[name] as (typeof filter)["value"]
274
+ }
275
+ onSet={(v) => updateFilterValue(name, v)}
276
+ onError={(e) => updateFilterErrors(name, e)}
277
+ />
278
+ );
279
+ case "dateRange":
280
+ return (
281
+ <DateRangeFilterComponent
282
+ key={name}
283
+ filter={filter}
284
+ current={
285
+ filterValues[name] as (typeof filter)["value"]
286
+ }
287
+ onSet={(v) => updateFilterValue(name, v)}
288
+ onError={(e) => updateFilterErrors(name, e)}
289
+ />
290
+ );
291
+ }
292
+ })}
293
+ <Box sx={styles.buttonRow}>
294
+ {activeFilterCount ? (
295
+ <Button
296
+ variant="contained"
297
+ color="error"
298
+ startIcon={<Delete />}
299
+ onClick={handleFilterClear}
300
+ >
301
+ Clear
302
+ </Button>
303
+ ) : (
304
+ ""
305
+ )}
297
306
 
298
- <Button
299
- variant="contained"
300
- color="primary"
301
- startIcon={<ArrowForwardIos />}
302
- disabled={Object.values(filterErrors).some((v) => v)}
303
- onClick={handleFilterSet}
304
- >
305
- Filter
306
- </Button>
307
- </Box>
308
- </Box>
309
- </Paper>
310
- </Popper>
311
- </Box>
312
- </ClickAwayListener>
313
- ) : (
314
- ""
315
- )}
307
+ <Button
308
+ variant="contained"
309
+ color="info"
310
+ startIcon={<ArrowForwardIos />}
311
+ disabled={Object.values(filterErrors).some((v) => v)}
312
+ onClick={handleFilterSet}
313
+ >
314
+ Filter
315
+ </Button>
316
+ </Box>
317
+ </Box>
318
+ </Paper>
319
+ </Popper>
320
+ </Box>
321
+ </ClickAwayListener>
322
+ ) : (
323
+ ""
324
+ )}
316
325
 
317
- {search ? (
318
- <TextField
319
- sx={styles.search}
320
- variant="outlined"
321
- label={searchLabel}
322
- value={searchTerm}
323
- onChange={(e) => setSearchTerm(e.target.value)}
324
- slotProps={{
325
- input: {
326
- startAdornment: (
327
- <InputAdornment position="start">
328
- <Search />
329
- </InputAdornment>
330
- ),
331
- },
332
- }}
333
- />
334
- ) : (
335
- ""
336
- )}
337
- {pagination ? (
338
- <Select
339
- sx={styles.select}
340
- value={perPage}
341
- onChange={(e) => handlePerPageUpdate(e.target.value as number)}
342
- >
343
- {pageOptions.map((option) => (
344
- <MenuItem value={option}>
345
- {option} {t("itemsPerPage")}
346
- </MenuItem>
347
- ))}
348
- </Select>
349
- ) : (
350
- ""
351
- )}
352
- </Box>
353
- );
326
+ {search ? (
327
+ <TextField
328
+ sx={styles.search}
329
+ variant="outlined"
330
+ label={searchLabel}
331
+ value={searchTerm}
332
+ onChange={(e) => setSearchTerm(e.target.value)}
333
+ slotProps={{
334
+ input: {
335
+ startAdornment: (
336
+ <InputAdornment position="start">
337
+ <Search />
338
+ </InputAdornment>
339
+ ),
340
+ },
341
+ }}
342
+ />
343
+ ) : (
344
+ ""
345
+ )}
346
+ {pagination ? (
347
+ <Select
348
+ sx={styles.select}
349
+ value={perPage}
350
+ onChange={(e) => handlePerPageUpdate(e.target.value as number)}
351
+ >
352
+ {pageOptions.map((option) => (
353
+ <MenuItem value={option}>
354
+ {option} {t("itemsPerPage")}
355
+ </MenuItem>
356
+ ))}
357
+ </Select>
358
+ ) : (
359
+ ""
360
+ )}
361
+ </Box>
362
+ );
354
363
  };
355
364
 
356
365
  const getStyles = (theme: Theme) => {
357
- return {
358
- filter: {
359
- width: "100%",
360
- maxWidth: "600px",
361
- display: "flex",
362
- justifyContent: "center",
363
- alignItems: "center",
364
- columnGap: theme.spacing(2),
365
- },
366
- popOut: {
367
- width: "512px",
368
- padding: theme.spacing(2),
369
- paddingTop: theme.spacing(1),
370
- margin: theme.spacing(1, 0),
371
- },
372
- filterRows: {
373
- display: "flex",
374
- flexDirection: "column",
375
- rowGap: theme.spacing(2),
376
- },
377
- closeRow: {
378
- display: "flex",
379
- justifyContent: "end",
380
- },
381
- bufferRow: {
382
- paddingTop: theme.spacing(1),
383
- },
384
- buttonRow: {
385
- width: "100%",
386
- display: "flex",
387
- justifyContent: "end",
388
- columnGap: theme.spacing(1),
389
- },
390
- search: {
391
- "& .MuiOutlinedInput-root": {
392
- "& input": {
393
- padding: theme.spacing(1.5),
394
- },
395
- },
396
- backgroundColor: theme.palette.common.white,
397
- width: "100%",
398
- flexGrow: "1",
399
- },
400
- select: {
401
- "& .MuiSelect-select": {
402
- padding: theme.spacing(1.5),
403
- },
404
- backgroundColor: theme.palette.common.white,
405
- },
406
- };
366
+ return {
367
+ filter: {
368
+ width: "100%",
369
+ maxWidth: "600px",
370
+ display: "flex",
371
+ justifyContent: "center",
372
+ alignItems: "center",
373
+ columnGap: theme.spacing(2),
374
+ },
375
+ popOut: {
376
+ width: "512px",
377
+ padding: theme.spacing(2),
378
+ paddingTop: theme.spacing(1),
379
+ margin: theme.spacing(1, 0),
380
+ },
381
+ filterRows: {
382
+ display: "flex",
383
+ flexDirection: "column",
384
+ rowGap: theme.spacing(2),
385
+ },
386
+ closeRow: {
387
+ display: "flex",
388
+ justifyContent: "end",
389
+ },
390
+ bufferRow: {
391
+ paddingTop: theme.spacing(1),
392
+ },
393
+ buttonRow: {
394
+ width: "100%",
395
+ display: "flex",
396
+ justifyContent: "end",
397
+ columnGap: theme.spacing(1),
398
+ },
399
+ search: {
400
+ "& .MuiOutlinedInput-root": {
401
+ "& input": {
402
+ padding: theme.spacing(1.5),
403
+ },
404
+ },
405
+ backgroundColor: theme.palette.common.white,
406
+ width: "100%",
407
+ flexGrow: "1",
408
+ },
409
+ select: {
410
+ "& .MuiSelect-select": {
411
+ padding: theme.spacing(1.5),
412
+ },
413
+ backgroundColor: theme.palette.common.white,
414
+ },
415
+ };
407
416
  };
408
417
 
409
418
  export default ListFilter;