@evenicanpm/admin-integrate 1.8.1 → 2.0.1
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.
- package/documents/ConfigImport/configuration-import.ts +1 -1
- package/documents/Endpoint/list.ts +3 -3
- package/documents/Process/list.ts +3 -3
- package/documents/ProcessExecution/list.ts +2 -2
- package/documents/ProcessExport/read.ts +1 -1
- package/documents/ProcessSchedule/read.ts +1 -1
- package/documents/ProcessScheduleGroup/read.ts +3 -3
- package/documents/ProcessTask/fragments.ts +4 -0
- package/documents/ProcessTemplate/fragments.ts +30 -0
- package/documents/ProcessTemplate/list.ts +29 -0
- package/documents/ProcessTemplate/read.ts +1 -1
- package/documents/ScheduledProcess/list.ts +1 -1
- package/package.json +2 -2
- package/src/api/process-config/mutation/process-config-import.mutation.ts +1 -1
- package/src/api/templates/queries/get-field-values.query.ts +48 -0
- package/src/api/templates/queries/get-field-values.server.ts +9 -0
- package/src/api/templates/queries/index.ts +4 -0
- package/src/api/templates/queries/types.ts +2 -2
- package/src/api/templates/queries/validate-field-on-check.query.ts +22 -0
- package/src/api/templates/queries/validate-field-on-check.server.ts +30 -0
- package/src/api/templates/queries/validate-field-on-submit.server.ts +18 -0
- package/src/components/data-table/table-row.tsx +1 -1
- package/src/components/icons/up-down.tsx +1 -0
- package/src/components/integration-view/animated-svg.tsx +0 -1
- package/src/components/integration-view/elk-types.ts +1 -1
- package/src/components/integration-view/integration-view.tsx +274 -187
- package/src/components/integration-view/nodes/execution-entry-node.tsx +3 -2
- package/src/components/integration-view/nodes/task-node.tsx +1 -1
- package/src/components/integration-view/types.ts +2 -0
- package/src/components/integration-view/utils/mapping.ts +68 -48
- package/src/components/list-filter/list-filter-types.ts +6 -0
- package/src/components/list-filter/list-filter.tsx +21 -15
- package/src/components/list-filter/multi-select-filter.tsx +7 -7
- package/src/components/list-filter/select-filter.tsx +6 -6
- package/src/components/scheduler/day-selector.tsx +1 -1
- package/src/components/templatesV2/inputs/FormikPageBody.tsx +191 -0
- package/src/components/templatesV2/inputs/context/FormModeContext.tsx +31 -0
- package/src/components/templatesV2/inputs/context/TemplateContext.tsx +37 -0
- package/src/components/templatesV2/inputs/field-names.ts +8 -0
- package/src/components/templatesV2/inputs/form-type.ts +54 -0
- package/src/components/templatesV2/inputs/index.tsx +33 -0
- package/src/components/templatesV2/inputs/input-type.ts +23 -0
- package/src/components/templatesV2/inputs/primitives/CheckboxInput.tsx +45 -0
- package/src/components/templatesV2/inputs/primitives/NumberInput.tsx +58 -0
- package/src/components/templatesV2/inputs/primitives/SelectorInput.tsx +111 -0
- package/src/components/templatesV2/inputs/primitives/TextInput.tsx +55 -0
- package/src/components/templatesV2/inputs/primitives/TextWithQueryInput.tsx +292 -0
- package/src/components/templatesV2/inputs/primitives/index.ts +10 -0
- package/src/components/templatesV2/inputs/renderers/index.ts +2 -0
- package/src/components/templatesV2/inputs/renderers/renderFormControl.tsx +384 -0
- package/src/components/templatesV2/inputs/renderers/renderSearchInput.tsx +128 -0
- package/src/components/templatesV2/inputs/search/MultiTextSearchInput.tsx +87 -0
- package/src/components/templatesV2/inputs/search/SearchInputWrapper.tsx +113 -0
- package/src/components/templatesV2/inputs/search/TextSearchInput.tsx +309 -0
- package/src/components/templatesV2/inputs/search/index.ts +4 -0
- package/src/components/templatesV2/inputs/search/search-query-key.ts +23 -0
- package/src/components/templatesV2/inputs/search/useFieldValuesOptions.ts +58 -0
- package/src/components/templatesV2/inputs/search/useSearchInput.ts +79 -0
- package/src/components/templatesV2/inputs/table/InputTable.tsx +268 -0
- package/src/components/templatesV2/inputs/table/index.ts +1 -0
- package/src/components/templatesV2/inputs/utils/applyTemplateConfig.ts +28 -0
- package/src/components/templatesV2/inputs/utils/arrangeInputs.ts +52 -0
- package/src/components/templatesV2/inputs/utils/index.ts +6 -0
- package/src/components/templatesV2/inputs/utils/parseQueryName.ts +20 -0
- package/src/components/templatesV2/inputs/utils/renderReadOnlyText.tsx +26 -0
- package/src/components/templatesV2/inputs/utils/selectors.ts +69 -0
- package/src/components/templatesV2/inputs/utils/toStringArray.ts +23 -0
- package/src/components/templatesV2/pageForm.tsx +153 -0
- package/src/components/{templates → templatesV2}/templates-list/templates-list-row.tsx +2 -1
- package/src/components/{templates → templatesV2}/templates-list/templates-list-table.tsx +2 -2
- package/src/components/{templates → templatesV2}/templates-list/templates-list.tsx +19 -28
- package/src/components/{templates → templatesV2}/types.tsx +12 -12
- package/src/components/templatesV2/validation/buildZodSchema.ts +212 -0
- package/src/components/templatesV2/validation/zodFormikValidate.ts +17 -0
- package/src/components/templatesV2/wizard.tsx +746 -0
- package/src/pages/dashboard/dashboard-link-cards.tsx +0 -1
- package/src/pages/dashboard/dashboard-list-sections.tsx +1 -1
- package/src/pages/dashboard/dashboard.tsx +0 -1
- package/src/pages/executions/executions-list/executions-list-table.tsx +0 -1
- package/src/pages/executions/executions.tsx +1 -1
- package/src/pages/integration-create/integration-create-steps.ts +8 -0
- package/src/pages/integration-create/integration-create.tsx +81 -28
- package/src/pages/integration-create/pre-creation-step.tsx +9 -9
- package/src/pages/integration-create/select-step.tsx +3 -3
- package/src/pages/integration-create/template-list-step.tsx +28 -3
- package/src/pages/integration-create/template-options-step.tsx +119 -0
- package/src/pages/integration-create/wizard-step.tsx +5 -4
- package/src/pages/integration-details/import-process/copy-tasks-list.tsx +1 -3
- package/src/pages/integration-details/integration-details.tsx +1 -1
- package/src/pages/integration-details/task-drawer/add-task.tsx +4 -4
- package/src/pages/integration-details/task-drawer/edit-task.tsx +5 -5
- package/src/pages/integration-details/task-drawer/task-drawer.tsx +2 -2
- package/src/components/templates/inputs/index.tsx +0 -1392
- package/src/components/templates/wizard.tsx +0 -737
- /package/src/components/{templates → templatesV2}/index.ts +0 -0
- /package/src/components/{templates → templatesV2}/template-filter.ts +0 -0
- /package/src/components/{templates → templatesV2}/templates-list/index.ts +0 -0
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
useTheme,
|
|
10
10
|
} from "@mui/material";
|
|
11
11
|
import { Handle, type NodeProps, Position } from "@xyflow/react";
|
|
12
|
+
import type { ReactNode } from "react";
|
|
12
13
|
|
|
13
14
|
export function ExecutionEntryNode(props: NodeProps): JSX.Element {
|
|
14
15
|
const theme = useTheme();
|
|
@@ -50,7 +51,7 @@ export function ExecutionEntryNode(props: NodeProps): JSX.Element {
|
|
|
50
51
|
}}
|
|
51
52
|
>
|
|
52
53
|
<CardContent sx={styles.cardContent}>
|
|
53
|
-
{props?.data?.icon as
|
|
54
|
+
{props?.data?.icon as ReactNode}
|
|
54
55
|
<Typography variant="h6" sx={styles.label}>
|
|
55
56
|
{props?.data?.label as string}
|
|
56
57
|
</Typography>
|
|
@@ -61,7 +62,7 @@ export function ExecutionEntryNode(props: NodeProps): JSX.Element {
|
|
|
61
62
|
<>
|
|
62
63
|
<Divider sx={styles.divider} />
|
|
63
64
|
<CardContent sx={styles.cardContent}>
|
|
64
|
-
{props?.data?.icon2 as
|
|
65
|
+
{props?.data?.icon2 as ReactNode}
|
|
65
66
|
<Typography variant="h6" sx={styles.label}>
|
|
66
67
|
{props?.data?.label2 as string}
|
|
67
68
|
</Typography>
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { MarkerType } from "@xyflow/react";
|
|
2
|
+
import _ from "lodash";
|
|
3
|
+
import type {
|
|
3
4
|
ConfigExport,
|
|
5
|
+
Process,
|
|
6
|
+
ProcessExecutionDetails,
|
|
4
7
|
ProcessExport,
|
|
5
8
|
ProcessTask,
|
|
6
|
-
ProcessTaskExport,
|
|
7
9
|
ProcessTaskExecution,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import {
|
|
10
|
+
ProcessTaskExport,
|
|
11
|
+
} from "../../../../../admin-core/src/api/e4/graphqlRequestSdk";
|
|
12
|
+
import type {
|
|
11
13
|
ElkExtendedEdgeWithFlow,
|
|
12
14
|
ElkNodeWithFlow,
|
|
13
15
|
GroupNode,
|
|
14
16
|
// ProcessTaskExecutionNode,
|
|
15
17
|
// ProcessTaskNode,
|
|
16
18
|
} from "../elk-types";
|
|
17
|
-
import { MarkerType } from "@xyflow/react";
|
|
18
|
-
import _ from "lodash";
|
|
19
19
|
|
|
20
20
|
const markerEnd = {
|
|
21
21
|
type: MarkerType.ArrowClosed,
|
|
@@ -23,11 +23,23 @@ const markerEnd = {
|
|
|
23
23
|
height: 20,
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
+
type TaskRecord = Record<string, unknown>;
|
|
27
|
+
type BuildGroupsTask = (group: GroupNode) => TaskRecord[];
|
|
28
|
+
type BuildNodeMapper = (
|
|
29
|
+
task: TaskRecord,
|
|
30
|
+
index: number,
|
|
31
|
+
partitionLen: number,
|
|
32
|
+
parentId: string,
|
|
33
|
+
) => ElkNodeWithFlow;
|
|
34
|
+
export type AddHandler = unknown;
|
|
35
|
+
const asIdentifierString = (value: unknown) =>
|
|
36
|
+
typeof value === "string" || typeof value === "number" ? String(value) : "";
|
|
37
|
+
|
|
26
38
|
export function mapToCamelCase(processTask: ProcessTaskExport): ProcessTask {
|
|
27
|
-
let key
|
|
28
|
-
|
|
39
|
+
let key: string;
|
|
40
|
+
const keys = Object.keys(processTask);
|
|
29
41
|
let n = keys.length;
|
|
30
|
-
|
|
42
|
+
const newobj: Record<string, unknown> = {};
|
|
31
43
|
while (n--) {
|
|
32
44
|
key = keys[n];
|
|
33
45
|
newobj[key.substring(0, 1).toLowerCase() + key.substring(1)] =
|
|
@@ -144,17 +156,17 @@ function buildGroupsAndEdges({
|
|
|
144
156
|
entryPoint: ElkNodeWithFlow;
|
|
145
157
|
entryPointConnector: ElkNodeWithFlow;
|
|
146
158
|
groupLabels: number[];
|
|
147
|
-
getGroupTasks:
|
|
159
|
+
getGroupTasks: BuildGroupsTask;
|
|
148
160
|
// nodeWidth: number,
|
|
149
161
|
variant: "process" | "process-execution";
|
|
150
|
-
onAdd?:
|
|
151
|
-
mapTaskToNode:
|
|
162
|
+
onAdd?: AddHandler;
|
|
163
|
+
mapTaskToNode: BuildNodeMapper;
|
|
152
164
|
groupSortKey: string;
|
|
153
165
|
taskIdKey: string;
|
|
154
166
|
taskNameKey: string;
|
|
155
167
|
connectorIdPrefix: string;
|
|
156
168
|
}) {
|
|
157
|
-
const groups: GroupNode[] = groupLabels.map((groupLabel:
|
|
169
|
+
const groups: GroupNode[] = groupLabels.map((groupLabel: number) => ({
|
|
158
170
|
id: `group-${groupLabel}`,
|
|
159
171
|
data: {
|
|
160
172
|
label: `Group ${groupLabel}`,
|
|
@@ -170,7 +182,7 @@ function buildGroupsAndEdges({
|
|
|
170
182
|
},
|
|
171
183
|
draggable: false,
|
|
172
184
|
}));
|
|
173
|
-
const edges:
|
|
185
|
+
const edges: ElkExtendedEdgeWithFlow[] = [];
|
|
174
186
|
edges.push({
|
|
175
187
|
id: `entry-point-entry-point-connector`,
|
|
176
188
|
sources: ["entry-point"],
|
|
@@ -187,21 +199,25 @@ function buildGroupsAndEdges({
|
|
|
187
199
|
});
|
|
188
200
|
const groupTasks = getGroupTasks(group);
|
|
189
201
|
const tasksPartitioned = _.values(_.groupBy(groupTasks, groupSortKey));
|
|
190
|
-
const groupNodes:
|
|
202
|
+
const groupNodes: ElkNodeWithFlow[] = [];
|
|
191
203
|
tasksPartitioned.forEach((partition) => {
|
|
192
|
-
partition.forEach((task:
|
|
204
|
+
partition.forEach((task: TaskRecord, index: number) => {
|
|
193
205
|
groupNodes.push(mapTaskToNode(task, index, partition.length, group.id));
|
|
194
206
|
});
|
|
195
207
|
});
|
|
196
208
|
let prevConnectorId: string | null = null;
|
|
197
209
|
tasksPartitioned.forEach((partition, tpIndex) => {
|
|
198
|
-
_.sortBy(partition, [(task:
|
|
210
|
+
_.sortBy(partition, [(task: TaskRecord) => task[taskNameKey]]);
|
|
199
211
|
if (prevConnectorId) {
|
|
200
|
-
|
|
212
|
+
const fromConnectorId = prevConnectorId;
|
|
213
|
+
partition.forEach((task: TaskRecord) => {
|
|
214
|
+
const taskIdentifier = asIdentifierString(
|
|
215
|
+
task[taskIdKey] ?? task[taskNameKey],
|
|
216
|
+
);
|
|
201
217
|
edges.push({
|
|
202
|
-
id: `${
|
|
203
|
-
sources: [
|
|
204
|
-
targets: [`task-${
|
|
218
|
+
id: `${fromConnectorId}-${taskIdentifier}`,
|
|
219
|
+
sources: [fromConnectorId],
|
|
220
|
+
targets: [`task-${taskIdentifier}`],
|
|
205
221
|
type: "default-edge",
|
|
206
222
|
markerEnd: markerEnd,
|
|
207
223
|
});
|
|
@@ -210,10 +226,13 @@ function buildGroupsAndEdges({
|
|
|
210
226
|
const connectorId = `${connectorIdPrefix}${group.id}-connect-${tpIndex}`;
|
|
211
227
|
prevConnectorId = connectorId;
|
|
212
228
|
if (tpIndex !== tasksPartitioned.length - 1) {
|
|
213
|
-
partition.forEach((task:
|
|
229
|
+
partition.forEach((task: TaskRecord) => {
|
|
230
|
+
const taskIdentifier = asIdentifierString(
|
|
231
|
+
task[taskIdKey] ?? task[taskNameKey],
|
|
232
|
+
);
|
|
214
233
|
edges.push({
|
|
215
|
-
id: `task-${
|
|
216
|
-
sources: [`task-${
|
|
234
|
+
id: `task-${taskIdentifier}-${connectorId}`,
|
|
235
|
+
sources: [`task-${taskIdentifier}`],
|
|
217
236
|
targets: [connectorId],
|
|
218
237
|
type: "default-edge",
|
|
219
238
|
});
|
|
@@ -240,7 +259,7 @@ export const mapProcessConfigToNodesAndEdges = (
|
|
|
240
259
|
data: ConfigExport,
|
|
241
260
|
nodeWidth: number,
|
|
242
261
|
variant: "process" | "process-execution",
|
|
243
|
-
onAdd?:
|
|
262
|
+
onAdd?: AddHandler,
|
|
244
263
|
) => {
|
|
245
264
|
const process = data.Processes?.[0] as ProcessExport;
|
|
246
265
|
const entryPoint: ElkNodeWithFlow = {
|
|
@@ -273,30 +292,29 @@ export const mapProcessConfigToNodesAndEdges = (
|
|
|
273
292
|
entryPoint,
|
|
274
293
|
entryPointConnector,
|
|
275
294
|
groupLabels,
|
|
276
|
-
getGroupTasks: (group:
|
|
295
|
+
getGroupTasks: (group: GroupNode) =>
|
|
277
296
|
_.sortBy(
|
|
278
297
|
process?.ProcessTasks?.filter(
|
|
279
|
-
(task) =>
|
|
280
|
-
task
|
|
281
|
-
group.id.split("-")[1],
|
|
282
|
-
),
|
|
298
|
+
(task): task is ProcessTaskExport =>
|
|
299
|
+
task != null &&
|
|
300
|
+
task.ParallelProcessingGroup?.toString() === group.id.split("-")[1],
|
|
301
|
+
) ?? [],
|
|
283
302
|
["Sort", "Name"],
|
|
284
|
-
),
|
|
303
|
+
) as TaskRecord[],
|
|
285
304
|
//nodeWidth,
|
|
286
305
|
variant,
|
|
287
306
|
onAdd,
|
|
288
307
|
mapTaskToNode: (
|
|
289
|
-
task:
|
|
308
|
+
task: TaskRecord,
|
|
290
309
|
index: number,
|
|
291
310
|
partitionLen: number,
|
|
292
311
|
parentId: string,
|
|
293
312
|
) => ({
|
|
294
|
-
id: `task-${task
|
|
313
|
+
id: `task-${asIdentifierString(task.ProcessTaskId ?? task.Name)}`,
|
|
295
314
|
data: {
|
|
296
315
|
processTask: mapToCamelCase(task as ProcessTaskExport) || undefined,
|
|
297
316
|
label: task?.Name as string,
|
|
298
317
|
isLast: onAdd ? index === partitionLen - 1 : false,
|
|
299
|
-
onAdd: index === partitionLen - 1 ? onAdd : undefined,
|
|
300
318
|
},
|
|
301
319
|
position: { x: 0, y: 0 },
|
|
302
320
|
type: variant === "process" ? "task-node" : "execution-task-node",
|
|
@@ -360,28 +378,30 @@ export const mapProcessTasksToNodesAndEdges = (
|
|
|
360
378
|
entryPoint,
|
|
361
379
|
entryPointConnector,
|
|
362
380
|
groupLabels,
|
|
363
|
-
getGroupTasks: (group:
|
|
364
|
-
|
|
365
|
-
|
|
381
|
+
getGroupTasks: (group: GroupNode) => {
|
|
382
|
+
const tasks =
|
|
383
|
+
variant === "process"
|
|
366
384
|
? (data as Process)?.processTasks
|
|
367
|
-
: (data as ProcessExecutionDetails)?.processTaskExecutions
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
385
|
+
: (data as ProcessExecutionDetails)?.processTaskExecutions;
|
|
386
|
+
return _.sortBy(
|
|
387
|
+
tasks?.filter(
|
|
388
|
+
(task): task is ProcessTask | ProcessTaskExecution =>
|
|
389
|
+
task != null &&
|
|
390
|
+
task.parallelProcessingGroup?.toString() === group.id.split("-")[1],
|
|
391
|
+
) ?? [],
|
|
373
392
|
["sort", "name"],
|
|
374
|
-
)
|
|
393
|
+
) as TaskRecord[];
|
|
394
|
+
},
|
|
375
395
|
//nodeWidth,
|
|
376
396
|
variant,
|
|
377
397
|
onAdd: undefined,
|
|
378
398
|
mapTaskToNode: (
|
|
379
|
-
task:
|
|
399
|
+
task: TaskRecord,
|
|
380
400
|
index: number,
|
|
381
401
|
partitionLen: number,
|
|
382
402
|
parentId: string,
|
|
383
403
|
) => ({
|
|
384
|
-
id: `task-${task
|
|
404
|
+
id: `task-${asIdentifierString(task.id)}`,
|
|
385
405
|
data: {
|
|
386
406
|
processTask: variant === "process" ? (task as ProcessTask) : undefined,
|
|
387
407
|
processTaskExecution:
|
|
@@ -38,6 +38,12 @@ export interface Filters {
|
|
|
38
38
|
| MultiSelectFilter;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
export type FilterValue =
|
|
42
|
+
| DateFilter["value"]
|
|
43
|
+
| DateRangeFilter["value"]
|
|
44
|
+
| SelectFilter["value"]
|
|
45
|
+
| MultiSelectFilter["value"];
|
|
46
|
+
|
|
41
47
|
export type FilterResult<FilterShape extends Filters> = {
|
|
42
48
|
[Property in keyof FilterShape]: FilterShape[Property]["value"];
|
|
43
49
|
};
|
|
@@ -33,6 +33,7 @@ import type {
|
|
|
33
33
|
FilterConfig,
|
|
34
34
|
FilterResult,
|
|
35
35
|
Filters,
|
|
36
|
+
FilterValue,
|
|
36
37
|
ListFilterOutput,
|
|
37
38
|
} from "./list-filter-types";
|
|
38
39
|
import MultiSelectFilterComponent from "./multi-select-filter";
|
|
@@ -71,22 +72,22 @@ const ListFilter = function ListFilter<FilterShape extends Filters>(
|
|
|
71
72
|
|
|
72
73
|
const defaultValues = Object.entries(filters).reduce(
|
|
73
74
|
(values, [key, filter]) => {
|
|
74
|
-
if (filter.type === "select" && filter.value)
|
|
75
|
-
return {
|
|
76
|
-
...values,
|
|
75
|
+
if (filter.type === "select" && filter.value) {
|
|
76
|
+
return Object.assign(values, {
|
|
77
77
|
// @ts-expect-error
|
|
78
78
|
[key]: filter.options.find((o) => o.id === filter.value[0].id),
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if (filter.type === "multiSelect" && filter.value) {
|
|
82
|
+
return Object.assign(values, {
|
|
83
83
|
[key]: filter.options.filter((o) =>
|
|
84
84
|
// @ts-expect-error
|
|
85
85
|
filter.value.find((v) => v.id === o.id),
|
|
86
86
|
),
|
|
87
|
-
};
|
|
87
|
+
});
|
|
88
|
+
}
|
|
88
89
|
|
|
89
|
-
return
|
|
90
|
+
return Object.assign(values, { [key]: filter.value });
|
|
90
91
|
},
|
|
91
92
|
{},
|
|
92
93
|
) as FilterResult<FilterShape>;
|
|
@@ -94,10 +95,10 @@ const ListFilter = function ListFilter<FilterShape extends Filters>(
|
|
|
94
95
|
const filterErrorsBase: FilterErrors<FilterShape> = Object.keys(
|
|
95
96
|
filters,
|
|
96
97
|
).reduce(
|
|
97
|
-
(values, key) =>
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
(values, key) =>
|
|
99
|
+
Object.assign(values, {
|
|
100
|
+
[key]: false,
|
|
101
|
+
}),
|
|
101
102
|
{},
|
|
102
103
|
) as FilterErrors<FilterShape>;
|
|
103
104
|
|
|
@@ -124,7 +125,7 @@ const ListFilter = function ListFilter<FilterShape extends Filters>(
|
|
|
124
125
|
0,
|
|
125
126
|
);
|
|
126
127
|
|
|
127
|
-
const updateFilterValue = (name: string, value:
|
|
128
|
+
const updateFilterValue = (name: string, value: FilterValue | undefined) => {
|
|
128
129
|
if (Array.isArray(value) && !value.length) value = null;
|
|
129
130
|
setFilterValues({
|
|
130
131
|
...filterValues,
|
|
@@ -174,7 +175,10 @@ const ListFilter = function ListFilter<FilterShape extends Filters>(
|
|
|
174
175
|
|
|
175
176
|
const handleFilterClear = () => {
|
|
176
177
|
const emptyFilterValues = Object.keys(filterValues).reduce(
|
|
177
|
-
(values, key) =>
|
|
178
|
+
(values, key) =>
|
|
179
|
+
Object.assign(values, {
|
|
180
|
+
[key]: null,
|
|
181
|
+
}),
|
|
178
182
|
{},
|
|
179
183
|
) as FilterResult<FilterShape>;
|
|
180
184
|
setPublishedFilterValues(emptyFilterValues);
|
|
@@ -293,6 +297,8 @@ const ListFilter = function ListFilter<FilterShape extends Filters>(
|
|
|
293
297
|
onError={(e) => updateFilterErrors(name, e)}
|
|
294
298
|
/>
|
|
295
299
|
);
|
|
300
|
+
default:
|
|
301
|
+
return null;
|
|
296
302
|
}
|
|
297
303
|
})}
|
|
298
304
|
<Box sx={styles.buttonRow}>
|
|
@@ -31,15 +31,15 @@ const MultiSelectFilterComponent: React.FC<FilterProps<MultiSelectFilter>> = ({
|
|
|
31
31
|
const t = useTranslations("Integrate");
|
|
32
32
|
|
|
33
33
|
const mappedOptionDictionary = filter.options.reduce(
|
|
34
|
-
(dictionary, option) =>
|
|
35
|
-
|
|
36
|
-
[option.id]: {
|
|
34
|
+
(dictionary, option) => {
|
|
35
|
+
dictionary[option.id] = {
|
|
37
36
|
id: option.id,
|
|
38
37
|
value: t(option.value),
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
};
|
|
39
|
+
return dictionary;
|
|
40
|
+
},
|
|
41
|
+
{} as { [id: string | number]: FilterOption },
|
|
42
|
+
);
|
|
43
43
|
|
|
44
44
|
const [value, setValue] = useState(current);
|
|
45
45
|
|
|
@@ -31,12 +31,12 @@ const SelectFilterComponent: React.FC<FilterProps<SelectFilter>> = ({
|
|
|
31
31
|
const [value, setValue] = useState(current);
|
|
32
32
|
|
|
33
33
|
const mappedOptionDictionary = filter.options.reduce(
|
|
34
|
-
(dictionary, option) =>
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
{},
|
|
39
|
-
)
|
|
34
|
+
(dictionary: { [id: string | number]: FilterOption }, option) => {
|
|
35
|
+
dictionary[option.id] = option;
|
|
36
|
+
return dictionary;
|
|
37
|
+
},
|
|
38
|
+
{} as { [id: string | number]: FilterOption },
|
|
39
|
+
);
|
|
40
40
|
|
|
41
41
|
const updateValue = (optionId: string | number | null) => {
|
|
42
42
|
if (optionId == null) {
|
|
@@ -158,7 +158,7 @@ const getStyles = (theme: Theme): DaySelectorStyles => {
|
|
|
158
158
|
},
|
|
159
159
|
dayOfWeek: {
|
|
160
160
|
color: "grey.700",
|
|
161
|
-
border:
|
|
161
|
+
border: `solid ${theme.palette.grey[500]} ${theme.spacing(0.5)}`,
|
|
162
162
|
backgroundColor: theme.palette.grey[50],
|
|
163
163
|
borderRadius: "50%",
|
|
164
164
|
width: theme.spacing(5),
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Box } from "@mui/material";
|
|
4
|
+
import { useFormikContext } from "formik";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
import type {
|
|
7
|
+
ErrorState,
|
|
8
|
+
FieldValue,
|
|
9
|
+
FormState,
|
|
10
|
+
ProcessTemplateInput,
|
|
11
|
+
ProcessTemplatePage,
|
|
12
|
+
} from "../types";
|
|
13
|
+
import { templateFieldKey } from "../types";
|
|
14
|
+
import { FormModeProvider } from "./context/FormModeContext";
|
|
15
|
+
import { TemplateProvider } from "./context/TemplateContext";
|
|
16
|
+
import { ReservedFieldName } from "./field-names";
|
|
17
|
+
import { InputType } from "./input-type";
|
|
18
|
+
import { renderFormControl } from "./renderers/renderFormControl";
|
|
19
|
+
import { arrangeInputs } from "./utils/arrangeInputs";
|
|
20
|
+
|
|
21
|
+
function isHiddenInput(input: ProcessTemplateInput | null): boolean {
|
|
22
|
+
return (
|
|
23
|
+
input?.inputType === InputType.HIDDEN ||
|
|
24
|
+
input?.inputType === InputType.HIDDEN_NUMBER ||
|
|
25
|
+
input?.inputType === InputType.HIDDEN_NUMBER_OR_NULL
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function getDefaultHiddenFieldValue(
|
|
30
|
+
input: ProcessTemplateInput | null,
|
|
31
|
+
): FieldValue {
|
|
32
|
+
if (input?.inputType === InputType.HIDDEN_NUMBER) return 0 as FieldValue;
|
|
33
|
+
if (input?.inputType === InputType.HIDDEN_NUMBER_OR_NULL) return null;
|
|
34
|
+
return "" as FieldValue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function FormikPageBody({
|
|
38
|
+
templateId,
|
|
39
|
+
page,
|
|
40
|
+
clearTemplateFields,
|
|
41
|
+
readOnly,
|
|
42
|
+
isEditMode,
|
|
43
|
+
isCreateMode,
|
|
44
|
+
disabledFields,
|
|
45
|
+
setFieldsDisabled,
|
|
46
|
+
templateConfig,
|
|
47
|
+
setFieldValidationInProgress,
|
|
48
|
+
submitValidationResults,
|
|
49
|
+
}: Readonly<{
|
|
50
|
+
templateId: string;
|
|
51
|
+
page: ProcessTemplatePage;
|
|
52
|
+
clearTemplateFields: (templateId: string) => void;
|
|
53
|
+
readOnly?: boolean;
|
|
54
|
+
isEditMode?: boolean;
|
|
55
|
+
isCreateMode?: boolean;
|
|
56
|
+
disabledFields?: Set<string>;
|
|
57
|
+
setFieldsDisabled?: (
|
|
58
|
+
templateId: string,
|
|
59
|
+
fieldNames: string[],
|
|
60
|
+
disabled: boolean,
|
|
61
|
+
) => void;
|
|
62
|
+
templateConfig?: string;
|
|
63
|
+
setFieldValidationInProgress?: (inProgress: boolean) => void;
|
|
64
|
+
submitValidationResults?: Record<
|
|
65
|
+
string,
|
|
66
|
+
{ isValid: boolean; message: string }
|
|
67
|
+
>;
|
|
68
|
+
}>) {
|
|
69
|
+
const {
|
|
70
|
+
values,
|
|
71
|
+
errors,
|
|
72
|
+
touched,
|
|
73
|
+
setFieldValue,
|
|
74
|
+
setFieldTouched,
|
|
75
|
+
validateField,
|
|
76
|
+
validateForm,
|
|
77
|
+
} = useFormikContext<FormState>();
|
|
78
|
+
|
|
79
|
+
React.useEffect(() => {
|
|
80
|
+
const hiddenInputs = page.inputs?.filter(isHiddenInput) ?? [];
|
|
81
|
+
|
|
82
|
+
for (const input of hiddenInputs) {
|
|
83
|
+
const key = templateFieldKey(templateId, input?.fieldName ?? "");
|
|
84
|
+
if (values[key] != null) continue;
|
|
85
|
+
|
|
86
|
+
if (input?.fieldName === ReservedFieldName.ProcessTemplateId) {
|
|
87
|
+
setFieldValue(key, Number(templateId));
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
setFieldValue(key, getDefaultHiddenFieldValue(input));
|
|
91
|
+
}
|
|
92
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
93
|
+
}, [templateId, page.id, page.inputs]);
|
|
94
|
+
|
|
95
|
+
const rows = React.useMemo(() => {
|
|
96
|
+
const inputs = (page.inputs ?? []).filter(
|
|
97
|
+
(inp): inp is ProcessTemplateInput => inp != null,
|
|
98
|
+
) as ProcessTemplateInput[];
|
|
99
|
+
return arrangeInputs(inputs);
|
|
100
|
+
}, [page.inputs]);
|
|
101
|
+
|
|
102
|
+
const handleFieldChange = (
|
|
103
|
+
tId: string,
|
|
104
|
+
fieldName: string,
|
|
105
|
+
value: FieldValue,
|
|
106
|
+
) => {
|
|
107
|
+
setFieldValue(templateFieldKey(tId, fieldName), value);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const errorsNormalized: ErrorState = React.useMemo(() => {
|
|
111
|
+
const out: Record<string, string | null> = {};
|
|
112
|
+
for (const key of Object.keys(errors ?? {})) {
|
|
113
|
+
const v = (errors as Record<string, unknown>)[key];
|
|
114
|
+
if (v == null) {
|
|
115
|
+
out[key] = null;
|
|
116
|
+
} else if (typeof v === "string") {
|
|
117
|
+
out[key] = v;
|
|
118
|
+
} else if (typeof v === "object") {
|
|
119
|
+
out[key] = "";
|
|
120
|
+
} else if (typeof v === "number" || typeof v === "boolean") {
|
|
121
|
+
out[key] = String(v);
|
|
122
|
+
} else {
|
|
123
|
+
out[key] = "";
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return out;
|
|
127
|
+
}, [errors]);
|
|
128
|
+
|
|
129
|
+
const touchedNormalized: Record<string, boolean> = React.useMemo(() => {
|
|
130
|
+
const t = touched ?? {};
|
|
131
|
+
const out: Record<string, boolean> = {};
|
|
132
|
+
for (const key of Object.keys(t)) {
|
|
133
|
+
out[key] = (t as Record<string, unknown>)[key] === true;
|
|
134
|
+
}
|
|
135
|
+
return out;
|
|
136
|
+
}, [touched]);
|
|
137
|
+
|
|
138
|
+
const formControlProps = {
|
|
139
|
+
templateId,
|
|
140
|
+
values,
|
|
141
|
+
errors: errorsNormalized,
|
|
142
|
+
touched: touchedNormalized,
|
|
143
|
+
handleFieldChange,
|
|
144
|
+
setFieldTouched,
|
|
145
|
+
validateField,
|
|
146
|
+
validateForm,
|
|
147
|
+
readOnly,
|
|
148
|
+
disabledFields,
|
|
149
|
+
setFieldValidationInProgress,
|
|
150
|
+
submitValidationResults,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const renderRow = (
|
|
154
|
+
row: (typeof rows)[number],
|
|
155
|
+
idx: number,
|
|
156
|
+
): React.ReactNode => {
|
|
157
|
+
if (row.type === "pair") {
|
|
158
|
+
const [a, b] = row.items;
|
|
159
|
+
return (
|
|
160
|
+
<Box key={idx} sx={{ display: "flex", gap: 2, marginTop: 2 }}>
|
|
161
|
+
{renderFormControl({ ...formControlProps, input: a })}
|
|
162
|
+
{renderFormControl({ ...formControlProps, input: b })}
|
|
163
|
+
</Box>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
if (row.type === "full") {
|
|
167
|
+
return (
|
|
168
|
+
<Box key={idx} sx={{ mb: 2, marginTop: 2 }}>
|
|
169
|
+
{renderFormControl({ ...formControlProps, input: row.item })}
|
|
170
|
+
</Box>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
return null;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<FormModeProvider value={{ readOnly, isEditMode, isCreateMode }}>
|
|
178
|
+
<TemplateProvider
|
|
179
|
+
value={{
|
|
180
|
+
templateId,
|
|
181
|
+
templateConfig,
|
|
182
|
+
disabledFields,
|
|
183
|
+
setFieldsDisabled,
|
|
184
|
+
clearTemplateFields,
|
|
185
|
+
}}
|
|
186
|
+
>
|
|
187
|
+
<Box>{rows.map(renderRow)}</Box>
|
|
188
|
+
</TemplateProvider>
|
|
189
|
+
</FormModeProvider>
|
|
190
|
+
);
|
|
191
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
type FormMode = {
|
|
4
|
+
readOnly?: boolean;
|
|
5
|
+
isEditMode?: boolean;
|
|
6
|
+
isCreateMode?: boolean;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const FormModeContext = React.createContext<FormMode | null>(null);
|
|
10
|
+
|
|
11
|
+
export function FormModeProvider({
|
|
12
|
+
value,
|
|
13
|
+
children,
|
|
14
|
+
}: Readonly<{
|
|
15
|
+
value: FormMode;
|
|
16
|
+
children: React.ReactNode;
|
|
17
|
+
}>) {
|
|
18
|
+
return (
|
|
19
|
+
<FormModeContext.Provider value={value}>
|
|
20
|
+
{children}
|
|
21
|
+
</FormModeContext.Provider>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function useFormMode() {
|
|
26
|
+
const ctx = React.useContext(FormModeContext);
|
|
27
|
+
if (!ctx) {
|
|
28
|
+
throw new Error("useFormMode must be used inside FormModeProvider");
|
|
29
|
+
}
|
|
30
|
+
return ctx;
|
|
31
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
type TemplateContextValue = {
|
|
4
|
+
templateId: string;
|
|
5
|
+
templateConfig?: string;
|
|
6
|
+
disabledFields?: Set<string>;
|
|
7
|
+
setFieldsDisabled?: (
|
|
8
|
+
templateId: string,
|
|
9
|
+
fieldNames: string[],
|
|
10
|
+
disabled: boolean,
|
|
11
|
+
) => void;
|
|
12
|
+
clearTemplateFields?: (templateId: string) => void;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const TemplateContext = React.createContext<TemplateContextValue | null>(null);
|
|
16
|
+
|
|
17
|
+
export function TemplateProvider({
|
|
18
|
+
value,
|
|
19
|
+
children,
|
|
20
|
+
}: Readonly<{
|
|
21
|
+
value: TemplateContextValue;
|
|
22
|
+
children: React.ReactNode;
|
|
23
|
+
}>) {
|
|
24
|
+
return (
|
|
25
|
+
<TemplateContext.Provider value={value}>
|
|
26
|
+
{children}
|
|
27
|
+
</TemplateContext.Provider>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function useTemplateContext() {
|
|
32
|
+
const ctx = React.useContext(TemplateContext);
|
|
33
|
+
if (!ctx) {
|
|
34
|
+
throw new Error("useTemplateContext must be used inside TemplateProvider");
|
|
35
|
+
}
|
|
36
|
+
return ctx;
|
|
37
|
+
}
|