@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.
Files changed (97) hide show
  1. package/documents/ConfigImport/configuration-import.ts +1 -1
  2. package/documents/Endpoint/list.ts +3 -3
  3. package/documents/Process/list.ts +3 -3
  4. package/documents/ProcessExecution/list.ts +2 -2
  5. package/documents/ProcessExport/read.ts +1 -1
  6. package/documents/ProcessSchedule/read.ts +1 -1
  7. package/documents/ProcessScheduleGroup/read.ts +3 -3
  8. package/documents/ProcessTask/fragments.ts +4 -0
  9. package/documents/ProcessTemplate/fragments.ts +30 -0
  10. package/documents/ProcessTemplate/list.ts +29 -0
  11. package/documents/ProcessTemplate/read.ts +1 -1
  12. package/documents/ScheduledProcess/list.ts +1 -1
  13. package/package.json +2 -2
  14. package/src/api/process-config/mutation/process-config-import.mutation.ts +1 -1
  15. package/src/api/templates/queries/get-field-values.query.ts +48 -0
  16. package/src/api/templates/queries/get-field-values.server.ts +9 -0
  17. package/src/api/templates/queries/index.ts +4 -0
  18. package/src/api/templates/queries/types.ts +2 -2
  19. package/src/api/templates/queries/validate-field-on-check.query.ts +22 -0
  20. package/src/api/templates/queries/validate-field-on-check.server.ts +30 -0
  21. package/src/api/templates/queries/validate-field-on-submit.server.ts +18 -0
  22. package/src/components/data-table/table-row.tsx +1 -1
  23. package/src/components/icons/up-down.tsx +1 -0
  24. package/src/components/integration-view/animated-svg.tsx +0 -1
  25. package/src/components/integration-view/elk-types.ts +1 -1
  26. package/src/components/integration-view/integration-view.tsx +274 -187
  27. package/src/components/integration-view/nodes/execution-entry-node.tsx +3 -2
  28. package/src/components/integration-view/nodes/task-node.tsx +1 -1
  29. package/src/components/integration-view/types.ts +2 -0
  30. package/src/components/integration-view/utils/mapping.ts +68 -48
  31. package/src/components/list-filter/list-filter-types.ts +6 -0
  32. package/src/components/list-filter/list-filter.tsx +21 -15
  33. package/src/components/list-filter/multi-select-filter.tsx +7 -7
  34. package/src/components/list-filter/select-filter.tsx +6 -6
  35. package/src/components/scheduler/day-selector.tsx +1 -1
  36. package/src/components/templatesV2/inputs/FormikPageBody.tsx +191 -0
  37. package/src/components/templatesV2/inputs/context/FormModeContext.tsx +31 -0
  38. package/src/components/templatesV2/inputs/context/TemplateContext.tsx +37 -0
  39. package/src/components/templatesV2/inputs/field-names.ts +8 -0
  40. package/src/components/templatesV2/inputs/form-type.ts +54 -0
  41. package/src/components/templatesV2/inputs/index.tsx +33 -0
  42. package/src/components/templatesV2/inputs/input-type.ts +23 -0
  43. package/src/components/templatesV2/inputs/primitives/CheckboxInput.tsx +45 -0
  44. package/src/components/templatesV2/inputs/primitives/NumberInput.tsx +58 -0
  45. package/src/components/templatesV2/inputs/primitives/SelectorInput.tsx +111 -0
  46. package/src/components/templatesV2/inputs/primitives/TextInput.tsx +55 -0
  47. package/src/components/templatesV2/inputs/primitives/TextWithQueryInput.tsx +292 -0
  48. package/src/components/templatesV2/inputs/primitives/index.ts +10 -0
  49. package/src/components/templatesV2/inputs/renderers/index.ts +2 -0
  50. package/src/components/templatesV2/inputs/renderers/renderFormControl.tsx +384 -0
  51. package/src/components/templatesV2/inputs/renderers/renderSearchInput.tsx +128 -0
  52. package/src/components/templatesV2/inputs/search/MultiTextSearchInput.tsx +87 -0
  53. package/src/components/templatesV2/inputs/search/SearchInputWrapper.tsx +113 -0
  54. package/src/components/templatesV2/inputs/search/TextSearchInput.tsx +309 -0
  55. package/src/components/templatesV2/inputs/search/index.ts +4 -0
  56. package/src/components/templatesV2/inputs/search/search-query-key.ts +23 -0
  57. package/src/components/templatesV2/inputs/search/useFieldValuesOptions.ts +58 -0
  58. package/src/components/templatesV2/inputs/search/useSearchInput.ts +79 -0
  59. package/src/components/templatesV2/inputs/table/InputTable.tsx +268 -0
  60. package/src/components/templatesV2/inputs/table/index.ts +1 -0
  61. package/src/components/templatesV2/inputs/utils/applyTemplateConfig.ts +28 -0
  62. package/src/components/templatesV2/inputs/utils/arrangeInputs.ts +52 -0
  63. package/src/components/templatesV2/inputs/utils/index.ts +6 -0
  64. package/src/components/templatesV2/inputs/utils/parseQueryName.ts +20 -0
  65. package/src/components/templatesV2/inputs/utils/renderReadOnlyText.tsx +26 -0
  66. package/src/components/templatesV2/inputs/utils/selectors.ts +69 -0
  67. package/src/components/templatesV2/inputs/utils/toStringArray.ts +23 -0
  68. package/src/components/templatesV2/pageForm.tsx +153 -0
  69. package/src/components/{templates → templatesV2}/templates-list/templates-list-row.tsx +2 -1
  70. package/src/components/{templates → templatesV2}/templates-list/templates-list-table.tsx +2 -2
  71. package/src/components/{templates → templatesV2}/templates-list/templates-list.tsx +19 -28
  72. package/src/components/{templates → templatesV2}/types.tsx +12 -12
  73. package/src/components/templatesV2/validation/buildZodSchema.ts +212 -0
  74. package/src/components/templatesV2/validation/zodFormikValidate.ts +17 -0
  75. package/src/components/templatesV2/wizard.tsx +746 -0
  76. package/src/pages/dashboard/dashboard-link-cards.tsx +0 -1
  77. package/src/pages/dashboard/dashboard-list-sections.tsx +1 -1
  78. package/src/pages/dashboard/dashboard.tsx +0 -1
  79. package/src/pages/executions/executions-list/executions-list-table.tsx +0 -1
  80. package/src/pages/executions/executions.tsx +1 -1
  81. package/src/pages/integration-create/integration-create-steps.ts +8 -0
  82. package/src/pages/integration-create/integration-create.tsx +81 -28
  83. package/src/pages/integration-create/pre-creation-step.tsx +9 -9
  84. package/src/pages/integration-create/select-step.tsx +3 -3
  85. package/src/pages/integration-create/template-list-step.tsx +28 -3
  86. package/src/pages/integration-create/template-options-step.tsx +119 -0
  87. package/src/pages/integration-create/wizard-step.tsx +5 -4
  88. package/src/pages/integration-details/import-process/copy-tasks-list.tsx +1 -3
  89. package/src/pages/integration-details/integration-details.tsx +1 -1
  90. package/src/pages/integration-details/task-drawer/add-task.tsx +4 -4
  91. package/src/pages/integration-details/task-drawer/edit-task.tsx +5 -5
  92. package/src/pages/integration-details/task-drawer/task-drawer.tsx +2 -2
  93. package/src/components/templates/inputs/index.tsx +0 -1392
  94. package/src/components/templates/wizard.tsx +0 -737
  95. /package/src/components/{templates → templatesV2}/index.ts +0 -0
  96. /package/src/components/{templates → templatesV2}/template-filter.ts +0 -0
  97. /package/src/components/{templates → templatesV2}/templates-list/index.ts +0 -0
