@evenicanpm/admin-integrate 1.8.1 → 2.0.0

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 (71) hide show
  1. package/documents/ProcessTask/fragments.ts +4 -0
  2. package/documents/ProcessTemplate/fragments.ts +30 -0
  3. package/documents/ProcessTemplate/list.ts +29 -0
  4. package/package.json +2 -2
  5. package/src/api/process-config/mutation/process-config-import.mutation.ts +1 -1
  6. package/src/api/templates/queries/get-field-values.query.ts +48 -0
  7. package/src/api/templates/queries/get-field-values.server.ts +9 -0
  8. package/src/api/templates/queries/index.ts +4 -0
  9. package/src/api/templates/queries/types.ts +2 -2
  10. package/src/api/templates/queries/validate-field-on-check.query.ts +22 -0
  11. package/src/api/templates/queries/validate-field-on-check.server.ts +30 -0
  12. package/src/api/templates/queries/validate-field-on-submit.server.ts +18 -0
  13. package/src/components/data-table/table-row.tsx +1 -1
  14. package/src/components/icons/up-down.tsx +1 -0
  15. package/src/components/integration-view/integration-view.tsx +10 -15
  16. package/src/components/integration-view/nodes/task-node.tsx +1 -1
  17. package/src/components/integration-view/utils/mapping.ts +10 -11
  18. package/src/components/list-filter/list-filter.tsx +2 -0
  19. package/src/components/templatesV2/inputs/FormikPageBody.tsx +191 -0
  20. package/src/components/templatesV2/inputs/context/FormModeContext.tsx +31 -0
  21. package/src/components/templatesV2/inputs/context/TemplateContext.tsx +37 -0
  22. package/src/components/templatesV2/inputs/field-names.ts +8 -0
  23. package/src/components/templatesV2/inputs/form-type.ts +48 -0
  24. package/src/components/templatesV2/inputs/index.tsx +33 -0
  25. package/src/components/templatesV2/inputs/input-type.ts +23 -0
  26. package/src/components/templatesV2/inputs/primitives/CheckboxInput.tsx +45 -0
  27. package/src/components/templatesV2/inputs/primitives/NumberInput.tsx +58 -0
  28. package/src/components/templatesV2/inputs/primitives/SelectorInput.tsx +111 -0
  29. package/src/components/templatesV2/inputs/primitives/TextInput.tsx +55 -0
  30. package/src/components/templatesV2/inputs/primitives/TextWithQueryInput.tsx +292 -0
  31. package/src/components/templatesV2/inputs/primitives/index.ts +10 -0
  32. package/src/components/templatesV2/inputs/renderers/index.ts +2 -0
  33. package/src/components/templatesV2/inputs/renderers/renderFormControl.tsx +376 -0
  34. package/src/components/templatesV2/inputs/renderers/renderSearchInput.tsx +129 -0
  35. package/src/components/templatesV2/inputs/search/MultiTextSearchInput.tsx +87 -0
  36. package/src/components/templatesV2/inputs/search/SearchInputWrapper.tsx +113 -0
  37. package/src/components/templatesV2/inputs/search/TextSearchInput.tsx +309 -0
  38. package/src/components/templatesV2/inputs/search/index.ts +4 -0
  39. package/src/components/templatesV2/inputs/search/search-query-key.ts +23 -0
  40. package/src/components/templatesV2/inputs/search/useFieldValuesOptions.ts +58 -0
  41. package/src/components/templatesV2/inputs/search/useSearchInput.ts +74 -0
  42. package/src/components/templatesV2/inputs/table/InputTable.tsx +259 -0
  43. package/src/components/templatesV2/inputs/table/index.ts +1 -0
  44. package/src/components/templatesV2/inputs/utils/applyTemplateConfig.ts +28 -0
  45. package/src/components/templatesV2/inputs/utils/arrangeInputs.ts +51 -0
  46. package/src/components/templatesV2/inputs/utils/index.ts +6 -0
  47. package/src/components/templatesV2/inputs/utils/parseQueryName.ts +20 -0
  48. package/src/components/templatesV2/inputs/utils/renderReadOnlyText.tsx +26 -0
  49. package/src/components/templatesV2/inputs/utils/selectors.ts +69 -0
  50. package/src/components/templatesV2/inputs/utils/toStringArray.ts +23 -0
  51. package/src/components/templatesV2/pageForm.tsx +151 -0
  52. package/src/components/{templates → templatesV2}/templates-list/templates-list-row.tsx +2 -1
  53. package/src/components/{templates → templatesV2}/templates-list/templates-list-table.tsx +2 -2
  54. package/src/components/{templates → templatesV2}/templates-list/templates-list.tsx +19 -28
  55. package/src/components/{templates → templatesV2}/types.tsx +12 -12
  56. package/src/components/templatesV2/validation/buildZodSchema.ts +216 -0
  57. package/src/components/templatesV2/validation/zodFormikValidate.ts +17 -0
  58. package/src/components/templatesV2/wizard.tsx +744 -0
  59. package/src/pages/integration-create/integration-create-steps.ts +8 -0
  60. package/src/pages/integration-create/integration-create.tsx +61 -14
  61. package/src/pages/integration-create/select-step.tsx +3 -3
  62. package/src/pages/integration-create/template-list-step.tsx +28 -3
  63. package/src/pages/integration-create/template-options-step.tsx +119 -0
  64. package/src/pages/integration-create/wizard-step.tsx +5 -4
  65. package/src/pages/integration-details/task-drawer/add-task.tsx +4 -4
  66. package/src/pages/integration-details/task-drawer/edit-task.tsx +3 -3
  67. package/src/components/templates/inputs/index.tsx +0 -1392
  68. package/src/components/templates/wizard.tsx +0 -737
  69. /package/src/components/{templates → templatesV2}/index.ts +0 -0
  70. /package/src/components/{templates → templatesV2}/template-filter.ts +0 -0
  71. /package/src/components/{templates → templatesV2}/templates-list/index.ts +0 -0
