@evenicanpm/admin-integrate 2.0.1 → 2.3.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.
@@ -39,6 +39,8 @@ const _GET_PROCESS_BY_ID = gql`
39
39
  extendedFields
40
40
  properties
41
41
  maxEndpointMessageAttempts
42
+ directionTypeId
43
+ processTemplateId
42
44
  created
43
45
  modified
44
46
  }
@@ -63,8 +63,6 @@ export const ENDPOINT_EXPORT_FRAGMENT = gql`
63
63
  Enabled
64
64
  }
65
65
  }
66
- TemplateConfig
67
- ProcessTemplateId
68
66
  }
69
67
  `;
70
68
 
@@ -109,6 +107,8 @@ export const PROCESS_EXPORT_FRAGMENT = gql`
109
107
  Sort
110
108
  Params
111
109
  ParallelProcessingGroup
110
+ DirectionTypeId
111
+ ProcessTemplateId
112
112
  }
113
113
  ProcessIcon
114
114
  IsTriggered
@@ -101,6 +101,8 @@ const _GET_CONFIGURATION_EXPORT = gql`
101
101
  Sort
102
102
  Params
103
103
  ParallelProcessingGroup
104
+ ProcessTemplateId
105
+ DirectionTypeId
104
106
  }
105
107
  }
106
108
  }
@@ -7,6 +7,7 @@ export const PROCESS_TEMPLATE_FRAGMENT = gql`
7
7
  isActive
8
8
  created
9
9
  modified
10
+ directionTypeId
10
11
  pages {
11
12
  id
12
13
  title
@@ -47,6 +48,13 @@ export const PROCESS_TEMPLATE_FRAGMENT = gql`
47
48
  }
48
49
  }
49
50
  }
51
+ mappings {
52
+ fieldName
53
+ jsonPath
54
+ jsonValuePath
55
+ format
56
+ inputTypeCode
57
+ }
50
58
  }
51
59
  `;
52
60
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evenicanpm/admin-integrate",
3
- "version": "2.0.1",
3
+ "version": "2.3.1",
4
4
  "description": "e4Integrate Admin Panel",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -21,5 +21,5 @@
21
21
  "elkjs": "^0.10.0",
22
22
  "lodash": "^4.17.21"
23
23
  },
24
- "gitHead": "bbaff05f6f592114ad7a25caeee531e4fa1b14cf"
24
+ "gitHead": "7a5a6a8c3c3dd9efe96fbbb884b21e7681d32550"
25
25
  }
@@ -38,7 +38,7 @@ export function GroupNode(props: NodeProps): JSX.Element {
38
38
  paddingRight: "48px",
39
39
  }}
40
40
  >
