@evenicanpm/admin-integrate 1.8.0 → 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
@@ -0,0 +1,129 @@
1
+ "use client";
2
+
3
+ import { Field, type FieldProps } from "formik";
4
+ import * as React from "react";
5
+ import {
6
+ type FieldValue,
7
+ type ProcessTemplateInput,
8
+ templateFieldKey,
9
+ } from "../../types";
10
+ import { ReservedFieldName } from "../field-names";
11
+ import { InputType } from "../input-type";
12
+ import { SearchInputWrapper } from "../search/SearchInputWrapper";
13
+ import { parseQueryName } from "../utils/parseQueryName";
14
+
15
+ export function renderSearchInput({
16
+ input,
17
+ k,
18
+ templateId,
19
+ err,
20
+ showError = true,
21
+ handleFieldChange,
22
+ onBlur,
23
+ }: {
24
+ input: ProcessTemplateInput;
25
+ k: string;
26
+ templateId: string;
27
+ err: string | null;
28
+ showError?: boolean;
29
+ handleFieldChange?: (
30
+ templateId: string,
31
+ fieldName: string,
32
+ value: FieldValue,
33
+ ) => void;
34
+ onBlur?: () => void;
35
+ }) {
36
+ const queryNameStr =
37
+ (input as { queryName?: string }).queryName ??
38
+ (input as { validationQueryOnCheck?: string }).validationQueryOnCheck ??
39
+ (input as { validationQueryOnSubmit?: string }).validationQueryOnSubmit ??
40
+ "";
41
+ if (!queryNameStr) {
42
+ return null;
43
+ }
44
+ const queryName = parseQueryName(queryNameStr);
45
+
46
+ if (input.inputType === InputType.MULTI_TEXT_SEARCH) {
47
+ return (
48
+ <Field name={k}>
49
+ {({ field, form, meta }: any) => (
50
+ <SearchInputWrapper
51
+ inputType={InputType.MULTI_TEXT_SEARCH}
52
+ queryName={queryName}
53
+ label={input.label ?? ""}
54
+ value={(field.value as string[]) ?? []}
55
+ setValue={(v: string[]) => form.setFieldValue(k, v)}
56
+ placeholder={input.placeholder}
57
+ helpText={input.helpText}
58
+ error={showError ? (meta.error ?? err) : undefined}
59
+ onBlur={onBlur}
60
+ />
61
+ )}
62
+ </Field>
63
+ );
64
+ }
65
+
66
+ if (input.inputType === InputType.TEXT_SEARCH) {
67
+ return (
68
+ <Field name={k}>
69
+ {({ field, form, meta }: any) => (
70
+ <SearchInputWrapper
71
+ inputType={InputType.TEXT_SEARCH}
72
+ queryName={queryName}
73
+ validationQuery={queryNameStr || undefined}
74
+ label={input.label ?? ""}
75
+ value={(field.value as string) ?? ""}
76
+ setValue={(v: string) => form.setFieldValue(k, v)}
77
+ placeholder={input.placeholder}
78
+ helpText={input.helpText}
79
+ error={showError ? (meta.error ?? err) : undefined}
80
+ handleFieldChange={handleFieldChange}
81
+ fieldName={input.fieldName ?? undefined}
82
+ onBlur={onBlur}
83
+ />
84
+ )}
85
+ </Field>
86
+ );
87
+ }
88
+
89
+ if (input.inputType === InputType.TEXT_UNIQUE_IDENTIFIER) {
90
+ return (
91
+ <>
92
+ <Field name={templateFieldKey(templateId, ReservedFieldName.IsUnique)}>
93
+ {({ field }: FieldProps) => (
94
+ <input type="hidden" {...field} value={field.value ?? false} />
95
+ )}
96
+ </Field>
97
+
98
+ <Field name={k}>
99
+ {({ field, form, meta }: FieldProps) => {
100
+ const setUniqueValue = (v: string, fieldName?: string) => {
101
+ const targetKey = fieldName
102
+ ? templateFieldKey(templateId, fieldName)
103
+ : k;
104
+ form.setFieldValue(targetKey, v);
105
+ };
106
+
107
+ return (
108
+ <SearchInputWrapper
109
+ inputType={InputType.TEXT_UNIQUE_IDENTIFIER}
110
+ queryName={queryName}
111
+ label={input.label ?? ""}
112
+ value={(field.value as string) ?? ""}
113
+ setValue={setUniqueValue}
114
+ placeholder={input.placeholder}
115
+ helpText={input.helpText}
116
+ error={showError ? (meta.error ?? err) : undefined}
117
+ handleFieldChange={handleFieldChange}
118
+ fieldName={input.fieldName ?? undefined}
119
+ onBlur={onBlur}
120
+ />
121
+ );
122
+ }}
123
+ </Field>
124
+ </>
125
+ );
126
+ }
127
+
128
+ return null;
129
+ }
@@ -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
+ }
@@ -0,0 +1,309 @@
1
+ "use client";
2
+
3
+ import type { AutocompleteChangeReason } from "@mui/material";
4
+ import { Autocomplete, Box, TextField } from "@mui/material";
5
+ import { debounce } from "lodash";
6
+ import * as React from "react";
7
+ import type { FieldValue } from "../../types";
8
+ import { templateFieldKey } from "../../types";
9
+ import { useFormMode } from "../context/FormModeContext";
10
+ import { useTemplateContext } from "../context/TemplateContext";
11
+ import { ReservedFieldName } from "../field-names";
12
+ import { applyTemplateConfig } from "../utils/applyTemplateConfig";
13
+
14
+ type SearchOption = { name: string; templateConfig: string; value?: string };
15
+
16
+ function getConfigFieldNames(
17
+ config: Record<string, unknown>,
18
+ fieldName: string | undefined,
19
+ ): string[] {
20
+ return Object.keys(config).filter(
21
+ (key) =>
22
+ key !== ReservedFieldName.ProcessTemplateId && key !== (fieldName ?? ""),
23
+ );
24
+ }
25
+
26
+ function setFieldsFromOption(
27
+ option: SearchOption,
28
+ templateId: string,
29
+ fieldName: string | undefined,
30
+ setFieldsDisabled: (
31
+ templateId: string,
32
+ fieldNames: string[],
33
+ disabled: boolean,
34
+ ) => void,
35
+ disabled: boolean,
36
+ ): void {
37
+ try {
38
+ const config = JSON.parse(option.templateConfig) as Record<string, unknown>;
39
+ const fieldNames = getConfigFieldNames(config, fieldName);
40
+ setFieldsDisabled(templateId, fieldNames, disabled);
41
+ } catch (err) {
42
+ console.error(
43
+ disabled
44
+ ? "Failed to parse template config"
45
+ : "Failed to parse template config while enabling fields",
46
+ err,
47
+ );
48
+ }
49
+ }
50
+
51
+ function findOptionByName(
52
+ options: SearchOption[],
53
+ name: string,
54
+ ): SearchOption | undefined {
55
+ return options.find((o) => o?.name === name) ?? undefined;
56
+ }
57
+
58
+ function findOptionByValue(
59
+ options: SearchOption[],
60
+ val: string,
61
+ ): SearchOption | undefined {
62
+ return (
63
+ options.find(
64
+ (o) => (o as SearchOption & { value?: string }).value === val,
65
+ ) ?? undefined
66
+ );
67
+ }
68
+
69
+ export function TextSearchInput({
70
+ fieldName,
71
+ label,
72
+ value,
73
+ setValue,
74
+ options,
75
+ optionsAsValueOptions = false,
76
+ placeholder,
77
+ helpText,
78
+ error,
79
+ setSearchText,
80
+ handleFieldChange,
81
+ readOnly,
82
+ onBlur,
83
+ loading,
84
+ }: Readonly<{
85
+ fieldName?: string;
86
+ label: string;
87
+ value: string;
88
+ setValue: (v: string) => void;
89
+ options: SearchOption[];
90
+ /** When true, options have .value; form stores option.value and displays option.name */
91
+ optionsAsValueOptions?: boolean;
92
+ placeholder?: string | null;
93
+ helpText?: string | null;
94
+ error?: string | null;
95
+ setSearchText: React.Dispatch<React.SetStateAction<string>>;
96
+ handleFieldChange?: (
97
+ templateId: string,
98
+ fieldName: string,
99
+ value: FieldValue,
100
+ ) => void;
101
+ readOnly?: boolean;
102
+ onBlur?: () => void;
103
+ /** When true (e.g. API in progress), show "Checking..." and disable input; when it becomes false, open option tray */
104
+ loading?: boolean;
105
+ }>) {
106
+ const getInitialSelected = () => {
107
+ if (optionsAsValueOptions && value) {
108
+ const byVal = findOptionByValue(options, value);
109
+ return byVal ? byVal.name : value;
110
+ }
111
+ return value ?? null;
112
+ };
113
+ const [selected, setSelected] = React.useState<string | null>(
114
+ getInitialSelected,
115
+ );
116
+
117
+ React.useEffect(() => {
118
+ if (optionsAsValueOptions && value && options.length > 0) {
119
+ const byVal = findOptionByValue(options, value);
120
+ setSelected(byVal ? byVal.name : value);
121
+ } else if (!value) {
122
+ setSelected(null);
123
+ }
124
+ }, [optionsAsValueOptions, value, options]);
125
+
126
+ const { templateId, disabledFields, setFieldsDisabled, templateConfig } =
127
+ useTemplateContext();
128
+
129
+ const { isEditMode } = useFormMode();
130
+
131
+ const appliedOptionRef = React.useRef<string | null>(null);
132
+
133
+ const selfKey =
134
+ templateId && fieldName ? templateFieldKey(templateId, fieldName) : null;
135
+ const isSelfDisabled = selfKey
136
+ ? Boolean(disabledFields?.has(selfKey))
137
+ : false;
138
+ const disabled = Boolean(readOnly || isSelfDisabled || loading);
139
+
140
+ const [open, setOpen] = React.useState(false);
141
+ const prevLoadingRef = React.useRef(false);
142
+ React.useEffect(() => {
143
+ if (prevLoadingRef.current && !loading) {
144
+ setOpen(true);
145
+ }
146
+ prevLoadingRef.current = Boolean(loading);
147
+ }, [loading]);
148
+
149
+ const debouncedSetSearchText = React.useMemo(
150
+ () => debounce((s: string) => setSearchText(s), 500),
151
+ [],
152
+ );
153
+ React.useEffect(() => {
154
+ return () => {
155
+ debouncedSetSearchText.cancel();
156
+ };
157
+ }, [debouncedSetSearchText]);
158
+
159
+ React.useEffect(() => {
160
+ if (optionsAsValueOptions) return;
161
+ if (!selected || !templateId) return;
162
+ const option = findOptionByName(options, selected);
163
+ if (!option) return;
164
+ try {
165
+ const config = JSON.parse(option.templateConfig) as Record<
166
+ string,
167
+ unknown
168
+ >;
169
+ if (config && templateConfig) {
170
+ option.templateConfig = templateConfig;
171
+ }
172
+ applyTemplateConfig(option, handleFieldChange ?? (() => {}));
173
+ appliedOptionRef.current = option.name ?? null;
174
+ if (setFieldsDisabled && !isEditMode) {
175
+ const parsed = JSON.parse(option.templateConfig) as Record<
176
+ string,
177
+ unknown
178
+ >;
179
+ const fieldNamesToDisable = getConfigFieldNames(parsed, fieldName);
180
+ setFieldsDisabled(templateId, fieldNamesToDisable, true);
181
+ }
182
+ } catch (err) {
183
+ console.error("Failed to parse template config", err);
184
+ }
185
+ }, [
186
+ optionsAsValueOptions,
187
+ selected,
188
+ templateId,
189
+ options,
190
+ templateConfig,
191
+ fieldName,
192
+ setFieldsDisabled,
193
+ handleFieldChange,
194
+ isEditMode,
195
+ ]);
196
+
197
+ const handleAutocompleteOnChange = (
198
+ event: React.SyntheticEvent,
199
+ newValue: string | null,
200
+ reason: AutocompleteChangeReason,
201
+ ) => {
202
+ if (reason !== "clear" || event.type !== "click") {
203
+ setSelected(newValue);
204
+ if (optionsAsValueOptions && newValue) {
205
+ const option = findOptionByName(options, newValue);
206
+ setValue(
207
+ option && "value" in option && option.value != null
208
+ ? option.value
209
+ : newValue,
210
+ );
211
+ } else {
212
+ setValue(newValue ?? "");
213
+ }
214
+ return;
215
+ }
216
+ setSelected(null);
217
+ setValue("");
218
+ const appliedName = appliedOptionRef.current;
219
+ if (
220
+ !optionsAsValueOptions &&
221
+ templateId &&
222
+ setFieldsDisabled &&
223
+ appliedName
224
+ ) {
225
+ const option = findOptionByName(options, appliedName);
226
+ if (option) {
227
+ setFieldsFromOption(
228
+ option,
229
+ templateId,
230
+ fieldName,
231
+ setFieldsDisabled,
232
+ false,
233
+ );
234
+ }
235
+ appliedOptionRef.current = null;
236
+ }
237
+ };
238
+
239
+ const handleInputChange = (
240
+ event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
241
+ ) => {
242
+ const newVal = event.target.value;
243
+ setValue(newVal);
244
+ debouncedSetSearchText(newVal);
245
+
246
+ const appliedName = appliedOptionRef.current;
247
+ if (
248
+ !optionsAsValueOptions &&
249
+ templateId &&
250
+ setFieldsDisabled &&
251
+ appliedName
252
+ ) {
253
+ const option = findOptionByName(options, appliedName);
254
+ if (option) {
255
+ setFieldsFromOption(
256
+ option,
257
+ templateId,
258
+ fieldName,
259
+ setFieldsDisabled,
260
+ false,
261
+ );
262
+ }
263
+ appliedOptionRef.current = null;
264
+ }
265
+ };
266
+
267
+ const shouldShowInfoMessage = !isEditMode && selected;
268
+ const helperTextFinal = (() => {
269
+ if (loading) return "Checking...";
270
+ if (shouldShowInfoMessage)
271
+ return "Existing data descriptions are not editable during creation.";
272
+ return helpText;
273
+ })();
274
+
275
+ return (
276
+ <Box sx={{ width: "50%" }}>
277
+ <Autocomplete
278
+ freeSolo={!selected}
279
+ options={options.map((o) => o.name)}
280
+ value={selected ?? ""}
281
+ onChange={handleAutocompleteOnChange}
282
+ disabled={disabled}
283
+ open={open}
284
+ onOpen={() => setOpen(true)}
285
+ onClose={() => setTimeout(() => setOpen(false), 0)}
286
+ noOptionsText="No option"
287
+ renderInput={(params) => (
288
+ <TextField
289
+ {...params}
290
+ label={label}
291
+ value={value ?? ""}
292
+ onChange={handleInputChange}
293
+ onBlur={onBlur}
294
+ placeholder={placeholder ?? undefined}
295
+ fullWidth
296
+ error={Boolean(error)}
297
+ helperText={error ?? helperTextFinal ?? " "}
298
+ disabled={disabled}
299
+ slotProps={{
300
+ formHelperText: {
301
+ sx: { marginLeft: 0 },
302
+ },
303
+ }}
304
+ />
305
+ )}
306
+ />
307
+ </Box>
308
+ );
309
+ }
@@ -0,0 +1,4 @@
1
+ export { MultiTextSearchInput } from "./MultiTextSearchInput";
2
+ export { SearchInputWrapper } from "./SearchInputWrapper";
3
+ export { TextSearchInput } from "./TextSearchInput";
4
+ export { useSearchInput } from "./useSearchInput";
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Search query keys for template input search (matches API TemplateInputSearchQueryKey).
3
+ * Use this enum instead of string literals in switches and comparisons.
4
+ */
5
+ export enum TemplateSearchQueryKey {
6
+ GetSourceEndpoints = "getSourceEndpoints",
7
+ GetDataDescriptions = "getDataDescriptions",
8
+ CheckUniqueIdentifier = "checkUniqueIdentifier",
9
+ }
10
+
11
+ /**
12
+ * Map template/API query name variants (e.g. from process template input.queryName)
13
+ * to the canonical key used by the client. Add new variants here when the API
14
+ * returns prefixed or alternate names.
15
+ */
16
+ export const TEMPLATE_QUERY_NAME_TO_KEY: Record<
17
+ string,
18
+ TemplateSearchQueryKey
19
+ > = {
20
+ Templates_GetSourceEndpoints: TemplateSearchQueryKey.GetSourceEndpoints,
21
+ Templates_GetDataDescriptions: TemplateSearchQueryKey.GetDataDescriptions,
22
+ Templates_CheckUniqueIdentifier: TemplateSearchQueryKey.CheckUniqueIdentifier,
23
+ };