@@ -1,737 +0,0 @@
1
- import type { GetProcessTemplateQuery } from "@evenicanpm/admin-core/api/e4/graphqlRequestSdk";
2
- import { getProcessTemplate } from "@evenicanpm/admin-integrate/api/templates/queries";
3
- import { Loading } from "@evenicanpm/admin-integrate/components/core";
4
- import ArrowLeftIcon from "@mui/icons-material/ArrowLeft";
5
- import ArrowRightIcon from "@mui/icons-material/ArrowRight";
6
- import {
7
- Box,
8
- Button,
9
- Card,
10
- CardContent,
11
- Divider,
12
- LinearProgress,
13
- Stack,
14
- Step,
15
- StepLabel,
16
- Stepper,
17
- Typography,
18
- useTheme,
19
- } from "@mui/material";
20
- import { usePathname, useRouter, useSearchParams } from "next/navigation";
21
- // useTranslations - Need to figure out how to use this if if templates are data driven
22
- import React, { useEffect, useState } from "react";
23
- import PageBody, { applyTemplateConfig } from "./inputs";
24
- import {
25
- type ErrorState,
26
- type FieldValue,
27
- type FlatPage,
28
- type FormState,
29
- type Metadata,
30
- type ProcessConfigMerge,
31
- type ProcessTemplate,
32
- type ProcessTemplateInput,
33
- type ProcessTemplateInputSchema,
34
- type ProcessTemplatePage,
35
- type SearchOption,
36
- sortBy,
37
- templateFieldKey,
38
- toRegex,
39
- } from "./types";
40
-
41
- function isEmptyValue(value: unknown): boolean {
42
- if (value == null) return true;
43
- if (Array.isArray(value)) return value.length === 0;
44
- if (typeof value === "number") return Number.isNaN(value);
45
- if (typeof value === "string") return value.trim().length === 0;
46
- return false;
47
- }
48
-
49
- function rowHasUserInput(
50
- row: Record<string, unknown>,
51
- schemas: ProcessTemplateInput["schemas"],
52
- ): boolean {
53
- if (!schemas) return false;
54
- return schemas.some((schema) => {
55
- if (!schema) return false;
56
- if (schema.inputType === "checkbox") return false;
57
- const colKey = schema.fieldKey ?? schema.label ?? "";
58
- const cell = row[colKey as string];
59
- return !isEmptyValue(cell);
60
- });
61
- }
62
-
63
- function buildProcessConfigMerge(
64
- templates: ProcessTemplate[],
65
- values: FormState,
66
- metadata: Metadata,
67
- ): ProcessConfigMerge {
68
- const config = templates.map((t) => {
69
- const obj: Record<string, FieldValue> = {};
70
-
71
- // For every page get input and map value
72
- t.pages?.forEach((page) => {
73
- page?.inputs?.forEach((input) => {
74
- const key = templateFieldKey(t.id, input?.fieldName ?? ""); // e.g. "1::dataDescription"
75
-
76
- // metadata override (no hardcoding)
77
- // allow any metafields to be mapped
78
- if (input?.fieldName && input.fieldName in metadata) {
79
- obj[input.fieldName] = metadata[
80
- input.fieldName as keyof Metadata
81
- ] as FieldValue;
82
- return;
83
- }
84
-
85
- if (!Object.hasOwn(values, key)) return;
86
-
87
- let v = values[key];
88
-
89
- if (
90
- input?.inputType === "input-table" &&
91
- Array.isArray(v) &&
92
- input.schemas?.length
93
- ) {
94
- v = v.map((row: any) =>
95
- (row?.DataType || row?.FieldType) === "String"
96
- ? {
97
- ...row,
98
- Length: 4000,
99
- }
100
- : row,
101
- );
102
-
103
- const isStr = (x: unknown): x is string => typeof x === "string";
104
- const valueCol =
105
- input.schemas.find((s) => s?.fieldKey === "{0}")?.fieldKey ?? null;
106
- if (!isStr(valueCol) && input.fieldName) {
107
- obj[input.fieldName] = v;
108
- return;
109
- }
110
- const keyCol = "{Variable}";
111
-
112
- if (!isStr(keyCol)) {
113
- // fallback: first non-placeholder key or literal "Variable"
114
- const nonPlaceholder = input.schemas.find(
115
- (s) =>
116
- s?.fieldKey &&
117
- isStr(s.fieldKey) &&
118
- !/^\{.*\}$/.test(s.fieldKey),
119
- )?.fieldKey;
120
- const keyColFinal = isStr(nonPlaceholder)
121
- ? nonPlaceholder
122
- : "{Variable}";
123
-
124
- const collapsed = (v as Array<Record<string, FieldValue>>).reduce(
125
- (acc, row) => {
126
- const rawKey = (row as any)[keyColFinal];
127
-
128
- let k = "";
129
- if (typeof rawKey === "string") {
130
- k = rawKey.trim();
131
- } else if (rawKey != null) {
132
- k = String(rawKey);
133
- }
134
-
135
- if (!k) return acc;
136
-
137
- const val = (row as any)[valueCol ?? ""];
138
- if (val !== undefined) acc[k] = val as FieldValue;
139
-
140
- return acc;
141
- },
142
- {} as Record<string, FieldValue>,
143
- );
144
-
145
- if (input.fieldName) {
146
- obj[input.fieldName] = collapsed as unknown as FieldValue; // or widen FieldValue to allow objects
147
- }
148
- return;
149
- }
150
-
151
- const collapsed = (v as Array<Record<string, FieldValue>>).reduce(
152
- (acc, row) => {
153
- const rawKey = (row as any)[keyCol]; // e.g., row["{Variable}"] === "ASD"
154
-
155
- let k = "";
156
- if (typeof rawKey === "string") {
157
- k = rawKey.trim();
158
- } else if (rawKey !== null && rawKey !== undefined) {
159
- k = String(rawKey);
160
- }
161
-
162
- if (!k) return acc;
163
-
164
- const val =
165
- valueCol !== null && valueCol !== undefined
166
- ? (row as any)[valueCol]
167
- : undefined; // e.g., row["{0}"] === "1"
168
-
169
- if (val !== undefined) acc[k] = val as FieldValue;
170
- return acc;
171
- },
172
- {} as Record<string, FieldValue>,
173
- );
174
-
175
- if (input.fieldName) {
176
- obj[input.fieldName] = collapsed as FieldValue;
177
- }
178
- return;
179
- }
180
-
181
- if (input?.fieldName) {
182
- obj[input.fieldName] = v;
183
- }
184
- });
185
- });
186
-
187
- return obj;
188
- });
189
-
190
- return { input: { config } };
191
- }
192
-
193
- export function removeTemplateConfig(input: any): any {
194
- if (Array.isArray(input)) {
195
- return input.map(removeTemplateConfig);
196
- }
197
-
198
- if (input !== null && typeof input === "object") {
199
- const newObj: any = {};
200
-
201
- for (const key in input) {
202
- if (key !== "templateConfig") {
203
- newObj[key] = removeTemplateConfig(input[key]);
204
- }
205
- }
206
-
207
- return newObj;
208
- }
209
-
210
- return input;
211
- }
212
-
213
- function Header({
214
- title,
215
- subtitle,
216
- }: Readonly<{ title: string; subtitle?: string }>) {
217
- return (
218
- <Box sx={{ mb: 2 }}>
219
- <Typography variant="h6" sx={{ mb: 0.5 }}>
220
- {title}
221
- </Typography>
222
- {subtitle ? <Typography variant="body2">{subtitle}</Typography> : null}
223
- </Box>
224
- );
225
- }
226
-
227
- function TemplateNav({
228
- templates,
229
- activeIndex,
230
- stylings,
231
- }: Readonly<{
232
- templates: ProcessTemplate[];
233
- activeIndex: number;
234
- stylings?: object;
235
- }>) {
236
- return (
237
- <Stepper activeStep={activeIndex} sx={stylings}>
238
- {templates.map((t) => (
239
- <Step key={t.id}>
240
- <StepLabel>{t.name ?? ""}</StepLabel>
241
- </Step>
242
- ))}
243
- </Stepper>
244
- );
245
- }
246
-
247
- function PageNav({
248
- pages,
249
- activeIndex,
250
- stylings,
251
- isEditMode,
252
- }: Readonly<{
253
- pages: ProcessTemplatePage[];
254
- activeIndex: number;
255
- stylings?: object;
256
- isEditMode?: boolean;
257
- }>) {
258
- return (
259
- <Stepper nonLinear={isEditMode} activeStep={activeIndex} sx={stylings}>
260
- {pages.map((p) => (
261
- <Step key={p.id} sx={{ color: "green" }}>
262
- <StepLabel>{p.title}</StepLabel>
263
- </Step>
264
- ))}
265
- </Stepper>
266
- );
267
- }
268
-
269
- interface Props {
270
- metadata: Metadata;
271
- processTemplateGroupId?: string;
272
- processTemplateId?: string;
273
- handleBack?: (event?: React.MouseEventHandler) => void;
274
- handleFinish?: (event?: React.MouseEventHandler) => void;
275
- setProcessConfigMerge: React.Dispatch<
276
- React.SetStateAction<ProcessConfigMerge | null>
277
- >;
278
- templateConfig?: string | null;
279
- disableBackToTemplates?: boolean;
280
- isEditMode?: boolean;
281
- isCreateMode?: boolean;
282
- readOnly?: boolean;
283
- sharedCount?: (count: number) => void;
284
- }
285
-
286
- export default function Wizard({
287
- metadata,
288
- sharedCount,
289
- ...props
290
- }: Readonly<Props>) {
291
- const theme = useTheme();
292
-
293
- const router = useRouter();
294
- const pathname = usePathname();
295
- const searchParams = useSearchParams();
296
-
297
- const [templates, setTemplates] = useState<ProcessTemplate[]>([]);
298
- const [templatesSorted, setTemplatesSorted] = useState<ProcessTemplate[]>([]);
299
-
300
- const { data, isLoading } = getProcessTemplate.useData({
301
- input: {
302
- processTemplateGroupId: props.processTemplateGroupId || null,
303
- processTemplateId:
304
- /*configTemplateId ||*/ props.processTemplateId || null,
305
- },
306
- });
307
-
308
- const [values, setValues] = React.useState<FormState>({});
309
-
310
- useEffect(() => {
311
- const typedData = data as GetProcessTemplateQuery;
312
- if (typedData?.processTemplate) {
313
- const templates = typedData.processTemplate as ProcessTemplate[];
314
-
315
- setTemplates(templates);
316
- setTemplatesSorted(
317
- templates.map((t) => ({
318
- ...t,
319
- pages: sortBy(t.pages as ProcessTemplatePage[]),
320
- })),
321
- );
322
-
323
- templates.forEach((tpl) => {
324
- tpl.pages?.forEach((page) => {
325
- page?.inputs?.forEach((input) => {
326
- const key = templateFieldKey(tpl.id, input?.fieldName ?? "");
327
- if (
328
- input?.defaultValue !== undefined &&
329
- input?.defaultValue !== null &&
330
- !values[key]
331
- ) {
332
- setValues((prev) => ({
333
- ...prev,
334
- [key]: input.defaultValue as FieldValue,
335
- }));
336
- }
337
- });
338
- });
339
- });
340
- }
341
- }, [data]);
342
-
343
- const [flatPages, setFlatPages] = useState<FlatPage[]>([]);
344
-
345
- useEffect(() => {
346
- let counter = 0;
347
- const newFlatPages = templatesSorted.flatMap((tpl, ti) => {
348
- if (tpl.pages) {
349
- return (tpl.pages as ProcessTemplatePage[]).map((p, pi) => ({
350
- ...p,
351
- _templateIndex: ti,
352
- _templateId: tpl.id,
353
- _templateName: tpl.name ?? "",
354
- _pageIndexWithinTemplate: pi,
355
- _flatIndex: counter++,
356
- }));
357
- } else {
358
- return [];
359
- }
360
- });
361
- setFlatPages(newFlatPages);
362
- }, [templatesSorted]);
363
-
364
- const [globalStep, setGlobalStep] = React.useState(0);
365
- const totalSteps = flatPages.length;
366
-
367
- const [currentFlat, setCurrentFlat] = useState<FlatPage | null>(null);
368
- const [currentTemplate, setCurrentTemplate] =
369
- useState<ProcessTemplate | null>(null);
370
- useEffect(() => {
371
- if (flatPages.length > 0) {
372
- const thisFlat = flatPages[globalStep];
373
- setCurrentFlat(thisFlat);
374
- setCurrentTemplate(templatesSorted[thisFlat._templateIndex]);
375
- }
376
- }, [flatPages, globalStep]);
377
-
378
- useEffect(() => {
379
- if (currentTemplate) {
380
- const currentParams = new URLSearchParams(searchParams.toString());
381
- currentParams.set("template", currentTemplate.id);
382
- router.replace(`${pathname}?${currentParams.toString()}`);
383
- }
384
- }, [currentTemplate]);
385
-
386
- const [errors, setErrors] = React.useState<ErrorState>({});
387
- const [disabledFields, setDisabledFields] = React.useState<Set<string>>(
388
- new Set(),
389
- );
390
-
391
- const isFirst = globalStep === 0;
392
- const isLast = globalStep === totalSteps - 1;
393
-
394
- const handleFieldChange = React.useCallback(
395
- (templateId: string, fieldName: string, value: FieldValue) => {
396
- if (!templateId || !fieldName || value == null) return;
397
- const key = templateFieldKey(templateId, fieldName);
398
-
399
- setValues((prev) => ({ ...prev, [key]: value }));
400
- setErrors((prev) => ({ ...prev, [key]: null }));
401
- },
402
- [setValues, setErrors],
403
- );
404
-
405
- const setFieldsDisabled = React.useCallback(
406
- (templateId: string, fieldNames: string[], disabled: boolean) => {
407
- setDisabledFields((prev) => {
408
- const newSet = new Set(prev);
409
- fieldNames.forEach((fieldName) => {
410
- const key = templateFieldKey(templateId, fieldName);
411
- if (disabled) {
412
- newSet.add(key);
413
- } else {
414
- newSet.delete(key);
415
- }
416
- });
417
- return newSet;
418
- });
419
- },
420
- [],
421
- );
422
-
423
- useEffect(() => {
424
- if (props.templateConfig) {
425
- const config = JSON.parse(props.templateConfig as string);
426
- applyTemplateConfig(
427
- {
428
- name: config.taskName as string,
429
- templateConfig: props.templateConfig as string,
430
- } as SearchOption,
431
- handleFieldChange,
432
- );
433
- }
434
- }, [props.templateConfig]);
435
-
436
- const clearTemplateFields = (templateId: string) => {
437
- const newValues = Object.keys(values)
438
- .filter((key) => key.split("::")[0] !== templateId)
439
- .reduce((acc, key) => {
440
- acc[key] = values[key];
441
- return acc;
442
- }, {} as FormState);
443
- const newErrors = Object.keys(errors)
444
- .filter((key) => key.split("::")[0] !== templateId)
445
- .reduce((acc, key) => {
446
- acc[key] = errors[key];
447
- return acc;
448
- }, {} as ErrorState);
449
- setValues(newValues);
450
- setErrors(newErrors);
451
- };
452
-
453
- function validatePage(
454
- page: ProcessTemplatePage,
455
- templateId: string,
456
- ): boolean {
457
- const pageErrs: ErrorState = {};
458
-
459
- const validateInput = (
460
- input: ProcessTemplateInput,
461
- value: unknown,
462
- ): string | null => {
463
- if (input.isRequired && isEmptyValue(value)) return "Required";
464
-
465
- if (
466
- input.validationRegex &&
467
- (typeof value === "string" || typeof value === "number")
468
- ) {
469
- const re = toRegex(input.validationRegex);
470
- const valStr = String(value ?? "");
471
- if (re && !re.test(valStr)) return "Invalid format";
472
- }
473
- return null;
474
- };
475
-
476
- const validateTable = (
477
- schema: ProcessTemplateInputSchema,
478
- cell: unknown,
479
- ): string | null => {
480
- if (schema.inputType === "checkbox") return null;
481
-
482
- if (schema.isRequired && isEmptyValue(cell)) {
483
- return `"${schema.label}" is required`;
484
- }
485
-
486
- if (
487
- schema.validationRegex &&
488
- (typeof cell === "string" || typeof cell === "number")
489
- ) {
490
- const re = toRegex(schema.validationRegex);
491
- const valStr = String(cell ?? "");
492
- if (re && !re.test(valStr))
493
- return `"${schema.label}" has invalid format`;
494
- }
495
- return null;
496
- };
497
-
498
- const formatRowErrors = (
499
- items: Array<{ rowIndex: number; errs: string[] }>,
500
- ) =>
501
- items
502
- .map(({ rowIndex, errs }) =>
503
- [`• Row ${rowIndex + 1}:`, ...errs.map((e) => ` - ${e}`)].join("\n"),
504
- )
505
- .join("\n");
506
-
507
- if (page.inputs) {
508
- for (const input of page.inputs) {
509
- if (!input) continue;
510
- if (input.inputType?.startsWith("hidden")) continue;
511
-
512
- const key = templateFieldKey(templateId, input.fieldName ?? "");
513
- const value = values[key];
514
-
515
- if (input.inputType === "input-table") {
516
- const rows = (Array.isArray(value) ? value : []) as Array<
517
- Record<string, unknown>
518
- >;
519
- const schemas = input.schemas ?? [];
520
-
521
- const hasStarted = rows.some((row) => rowHasUserInput(row, schemas));
522
- if (input.isRequired && !hasStarted) {
523
- pageErrs[key] = "• At least one row is required.";
524
- continue;
525
- }
526
-
527
- const rowErrorItems: Array<{ rowIndex: number; errs: string[] }> = [];
528
- rows.forEach((row, rowIndex) => {
529
- if (!rowHasUserInput(row, schemas)) return;
530
- const errs: string[] = [];
531
-
532
- for (const s of schemas) {
533
- if (s) {
534
- const colKey = s.fieldKey ?? s.label ?? "";
535
- const cell = row[colKey as string];
536
- const err = validateTable(s, cell);
537
- if (err) errs.push(err);
538
- }
539
- }
540
-
541
- if (errs.length) rowErrorItems.push({ rowIndex, errs });
542
- });
543
-
544
- if (rowErrorItems.length) {
545
- pageErrs[key] = formatRowErrors(rowErrorItems);
546
- }
547
- continue;
548
- }
549
-
550
- if (
551
- input.fieldName === "uniqueIdentifier" &&
552
- typeof value === "string"
553
- ) {
554
- const notUnique = Object.entries(values).find(
555
- ([k, v]) => k.endsWith(`${templateId}::isUnique`) && v === false,
556
- );
557
- if (notUnique) {
558
- pageErrs[key] = "This identifier is not unique.";
559
- continue;
560
- }
561
- }
562
-
563
- const fieldErr = validateInput(input, value);
564
- if (fieldErr) pageErrs[key] = fieldErr;
565
- }
566
- }
567
-
568
- setErrors((prev) => ({ ...prev, ...pageErrs }));
569
- return Object.values(pageErrs).every((e) => !e);
570
- }
571
-
572
- const next = () => {
573
- if (currentFlat && !validatePage(currentFlat, currentFlat._templateId))
574
- return;
575
- setGlobalStep((s) => Math.min(s + 1, totalSteps - 1));
576
- };
577
-
578
- const back = () => {
579
- const newStep = globalStep - 1;
580
- setGlobalStep(Math.max(newStep, 0));
581
- if (newStep < 0 && props.handleBack) {
582
- props.handleBack();
583
- }
584
- };
585
-
586
- const finish = () => {
587
- if (currentFlat && !validatePage(currentFlat, currentFlat._templateId))
588
- return;
589
-
590
- let payload = buildProcessConfigMerge(templatesSorted, values, metadata);
591
-
592
- props.setProcessConfigMerge(payload);
593
- if (props.handleFinish) {
594
- props.handleFinish();
595
- }
596
- };
597
-
598
- const progress =
599
- totalSteps > 0 ? Math.round(((globalStep + 1) / totalSteps) * 100) : 0;
600
-
601
- if (
602
- isLoading &&
603
- !currentFlat &&
604
- !currentTemplate &&
605
- !props.templateConfig &&
606
- (!props.processTemplateGroupId || !props.processTemplateId) /*||
607
- !configTemplateId*/
608
- ) {
609
- return (
610
- <Box sx={{ padding: theme.spacing(10, 3) }}>
611
- <Loading />
612
- </Box>
613
- );
614
- }
615
-
616
- if (!isLoading && !templates.length)
617
- return <Typography>No templates found.</Typography>;
618
-
619
- return (
620
- currentFlat &&
621
- currentTemplate && (
622
- <Card
623
- sx={
624
- props.isEditMode
625
- ? { border: 0, boxShadow: 0, borderRadius: 0 }
626
- : {
627
- border: `1px solid ${theme.palette.grey[300]}`,
628
- borderRadius: 0,
629
- }
630
- }
631
- >
632
- <CardContent>
633
- {!props.isEditMode && (
634
- <Typography variant="h5" gutterBottom>
635
- {currentTemplate.name}
636
- </Typography>
637
- )}
638
-
639
- {templates.length > 1 ? (
640
- <>
641
- <TemplateNav
642
- templates={templates}
643
- activeIndex={currentFlat._templateIndex}
644
- />
645
- <Divider sx={{ my: 2 }} />
646
- </>
647
- ) : null}
648
-
649
- <PageNav
650
- pages={currentTemplate.pages as ProcessTemplatePage[]}
651
- activeIndex={currentFlat._pageIndexWithinTemplate}
652
- isEditMode={props.isEditMode}
653
- />
654
-
655
- <Divider sx={{ my: 2 }} />
656
-
657
- {/* Page header */}
658
- <Header
659
- title={currentFlat.title ?? ""}
660
- subtitle={props.isEditMode ? "" : (currentFlat.description ?? "")}
661
- />
662
-
663
- {/* Inputs */}
664
- <Box>
665
- <PageBody
666
- templateId={currentFlat._templateId}
667
- page={currentFlat}
668
- values={values}
669
- errors={errors}
670
- handleFieldChange={handleFieldChange}
671
- clearTemplateFields={clearTemplateFields}
672
- readOnly={props.readOnly}
673
- sharedCount={(count) => sharedCount?.(count)}
674
- isEditMode={props.isEditMode}
675
- isCreateMode={props.isCreateMode}
676
- disabledFields={disabledFields}
677
- setFieldsDisabled={setFieldsDisabled}
678
- templateConfig={props.templateConfig || ""}
679
- />
680
- </Box>
681
-
682
- <Divider sx={{ my: 2 }} />
683
-
684
- <Box sx={{ mt: 1, mb: 2 }}>
685
- <LinearProgress
686
- variant="determinate"
687
- value={isFirst ? 0 : progress}
688
- />
689
- </Box>
690
-
691
- <Stack
692
- direction="row"
693
- justifyContent="right"
694
- sx={{ mt: 3 }}
695
- spacing={3}
696
- >
697
- {isFirst && !props.disableBackToTemplates && (
698
- <Button
699
- variant="contained"
700
- onClick={back}
701
- color="info"
702
- startIcon={<ArrowLeftIcon />}
703
- >
704
- Back to templates
705
- </Button>
706
- )}
707
- {!isFirst && (
708
- <Button
709
- variant="contained"
710
- onClick={back}
711
- color="info"
712
- startIcon={<ArrowLeftIcon />}
713
- >
714
- Back
715
- </Button>
716
- )}
717
- {!isLast && (
718
- <Button variant="contained" onClick={next} color="info">
719
- Next
720
- </Button>
721
- )}
722
- {isLast && !props.readOnly && (
723
- <Button
724
- variant="contained"
725
- onClick={finish}
726
- color="success"
727
- endIcon={<ArrowRightIcon />}
728
- >
729
- Finish
730
- </Button>
731
- )}
732
- </Stack>
733
- </CardContent>
734
- </Card>
735
- )
736
- );
737
- }