@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
@@ -0,0 +1,384 @@
1
+ "use client";
2
+
3
+ import { Box, FormHelperText, Typography } from "@mui/material";
4
+ import { Field, type FieldProps } from "formik";
5
+ import type * as React from "react";
6
+ import type {
7
+ ErrorState,
8
+ FieldValue,
9
+ FormState,
10
+ ProcessTemplateInput,
11
+ ProcessTemplateInputSchema,
12
+ } from "../../types";
13
+ import { templateFieldKey } from "../../types";
14
+ import { InputType } from "../input-type";
15
+ import { CheckboxInput } from "../primitives/CheckboxInput";
16
+ import { NumberInput } from "../primitives/NumberInput";
17
+ import { SelectorInput } from "../primitives/SelectorInput";
18
+ import { TextInput } from "../primitives/TextInput";
19
+ import type { TextWithQueryInputType } from "../primitives/TextWithQueryInput";
20
+ import { TextWithQueryInput } from "../primitives/TextWithQueryInput";
21
+ import { InputTable } from "../table/InputTable";
22
+ import {
23
+ getSelectorOptionsFromInput,
24
+ getTableRowErrors,
25
+ } from "../utils/selectors";
26
+ import { renderSearchInput } from "./renderSearchInput";
27
+
28
+ const QUERY_NAME_INPUT_TYPES = [
29
+ InputType.TEXT,
30
+ InputType.TEXT_UNIQUE_IDENTIFIER,
31
+ InputType.NUMBER,
32
+ InputType.SELECTOR,
33
+ InputType.CHECKBOX,
34
+ ] as const;
35
+
36
+ function isHiddenInput(input: ProcessTemplateInput): boolean {
37
+ return (
38
+ input.inputType === InputType.HIDDEN ||
39
+ input.inputType === InputType.HIDDEN_NUMBER ||
40
+ input.inputType === InputType.HIDDEN_NUMBER_OR_NULL
41
+ );
42
+ }
43
+
44
+ function isFieldTouched(
45
+ touched: Record<string, boolean> | undefined,
46
+ fieldKey: string,
47
+ ): boolean {
48
+ return (
49
+ typeof touched === "object" &&
50
+ touched !== null &&
51
+ touched[fieldKey] === true
52
+ );
53
+ }
54
+
55
+ function getValidationQueryOnCheckIfSupported(
56
+ input: ProcessTemplateInput,
57
+ ): string | null {
58
+ const validationQueryOnCheck = (input as { validationQueryOnCheck?: string })
59
+ .validationQueryOnCheck;
60
+ if (!validationQueryOnCheck) return null;
61
+ const hasSupportedType = QUERY_NAME_INPUT_TYPES.includes(
62
+ input.inputType as (typeof QUERY_NAME_INPUT_TYPES)[number],
63
+ );
64
+ return hasSupportedType ? validationQueryOnCheck : null;
65
+ }
66
+
67
+ function queryInputType(
68
+ inputType: string | null | undefined,
69
+ ): TextWithQueryInputType {
70
+ if (inputType === InputType.NUMBER) return InputType.NUMBER;
71
+ if (inputType === InputType.SELECTOR) return InputType.SELECTOR;
72
+ if (inputType === InputType.CHECKBOX) return InputType.CHECKBOX;
73
+ return InputType.TEXT;
74
+ }
75
+
76
+ function normalizeInputTableRows(
77
+ value: unknown,
78
+ input: ProcessTemplateInput,
79
+ ): Array<Record<string, string | number | boolean | null | undefined>> {
80
+ const rows: Array<
81
+ Record<string, string | number | boolean | null | undefined>
82
+ > = Array.isArray(value)
83
+ ? (value as Array<
84
+ Record<string, string | number | boolean | null | undefined>
85
+ >)
86
+ : [];
87
+ if (!value || rows.length > 0 || (input.schemas?.length ?? 0) !== 2) {
88
+ return rows;
89
+ }
90
+ const valueRecord = value as Record<string, string>;
91
+ for (const schemaKey of Object.keys(valueRecord)) {
92
+ const schema0 = input.schemas?.[0];
93
+ const schema1 = input.schemas?.[1];
94
+ if (schema0?.fieldKey && schema1?.fieldKey) {
95
+ rows.push({
96
+ [schema0.fieldKey]: schemaKey,
97
+ [schema1.fieldKey]: valueRecord[schemaKey],
98
+ });
99
+ }
100
+ }
101
+ return rows;
102
+ }
103
+
104
+ function renderInputTable({
105
+ input,
106
+ k,
107
+ values,
108
+ errors,
109
+ showError,
110
+ err,
111
+ templateId,
112
+ handleFieldChange,
113
+ finalReadOnly,
114
+ readOnly,
115
+ setFieldTouched,
116
+ validateForm,
117
+ }: {
118
+ input: ProcessTemplateInput;
119
+ k: string;
120
+ values: FormState;
121
+ errors: ErrorState;
122
+ showError: boolean;
123
+ err: string | null;
124
+ templateId: string;
125
+ handleFieldChange: (
126
+ templateId: string,
127
+ fieldName: string,
128
+ value: FieldValue,
129
+ ) => void;
130
+ finalReadOnly: boolean;
131
+ readOnly?: boolean;
132
+ setFieldTouched?: (field: string, value: boolean) => void;
133
+ validateForm?: () => Promise<Record<string, unknown>>;
134
+ }): React.ReactNode {
135
+ const value = values[k];
136
+ const rows = normalizeInputTableRows(value, input);
137
+ const tableRowErrors = getTableRowErrors(errors, k);
138
+ const hasRowErrors = tableRowErrors.length > 0;
139
+ const tableErrorToShow = showError ? err : undefined;
140
+ const rowErrorsToShow =
141
+ showError && hasRowErrors ? tableRowErrors : undefined;
142
+ const hasError = Boolean(tableErrorToShow ?? rowErrorsToShow?.length);
143
+ const showHelpText = Boolean(
144
+ !readOnly && input.helpText && !tableErrorToShow,
145
+ );
146
+
147
+ return (
148
+ <Box>
149
+ <Typography
150
+ variant="subtitle2"
151
+ sx={{ mb: 1, color: hasError ? "error.main" : "main.900" }}
152
+ >
153
+ {input.label}
154
+ </Typography>
155
+ <InputTable
156
+ rows={rows}
157
+ setRows={(newRows) =>
158
+ handleFieldChange(
159
+ templateId,
160
+ input.fieldName ?? "",
161
+ newRows as Array<Record<string, string>>,
162
+ )
163
+ }
164
+ schemas={(input.schemas as ProcessTemplateInputSchema[]) ?? []}
165
+ tableError={tableErrorToShow ?? null}
166
+ rowErrors={rowErrorsToShow}
167
+ readOnly={finalReadOnly}
168
+ onCellBlur={() => {
169
+ setFieldTouched?.(k, true);
170
+ validateForm?.();
171
+ }}
172
+ />
173
+ {showHelpText ? (
174
+ <FormHelperText sx={{ my: 1, ml: 2 }}>
175
+ {input.helpText ?? ""}
176
+ </FormHelperText>
177
+ ) : null}
178
+ {tableErrorToShow ? (
179
+ <FormHelperText
180
+ error
181
+ sx={{ my: 1, ml: 2, whiteSpace: "pre-wrap", fontFamily: "inherit" }}
182
+ >
183
+ {String(tableErrorToShow)}
184
+ </FormHelperText>
185
+ ) : null}
186
+ </Box>
187
+ );
188
+ }
189
+
190
+ export function renderFormControl({
191
+ input,
192
+ templateId,
193
+ values,
194
+ errors,
195
+ touched,
196
+ handleFieldChange,
197
+ setFieldTouched,
198
+ validateForm,
199
+ readOnly,
200
+ disabledFields,
201
+ setFieldValidationInProgress,
202
+ submitValidationResults,
203
+ }: {
204
+ input: ProcessTemplateInput;
205
+ templateId: string;
206
+ values: FormState;
207
+ errors: ErrorState;
208
+ touched?: Record<string, boolean>;
209
+ handleFieldChange: (
210
+ templateId: string,
211
+ fieldName: string,
212
+ value: FieldValue,
213
+ ) => void;
214
+ setFieldTouched?: (field: string, value: boolean) => void;
215
+ validateField?: (field: string) => undefined | Promise<unknown>;
216
+ validateForm?: () => Promise<Record<string, unknown>>;
217
+ readOnly?: boolean;
218
+ disabledFields?: Set<string>;
219
+ setFieldValidationInProgress?: (inProgress: boolean) => void;
220
+ submitValidationResults?: Record<
221
+ string,
222
+ { isValid: boolean; message: string }
223
+ >;
224
+ }) {
225
+ const k = templateFieldKey(templateId, input.fieldName ?? "");
226
+ const err = errors[k] ?? null;
227
+ const showError = isFieldTouched(touched, k);
228
+ const isFieldDisabled = disabledFields?.has(k) ?? false;
229
+ const finalReadOnly = readOnly || isFieldDisabled;
230
+
231
+ if (isHiddenInput(input)) {
232
+ return null;
233
+ }
234
+
235
+ const validationQueryOnCheck = getValidationQueryOnCheckIfSupported(input);
236
+ if (validationQueryOnCheck) {
237
+ const queryInputTypeVal = queryInputType(input.inputType);
238
+ const submitValidationResult = submitValidationResults?.[k] ?? null;
239
+ return (
240
+ <Field name={k}>
241
+ {({ field, form, meta }: FieldProps) => (
242
+ <TextWithQueryInput
243
+ inputType={queryInputTypeVal}
244
+ label={input.label ?? ""}
245
+ value={
246
+ field.value ??
247
+ (queryInputTypeVal === InputType.CHECKBOX ? false : "")
248
+ }
249
+ setValue={(v) => form.setFieldValue(k, v)}
250
+ placeholder={input.placeholder ?? ""}
251
+ helpText={input.helpText ?? ""}
252
+ error={showError ? (meta.error ?? err) : undefined}
253
+ readOnly={finalReadOnly}
254
+ onBlur={() => form.setFieldTouched(k, true)}
255
+ fieldName={input.fieldName ?? ""}
256
+ validationQuery={validationQueryOnCheck}
257
+ fieldId={input.id ?? ""}
258
+ validateFormikBeforeApi={() =>
259
+ form
260
+ .validateForm()
261
+ .then((errors: Record<string, unknown>) => !errors[k])
262
+ }
263
+ setFieldValidationInProgress={setFieldValidationInProgress}
264
+ options={getSelectorOptionsFromInput(input)}
265
+ submitValidationResult={submitValidationResult}
266
+ />
267
+ )}
268
+ </Field>
269
+ );
270
+ }
271
+
272
+ if (input.inputType === InputType.CHECKBOX) {
273
+ return (
274
+ <Field name={k}>
275
+ {({ field, form }: FieldProps) => (
276
+ <CheckboxInput
277
+ label={input.label ?? ""}
278
+ checked={Boolean(field.value ?? false)}
279
+ setChecked={(v) => form.setFieldValue(k, v)}
280
+ helpText={input.helpText ?? ""}
281
+ readOnly={finalReadOnly}
282
+ />
283
+ )}
284
+ </Field>
285
+ );
286
+ }
287
+
288
+ if (input.inputType === InputType.NUMBER) {
289
+ return (
290
+ <Field name={k}>
291
+ {({ field, form, meta }: FieldProps) => (
292
+ <NumberInput
293
+ label={input.label ?? ""}
294
+ value={(field.value as number) ?? ""}
295
+ setValue={(v) => form.setFieldValue(k, v)}
296
+ placeholder={input.placeholder ?? ""}
297
+ helpText={input.helpText ?? ""}
298
+ error={showError ? (meta.error ?? err) : undefined}
299
+ readOnly={finalReadOnly}
300
+ onBlur={() => form.setFieldTouched(k, true)}
301
+ />
302
+ )}
303
+ </Field>
304
+ );
305
+ }
306
+
307
+ if (input.inputType === InputType.SELECTOR) {
308
+ return (
309
+ <Field name={k}>
310
+ {({ field, form, meta }: FieldProps) => (
311
+ <SelectorInput
312
+ fieldName={input.fieldName ?? ""}
313
+ label={input.label ?? ""}
314
+ value={(field.value as string) ?? ""}
315
+ setValue={(v) => form.setFieldValue(k, v)}
316
+ helpText={input.helpText ?? ""}
317
+ error={showError ? (meta.error ?? err) : undefined}
318
+ options={getSelectorOptionsFromInput(input)}
319
+ readOnly={finalReadOnly}
320
+ onBlur={() => form.setFieldTouched(k, true)}
321
+ />
322
+ )}
323
+ </Field>
324
+ );
325
+ }
326
+
327
+ if (input.inputType === InputType.MULTI_TEXT_SEARCH) {
328
+ return renderSearchInput({
329
+ input,
330
+ k,
331
+ templateId,
332
+ err,
333
+ showError,
334
+ handleFieldChange,
335
+ onBlur: () => setFieldTouched?.(k, true),
336
+ });
337
+ }
338
+
339
+ if (input.inputType === InputType.TEXT_SEARCH) {
340
+ return renderSearchInput({
341
+ input,
342
+ k,
343
+ templateId,
344
+ err,
345
+ showError,
346
+ handleFieldChange,
347
+ onBlur: () => setFieldTouched?.(k, true),
348
+ });
349
+ }
350
+
351
+ if (input.inputType === InputType.INPUT_TABLE) {
352
+ return renderInputTable({
353
+ input,
354
+ k,
355
+ values,
356
+ errors,
357
+ showError,
358
+ err,
359
+ templateId,
360
+ handleFieldChange,
361
+ finalReadOnly,
362
+ readOnly,
363
+ setFieldTouched,
364
+ validateForm,
365
+ });
366
+ }
367
+
368
+ return (
369
+ <Field name={k}>
370
+ {({ field, form, meta }: FieldProps) => (
371
+ <TextInput
372
+ label={input.label ?? ""}
373
+ value={(field.value as string) ?? ""}
374
+ setValue={(v) => form.setFieldValue(k, v)}
375
+ placeholder={input.placeholder ?? ""}
376
+ helpText={input.helpText ?? ""}
377
+ error={showError ? (meta.error ?? err) : undefined}
378
+ readOnly={finalReadOnly}
379
+ onBlur={() => form.setFieldTouched(k, true)}
380
+ />
381
+ )}
382
+ </Field>
383
+ );
384
+ }
@@ -0,0 +1,128 @@
1
+ "use client";
2
+
3
+ import { Field, type FieldProps } from "formik";
4
+ import {
5
+ type FieldValue,
6
+ type ProcessTemplateInput,
7
+ templateFieldKey,
8
+ } from "../../types";
9
+ import { ReservedFieldName } from "../field-names";
10
+ import { InputType } from "../input-type";
11
+ import { SearchInputWrapper } from "../search/SearchInputWrapper";
12
+ import { parseQueryName } from "../utils/parseQueryName";
13
+
14
+ export function renderSearchInput({
15
+ input,
16
+ k,
17
+ templateId,
18
+ err,
19
+ showError = true,
20
+ handleFieldChange,
21
+ onBlur,
22
+ }: {
23
+ input: ProcessTemplateInput;
24
+ k: string;
25
+ templateId: string;
26
+ err: string | null;
27
+ showError?: boolean;
28
+ handleFieldChange?: (
29
+ templateId: string,
30
+ fieldName: string,
31
+ value: FieldValue,
32
+ ) => void;
33
+ onBlur?: () => void;
34
+ }) {
35
+ const queryNameStr =
36
+ (input as { queryName?: string }).queryName ??
37
+ (input as { validationQueryOnCheck?: string }).validationQueryOnCheck ??
38
+ (input as { validationQueryOnSubmit?: string }).validationQueryOnSubmit ??
39
+ "";
40
+ if (!queryNameStr) {
41
+ return null;
42
+ }
43
+ const queryName = parseQueryName(queryNameStr);
44
+
45
+ if (input.inputType === InputType.MULTI_TEXT_SEARCH) {
46
+ return (
47
+ <Field name={k}>
48
+ {({ field, form, meta }: FieldProps) => (
49
+ <SearchInputWrapper
50
+ inputType={InputType.MULTI_TEXT_SEARCH}
51
+ queryName={queryName}
52
+ label={input.label ?? ""}
53
+ value={(field.value as string[]) ?? []}
54
+ setValue={(v: string[]) => form.setFieldValue(k, v)}
55
+ placeholder={input.placeholder}
56
+ helpText={input.helpText}
57
+ error={showError ? (meta.error ?? err) : undefined}
58
+ onBlur={onBlur}
59
+ />
60
+ )}
61
+ </Field>
62
+ );
63
+ }
64
+
65
+ if (input.inputType === InputType.TEXT_SEARCH) {
66
+ return (
67
+ <Field name={k}>
68
+ {({ field, form, meta }: FieldProps) => (
69
+ <SearchInputWrapper
70
+ inputType={InputType.TEXT_SEARCH}
71
+ queryName={queryName}
72
+ validationQuery={queryNameStr || undefined}
73
+ label={input.label ?? ""}
74
+ value={(field.value as string) ?? ""}
75
+ setValue={(v: string) => form.setFieldValue(k, v)}
76
+ placeholder={input.placeholder}
77
+ helpText={input.helpText}
78
+ error={showError ? (meta.error ?? err) : undefined}
79
+ handleFieldChange={handleFieldChange}
80
+ fieldName={input.fieldName ?? undefined}
81
+ onBlur={onBlur}
82
+ />
83
+ )}
84
+ </Field>
85
+ );
86
+ }
87
+
88
+ if (input.inputType === InputType.TEXT_UNIQUE_IDENTIFIER) {
89
+ return (
90
+ <>
91
+ <Field name={templateFieldKey(templateId, ReservedFieldName.IsUnique)}>
92
+ {({ field }: FieldProps) => (
93
+ <input type="hidden" {...field} value={field.value ?? false} />
94
+ )}
95
+ </Field>
96
+
97
+ <Field name={k}>
98
+ {({ field, form, meta }: FieldProps) => {
99
+ const setUniqueValue = (v: FieldValue, fieldName?: string) => {
100
+ const targetKey = fieldName
101
+ ? templateFieldKey(templateId, fieldName)
102
+ : k;
103
+ form.setFieldValue(targetKey, v);
104
+ };
105
+
106
+ return (
107
+ <SearchInputWrapper
108
+ inputType={InputType.TEXT_UNIQUE_IDENTIFIER}
109
+ queryName={queryName}
110
+ label={input.label ?? ""}
111
+ value={(field.value as string) ?? ""}
112
+ setValue={setUniqueValue}
113
+ placeholder={input.placeholder}
114
+ helpText={input.helpText}
115
+ error={showError ? (meta.error ?? err) : undefined}
116
+ handleFieldChange={handleFieldChange}
117
+ fieldName={input.fieldName ?? undefined}
118
+ onBlur={onBlur}
119
+ />
120
+ );
121
+ }}
122
+ </Field>
123
+ </>
124
+ );
125
+ }
126
+
127
+ return null;
128
+ }
@@ -0,0 +1,87 @@
1
+ "use client";
2
+
3
+ import { Autocomplete, TextField } from "@mui/material";
4
+ import { debounce } from "lodash";
5
+ import * as React from "react";
6
+
7
+ export function MultiTextSearchInput({
8
+ label,
9
+ value,
10
+ setValue,
11
+ options,
12
+ placeholder,
13
+ helpText,
14
+ error,
15
+ setSearchText,
16
+ readOnly,
17
+ onBlur,
18
+ }: Readonly<{
19
+ label: string;
20
+ value: string[];
21
+ setValue: (v: string[]) => void;
22
+ options: { name: string; templateConfig: string }[];
23
+ placeholder?: string | null;
24
+ helpText?: string | null;
25
+ error?: string | null;
26
+ setSearchText: React.Dispatch<React.SetStateAction<string>>;
27
+ readOnly?: boolean;
28
+ onBlur?: () => void;
29
+ }>) {
30
+ const [input, setInput] = React.useState<string>("");
31
+
32
+ const debouncedSetSearchText = React.useMemo(
33
+ () => debounce((s: string) => setSearchText(s), 500),
34
+ [],
35
+ );
36
+
37
+ React.useEffect(() => {
38
+ return () => {
39
+ debouncedSetSearchText.cancel();
40
+ };
41
+ }, [debouncedSetSearchText]);
42
+
43
+ const handleAutocompleteOnChange = (
44
+ _e: React.SyntheticEvent,
45
+ val: string[],
46
+ ) => {
47
+ setValue(val);
48
+ debouncedSetSearchText("");
49
+ };
50
+
51
+ const handleInputChange = (
52
+ event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
53
+ ) => {
54
+ setInput(event.target.value);
55
+ debouncedSetSearchText(event.target.value);
56
+ };
57
+
58
+ return (
59
+ <Autocomplete
60
+ multiple
61
+ freeSolo
62
+ options={options.map((o) => o.name)}
63
+ value={value ?? []}
64
+ onChange={handleAutocompleteOnChange}
65
+ disabled={readOnly}
66
+ renderInput={(params) => (
67
+ <TextField
68
+ {...params}
69
+ value={input}
70
+ onChange={handleInputChange}
71
+ onBlur={onBlur}
72
+ label={label}
73
+ placeholder={placeholder ?? undefined}
74
+ error={Boolean(error)}
75
+ helperText={error ?? helpText ?? " "}
76
+ fullWidth
77
+ disabled={readOnly}
78
+ slotProps={{
79
+ formHelperText: {
80
+ sx: { marginLeft: 0 },
81
+ },
82
+ }}
83
+ />
84
+ )}
85
+ />
86
+ );
87
+ }
@@ -0,0 +1,113 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { useFormMode } from "../context/FormModeContext";
5
+ import type { SearchInputWrapperProps } from "../form-type";
6
+ import { InputType } from "../input-type";
7
+ import { renderReadOnlyText } from "../utils/renderReadOnlyText";
8
+ import { MultiTextSearchInput } from "./MultiTextSearchInput";
9
+ import { TextSearchInput } from "./TextSearchInput";
10
+ import { useFieldValuesOptions } from "./useFieldValuesOptions";
11
+ import { useSearchInput } from "./useSearchInput";
12
+
13
+ export function SearchInputWrapper(props: Readonly<SearchInputWrapperProps>) {
14
+ const {
15
+ inputType,
16
+ label,
17
+ value,
18
+ setValue,
19
+ placeholder,
20
+ helpText,
21
+ error,
22
+ handleFieldChange,
23
+ fieldName,
24
+ onBlur,
25
+ validationQuery,
26
+ } = props;
27
+
28
+ const [fieldValuesSearchText, setFieldValuesSearchText] = React.useState("");
29
+ const { options: fieldValuesOptions, loading: fieldValuesLoading } =
30
+ useFieldValuesOptions(fieldName, validationQuery, fieldValuesSearchText);
31
+
32
+ const searchInputResult = useSearchInput(props);
33
+ const { setSearchText, options: searchInputOptions } = searchInputResult;
34
+ const { readOnly, isCreateMode } = useFormMode();
35
+
36
+ const useGetFieldValues =
37
+ inputType === InputType.TEXT_SEARCH && Boolean(validationQuery);
38
+ const textSearchOptions = useGetFieldValues
39
+ ? fieldValuesOptions
40
+ : searchInputOptions;
41
+ const textSearchSetSearchText = useGetFieldValues
42
+ ? setFieldValuesSearchText
43
+ : setSearchText;
44
+ const textSearchOptionsAsValue = useGetFieldValues;
45
+
46
+ const isUniqueReadOnly =
47
+ inputType === InputType.TEXT_UNIQUE_IDENTIFIER && isCreateMode === false;
48
+
49
+ if (readOnly) {
50
+ return renderReadOnlyText(label, value);
51
+ }
52
+
53
+ return (
54
+ <>
55
+ {inputType === InputType.MULTI_TEXT_SEARCH && (
56
+ <MultiTextSearchInput
57
+ label={label}
58
+ value={value as string[]}
59
+ setValue={setValue as (v: string[]) => void}
60
+ options={searchInputOptions}
61
+ placeholder={placeholder}
62
+ helpText={helpText}
63
+ error={error}
64
+ setSearchText={setSearchText}
65
+ onBlur={onBlur}
66
+ />
67
+ )}
68
+
69
+ {inputType === InputType.TEXT_SEARCH && (
70
+ <TextSearchInput
71
+ fieldName={fieldName}
72
+ label={label}
73
+ value={value as string}
74
+ setValue={setValue as (v: string) => void}
75
+ options={textSearchOptions.map((o) =>
76
+ "value" in o && o.value != null
77
+ ? { name: o.name, templateConfig: "", value: o.value }
78
+ : {
79
+ name: (o as { name: string }).name,
80
+ templateConfig:
81
+ (o as { templateConfig?: string }).templateConfig ?? "",
82
+ },
83
+ )}
84
+ optionsAsValueOptions={textSearchOptionsAsValue}
85
+ placeholder={placeholder}
86
+ helpText={helpText}
87
+ error={error}
88
+ setSearchText={textSearchSetSearchText}
89
+ handleFieldChange={handleFieldChange}
90
+ onBlur={onBlur}
91
+ loading={useGetFieldValues ? fieldValuesLoading : undefined}
92
+ />
93
+ )}
94
+
95
+ {inputType === InputType.TEXT_UNIQUE_IDENTIFIER && (
96
+ <TextSearchInput
97
+ fieldName={fieldName}
98
+ label={label}
99
+ value={value as string}
100
+ setValue={setValue as (v: string) => void}
101
+ options={[]}
102
+ placeholder={placeholder}
103
+ helpText={helpText}
104
+ error={error}
105
+ setSearchText={setSearchText}
106
+ handleFieldChange={handleFieldChange}
107
+ readOnly={isUniqueReadOnly}
108
+ onBlur={onBlur}
109
+ />
110
+ )}
111
+ </>
112
+ );
113
+ }