@@ -1,1392 +0,0 @@
1
- "use client";
2
-
3
- import type {
4
- CheckUniqueIdentifierQuery,
5
- GetSourceEndpointsQuery,
6
- } from "@evenicanpm/admin-core/api/e4/graphqlRequestSdk";
7
- import { templateInputSearch } from "@evenicanpm/admin-integrate/api/templates/queries";
8
- import {
9
- isTemplateInputSearchQueryKey,
10
- type TemplateInputSearchQueryKey,
11
- } from "@evenicanpm/admin-integrate/api/templates/queries/types";
12
- import AddIcon from "@mui/icons-material/Add";
13
- import DeleteIcon from "@mui/icons-material/Delete";
14
- import {
15
- Autocomplete,
16
- type AutocompleteChangeReason,
17
- Box,
18
- Button,
19
- Card,
20
- CardContent,
21
- Checkbox,
22
- FormControlLabel,
23
- FormHelperText,
24
- IconButton,
25
- Table,
26
- TableBody,
27
- TableCell,
28
- TableHead,
29
- TableRow,
30
- TextField,
31
- Typography,
32
- } from "@mui/material";
33
- import { debounce } from "lodash";
34
- import { useSearchParams } from "next/navigation";
35
- import * as React from "react";
36
- import {
37
- type ErrorState,
38
- type FieldValue,
39
- type FormState,
40
- type ProcessConfigMerge,
41
- type ProcessTemplateInput,
42
- type ProcessTemplateInputSchema,
43
- type ProcessTemplatePage,
44
- type SearchOption,
45
- sortBy,
46
- templateFieldKey,
47
- } from "../types";
48
-
49
- type InputRow =
50
- | { type: "pair"; items: [ProcessTemplateInput, ProcessTemplateInput] }
51
- | { type: "full"; item: ProcessTemplateInput };
52
-
53
- export function TextInput({
54
- label,
55
- placeholder,
56
- value,
57
- setValue,
58
- error,
59
- helpText,
60
- readOnly,
61
- }: Readonly<{
62
- label: string;
63
- placeholder?: string | null;
64
- value: string;
65
- setValue: (v: string) => void;
66
- error?: string | null;
67
- helpText?: string | null;
68
- readOnly?: boolean;
69
- }>) {
70
- if (readOnly) return renderReadOnlyText(label, value);
71
-
72
- return (
73
- <TextField
74
- label={label}
75
- value={value ?? ""}
76
- onChange={(e) => setValue(e.target.value)}
77
- placeholder={placeholder ?? undefined}
78
- fullWidth
79
- error={Boolean(error)}
80
- helperText={error ?? helpText ?? ""}
81
- disabled={readOnly}
82
- />
83
- );
84
- }
85
-
86
- export function NumberInput({
87
- label,
88
- placeholder,
89
- value,
90
- setValue,
91
- error,
92
- helpText,
93
- readOnly,
94
- }: Readonly<{
95
- label: string;
96
- placeholder?: string | null;
97
- value: number | "" | null;
98
- setValue: (v: number | "") => void;
99
- error?: string | null;
100
- helpText?: string | null;
101
- readOnly?: boolean;
102
- }>) {
103
- if (readOnly) return renderReadOnlyText(label, value);
104
-
105
- return (
106
- <TextField
107
- type="number"
108
- label={label}
109
- value={value ?? ""}
110
- onChange={(e) =>
111
- setValue(e.target.value === "" ? "" : Number(e.target.value))
112
- }
113
- placeholder={placeholder ?? undefined}
114
- fullWidth
115
- error={Boolean(error)}
116
- helperText={error ?? helpText ?? ""}
117
- disabled={readOnly}
118
- />
119
- );
120
- }
121
-
122
- export function CheckboxInput({
123
- label,
124
- checked,
125
- setChecked,
126
- helpText,
127
- readOnly,
128
- }: Readonly<{
129
- label: string;
130
- checked: boolean;
131
- setChecked: (v: boolean) => void;
132
- helpText?: string | null;
133
- readOnly?: boolean;
134
- }>) {
135
- return (
136
- <Box>
137
- <FormControlLabel
138
- control={
139
- <Checkbox
140
- checked={checked}
141
- onChange={(e) => setChecked(e.target.checked)}
142
- />
143
- }
144
- label={label}
145
- disabled={readOnly}
146
- />
147
- {helpText ? (
148
- <FormHelperText sx={{ mt: -1.5, mb: 1 }}>{helpText}</FormHelperText>
149
- ) : null}
150
- </Box>
151
- );
152
- }
153
-
154
- function toStringArray(value: unknown): string[] {
155
- if (value == null) return [];
156
-
157
- // Already an array: coerce each item to string
158
- if (Array.isArray(value)) return value.map(String);
159
-
160
- // String: try JSON-parse; if it isn't a JSON array, treat as a single option
161
- if (typeof value === "string") {
162
- try {
163
- const parsed = JSON.parse(value);
164
- return Array.isArray(parsed) ? parsed.map(String) : [value];
165
- } catch {
166
- return [value];
167
- }
168
- }
169
-
170
- // Plain object: use its values as the option labels/values
171
- if (typeof value === "object") {
172
- return Object.values(value as Record<string, unknown>).map(String);
173
- }
174
-
175
- // Everything else → no options
176
- return [];
177
- }
178
-
179
- function getSelectorOptionsFromInput(input: ProcessTemplateInput): string[] {
180
- const schemaWithOptions =
181
- input?.schemas?.find(
182
- (s: ProcessTemplateInputSchema | null) => s?.inputType === "selector",
183
- ) ?? input?.schemas?.[0];
184
-
185
- return toStringArray(schemaWithOptions?.options);
186
- }
187
-
188
- export function SelectorInput({
189
- fieldName,
190
- label,
191
- value,
192
- setValue,
193
- helpText,
194
- error,
195
- options,
196
- insideTable = false,
197
- minWidth,
198
- readOnly,
199
- }: Readonly<{
200
- fieldName: string;
201
- label: string;
202
- value: string;
203
- setValue: (v: string | null) => void;
204
- helpText?: string | null;
205
- error?: string | null;
206
- options?: string[];
207
- insideTable?: boolean;
208
- minWidth?: number;
209
- readOnly?: boolean;
210
- }>) {
211
- const opts = options ?? [];
212
- const allowFreeText = opts.length === 0;
213
- const id = `selector-${fieldName}`;
214
-
215
- if (readOnly)
216
- return renderReadOnlyText(insideTable ? undefined : label, value);
217
-
218
- return (
219
- <Box
220
- sx={{
221
- minWidth: minWidth ?? (insideTable ? 220 : undefined),
222
- width: "100%",
223
- }}
224
- >
225
- <Autocomplete
226
- id={id}
227
- disablePortal={insideTable}
228
- disableClearable
229
- options={opts}
230
- freeSolo={allowFreeText}
231
- value={value ?? ""}
232
- onChange={(_e, val) => setValue((val as string | null) ?? "")}
233
- onInputChange={(_e, val) => setValue(val)}
234
- disabled={readOnly}
235
- renderInput={(params) => (
236
- <TextField
237
- {...params}
238
- size={insideTable ? "small" : "medium"}
239
- label={label}
240
- name={fieldName}
241
- error={Boolean(error)}
242
- helperText={error ?? helpText ?? ""}
243
- fullWidth
244
- disabled={readOnly}
245
- />
246
- )}
247
- />
248
- </Box>
249
- );
250
- }
251
-
252
- function SearchInputWrapper({
253
- fieldName,
254
- inputType,
255
- label,
256
- value,
257
- setValue,
258
- placeholder,
259
- helpText,
260
- error,
261
- queryName,
262
- handleFieldChange,
263
- clearTemplateFields,
264
- readOnly,
265
- isEditMode,
266
- isCreateMode,
267
- setFieldsDisabled,
268
- templateId,
269
- disabledFields,
270
- templateConfig,
271
- }: Readonly<{
272
- fieldName?: string;
273
- inputType: "multi-text-search" | "text-search" | "text-unique-identifier";
274
- label: string;
275
- value: string | string[] | null;
276
- setValue:
277
- | ((v: string | string[] | boolean | null, fieldName?: string) => void)
278
- | ((v: string | string[] | boolean | null) => void);
279
- placeholder?: string | null;
280
- helpText?: string | null;
281
- error?: string | null;
282
- queryName: TemplateInputSearchQueryKey;
283
- handleFieldChange: (
284
- templateId: string,
285
- fieldName: string,
286
- value: FieldValue,
287
- ) => void;
288
- clearTemplateFields?: (templateId: string) => void;
289
- readOnly?: boolean;
290
- sharedCountCallBack?: (count: number) => void;
291
- isEditMode?: boolean;
292
- isCreateMode?: boolean;
293
- setFieldsDisabled?: (
294
- templateId: string,
295
- fieldNames: string[],
296
- disabled: boolean,
297
- ) => void;
298
- templateId?: string;
299
- disabledFields?: Set<string>;
300
- templateConfig?: string;
301
- }>) {
302
- const searchParams = useSearchParams();
303
-
304
- const [searchText, setSearchText] = React.useState<string>("");
305
- const [options, setOptions] = React.useState<SearchOption[]>([]);
306
-
307
- const { data } = templateInputSearch.useData(
308
- inputType === "text-unique-identifier"
309
- ? {
310
- uniqueIdentifier: searchText,
311
- }
312
- : {
313
- input: {
314
- processTemplateId: searchParams.get("template")?.toString() ?? null,
315
- searchText: searchText,
316
- },
317
- },
318
- queryName,
319
- );
320
-
321
- React.useEffect(() => {
322
- if (data) {
323
- switch (queryName) {
324
- case "getSourceEndpoints": {
325
- const typedData = data as GetSourceEndpointsQuery;
326
- setOptions(
327
- typedData.getSourceEndpoints?.map((e) => ({
328
- name: e?.name ?? "",
329
- templateConfig: e?.templateConfig ?? "",
330
- })) ?? [],
331
- );
332
- break;
333
- }
334
- case "checkUniqueIdentifier": {
335
- const typedData = data as CheckUniqueIdentifierQuery;
336
- setValue(
337
- (typedData.checkUniqueIdentifier?.isUnique ?? null) as
338
- | string
339
- | string[]
340
- | boolean
341
- | null,
342
- "isUnique",
343
- );
344
- break;
345
- }
346
- }
347
- }
348
- }, [data]);
349
-
350
- if (readOnly) {
351
- return renderReadOnlyText(label, value);
352
- }
353
-
354
- return (
355
- <>
356
- {inputType === "multi-text-search" && (
357
- <Box sx={{ display: "flex", gap: "10px" }}>
358
- <MultiTextSearchInput
359
- label={label}
360
- value={value as string[]}
361
- setValue={setValue as (v: string[]) => void}
362
- options={options}
363
- placeholder={placeholder}
364
- helpText={helpText}
365
- error={error}
366
- setSearchText={setSearchText}
367
- readOnly={readOnly}
368
- />
369
- </Box>
370
- )}
371
-
372
- {inputType === "text-search" && (
373
- <Box sx={{ display: "flex", gap: "10px" }}>
374
- <TextSearchInput
375
- fieldName={fieldName}
376
- label={label}
377
- value={value as string}
378
- setValue={
379
- setValue as (v: string | string[] | boolean | null) => void
380
- }
381
- options={options}
382
- placeholder={placeholder}
383
- helpText={helpText}
384
- error={error}
385
- setSearchText={setSearchText}
386
- handleFieldChange={handleFieldChange}
387
- clearTemplateFields={clearTemplateFields}
388
- readOnly={readOnly}
389
- setFieldsDisabled={setFieldsDisabled}
390
- templateId={templateId}
391
- disabledFields={disabledFields}
392
- isEditMode={isEditMode}
393
- templateConfig={templateConfig}
394
- />
395
- </Box>
396
- )}
397
-
398
- {inputType === "text-unique-identifier" && (
399
- <Box sx={{ display: "flex", gap: "10px" }}>
400
- <TextSearchInput
401
- fieldName={fieldName}
402
- label={label}
403
- value={value as string}
404
- setValue={
405
- setValue as (v: string | string[] | boolean | null) => void
406
- }
407
- options={[]}
408
- placeholder={placeholder}
409
- helpText={helpText}
410
- error={error}
411
- setSearchText={setSearchText}
412
- handleFieldChange={handleFieldChange}
413
- clearTemplateFields={clearTemplateFields}
414
- readOnly={isCreateMode === false || readOnly}
415
- setFieldsDisabled={setFieldsDisabled}
416
- templateId={templateId}
417
- disabledFields={disabledFields}
418
- isEditMode={isEditMode}
419
- templateConfig={templateConfig}
420
- />
421
- </Box>
422
- )}
423
- </>
424
- );
425
- }
426
-
427
- export function applyTemplateConfig(
428
- selectedOption: SearchOption,
429
- handleFieldChange: (
430
- templateId: string,
431
- fieldName: string,
432
- value: FieldValue,
433
- ) => void,
434
- ) {
435
- const merge: ProcessConfigMerge = { input: { config: [] } };
436
- try {
437
- merge.input = { config: [JSON.parse(selectedOption.templateConfig)] };
438
-
439
- if (merge.input.config.length > 0) {
440
- const config = merge.input.config[0];
441
- const processTemplateId = config.processTemplateId;
442
- for (const key of Object.keys(config)) {
443
- handleFieldChange(processTemplateId as string, key, config[key]);
444
- }
445
- }
446
- } catch {
447
- console.error(
448
- `Failed to apply template config for ${selectedOption.name}. Skipping...`,
449
- );
450
- }
451
- }
452
-
453
- export function TextSearchInput({
454
- fieldName,
455
- label,
456
- value,
457
- setValue,
458
- options,
459
- placeholder,
460
- helpText,
461
- error,
462
- setSearchText,
463
- handleFieldChange,
464
- clearTemplateFields,
465
- readOnly,
466
- setFieldsDisabled,
467
- templateId,
468
- disabledFields,
469
- isEditMode,
470
- templateConfig,
471
- }: Readonly<{
472
- fieldName?: string;
473
- label: string;
474
- value: string;
475
- setValue: (v: string | string[] | boolean | null) => void;
476
- options: SearchOption[];
477
- placeholder?: string | null;
478
- helpText?: string | null;
479
- error?: string | null;
480
- setSearchText: React.Dispatch<React.SetStateAction<string>>;
481
- handleFieldChange: (
482
- templateId: string,
483
- fieldName: string,
484
- value: FieldValue,
485
- ) => void;
486
- clearTemplateFields?: (templateId: string) => void;
487
- readOnly?: boolean;
488
- setFieldsDisabled?: (
489
- templateId: string,
490
- fieldNames: string[],
491
- disabled: boolean,
492
- ) => void;
493
- templateId?: string;
494
- disabledFields?: Set<string>;
495
- isEditMode?: boolean;
496
- templateConfig?: string;
497
- }>) {
498
- const searchParams = useSearchParams();
499
- const [selected, setSelected] = React.useState<string | null>(value ?? null);
500
-
501
- // remember which option was actually applied so we can reliably re-enable later
502
- const appliedOptionRef = React.useRef<string | null>(null);
503
-
504
- // disabledFields + readOnly
505
- const selfKey =
506
- templateId && fieldName ? templateFieldKey(templateId, fieldName) : null;
507
- const isSelfDisabled = selfKey
508
- ? Boolean(disabledFields?.has(selfKey))
509
- : false;
510
- const disabled = Boolean(readOnly || isSelfDisabled);
511
-
512
- const debouncedSetSearchText = React.useMemo(
513
- () => debounce((s: string) => setSearchText(s), 500),
514
- [],
515
- );
516
- React.useEffect(() => {
517
- // Cleanup function to cancel any pending debounced calls
518
- return () => {
519
- debouncedSetSearchText.cancel();
520
- };
521
- }, [debouncedSetSearchText]);
522
-
523
- React.useEffect(() => {
524
- if (selected && templateId) {
525
- const option = options.find((o) => o?.name === selected);
526
- if (option) {
527
- try {
528
- const config = JSON.parse(option.templateConfig);
529
-
530
- if (
531
- config &&
532
- templateConfig //&&
533
- //config.dataDescription === inputConfig.dataDescription
534
- ) {
535
- option.templateConfig = templateConfig;
536
- }
537
-
538
- // Always apply config (both edit and non-edit modes)
539
- applyTemplateConfig(option, handleFieldChange);
540
- appliedOptionRef.current = option.name ?? null;
541
-
542
- // Only disable fields when NOT in edit mode
543
- if (setFieldsDisabled && !isEditMode) {
544
- const config = JSON.parse(option.templateConfig);
545
- const fieldNamesToDisable = Object.keys(config).filter(
546
- (key) => key !== "processTemplateId" && key !== (fieldName ?? ""),
547
- );
548
- setFieldsDisabled(templateId, fieldNamesToDisable, true);
549
- }
550
- } catch (err) {
551
- console.error("Failed to parse template config", err);
552
- }
553
- }
554
- }
555
- }, [
556
- selected,
557
- templateId,
558
- options,
559
- setFieldsDisabled,
560
- handleFieldChange,
561
- isEditMode,
562
- ]);
563
-
564
- const handleAutocompleteOnChange = (
565
- event: React.SyntheticEvent,
566
- value: string | null,
567
- reason: AutocompleteChangeReason,
568
- ) => {
569
- if (reason === "clear" && event.type === "click") {
570
- setSelected(null);
571
- // Re-enable all fields when cleared
572
- if (templateId && setFieldsDisabled) {
573
- const appliedName = appliedOptionRef.current;
574
- if (appliedName) {
575
- const option = options.find((o) => o?.name === appliedName);
576
- if (option) {
577
- try {
578
- const config = JSON.parse(option.templateConfig);
579
- // exclude the search field itself when re-enabling (it was never disabled)
580
- const fieldNamesToEnable = Object.keys(config).filter(
581
- (key) =>
582
- key !== "processTemplateId" && key !== (fieldName ?? ""),
583
- );
584
- setFieldsDisabled(templateId, fieldNamesToEnable, false);
585
- } catch (err) {
586
- console.error("Failed to parse template config", err);
587
- } finally {
588
- appliedOptionRef.current = null;
589
- }
590
- }
591
- }
592
- }
593
- const templateIdFromParams = searchParams.get("template");
594
- if (clearTemplateFields && templateIdFromParams) {
595
- clearTemplateFields(templateIdFromParams);
596
- }
597
- } else {
598
- setSelected(value);
599
- }
600
- };
601
-
602
- const handleInputChange = (
603
- event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
604
- ) => {
605
- const newVal = event.target.value;
606
- setValue(newVal);
607
- debouncedSetSearchText(newVal);
608
-
609
- if (newVal.trim() === "" && templateId && setFieldsDisabled) {
610
- const appliedName = appliedOptionRef.current;
611
- if (appliedName) {
612
- const option = options.find((o) => o?.name === appliedName);
613
- if (option) {
614
- try {
615
- const config = JSON.parse(option.templateConfig);
616
- const fieldNamesToEnable = Object.keys(config).filter(
617
- (key) => key !== "processTemplateId" && key !== (fieldName ?? ""),
618
- );
619
- setFieldsDisabled(templateId, fieldNamesToEnable, false);
620
- } catch (err) {
621
- console.error(
622
- "Failed to parse template config while enabling fields",
623
- err,
624
- );
625
- } finally {
626
- appliedOptionRef.current = null;
627
- }
628
- }
629
- }
630
-
631
- if (clearTemplateFields) {
632
- clearTemplateFields(templateId);
633
- }
634
- }
635
- };
636
-
637
- const shouldShowInfoMessage = !isEditMode && selected;
638
- const helperTextFinal = shouldShowInfoMessage
639
- ? "Existing data descriptions are not editable during creation."
640
- : helpText;
641
-
642
- return (
643
- <Box>
644
- <Autocomplete
645
- freeSolo={!selected}
646
- options={options.map((o) => o.name)}
647
- value={selected ?? ""}
648
- onChange={handleAutocompleteOnChange}
649
- disabled={disabled}
650
- renderInput={(params) => (
651
- <TextField
652
- {...params}
653
- label={label}
654
- value={value ?? ""}
655
- onChange={handleInputChange}
656
- placeholder={placeholder ?? undefined}
657
- fullWidth
658
- error={Boolean(error)}
659
- helperText={error ?? helperTextFinal ?? ""}
660
- disabled={disabled}
661
- />
662
- )}
663
- />
664
- </Box>
665
- );
666
- }
667
-
668
- export function MultiTextSearchInput({
669
- label,
670
- value,
671
- setValue,
672
- options,
673
- placeholder,
674
- helpText,
675
- error,
676
- setSearchText,
677
- readOnly,
678
- }: Readonly<{
679
- label: string;
680
- value: string[];
681
- setValue: (v: string[]) => void;
682
- options: SearchOption[];
683
- placeholder?: string | null;
684
- helpText?: string | null;
685
- error?: string | null;
686
- setSearchText: React.Dispatch<React.SetStateAction<string>>;
687
- readOnly?: boolean;
688
- }>) {
689
- const [input, setInput] = React.useState<string>("");
690
-
691
- const debouncedSetSearchText = React.useMemo(
692
- () => debounce((s: string) => setSearchText(s), 500),
693
- [],
694
- );
695
-
696
- React.useEffect(() => {
697
- // Cleanup function to cancel any pending debounced calls
698
- return () => {
699
- debouncedSetSearchText.cancel();
700
- };
701
- }, [debouncedSetSearchText]);
702
-
703
- const handleAutocompleteOnChange = (
704
- _e: React.SyntheticEvent,
705
- val: string[],
706
- ) => {
707
- setValue(val);
708
- debouncedSetSearchText("");
709
- };
710
-
711
- const handleInputChange = (
712
- event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
713
- ) => {
714
- setInput(event.target.value);
715
- debouncedSetSearchText(event.target.value);
716
- };
717
-
718
- return (
719
- <Autocomplete
720
- multiple
721
- freeSolo
722
- options={options.map((o) => o.name)}
723
- value={value ?? []}
724
- onChange={handleAutocompleteOnChange}
725
- disabled={readOnly}
726
- renderInput={(params) => (
727
- <TextField
728
- {...params}
729
- value={input}
730
- onChange={handleInputChange}
731
- label={label}
732
- placeholder={placeholder ?? undefined}
733
- error={Boolean(error)}
734
- helperText={error ?? helpText ?? ""}
735
- fullWidth
736
- disabled={readOnly}
737
- />
738
- )}
739
- />
740
- );
741
- }
742
-
743
- export function InputTable({
744
- rows,
745
- setRows,
746
- schemas,
747
- tableError,
748
- readOnly,
749
- }: Readonly<{
750
- rows: Array<Record<string, any>>;
751
- setRows: (rows: Array<Record<string, any>>) => void;
752
- schemas: ProcessTemplateInputSchema[];
753
- tableError: string | null;
754
- readOnly?: boolean;
755
- }>) {
756
- const ordered = React.useMemo(
757
- () => sortBy<ProcessTemplateInputSchema>(schemas),
758
- [schemas],
759
- );
760
- const addRow = () => {
761
- const empty: Record<string, any> = {};
762
- for (const s of ordered) {
763
- const key = s.fieldKey ?? s.label ?? "";
764
- empty[key] =
765
- s.inputType === "checkbox" ? false : ((s.defaultValue as any) ?? "");
766
- }
767
- setRows([...(rows ?? []), empty]);
768
- };
769
-
770
- const delRow = (idx: number) => {
771
- const copy = [...rows];
772
- copy.splice(idx, 1);
773
- setRows(copy);
774
- };
775
-
776
- const updateCell = (idx: number, columnKey: string, value: any) => {
777
- const copy = [...rows];
778
- copy[idx] = { ...copy[idx], [columnKey]: value };
779
- setRows(copy);
780
- };
781
-
782
- return (
783
- <>
784
- {readOnly && rows.length === 0 && (
785
- <Box>
786
- <Typography>No values</Typography>
787
- </Box>
788
- )}
789
- {(!readOnly || (readOnly && rows.length > 0)) && (
790
- <Card
791
- variant="outlined"
792
- sx={{ mb: 1, borderColor: tableError ? "error.main" : "grey.400" }}
793
- >
794
- <CardContent>
795
- <Table size="small">
796
- <TableHead>
797
- <TableRow>
798
- {ordered.map((s: ProcessTemplateInputSchema) => (
799
- <TableCell key={s.id}>{s.label}</TableCell>
800
- ))}
801
- <TableCell width={1} />
802
- </TableRow>
803
- </TableHead>
804
- <TableBody>
805
- {(rows ?? []).map((r: Record<string, any>, i: number) => (
806
- <TableRow key={`table-row-${i + 1}`}>
807
- {ordered.map((s: ProcessTemplateInputSchema) => {
808
- const columnKey = s.fieldKey ?? s.label;
809
- const cell = columnKey ? r[columnKey] : "";
810
- if (s.inputType === "checkbox") {
811
- return (
812
- <TableCell key={s.id}>
813
- <Checkbox
814
- checked={Boolean(cell)}
815
- onChange={(e) =>
816
- updateCell(i, columnKey ?? "", e.target.checked)
817
- }
818
- disabled={readOnly}
819
- />
820
- </TableCell>
821
- );
822
- }
823
- if (s.inputType === "selector") {
824
- const columnKey = s.fieldKey ?? s.label;
825
- const cell = columnKey ? r[columnKey] : "";
826
- const opts = toStringArray(s.options);
827
-
828
- return (
829
- <TableCell key={s.id}>
830
- <SelectorInput
831
- insideTable={true}
832
- minWidth={120}
833
- fieldName={columnKey ?? ""}
834
- label={s.label ?? ""}
835
- value={cell}
836
- options={opts}
837
- setValue={(v) =>
838
- updateCell(i, columnKey ?? "", v)
839
- }
840
- readOnly={readOnly}
841
- />
842
- </TableCell>
843
- );
844
- }
845
- return (
846
- <TableCell key={s.id}>
847
- {readOnly ? (
848
- renderReadOnlyText(undefined, cell)
849
- ) : (
850
- <TextField
851
- size="small"
852
- value={cell}
853
- onChange={(e) =>
854
- updateCell(i, columnKey ?? "", e.target.value)
855
- }
856
- fullWidth
857
- disabled={readOnly}
858
- />
859
- )}
860
- </TableCell>
861
- );
862
- })}
863
- <TableCell>
864
- {!readOnly && (
865
- <IconButton
866
- onClick={() => delRow(i)}
867
- size="small"
868
- disabled={readOnly}
869
- >
870
- <DeleteIcon fontSize="small" />
871
- </IconButton>
872
- )}
873
- </TableCell>
874
- </TableRow>
875
- ))}
876
- {!readOnly && (
877
- <TableRow>
878
- <TableCell colSpan={ordered.length + 1}>
879
- <Button
880
- onClick={addRow}
881
- startIcon={<AddIcon />}
882
- variant="outlined"
883
- size="small"
884
- disabled={readOnly}
885
- >
886
- Add row
887
- </Button>
888
- </TableCell>
889
- </TableRow>
890
- )}
891
- </TableBody>
892
- </Table>
893
- </CardContent>
894
- </Card>
895
- )}
896
- </>
897
- );
898
- }
899
-
900
- function renderFormControl({
901
- input,
902
- templateId,
903
- values,
904
- errors,
905
- handleFieldChange,
906
- clearTemplateFields,
907
- readOnly,
908
- sharedCount,
909
- isEditMode,
910
- isCreateMode,
911
- disabledFields,
912
- setFieldsDisabled,
913
- templateConfig,
914
- }: Readonly<{
915
- input: ProcessTemplateInput;
916
- templateId: string;
917
- values: FormState;
918
- errors: ErrorState;
919
- handleFieldChange: (
920
- templateId: string,
921
- fieldName: string,
922
- value: FieldValue,
923
- ) => void;
924
- clearTemplateFields: (templateId: string) => void;
925
- readOnly?: boolean;
926
- sharedCount?: (count: number) => void;
927
- isEditMode?: boolean;
928
- isCreateMode?: boolean;
929
- disabledFields?: Set<string>;
930
- setFieldsDisabled?: (
931
- templateId: string,
932
- fieldNames: string[],
933
- disabled: boolean,
934
- ) => void;
935
- templateConfig?: string;
936
- }>) {
937
- const k = templateFieldKey(templateId, input.fieldName ?? "");
938
- const err = errors[k] ?? null;
939
- const isFieldDisabled = disabledFields?.has(k) ?? false;
940
- const finalReadOnly = readOnly || isFieldDisabled;
941
-
942
- if (
943
- input.inputType === "hidden" ||
944
- input.inputType === "hidden-number" ||
945
- input.inputType === "hidden-number-or-null"
946
- ) {
947
- return null;
948
- }
949
-
950
- if (input.inputType === "checkbox") {
951
- return (
952
- <CheckboxInput
953
- label={input.label ?? ""}
954
- checked={Boolean(values[k] ?? false)}
955
- setChecked={(v) =>
956
- handleFieldChange(templateId, input.fieldName ?? "", v)
957
- }
958
- helpText={input.helpText ?? ""}
959
- readOnly={finalReadOnly}
960
- />
961
- );
962
- }
963
-
964
- if (input.inputType === "number") {
965
- return (
966
- <NumberInput
967
- label={input.label ?? ""}
968
- value={(values[k] as number) ?? ""}
969
- setValue={(v) =>
970
- handleFieldChange(templateId, input.fieldName ?? "", v as any)
971
- }
972
- placeholder={input.placeholder ?? ""}
973
- helpText={input.helpText ?? ""}
974
- error={err}
975
- readOnly={finalReadOnly}
976
- />
977
- );
978
- }
979
-
980
- if (input.inputType === "selector") {
981
- return (
982
- <SelectorInput
983
- fieldName={input.fieldName ?? ""}
984
- label={input.label ?? ""}
985
- value={(values[k] as string) ?? ""}
986
- setValue={(v) =>
987
- handleFieldChange(templateId, input.fieldName ?? "", v)
988
- }
989
- helpText={input.helpText ?? ""}
990
- error={err}
991
- options={getSelectorOptionsFromInput(input)}
992
- readOnly={finalReadOnly}
993
- />
994
- );
995
- }
996
-
997
- if (input.inputType === "multi-text-search") {
998
- const queryName = parseQueryName(input.queryName ?? "");
999
- return (
1000
- <SearchInputWrapper
1001
- inputType="multi-text-search"
1002
- queryName={queryName}
1003
- label={input.label ?? ""}
1004
- value={(values[k] as string[]) ?? []}
1005
- setValue={(v: string | string[] | boolean | null) =>
1006
- handleFieldChange(templateId, input.fieldName ?? "", v)
1007
- }
1008
- placeholder={input.placeholder ?? ""}
1009
- helpText={input.helpText ?? ""}
1010
- error={err}
1011
- handleFieldChange={handleFieldChange}
1012
- readOnly={readOnly}
1013
- sharedCountCallBack={(count) => sharedCount?.(count)}
1014
- isEditMode={isEditMode}
1015
- setFieldsDisabled={setFieldsDisabled}
1016
- templateId={templateId}
1017
- disabledFields={disabledFields}
1018
- fieldName={input.fieldName ?? ""}
1019
- templateConfig={templateConfig}
1020
- />
1021
- );
1022
- }
1023
-
1024
- if (input.inputType === "text-search") {
1025
- const queryName = parseQueryName(input.queryName ?? "");
1026
- return (
1027
- <SearchInputWrapper
1028
- inputType="text-search"
1029
- queryName={queryName}
1030
- label={input.label ?? ""}
1031
- value={(values[k] as string) ?? ""}
1032
- setValue={(v: string | string[] | boolean | null) =>
1033
- handleFieldChange(templateId, input.fieldName ?? "", v)
1034
- }
1035
- placeholder={input.placeholder ?? ""}
1036
- helpText={input.helpText ?? ""}
1037
- error={err}
1038
- handleFieldChange={handleFieldChange}
1039
- clearTemplateFields={clearTemplateFields}
1040
- readOnly={readOnly}
1041
- sharedCountCallBack={(count) => sharedCount?.(count)}
1042
- isEditMode={isEditMode}
1043
- setFieldsDisabled={setFieldsDisabled}
1044
- templateId={templateId}
1045
- disabledFields={disabledFields}
1046
- fieldName={input.fieldName ?? ""}
1047
- templateConfig={templateConfig}
1048
- />
1049
- );
1050
- }
1051
-
1052
- if (input.inputType === "text-unique-identifier") {
1053
- const queryName = parseQueryName(input.queryName ?? "");
1054
- return (
1055
- <SearchInputWrapper
1056
- inputType="text-unique-identifier"
1057
- queryName={queryName}
1058
- label={input.label ?? ""}
1059
- value={(values[k] as string) ?? ""}
1060
- setValue={(
1061
- v: string | string[] | boolean | null,
1062
- fieldName?: string,
1063
- ) => {
1064
- if (fieldName) {
1065
- handleFieldChange(templateId, fieldName, v);
1066
- } else {
1067
- handleFieldChange(templateId, input.fieldName ?? "", v);
1068
- }
1069
- }}
1070
- placeholder={input.placeholder ?? ""}
1071
- helpText={input.helpText ?? ""}
1072
- error={err}
1073
- handleFieldChange={handleFieldChange}
1074
- clearTemplateFields={clearTemplateFields}
1075
- readOnly={readOnly}
1076
- sharedCountCallBack={(count) => sharedCount?.(count)}
1077
- isEditMode={isEditMode}
1078
- isCreateMode={isCreateMode}
1079
- setFieldsDisabled={setFieldsDisabled}
1080
- templateId={templateId}
1081
- disabledFields={disabledFields}
1082
- fieldName={input.fieldName ?? ""}
1083
- templateConfig={templateConfig}
1084
- />
1085
- );
1086
- }
1087
-
1088
- if (input.inputType === "input-table") {
1089
- const value = values[k];
1090
- const rows = (Array.isArray(value) ? value : []) as Array<
1091
- Record<string, unknown>
1092
- >;
1093
-
1094
- // Specifically for "properties" key in values. Need to convert to array. Might need to consider a less specific solution
1095
- if (value && rows.length === 0 && input.schemas?.length === 2) {
1096
- for (const schemaKey of Object.keys(value)) {
1097
- if (input.schemas[0]?.fieldKey && input.schemas[1]?.fieldKey) {
1098
- const row: Record<string, unknown> = {};
1099
- row[input.schemas[0].fieldKey] = schemaKey;
1100
- row[input.schemas[1].fieldKey] = (value as Record<string, string>)[
1101
- schemaKey
1102
- ];
1103
- rows.push(row);
1104
- }
1105
- }
1106
- }
1107
-
1108
- return (
1109
- <Box>
1110
- <Typography
1111
- variant="subtitle2"
1112
- sx={{ mb: 1, color: err ? "error.main" : "main.900" }}
1113
- >
1114
- {input.label}
1115
- </Typography>
1116
- <InputTable
1117
- rows={rows}
1118
- setRows={(rows) =>
1119
- handleFieldChange(templateId, input.fieldName ?? "", rows)
1120
- }
1121
- schemas={(input.schemas as ProcessTemplateInputSchema[]) ?? []}
1122
- tableError={err}
1123
- readOnly={finalReadOnly}
1124
- />
1125
- {!readOnly && input.helpText && !err ? (
1126
- <FormHelperText sx={{ my: 1, ml: 2 }}>
1127
- {input.helpText ?? ""}
1128
- </FormHelperText>
1129
- ) : null}
1130
- {err ? (
1131
- <FormHelperText
1132
- error
1133
- sx={{ my: 1, ml: 2, whiteSpace: "pre-wrap", fontFamily: "inherit" }}
1134
- >
1135
- {String(err)}
1136
- </FormHelperText>
1137
- ) : null}
1138
- </Box>
1139
- );
1140
- }
1141
-
1142
- return (
1143
- <TextInput
1144
- label={input.label ?? ""}
1145
- value={(values[k] as string) ?? ""}
1146
- setValue={(v) => handleFieldChange(templateId, input.fieldName ?? "", v)}
1147
- placeholder={input.placeholder ?? ""}
1148
- helpText={input.helpText ?? ""}
1149
- error={err}
1150
- readOnly={finalReadOnly}
1151
- />
1152
- );
1153
- }
1154
-
1155
- function arrangeInputs(inputs: ProcessTemplateInput[]): InputRow[] {
1156
- const ordered = sortBy(inputs);
1157
-
1158
- const isTableOrHidden = (inp: ProcessTemplateInput) =>
1159
- inp.inputType === "input-table" || inp.inputType?.startsWith("hidden");
1160
-
1161
- const isPairable = (inp?: ProcessTemplateInput) =>
1162
- !!inp && !isTableOrHidden(inp);
1163
-
1164
- const areConsecutiveSorts = (
1165
- a?: ProcessTemplateInput,
1166
- b?: ProcessTemplateInput,
1167
- ) =>
1168
- !!a &&
1169
- !!b &&
1170
- typeof a.sort === "number" &&
1171
- typeof b.sort === "number" &&
1172
- b.sort === a.sort + 1;
1173
-
1174
- const canPairAtAll = ordered.filter(isPairable).length >= 2;
1175
-
1176
- const rows: InputRow[] = [];
1177
- for (let i = 0; i < ordered.length; ) {
1178
- const curr = ordered[i];
1179
-
1180
- if (!isPairable(curr) || !canPairAtAll) {
1181
- rows.push({ type: "full", item: curr });
1182
- i += 1;
1183
- continue;
1184
- }
1185
-
1186
- const next = ordered[i + 1];
1187
- if (isPairable(next) && areConsecutiveSorts(curr, next)) {
1188
- rows.push({ type: "pair", items: [curr, next!] });
1189
- i += 2;
1190
- } else {
1191
- rows.push({ type: "full", item: curr });
1192
- i += 1;
1193
- }
1194
- }
1195
-
1196
- return rows;
1197
- }
1198
-
1199
- export default function PageBody({
1200
- templateId,
1201
- page,
1202
- values,
1203
- errors,
1204
- handleFieldChange,
1205
- clearTemplateFields,
1206
- readOnly,
1207
- sharedCount,
1208
- isEditMode,
1209
- isCreateMode,
1210
- disabledFields,
1211
- setFieldsDisabled,
1212
- templateConfig,
1213
- }: Readonly<{
1214
- templateId: string;
1215
- page: ProcessTemplatePage;
1216
- values: FormState;
1217
- errors: ErrorState;
1218
- handleFieldChange: (
1219
- templateId: string,
1220
- fieldName: string,
1221
- value: FieldValue,
1222
- ) => void;
1223
- clearTemplateFields: (templateId: string) => void;
1224
- readOnly?: boolean;
1225
- isEditMode?: boolean;
1226
- isCreateMode?: boolean;
1227
- sharedCount?: (count: number) => void;
1228
- disabledFields?: Set<string>;
1229
- setFieldsDisabled?: (
1230
- templateId: string,
1231
- fieldNames: string[],
1232
- disabled: boolean,
1233
- ) => void;
1234
- templateConfig?: string;
1235
- }>) {
1236
- React.useEffect(
1237
- function ensureHiddenDefaults() {
1238
- const hiddenInputs = page.inputs?.filter(
1239
- (i: ProcessTemplateInput | null) =>
1240
- i?.inputType === "hidden" ||
1241
- i?.inputType === "hidden-number" ||
1242
- i?.inputType === "hidden-number-or-null",
1243
- );
1244
-
1245
- if (hiddenInputs) {
1246
- for (const input of hiddenInputs) {
1247
- const key = templateFieldKey(templateId, input?.fieldName ?? "");
1248
- if (values[key] != null) continue;
1249
-
1250
- switch (input?.fieldName) {
1251
- case "processTemplateId":
1252
- handleFieldChange(
1253
- templateId,
1254
- input.fieldName ?? "",
1255
- Number(templateId),
1256
- );
1257
- break;
1258
- default:
1259
- handleFieldChange(
1260
- templateId,
1261
- input?.fieldName ?? "",
1262
- input?.inputType === "hidden-number"
1263
- ? (0 as FieldValue)
1264
- : input?.inputType === "hidden-number-or-null"
1265
- ? null
1266
- : ("" as FieldValue),
1267
- );
1268
- break;
1269
- }
1270
- }
1271
- }
1272
- },
1273
- [templateId, page.id, page.inputs, values, handleFieldChange],
1274
- );
1275
-
1276
- const arrangedInputs = React.useMemo(
1277
- () => arrangeInputs(page.inputs as ProcessTemplateInput[]),
1278
- [page.inputs],
1279
- );
1280
-
1281
- return (
1282
- <Box sx={{ display: "grid", rowGap: 3 }}>
1283
- {arrangedInputs.map((ai: InputRow, idx: number) => {
1284
- if (ai.type === "full") {
1285
- return (
1286
- <Box key={`full-${idx + 1}`} sx={{ width: "100%" }}>
1287
- {renderFormControl({
1288
- input: ai.item,
1289
- templateId,
1290
- values,
1291
- errors,
1292
- handleFieldChange,
1293
- clearTemplateFields,
1294
- readOnly,
1295
- sharedCount,
1296
- isEditMode,
1297
- isCreateMode,
1298
- disabledFields,
1299
- setFieldsDisabled,
1300
- templateConfig,
1301
- })}
1302
- </Box>
1303
- );
1304
- }
1305
-
1306
- const [i, j] = ai.items;
1307
- return (
1308
- <Box
1309
- key={`pair-${idx + 1}`}
1310
- sx={{
1311
- display: "grid",
1312
- gridTemplateColumns: { xs: "1fr", md: "1fr 1fr" },
1313
- columnGap: 3,
1314
- rowGap: 3,
1315
- width: "100%",
1316
- }}
1317
- >
1318
- <Box>
1319
- {renderFormControl({
1320
- input: i,
1321
- templateId,
1322
- values,
1323
- errors,
1324
- handleFieldChange,
1325
- clearTemplateFields,
1326
- readOnly,
1327
- sharedCount,
1328
- isEditMode,
1329
- isCreateMode,
1330
- disabledFields,
1331
- setFieldsDisabled,
1332
- templateConfig,
1333
- })}
1334
- </Box>
1335
- <Box>
1336
- {renderFormControl({
1337
- input: j,
1338
- templateId,
1339
- values,
1340
- errors,
1341
- handleFieldChange,
1342
- clearTemplateFields,
1343
- readOnly,
1344
- sharedCount,
1345
- isEditMode,
1346
- isCreateMode,
1347
- disabledFields,
1348
- setFieldsDisabled,
1349
- templateConfig,
1350
- })}
1351
- </Box>
1352
- </Box>
1353
- );
1354
- })}
1355
- </Box>
1356
- );
1357
- }
1358
-
1359
- const parseQueryName = (queryName: string) => {
1360
- let result: TemplateInputSearchQueryKey | null = null;
1361
- const indexOf = queryName.indexOf("(");
1362
- const qn = queryName.substring(0, indexOf < 0 ? undefined : indexOf);
1363
-
1364
- if (isTemplateInputSearchQueryKey(qn)) {
1365
- result = qn as TemplateInputSearchQueryKey;
1366
- return result;
1367
- }
1368
-
1369
- throw new Error(`queryName ${queryName} invalid`);
1370
- };
1371
-
1372
- const renderReadOnlyText = (
1373
- label?: string,
1374
- value?: string | string[] | number | null,
1375
- ) => {
1376
- return (
1377
- <>
1378
- {value && (
1379
- <Box>
1380
- {label && (
1381
- <Typography color="grey" variant="caption">
1382
- {label}
1383
- </Typography>
1384
- )}
1385
- <Typography color="inherit" variant="body1">
1386
- {Array.isArray(value) ? (value as string[]).join(", ") : value}
1387
- </Typography>
1388
- </Box>
1389
- )}
1390
- </>
1391
- );
1392
- };