41
- {props.data.variant === "process" && !!props?.data?.canEdit ? (
41
+ {props.data.variant === "process" && props?.data?.canEdit ? (
42
42
  <AddCircleOutlineIcon
43
43
  sx={{
44
44
  color: "grey.600",
@@ -148,6 +148,7 @@ export function FormikPageBody({
148
148
  disabledFields,
149
149
  setFieldValidationInProgress,
150
150
  submitValidationResults,
151
+ isCreateMode,
151
152
  };
152
153
 
153
154
  const renderRow = (
@@ -66,6 +66,8 @@ interface DisplayStateInput {
66
66
  helpText?: string | null;
67
67
  validating: boolean;
68
68
  readOnly?: boolean;
69
+ isEditableAfterCreate?: boolean;
70
+ isCreateMode?: boolean;
69
71
  }
70
72
 
71
73
  function getInputDisplayState({
@@ -75,11 +77,14 @@ function getInputDisplayState({
75
77
  helpText,
76
78
  validating,
77
79
  readOnly,
80
+ isEditableAfterCreate,
81
+ isCreateMode,
78
82
  }: DisplayStateInput): {
79
83
  displayError: string | undefined;
80
84
  displayHelpText: string;
81
85
  formHelperTextSx: SxProps<Theme>;
82
86
  isDisabled: boolean;
87
+ isCreateMode: boolean;
83
88
  } {
84
89
  const showError = Boolean(error);
85
90
  const effectiveValidation = getEffectiveValidation(
@@ -94,7 +99,11 @@ function getInputDisplayState({
94
99
  const isError = showError || effectiveValidation?.isValid === false;
95
100
  const displayError = getDisplayError(validating, isError, helperMessage);
96
101
  const displayHelpText = validating ? "Checking…" : helperMessage;
97
- const isDisabled = Boolean(readOnly || validating);
102
+ const isDisabled = Boolean(
103
+ readOnly ||
104
+ validating ||
105
+ (isEditableAfterCreate === false && !isCreateMode),
106
+ );
98
107
  const formHelperTextSx: SxProps<Theme> = {
99
108
  ...(validating && { color: "text.secondary" }),
100
109
  ...(isSuccess && !validating && { color: "success.main" }),
@@ -105,6 +114,7 @@ function getInputDisplayState({
105
114
  displayHelpText,
106
115
  formHelperTextSx,
107
116
  isDisabled,
117
+ isCreateMode: isCreateMode || false,
108
118
  };
109
119
  }
110
120
 
@@ -128,6 +138,8 @@ export interface TextWithQueryInputProps {
128
138
  options?: SelectorOption[];
129
139
  /** Result from validateFieldOnSubmit (on Next click); shown below field. */
130
140
  submitValidationResult?: SubmitValidationResult | null;
141
+ isEditableAfterCreate?: boolean;
142
+ isCreateMode?: boolean;
131
143
  }
132
144
 
133
145
  export function TextWithQueryInput({
@@ -147,6 +159,8 @@ export function TextWithQueryInput({
147
159
  placeholder,
148
160
  options = [],
149
161
  submitValidationResult,
162
+ isEditableAfterCreate,
163
+ isCreateMode,
150
164
  }: Readonly<TextWithQueryInputProps>) {
151
165
  const [validation, setValidation] = React.useState<{
152
166
  isValid: boolean;
@@ -156,6 +170,7 @@ export function TextWithQueryInput({
156
170
 
157
171
  const handleBlur = React.useCallback(async () => {
158
172
  onBlur?.();
173
+ if (isEditableAfterCreate === false && !isCreateMode) return;
159
174
  const strValue = valueToString(value);
160
175
  if (!strValue && inputType !== InputType.CHECKBOX) {
161
176
  setValidation(null);
@@ -195,6 +210,8 @@ export function TextWithQueryInput({
195
210
  onBlur,
196
211
  validateFormikBeforeApi,
197
212
  setFieldValidationInProgress,
213
+ isEditableAfterCreate,
214
+ isCreateMode,
198
215
  ]);
199
216
 
200
217
  if (readOnly) {
@@ -216,6 +233,8 @@ export function TextWithQueryInput({
216
233
  helpText,
217
234
  validating,
218
235
  readOnly,
236
+ isEditableAfterCreate,
237
+ isCreateMode,
219
238
  });
220
239
 
221
240
  const clearValidationAndSet = (v: TextWithQueryValue) => {
@@ -200,6 +200,7 @@ export function renderFormControl({
200
200
  disabledFields,
201
201
  setFieldValidationInProgress,
202
202
  submitValidationResults,
203
+ isCreateMode,
203
204
  }: {
204
205
  input: ProcessTemplateInput;
205
206
  templateId: string;
@@ -221,6 +222,7 @@ export function renderFormControl({
221
222
  string,
222
223
  { isValid: boolean; message: string }
223
224
  >;
225
+ isCreateMode?: boolean;
224
226
  }) {
225
227
  const k = templateFieldKey(templateId, input.fieldName ?? "");
226
228
  const err = errors[k] ?? null;
@@ -263,6 +265,8 @@ export function renderFormControl({
263
265
  setFieldValidationInProgress={setFieldValidationInProgress}
264
266
  options={getSelectorOptionsFromInput(input)}
265
267
  submitValidationResult={submitValidationResult}
268
+ isEditableAfterCreate={input.isEditableAfterCreate ?? true}
269
+ isCreateMode={isCreateMode}
266
270
  />
267
271
  )}
268
272
  </Field>
@@ -19,8 +19,8 @@ export type ProcessTemplateInputSchema = ArrayElement<
19
19
  export type Data = { processTemplate: ProcessTemplate[] };
20
20
 
21
21
  export type ProcessTemplateFilter = {
22
- processTemplateGroupId?: string | null;
23
- processTemplateId?: string | null;
22
+ sourceTemplateId?: string | null;
23
+ targetTemplateId?: string | null;
24
24
  };
25
25
 
26
26
  export type FieldValue =
@@ -0,0 +1,311 @@
1
+ export interface Mapping {
2
+ fieldName: string;
3
+ jsonPath: string;
4
+ jsonValuePath: string;
5
+ format: string;
6
+ inputTypeCode?: string;
7
+ }
8
+
9
+ export function parseWithMappings(
10
+ input: { json: Record<string, unknown> },
11
+ mappings: Mapping[],
12
+ ): Record<string, unknown> {
13
+ const result: Record<string, unknown> = {};
14
+
15
+ for (const mapping of mappings) {
16
+ const { fieldName, jsonPath, format } = mapping;
17
+
18
+ const pathValue = resolvePath(input.json, jsonPath);
19
+ if (pathValue === undefined || pathValue === null) {
20
+ result[fieldName] = mapping.inputTypeCode === "input-table" ? [] : null;
21
+ continue;
22
+ }
23
+
24
+ const parsed = parseValueFromFormat(format, fieldName, pathValue);
25
+ const isEmptyForInputTable =
26
+ mapping.inputTypeCode === "input-table" &&
27
+ (parsed === "" ||
28
+ (typeof parsed === "object" &&
29
+ parsed !== null &&
30
+ !Array.isArray(parsed) &&
31
+ Object.keys(parsed).length === 0));
32
+ result[fieldName] = isEmptyForInputTable ? [] : parsed;
33
+ }
34
+
35
+ return result;
36
+ }
37
+
38
+ function stripPathPrefix(path: string): string {
39
+ if (path.startsWith("$.")) return path.slice(2);
40
+ if (path.startsWith("$")) return path.slice(1);
41
+ return path;
42
+ }
43
+
44
+ function walkSegment(current: unknown, segment: string | number): unknown {
45
+ if (typeof segment === "number") {
46
+ if (!Array.isArray(current)) return undefined;
47
+ return current[segment];
48
+ }
49
+ if (typeof current !== "object" || Array.isArray(current)) return undefined;
50
+ return (current as Record<string, unknown>)[segment];
51
+ }
52
+
53
+ /** Walks an object/array using a simple JSONPath expression (dot + bracket notation). */
54
+ function resolvePath(obj: unknown, path: string): unknown {
55
+ if (path === "$") return obj;
56
+
57
+ const stripped = stripPathPrefix(path);
58
+ if (!stripped) return obj;
59
+
60
+ let current: unknown = obj;
61
+ for (const segment of parsePathSegments(stripped)) {
62
+ if (current === null || current === undefined) return undefined;
63
+ current = walkSegment(current, segment);
64
+ }
65
+ return current;
66
+ }
67
+
68
+ /** Splits a stripped JSONPath string into typed segments, e.g. "Processes[0].ProcessTasks[1].Name" */
69
+ function parsePathSegments(path: string): (string | number)[] {
70
+ const segments: (string | number)[] = [];
71
+ const regex = /([^.[\]]+)|\[(\d+)\]/g;
72
+ let match: RegExpExecArray | null;
73
+ while ((match = regex.exec(path)) !== null) {
74
+ if (match[1] !== undefined) segments.push(match[1]);
75
+ else if (match[2] !== undefined)
76
+ segments.push(Number.parseInt(match[2], 10));
77
+ }
78
+ return segments;
79
+ }
80
+
81
+ /**
82
+ * Applies the format string to the extracted value.
83
+ * - If format is exactly `{0}` or `{fieldName}`, the raw value is returned as-is.
84
+ * - Otherwise `{0}` / `{fieldName}` tokens are replaced with the stringified value.
85
+ * If the result is valid JSON it is parsed back to an object.
86
+ */
87
+ function toValueString(value: unknown): string {
88
+ if (value == null) return "";
89
+ if (typeof value === "string") return value;
90
+ if (
91
+ typeof value === "number" ||
92
+ typeof value === "boolean" ||
93
+ typeof value === "bigint"
94
+ )
95
+ return String(value);
96
+ if (typeof value === "object") return JSON.stringify(value);
97
+ return "";
98
+ }
99
+
100
+ function escapeRegex(str: string): string {
101
+ return str.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
102
+ }
103
+
104
+ function findPlaceholderInArray(
105
+ arr: unknown[],
106
+ placeholder: string,
107
+ ): string[] | null {
108
+ for (let i = 0; i < arr.length; i++) {
109
+ const path = findPlaceholderPath(arr[i], placeholder);
110
+ if (path !== null) return [String(i), ...path];
111
+ }
112
+ return null;
113
+ }
114
+
115
+ function findPlaceholderInObject(
116
+ obj: Record<string, unknown>,
117
+ placeholder: string,
118
+ ): string[] | null {
119
+ for (const [key, val] of Object.entries(obj)) {
120
+ const path = findPlaceholderPath(val, placeholder);
121
+ if (path !== null) return [key, ...path];
122
+ }
123
+ return null;
124
+ }
125
+
126
+ /**
127
+ * Recursively searches an object/array for the first occurrence of `placeholder`
128
+ * as a string value, returning the key path to it, or `null` if not found.
129
+ */
130
+ function findPlaceholderPath(
131
+ obj: unknown,
132
+ placeholder: string,
133
+ ): string[] | null {
134
+ if (typeof obj === "string") return obj === placeholder ? [] : null;
135
+ if (Array.isArray(obj)) return findPlaceholderInArray(obj, placeholder);
136
+ if (typeof obj === "object" && obj !== null) {
137
+ return findPlaceholderInObject(obj as Record<string, unknown>, placeholder);
138
+ }
139
+ return null;
140
+ }
141
+
142
+ function navigatePath(obj: unknown, path: string[]): unknown {
143
+ let current: unknown = obj;
144
+ for (const key of path) {
145
+ if (current === null || current === undefined) return undefined;
146
+ if (Array.isArray(current)) {
147
+ current = current[Number(key)];
148
+ } else if (typeof current === "object") {
149
+ current = (current as Record<string, unknown>)[key];
150
+ } else {
151
+ return undefined;
152
+ }
153
+ }
154
+ return current;
155
+ }
156
+
157
+ /**
158
+ * Like findPlaceholderPath but matches any string leaf that CONTAINS the placeholder
159
+ * as a substring rather than being exactly equal to it.
160
+ */
161
+ function findContainingInArray(
162
+ arr: unknown[],
163
+ placeholder: string,
164
+ ): string[] | null {
165
+ for (let i = 0; i < arr.length; i++) {
166
+ const path = findContainingPath(arr[i], placeholder);
167
+ if (path !== null) return [String(i), ...path];
168
+ }
169
+ return null;
170
+ }
171
+
172
+ function findContainingInObject(
173
+ obj: Record<string, unknown>,
174
+ placeholder: string,
175
+ ): string[] | null {
176
+ for (const [key, val] of Object.entries(obj)) {
177
+ const path = findContainingPath(val, placeholder);
178
+ if (path !== null) return [key, ...path];
179
+ }
180
+ return null;
181
+ }
182
+
183
+ function findContainingPath(
184
+ obj: unknown,
185
+ placeholder: string,
186
+ ): string[] | null {
187
+ if (typeof obj === "string") return obj.includes(placeholder) ? [] : null;
188
+ if (Array.isArray(obj)) return findContainingInArray(obj, placeholder);
189
+ if (typeof obj === "object" && obj !== null)
190
+ return findContainingInObject(obj as Record<string, unknown>, placeholder);
191
+ return null;
192
+ }
193
+
194
+ /**
195
+ * Reverse of `applyFormat`: given a format string containing `{0}` and a value
196
+ * that was produced by that format, extracts and returns the original `{0}` value.
197
+ *
198
+ * For JSON formats (e.g. `{ "ConnectionName": "{0}" }`): parses both the format and
199
+ * the value as JSON, locates the key path of `{0}` in the format structure, then
200
+ * extracts the value at that same path from the actual value. This correctly handles
201
+ * values with extra keys not present in the format template.
202
+ *
203
+ * When the placeholder is embedded within a format leaf string
204
+ * (e.g. `{ "Sql": "exec {0}" }`), navigates to that leaf in the actual value
205
+ * and applies regex capture within that field.
206
+ *
207
+ * For non-JSON formats (e.g. `"{0}_entity"`): falls back to regex capture.
208
+ *
209
+ * Returns `null` when no match is found.
210
+ */
211
+ function extractViaJsonFormat(
212
+ format: string,
213
+ placeholder0: string,
214
+ placeholderField: string,
215
+ formattedValue: unknown,
216
+ ): unknown {
217
+ let parsedFormat: unknown;
218
+ try {
219
+ parsedFormat = JSON.parse(format);
220
+ } catch {
221
+ return undefined;
222
+ }
223
+
224
+ let parsedValue: unknown = formattedValue;
225
+ if (typeof formattedValue === "string") {
226
+ try {
227
+ parsedValue = JSON.parse(formattedValue);
228
+ } catch {
229
+ /* use as-is */
230
+ }
231
+ }
232
+
233
+ // Exact match: the format leaf IS the placeholder
234
+ const exactPath =
235
+ findPlaceholderPath(parsedFormat, placeholder0) ??
236
+ findPlaceholderPath(parsedFormat, placeholderField);
237
+ if (exactPath !== null) {
238
+ return navigatePath(parsedValue, exactPath);
239
+ }
240
+
241
+ // Embedded match: the placeholder appears inside a format leaf string
242
+ const containingPath =
243
+ findContainingPath(parsedFormat, placeholder0) ??
244
+ findContainingPath(parsedFormat, placeholderField);
245
+ if (containingPath === null) return undefined;
246
+
247
+ const formatLeaf = navigatePath(parsedFormat, containingPath);
248
+ if (typeof formatLeaf !== "string") return undefined;
249
+
250
+ return extractViaRegex(
251
+ formatLeaf,
252
+ placeholder0,
253
+ placeholderField,
254
+ navigatePath(parsedValue, containingPath),
255
+ );
256
+ }
257
+
258
+ function extractViaRegex(
259
+ format: string,
260
+ placeholder0: string,
261
+ placeholderField: string,
262
+ formattedValue: unknown,
263
+ ): unknown {
264
+ const segments = format.includes(placeholder0)
265
+ ? format.split(placeholder0)
266
+ : format.split(placeholderField);
267
+
268
+ if (segments.length === 1) return null;
269
+
270
+ const captureGroup = String.raw`([\s\S]*?)`;
271
+ const regexSource = segments.map(escapeRegex).join(captureGroup);
272
+ const match = new RegExp(`^${regexSource}$`).exec(
273
+ toValueString(formattedValue).trim(),
274
+ );
275
+ if (!match) return null;
276
+
277
+ const captured = match[1];
278
+ try {
279
+ return JSON.parse(captured);
280
+ } catch {
281
+ return captured;
282
+ }
283
+ }
284
+
285
+ export function parseValueFromFormat(
286
+ format: string,
287
+ fieldName: string,
288
+ formattedValue: unknown,
289
+ ): unknown {
290
+ if (format === "{0}" || format === `{${fieldName}}`) {
291
+ return formattedValue;
292
+ }
293
+
294
+ const placeholder0 = "{0}";
295
+ const placeholderField = `{${fieldName}}`;
296
+
297
+ const jsonResult = extractViaJsonFormat(
298
+ format,
299
+ placeholder0,
300
+ placeholderField,
301
+ formattedValue,
302
+ );
303
+ if (jsonResult !== undefined) return jsonResult;
304
+
305
+ return extractViaRegex(
306
+ format,
307
+ placeholder0,
308
+ placeholderField,
309
+ formattedValue,
310
+ );
311
+ }
@@ -21,6 +21,10 @@ import {
21
21
  import { usePathname, useRouter, useSearchParams } from "next/navigation";
22
22
  import React, { useEffect, useRef, useState } from "react";
23
23
  import { applyTemplateConfig, InputType } from "./inputs";
24
+ import {
25
+ parseWithMappings,
26
+ type Mapping,
27
+ } from "./utils/parseTemplateValuesFromConfig";
24
28
  import PageForm from "./pageForm";
25
29
  import {
26
30
  type FieldValue,
@@ -277,7 +281,8 @@ function PageNav({
277
281
 
278
282
  interface Props {
279
283
  readonly metadata: Metadata;
280
- readonly processTemplateGroupId?: string;
284
+ readonly sourceTemplateId?: string;
285
+ readonly targetTemplateId?: string;
281
286
  readonly processTemplateId?: string;
282
287
  readonly handleBack?: (event?: React.MouseEventHandler) => void;
283
288
  readonly handleFinish?: (event?: React.MouseEventHandler) => void;
@@ -290,6 +295,7 @@ interface Props {
290
295
  readonly isCreateMode?: boolean;
291
296
  readonly readOnly?: boolean;
292
297
  readonly sharedCount?: (count: number) => void;
298
+ readonly config: Record<string, unknown>;
293
299
  }
294
300
 
295
301
  export default function Wizard({ metadata, sharedCount, ...props }: Props) {
@@ -304,11 +310,54 @@ export default function Wizard({ metadata, sharedCount, ...props }: Props) {
304
310
 
305
311
  const { data, isLoading } = getProcessTemplate.useData({
306
312
  input: {
307
- processTemplateGroupId: props.processTemplateGroupId || null,
308
- processTemplateId: props.processTemplateId || null,
313
+ sourceTemplateId: props.sourceTemplateId || null,
314
+ targetTemplateId: props.targetTemplateId || null,
309
315
  },
310
316
  });
311
317
 
318
+ const parsedTemplateConfigs = React.useMemo((): Array<
319
+ Record<string, unknown>
320
+ > => {
321
+ if (props.config == null) return [];
322
+ type TemplateEntry = {
323
+ id?: unknown;
324
+ processTemplateId?: unknown;
325
+ mappings?: unknown[];
326
+ };
327
+ const allTemplates =
328
+ (data as { processTemplate?: TemplateEntry[] })?.processTemplate ?? [];
329
+ return allTemplates.flatMap((template) => {
330
+ const mappings = (template?.mappings ?? []) as Mapping[];
331
+ if (!mappings.length) return [];
332
+ const processTemplateId = template?.processTemplateId ?? template?.id;
333
+ return [
334
+ {
335
+ ...parseWithMappings(
336
+ props.config as { json: Record<string, unknown> },
337
+ mappings,
338
+ ),
339
+ processTemplateId,
340
+ },
341
+ ];
342
+ });
343
+ }, [data, props.config]);
344
+
345
+ const parsedTemplateConfigString = React.useMemo(
346
+ () =>
347
+ parsedTemplateConfigs.length > 0
348
+ ? JSON.stringify(parsedTemplateConfigs[0])
349
+ : null,
350
+ [parsedTemplateConfigs],
351
+ );
352
+
353
+ const parsedAllTemplateConfigsString = React.useMemo(
354
+ () =>
355
+ parsedTemplateConfigs.length > 0
356
+ ? JSON.stringify(parsedTemplateConfigs)
357
+ : null,
358
+ [parsedTemplateConfigs],
359
+ );
360
+
312
361
  const [initialValues, setInitialValues] = React.useState<FormState>({});
313
362
  // aggregated form values across pages
314
363
  const [aggregatedValues, setAggregatedValues] =
@@ -340,7 +389,6 @@ export default function Wizard({ metadata, sharedCount, ...props }: Props) {
340
389
  const typedData = data as GetProcessTemplateQuery;
341
390
  if (typedData?.processTemplate) {
342
391
  const templates = typedData.processTemplate as ProcessTemplate[];
343
-
344
392
  setTemplates(templates);
345
393
  setTemplatesSorted(
346
394
  templates.map((t) => ({
@@ -425,12 +473,12 @@ export default function Wizard({ metadata, sharedCount, ...props }: Props) {
425
473
  );
426
474
 
427
475
  useEffect(() => {
428
- if (props.templateConfig) {
429
- const config = JSON.parse(props.templateConfig);
476
+ if (!parsedAllTemplateConfigsString) return;
477
+ for (const config of parsedTemplateConfigs) {
430
478
  applyTemplateConfig(
431
479
  {
432
- name: config.taskName as string,
433
- templateConfig: props.templateConfig,
480
+ name: "",
481
+ templateConfig: JSON.stringify(config),
434
482
  } as SearchOption,
435
483
  (templateId: string, fieldName: string, value: FieldValue) => {
436
484
  const key = templateFieldKey(templateId, fieldName);
@@ -438,7 +486,7 @@ export default function Wizard({ metadata, sharedCount, ...props }: Props) {
438
486
  },
439
487
  );
440
488
  }
441
- }, [props.templateConfig]);
489
+ }, [parsedAllTemplateConfigsString]);
442
490
 
443
491
  const clearTemplateFields = (templateId: string) => {
444
492
  setAggregatedValues((prev) => {
@@ -600,8 +648,9 @@ export default function Wizard({ metadata, sharedCount, ...props }: Props) {
600
648
  isLoading &&
601
649
  !currentFlat &&
602
650
  !currentTemplate &&
603
- !props.templateConfig &&
604
- (!props.processTemplateGroupId || !props.processTemplateId);
651
+ !parsedTemplateConfigString &&
652
+ !props.sourceTemplateId &&
653
+ !props.targetTemplateId;
605
654
  if (showLoading) {
606
655
  return (
607
656
  <Box sx={{ padding: theme.spacing(10, 3) }}>
@@ -676,7 +725,7 @@ export default function Wizard({ metadata, sharedCount, ...props }: Props) {
676
725
  isCreateMode={props.isCreateMode}
677
726
  disabledFields={disabledFields}
678
727
  setFieldsDisabled={setFieldsDisabled}
679
- templateConfig={props.templateConfig || ""}
728
+ templateConfig={parsedTemplateConfigString ?? ""}
680
729
  setFieldValidationInProgress={setIsFieldValidationInProgress}
681
730
  submitValidationResults={submitValidationResults ?? undefined}
682
731
  />
@@ -1,7 +1,8 @@
1
1
  import type {
2
2
  ConfigExport,
3
3
  ConfigImport,
4
- ConfigurationMergeMutationVariables,
4
+ ConfigMerge,
5
+ InputMaybe,
5
6
  } from "@evenicanpm/admin-core/api/e4/graphqlRequestSdk";
6
7
  import {
7
8
  processConfigImport,
@@ -26,6 +27,7 @@ import SelectStep from "./select-step";
26
27
  import TemplateListStep, { DirectionType } from "./template-list-step";
27
28
  import TemplateOptionsStep from "./template-options-step";
28
29
  import WizardStep from "./wizard-step";
30
+ import { TemplateOption } from "./template-options-step";
29
31
 
30
32
  export default function IntegrationCreate() {
31
33
  const t = useTranslations("Integrate");
@@ -59,7 +61,17 @@ export default function IntegrationCreate() {
59
61
  const [dualSourceThenTarget, setDualSourceThenTarget] = useState(false);
60
62
 
61
63
  useEffect(() => {
62
- if (searchParams.get("group")) {
64
+ const templateOption = searchParams.get("option");
65
+
66
+ if (
67
+ (templateOption === TemplateOption.Source &&
68
+ searchParams.get(DirectionType.Incoming)) ||
69
+ (templateOption === TemplateOption.Target &&
70
+ searchParams.get(DirectionType.Outgoing)) ||
71
+ (templateOption === TemplateOption.SourceAndTarget &&
72
+ searchParams.get(DirectionType.Incoming) &&
73
+ searchParams.get(DirectionType.Outgoing))
74
+ ) {
63
75
  setStep(INTEGRATION_CREATE_STEPS.wizard);
64
76
  }
65
77
  }, [searchParams]);
@@ -93,32 +105,31 @@ export default function IntegrationCreate() {
93
105
  c.code = code;
94
106
  }
95
107
 
96
- console.log("configMergeInput", configMergeInput);
108
+ const templateOption = searchParams.get("option");
97
109
 
98
- const sourceName = configMergeInput?.input?.config?.find(
99
- (item) => (item?.source as string)?.toLowerCase() === "source",
100
- )?.uniqueIdentifier;
110
+ let sourceConfig: InputMaybe<ConfigMerge> | undefined = null;
111
+ let targetConfig: InputMaybe<ConfigMerge> | undefined = null;
112
+ let targetSourcesConfig: InputMaybe<ConfigMerge> | undefined = null;
101
113
 
102
- if (sourceName) {
103
- configMergeInput.input.config = configMergeInput.input.config.map(
104
- (item) => {
105
- if (item?.queues) {
106
- return {
107
- ...item,
108
- queues: `${sourceName}_endpoint`,
109
- };
110
- } else {
111
- return item;
112
- }
113
- },
114
- );
114
+ const mergedInput = removeTemplateConfig(newProcessConfigMerge?.input) as
115
+ | { config: Array<Record<string, unknown>> }
116
+ | undefined;
117
+
118
+ if (templateOption === TemplateOption.SourceAndTarget) {
119
+ sourceConfig = (mergedInput?.config?.[0] ?? null) as ConfigMerge | null;
120
+ targetConfig = (mergedInput?.config?.[1] ?? null) as ConfigMerge | null;
121
+ } else if (templateOption === TemplateOption.Source) {
122
+ sourceConfig = (mergedInput?.config?.[0] ?? null) as ConfigMerge | null;
123
+ } else if (templateOption === TemplateOption.Target) {
124
+ targetSourcesConfig = (mergedInput?.config?.[0] ??
125
+ null) as ConfigMerge | null;
126
+ targetConfig = (mergedInput?.config?.[1] ?? null) as ConfigMerge | null;
115
127
  }
116
128
 
117
129
  processConfigMergeMutation.mutate(
118
130
  {
119
- input:
120
- removeTemplateConfig(newProcessConfigMerge?.input) ||
121
- ({} as ConfigurationMergeMutationVariables),
131
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
132
+ input: { sourceConfig, targetConfig, targetSourcesConfig } as any,
122
133
  },
123
134
  {
124
135
  onSuccess: ({ configurationMerge }) => {
@@ -236,23 +247,20 @@ export default function IntegrationCreate() {
236
247
  <TemplateListStep
237
248
  listStepTitle={t("Templates.SelectSource")}
238
249
  directionType={DirectionType.Incoming}
239
- onSelectTemplate={
240
- dualSourceThenTarget
241
- ? (id) => {
242
- const p = new URLSearchParams(searchParams.toString());
243
- p.set(`${t("Templates.sourceGroup")}`, id);
244
- p.delete(`${t("Templates.group")}`);
245
- router.replace(`${pathname}?${p}`);
246
- setStep(INTEGRATION_CREATE_STEPS.targetTemplateList);
247
- }
248
- : undefined
249
- }
250
+ onSelectTemplate={() => {
251
+ if (dualSourceThenTarget)
252
+ setStep(INTEGRATION_CREATE_STEPS.targetTemplateList);
253
+ //else setStep(INTEGRATION_CREATE_STEPS.wizard)
254
+ }}
250
255
  />
251
256
  )}
252
257
  {step === INTEGRATION_CREATE_STEPS.targetTemplateList && (
253
258
  <TemplateListStep
254
259
  listStepTitle={t("Templates.SelectTarget")}
255
260
  directionType={DirectionType.Outgoing}
261
+ // onSelectTemplate={
262
+ // ()=>setStep(INTEGRATION_CREATE_STEPS.wizard)
263
+ // }
256
264
  />
257
265
  )}
258
266
  {/* </Card> */}
@@ -1,8 +1,8 @@
1
1
  import type {
2
2
  FilterQueryInput,
3
- GetProcessTemplateGroupsQuery,
3
+ GetProcessTemplatesQuery,
4
4
  } from "@evenicanpm/admin-core/api/e4/graphqlRequestSdk";
5
- import { getProcessTemplateGroups } from "@evenicanpm/admin-integrate/api/templates/queries";
5
+ import { getProcessTemplates } from "@evenicanpm/admin-integrate/api/templates/queries";
6
6
  import type { Head } from "@evenicanpm/admin-integrate/components/data-table/table-header";
7
7
  import TemplatesList from "@evenicanpm/admin-integrate/components/templatesV2/templates-list";
8
8
  import { Box, Typography, useTheme } from "@mui/material";
@@ -17,12 +17,12 @@ export enum DirectionType {
17
17
 
18
18
  interface Props {
19
19
  listStepTitle?: string;
20
- directionType?: DirectionType;
20
+ directionType: DirectionType;
21
21
  /** When set, skips default `group` URL handoff (e.g. source pick in dual flow). */
22
22
  onSelectTemplate?: (id: string) => void;
23
23
  }
24
24
 
25
- export default function TemplateListStep(props?: Props) {
25
+ export default function TemplateListStep(props: Props) {
26
26
  const t = useTranslations("Integrate.Templates");
27
27
 
28
28
  const router = useRouter();
@@ -62,18 +62,15 @@ export default function TemplateListStep(props?: Props) {
62
62
  skip: 0,
63
63
  });
64
64
 
65
- const { data, isLoading } = getProcessTemplateGroups.useData({
65
+ const { data, isLoading } = getProcessTemplates.useData({
66
66
  input: input,
67
67
  });
68
68
 
69
69
  const handleRedirect = (id: string) => {
70
- if (props?.onSelectTemplate) {
71
- props.onSelectTemplate(id);
72
- return;
73
- }
74
70
  const currentParams = new URLSearchParams(searchParams.toString());
75
- currentParams.set("group", id);
71
+ currentParams.set(props.directionType, id);
76
72
  router.replace(`${pathname}?${currentParams.toString()}`);
73
+ if (props?.onSelectTemplate) props.onSelectTemplate(id);
77
74
  };
78
75
 
79
76
  return (
@@ -89,13 +86,11 @@ export default function TemplateListStep(props?: Props) {
89
86
  </Box>
90
87
  <TemplatesList
91
88
  heading={heading}
92
- data={data as GetProcessTemplateGroupsQuery}
89
+ data={data as GetProcessTemplatesQuery}
93
90
  loading={isLoading}
94
- // input={{ ...input, directionType: props?.directionType }}
95
91
  input={input}
96
92
  setInput={setInput}
97
93
  handleRedirect={handleRedirect}
98
- isTemplateGroupsList
99
94
  />
100
95
  </Box>
101
96
  );
@@ -12,6 +12,13 @@ import {
12
12
  import { useTranslations } from "next-intl";
13
13
  import type React from "react";
14
14
  import { INTEGRATION_CREATE_STEPS } from "./integration-create-steps";
15
+ import { usePathname, useRouter, useSearchParams } from "next/navigation";
16
+
17
+ export enum TemplateOption {
18
+ Source = "source",
19
+ Target = "target",
20
+ SourceAndTarget = "both",
21
+ }
15
22
 
16
23
  interface Props {
17
24
  setStep: React.Dispatch<React.SetStateAction<number>>;
@@ -24,14 +31,27 @@ export default function TemplateOptionsStep(props: Props) {
24
31
  const theme = useTheme();
25
32
  const styles = getStyles(theme);
26
33
 
27
- const handleClickOption1 = () => {
28
- props.onSourceAndTarget?.();
29
- props.setStep(INTEGRATION_CREATE_STEPS.sourceTemplateList);
34
+ const router = useRouter();
35
+ const pathname = usePathname();
36
+ const searchParams = useSearchParams();
37
+
38
+ const handleClickOption = (option: TemplateOption) => {
39
+ switch (option) {
40
+ case TemplateOption.SourceAndTarget:
41
+ props.onSourceAndTarget?.();
42
+ props.setStep(INTEGRATION_CREATE_STEPS.sourceTemplateList);
43
+ break;
44
+ case TemplateOption.Source:
45
+ props.setStep(INTEGRATION_CREATE_STEPS.sourceTemplateList);
46
+ break;
47
+ case TemplateOption.Target:
48
+ props.setStep(INTEGRATION_CREATE_STEPS.targetTemplateList);
49
+ }
50
+
51
+ const currentParams = new URLSearchParams(searchParams.toString());
52
+ currentParams.set("option", option);
53
+ router.replace(`${pathname}?${currentParams.toString()}`);
30
54
  };
31
- const handleClickOption2 = () =>
32
- props.setStep(INTEGRATION_CREATE_STEPS.sourceTemplateList);
33
- const handleClickOption3 = () =>
34
- props.setStep(INTEGRATION_CREATE_STEPS.targetTemplateList);
35
55
 
36
56
  return (
37
57
  <Box sx={styles.main}>
@@ -45,7 +65,7 @@ export default function TemplateOptionsStep(props: Props) {
45
65
  <LinkCard
46
66
  title={t("templateOption1")}
47
67
  description={t("templateOption1Description")}
48
- onClick={handleClickOption1}
68
+ onClick={() => handleClickOption(TemplateOption.SourceAndTarget)}
49
69
  >
50
70
  <Box
51
71
  sx={{
@@ -62,7 +82,7 @@ export default function TemplateOptionsStep(props: Props) {
62
82
  <LinkCard
63
83
  title={t("templateOption2")}
64
84
  description={t("templateOption2Description")}
65
- onClick={handleClickOption2}
85
+ onClick={() => handleClickOption(TemplateOption.Source)}
66
86
  >
67
87
  <Box
68
88
  sx={{
@@ -79,7 +99,7 @@ export default function TemplateOptionsStep(props: Props) {
79
99
  <LinkCard
80
100
  title={t("templateOption3")}
81
101
  description={t("templateOption3Description")}
82
- onClick={handleClickOption3}
102
+ onClick={() => handleClickOption(TemplateOption.Target)}
83
103
  >
84
104
  <Box
85
105
  sx={{
@@ -8,6 +8,8 @@ import { Box } from "@mui/material";
8
8
  import { usePathname, useRouter, useSearchParams } from "next/navigation";
9
9
  import { INTEGRATION_CREATE_STEPS } from "./integration-create-steps";
10
10
 
11
+ import { DirectionType } from "./template-list-step";
12
+
11
13
  const metadata = {
12
14
  name: "",
13
15
  code: "",
@@ -37,14 +39,26 @@ export default function WizardStep(props: Readonly<Props>) {
37
39
  router.replace(pathname);
38
40
  };
39
41
 
42
+ console.log(
43
+ "SEARHCPARAMS",
44
+ searchParams.get(DirectionType.Incoming)?.toString(),
45
+ );
46
+ console.log(
47
+ "SEARHCPARAMS",
48
+ searchParams.get(DirectionType.Outgoing)?.toString(),
49
+ );
50
+
40
51
  return (
41
52
  <Box>
42
53
  <Wizard
43
54
  metadata={metadata}
44
- processTemplateGroupId={searchParams.get("group")?.toString()}
55
+ // processTemplateGroupId={}
56
+ sourceTemplateId={searchParams.get(DirectionType.Incoming)?.toString()}
57
+ targetTemplateId={searchParams.get(DirectionType.Outgoing)?.toString()}
45
58
  handleBack={handleBack}
46
59
  handleFinish={handleFinish}
47
60
  setProcessConfigMerge={props.setProcessConfigMerge}
61
+ config={{}}
48
62
  isCreateMode
49
63
  />
50
64
  </Box>
@@ -37,22 +37,22 @@ import {
37
37
  taskReducer,
38
38
  } from "./task-drawer/task-reducer";
39
39
 
40
- function mergeByKey(
40
+ function mergeByKey<T>(
41
41
  keyFields: string | string[],
42
- arr1: any[] = [],
43
- arr2: any[] = [],
44
- ) {
42
+ arr1: T[] = [],
43
+ arr2: T[] = [],
44
+ ): T[] {
45
45
  const keys = Array.isArray(keyFields) ? keyFields : [keyFields];
46
46
 
47
47
  // Build lookup maps for each key -> value => baseItem (from arr1)
48
- const lookupByKey: Record<string, Map<any, any>> = {};
48
+ const lookupByKey: Record<string, Map<unknown, T>> = {};
49
49
  for (const k of keys) {
50
50
  lookupByKey[k] = new Map();
51
51
  }
52
52
 
53
53
  for (const item of arr1) {
54
54
  for (const key of keys) {
55
- const val = item?.[key];
55
+ const val = (item as Record<string, unknown> | null | undefined)?.[key];
56
56
  if (val !== undefined && val !== null) {
57
57
  lookupByKey[key].set(val, item);
58
58
  }
@@ -60,28 +60,29 @@ function mergeByKey(
60
60
  }
61
61
 
62
62
  // Use a Set to preserve insertion order and ensure uniqueness
63
- const resultSet = new Set<any>(arr1);
63
+ const resultSet = new Set<T>(arr1);
64
64
 
65
65
  for (const item of arr2) {
66
- let matchedBase: any = null;
66
+ let matchedBase: T | undefined;
67
67
 
68
68
  // Try keys in order of priority
69
69
  for (const key of keys) {
70
- const val = item?.[key];
70
+ const val = (item as Record<string, unknown> | null | undefined)?.[key];
71
71
  if (val !== undefined && val !== null) {
72
72
  const candidate = lookupByKey[key].get(val);
73
- if (candidate) {
73
+ if (candidate !== undefined) {
74
74
  matchedBase = candidate;
75
75
  break;
76
76
  }
77
77
  }
78
78
  }
79
79
 
80
- if (matchedBase) {
80
+ if (matchedBase !== undefined) {
81
81
  // Merge properties from item into matched base (respecting exclusions)
82
- for (const prop of Object.keys(item)) {
82
+ const itemRecord = item as Record<string, unknown> | null | undefined;
83
+ for (const prop of Object.keys(itemRecord ?? {})) {
83
84
  if (prop !== "Sort" && prop !== "ParallelProcessingGroup") {
84
- matchedBase[prop] = item[prop];
85
+ (matchedBase as Record<string, unknown>)[prop] = itemRecord?.[prop];
85
86
  }
86
87
  }
87
88
  } else {
@@ -89,7 +90,7 @@ function mergeByKey(
89
90
  resultSet.add(item);
90
91
  // Register the newly added item in lookups so subsequent arr2 items can match it
91
92
  for (const key of keys) {
92
- const val = item?.[key];
93
+ const val = (item as Record<string, unknown> | null | undefined)?.[key];
93
94
  if (val !== undefined && val !== null) {
94
95
  lookupByKey[key].set(val, item);
95
96
  }
@@ -175,7 +176,9 @@ export default function IntegrationDetails() {
175
176
  //setConfig(newConfig);
176
177
 
177
178
  const configData: ConfigExport =
178
- config ?? (configFromApi as any)?.configurationExport;
179
+ config ??
180
+ ((configFromApi as ConfigurationExportQuery)
181
+ ?.configurationExport as ConfigExport);
179
182
 
180
183
  configData.DepotGroups = mergeByKey(
181
184
  "DepotGroup",
@@ -287,7 +290,10 @@ export default function IntegrationDetails() {
287
290
  }
288
291
  }, [scheduleLoading]);
289
292
 
290
- const onAdd = (data: any) => {
293
+ const onAdd = (data: {
294
+ sort?: number;
295
+ paralellelProcessingGroup?: string;
296
+ }) => {
291
297
  dispatchTaskState({
292
298
  type: TaskActionType.OPEN_ADD,
293
299
  sort: data.sort,
@@ -314,14 +320,21 @@ export default function IntegrationDetails() {
314
320
  <IntegrateBreadcrumbs breadcrumbs={breadcrumbs} />
315
321
  <IntegrationDetailsHeader
316
322
  name={
317
- (config || (configFromApi as any)?.configurationExport)?.Processes[0]
318
- ?.Name || "Untitled Integration"
323
+ (
324
+ config ||
325
+ ((configFromApi as ConfigurationExportQuery)
326
+ ?.configurationExport as ConfigExport)
327
+ )?.Processes?.[0]?.Name || "Untitled Integration"
319
328
  }
320
329
  icon={details?.processById?.processIcon ?? ""}
321
330
  setName={(name) => {
322
331
  const configData =
323
- config || (configFromApi as any)?.configurationExport;
324
- configData.Processes[0].Name = name;
332
+ config ||
333
+ ((configFromApi as ConfigurationExportQuery)
334
+ ?.configurationExport as ConfigExport);
335
+ if (configData?.Processes?.[0]) {
336
+ configData.Processes[0].Name = name as string;
337
+ }
325
338
  setConfig(configData);
326
339
  }}
327
340
  onImportClick={() => {
@@ -399,24 +412,54 @@ export default function IntegrationDetails() {
399
412
  (ep) => !!ep && ep.Name === processTask.endpoint,
400
413
  );
401
414
 
402
- let templateConfig: any;
403
-
404
- if (endpoint) {
405
- if (typeof endpoint.TemplateConfig === "string") {
406
- templateConfig = endpoint.TemplateConfig;
407
- } else if (typeof endpoint.TemplateConfig === "object") {
408
- templateConfig = JSON.stringify(endpoint.TemplateConfig);
415
+ let filteredConfig: ConfigExport | null =
416
+ config ||
417
+ (configFromApi as ConfigurationExportQuery)?.configurationExport
418
+ ? JSON.parse(
419
+ JSON.stringify(
420
+ config ||
421
+ (configFromApi as ConfigurationExportQuery)
422
+ ?.configurationExport,
423
+ ),
424
+ )
425
+ : null;
426
+
427
+ if (filteredConfig?.Processes?.[0]) {
428
+ filteredConfig.Processes[0].ProcessTasks =
429
+ filteredConfig.Processes[0].ProcessTasks?.filter(
430
+ (task) =>
431
+ !!task &&
432
+ Number.parseInt(task.ProcessTaskId ?? "") ===
433
+ Number.parseInt(processTask.id),
434
+ ) || [];
435
+
436
+ filteredConfig.Endpoints =
437
+ filteredConfig.Endpoints?.filter(
438
+ (ep) => !!ep && ep.Name === processTask.endpoint,
439
+ ) || [];
440
+
441
+ if ((filteredConfig.Endpoints?.length ?? 0) > 0 && endpoint) {
442
+ filteredConfig.Entities =
443
+ filteredConfig.Entities?.filter(
444
+ (entity) =>
445
+ !!entity &&
446
+ entity.Name === filteredConfig?.Endpoints?.[0]?.Entity,
447
+ ) || [];
409
448
  }
410
449
  }
411
450
 
451
+ console.log("filtered down Config", filteredConfig);
452
+
412
453
  dispatchTaskState({
413
454
  type: TaskActionType.OPEN_EDIT,
414
455
  processTaskId: processTask.id,
415
- templateConfig: templateConfig,
456
+ //templateConfig: templateConfig,
457
+ config: filteredConfig,
416
458
  sort: processTask.sort,
417
459
  parallelProcessingGroup: processTask.parallelProcessingGroup,
418
- processTemplateId: endpoint?.ProcessTemplateId,
460
+ processTemplateId: processTask.processTemplateId,
419
461
  name: processTask.name,
462
+ directionTypeId: processTask.directionTypeId,
420
463
  } as EditAction);
421
464
  }}
422
465
  drawerOpen={taskState.open}
@@ -437,7 +480,11 @@ export default function IntegrationDetails() {
437
480
  renderKey={taskState.processTaskId ?? 0}
438
481
  dispatch={dispatchTaskState}
439
482
  handleUpdateConfig={handleUpdateConfig}
440
- config={config ?? (configFromApi as any)?.configurationExport}
483
+ config={
484
+ config ??
485
+ ((configFromApi as ConfigurationExportQuery)
486
+ ?.configurationExport as ConfigExport)
487
+ }
441
488
  />
442
489
  </Box>
443
490
  <ScheduleDrawer
@@ -160,6 +160,7 @@ export default function AddTask(props: Readonly<Props>) {
160
160
  isCreateMode
161
161
  handleBack={handleBack}
162
162
  handleFinish={handleNext}
163
+ config={{}}
163
164
  />
164
165
  )}
165
166
  {props.taskState.step === 2 && (
@@ -1,7 +1,6 @@
1
1
  import type {
2
2
  ConfigExport,
3
3
  ConfigurationMergeMutation,
4
- ConfigurationMergeMutationVariables,
5
4
  } from "@evenicanpm/admin-core/api/e4/graphqlRequestSdk";
6
5
  import { processConfigMerge } from "@evenicanpm/admin-integrate/api/process-config/mutation";
7
6
  import { TitleEdit } from "@evenicanpm/admin-integrate/components/core";
@@ -63,12 +62,21 @@ export default function EditTask(props: Readonly<Props>) {
63
62
 
64
63
  useEffect(() => {
65
64
  if (processConfigMergeInput) {
66
- const input =
67
- removeTemplateConfig(processConfigMergeInput?.input) ||
68
- ({} as ConfigurationMergeMutationVariables);
65
+ const input = (removeTemplateConfig(processConfigMergeInput?.input) ||
66
+ {}) as {
67
+ config: Array<Record<string, unknown>>;
68
+ };
69
69
  processConfigMergeMutation.mutate(
70
70
  {
71
- input: input,
71
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
72
+ input: {
73
+ sourceConfig:
74
+ props.taskState.directionTypeId === 0 ? input?.config[0] : null,
75
+ targetConfig:
76
+ props.taskState.directionTypeId === 1 ? input?.config[1] : null,
77
+ targetSourcesConfig:
78
+ props.taskState.directionTypeId === 1 ? input?.config[0] : null,
79
+ } as any,
72
80
  },
73
81
  {
74
82
  onSuccess: async (data: ConfigurationMergeMutation) => {
@@ -143,13 +151,24 @@ export default function EditTask(props: Readonly<Props>) {
143
151
  setProcessConfigMerge={setProcessConfigMergeInput}
144
152
  handleBack={handleBack}
145
153
  handleFinish={handleFinish}
146
- templateConfig={props.taskState.templateConfig}
154
+ //templateConfig={props.taskState.templateConfig}
147
155
  processTemplateId={props.taskState.processTemplateId as string}
148
156
  isEditMode
149
157
  isCreateMode={false}
150
158
  disableBackToTemplates
151
159
  readOnly={!props.editable}
152
160
  sharedCount={(count: number) => setSharedCount(count)}
161
+ sourceTemplateId={
162
+ (props.taskState.directionTypeId as number) == 0
163
+ ? props.taskState.processTemplateId
164
+ : undefined
165
+ }
166
+ targetTemplateId={
167
+ (props.taskState.directionTypeId as number) == 1
168
+ ? props.taskState.processTemplateId
169
+ : undefined
170
+ }
171
+ config={{ json: props.taskState.config as Record<string, unknown> }}
153
172
  />
154
173
  )}
155
174
  {props.taskState.step === 1 && (
@@ -23,6 +23,8 @@ export interface EditAction extends TaskAction {
23
23
  parallelProcessingGroup?: number;
24
24
  processTemplateId?: string;
25
25
  name?: string;
26
+ directionTypeId?: number;
27
+ config?: object;
26
28
  }
27
29
 
28
30
  export interface TaskState {
@@ -35,6 +37,8 @@ export interface TaskState {
35
37
  parallelProcessingGroup?: number;
36
38
  processTemplateId?: string;
37
39
  name?: string;
40
+ directionTypeId?: number;
41
+ config?: object | null;
38
42
  }
39
43
 
40
44
  export function taskReducer(state: TaskState, action: TaskAction): TaskState {
@@ -69,6 +73,8 @@ export function taskReducer(state: TaskState, action: TaskAction): TaskState {
69
73
  parallelProcessingGroup: (action as EditAction).parallelProcessingGroup,
70
74
  processTemplateId: (action as EditAction).processTemplateId,
71
75
  name: (action as EditAction).name,
76
+ directionTypeId: (action as EditAction).directionTypeId,
77
+ config: (action as EditAction).config,
72
78
  };
73
79
  }
74
80
  case TaskActionType.INCREMENT_STEP: {