@integry/sdk 4.6.92 → 4.6.94

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integry/sdk",
3
- "version": "4.6.92",
3
+ "version": "4.6.94",
4
4
  "description": "Integry SDK",
5
5
  "main": "dist/umd/index.umd.js",
6
6
  "module": "dist/esm/index.csm.js",
@@ -1,11 +1,12 @@
1
+ 'use client';
2
+
1
3
  import { html } from 'htm/preact';
2
4
  import { connect } from 'unistore/preact';
3
- import { useContext, useEffect, useState } from 'preact/hooks';
4
- import { DynamicField, NestedObject } from '@/interfaces';
5
+ import { useContext, useEffect, useState, useRef } from 'preact/hooks';
6
+ import type { DynamicField, NestedObject } from '@/interfaces';
5
7
  import AppContext from '@/contexts/AppContext';
6
- import { StoreType } from '@/types/store';
7
- import { IntegryAPI } from '@/modules/api';
8
- import { Input } from '@/components/Input';
8
+ import type { StoreType } from '@/types/store';
9
+ import type { IntegryAPI } from '@/modules/api';
9
10
  import { MultipurposeField } from '@/components/MultipurposeField';
10
11
  import { Loader } from '@/components/Loader';
11
12
  import { actionFunctions } from '@/store';
@@ -36,6 +37,8 @@ export type DynamicFieldsProps = {
36
37
  value?: any;
37
38
  isDisabled?: boolean;
38
39
  onChangeCallback?: (val: any) => void;
40
+ dependsOn?: string[];
41
+ isArray?: boolean;
39
42
  } & StoreType;
40
43
 
41
44
  interface DynamicDataItem {
@@ -55,8 +58,6 @@ interface ApiResponse {
55
58
  const DynamicTypedFields = (props: DynamicFieldsProps) => {
56
59
  const {
57
60
  dynamicField,
58
- endpointData,
59
- sourceFunctionData = {},
60
61
  sourceFlowIntegrataionInvocationUrl = '',
61
62
  selectedAuthId = '',
62
63
  dataSourceBody,
@@ -67,21 +68,57 @@ const DynamicTypedFields = (props: DynamicFieldsProps) => {
67
68
  onChange,
68
69
  isDisabled = false,
69
70
  onChangeCallback = () => null,
71
+ dependsOn = [],
72
+ isArray = false,
70
73
  } = props;
74
+
71
75
  const [dynamicItems, setDynamicItems] = useState<DynamicDataItem[]>([]);
72
76
  const [loading, setLoading] = useState<boolean>(true);
73
- const [customFieldsData, setCustomFieldsData] = useState<any>(
74
- value && value !== '' && typeof value === 'string' ? JSON.parse(value) : {},
75
- );
77
+
78
+ // For array mode, we'll track multiple sets of field data
79
+ const [fieldSets, setFieldSets] = useState<any[]>(() => {
80
+ if (value) {
81
+ const parsedValue = typeof value === 'string' ? JSON.parse(value) : value;
82
+
83
+ if (isArray) {
84
+ // If it's already an array, use it
85
+ if (Array.isArray(parsedValue)) {
86
+ return parsedValue;
87
+ }
88
+ // If it's an object, wrap it in an array
89
+ return [parsedValue];
90
+ }
91
+
92
+ // For non-array mode, we still initialize with a single object
93
+ return [parsedValue];
94
+ }
95
+ return [{}]; // Start with one empty object
96
+ });
97
+
98
+ // For backward compatibility, maintain customFieldsData for the first set
99
+ const [customFieldsData, setCustomFieldsData] = useState<any>(() => {
100
+ if (value) {
101
+ const parsedValue = typeof value === 'string' ? JSON.parse(value) : value;
102
+
103
+ if (isArray && Array.isArray(parsedValue) && parsedValue.length > 0) {
104
+ return parsedValue[0];
105
+ }
106
+
107
+ return parsedValue;
108
+ }
109
+ return {};
110
+ });
111
+
76
112
  const [
77
113
  isErrorOnLoadingCustomFields,
78
114
  setIsErrorOnLoadingCustomFields,
79
115
  ] = useState<boolean>(false);
80
116
 
81
117
  const context = useContext(AppContext);
82
- const isReadOnly = context?.isReadOnly;
83
118
  const apiHandlerRef = context?.apiHandler || props.apiHandler;
84
119
 
120
+ const fieldsHaveBeenFetched = useRef(false);
121
+
85
122
  const resolveValue = (path: string, item: any): any => {
86
123
  // If the path contains template tags, process it
87
124
  if (/{([^}]+)}/.test(path)) {
@@ -120,13 +157,13 @@ const DynamicTypedFields = (props: DynamicFieldsProps) => {
120
157
  if (Array.isArray(res)) {
121
158
  transformedItems = res;
122
159
  } else if (res.output && Array.isArray(res.output)) {
123
- const { idKeyPath, titleKeyPath, typeKeyPath } = props; // Assuming these are passed as part of dynamicField prop
160
+ const { idKeyPath, titleKeyPath, typeKeyPath } = props;
124
161
 
125
162
  if (idKeyPath && titleKeyPath) {
126
163
  transformedItems = res.output.map((item: any) => ({
127
- id: resolveValue(idKeyPath, item), // Use idKeyPath to access the ID
128
- title: resolveValue(titleKeyPath, item), // Use titleKeyPath to access the title
129
- type: 'TEXTFIELD', // Use typeKeyPath to access the type
164
+ id: resolveValue(idKeyPath, item),
165
+ title: resolveValue(titleKeyPath, item),
166
+ type: 'TEXTFIELD',
130
167
  }));
131
168
  } else {
132
169
  setDynamicItems([]);
@@ -149,25 +186,124 @@ const DynamicTypedFields = (props: DynamicFieldsProps) => {
149
186
  }
150
187
  };
151
188
 
189
+ // Effect for fetching fields
152
190
  useEffect(() => {
153
- if (value && Object.keys(value).length > 0) {
154
- setCustomFieldsData(value);
191
+ if (dependsOn.length > 0 && !parentFieldsChanged) {
192
+ setLoading(false);
193
+ setIsErrorOnLoadingCustomFields(false);
194
+ return;
195
+ }
196
+ if (!fieldsHaveBeenFetched.current || parentFieldsChanged) {
197
+ fetchDynamicFields();
198
+ fieldsHaveBeenFetched.current = true;
155
199
  }
156
- fetchDynamicFields();
157
200
  }, [parentFieldsChanged]);
158
201
 
159
- const onFieldChange = (id: string, val: string, type: string) => {
160
- const data = {
161
- ...customFieldsData,
162
- [id]: val,
163
- };
164
- setCustomFieldsData(data);
165
- if (onChange) {
166
- onChange(data);
167
- }
168
- if (onChangeCallback) {
169
- onChangeCallback(JSON.stringify(data));
202
+ // Effect for handling external value changes
203
+ useEffect(() => {
204
+ if (value && !loading) {
205
+ const parsedValue = typeof value === 'string' ? JSON.parse(value) : value;
206
+
207
+ if (isArray) {
208
+ // Handle array values
209
+ const newFieldSets = Array.isArray(parsedValue)
210
+ ? parsedValue
211
+ : [parsedValue];
212
+
213
+ // Update fieldSets
214
+ setFieldSets(newFieldSets);
215
+
216
+ // Update customFieldsData with the first item for backward compatibility
217
+ if (newFieldSets.length > 0) {
218
+ setCustomFieldsData(newFieldSets[0]);
219
+ }
220
+ } else {
221
+ // Handle single object value
222
+ setCustomFieldsData(parsedValue);
223
+ setFieldSets([parsedValue]);
224
+ }
170
225
  }
226
+ }, [value, loading, isArray]);
227
+
228
+ // Function to add a new empty set of fields
229
+ const addNewFieldSet = () => {
230
+ setFieldSets((prev) => {
231
+ const newFieldSets = [...prev, {}];
232
+
233
+ // Call parent callbacks with the updated array
234
+ if (onChange) {
235
+ onChange(newFieldSets);
236
+ }
237
+ if (onChangeCallback) {
238
+ onChangeCallback(JSON.stringify(newFieldSets));
239
+ }
240
+
241
+ return newFieldSets;
242
+ });
243
+ };
244
+
245
+ // Function to remove a field set
246
+ const removeFieldSet = (index: number) => {
247
+ if (fieldSets.length <= 1) return; // Don't remove the last set
248
+
249
+ setFieldSets((prev) => {
250
+ const newFieldSets = prev.filter((_, i) => i !== index);
251
+
252
+ // Update customFieldsData if we're removing the first set
253
+ if (index === 0 && newFieldSets.length > 0) {
254
+ setCustomFieldsData(newFieldSets[0]);
255
+ }
256
+
257
+ // Call parent callbacks with the updated array
258
+ if (onChange) {
259
+ onChange(newFieldSets);
260
+ }
261
+ if (onChangeCallback) {
262
+ onChangeCallback(JSON.stringify(newFieldSets));
263
+ }
264
+
265
+ return newFieldSets;
266
+ });
267
+ };
268
+
269
+ // Function to update a specific field in a specific set
270
+ const onFieldSetChange = (
271
+ setIndex: number,
272
+ fieldId: string,
273
+ val: string,
274
+ type: string,
275
+ ) => {
276
+ setFieldSets((prev) => {
277
+ const newFieldSets = [...prev];
278
+
279
+ // Create or update the field in the specified set
280
+ newFieldSets[setIndex] = {
281
+ ...newFieldSets[setIndex],
282
+ [fieldId]: val,
283
+ };
284
+
285
+ // If this is the first set, also update customFieldsData for backward compatibility
286
+ if (setIndex === 0) {
287
+ setCustomFieldsData(newFieldSets[0]);
288
+ }
289
+
290
+ // Call parent callbacks with the updated array
291
+ if (onChange) {
292
+ onChange(isArray ? newFieldSets : newFieldSets[0]);
293
+ }
294
+ if (onChangeCallback) {
295
+ onChangeCallback(
296
+ JSON.stringify(isArray ? newFieldSets : newFieldSets[0]),
297
+ );
298
+ }
299
+
300
+ return newFieldSets;
301
+ });
302
+ };
303
+
304
+ // For backward compatibility, maintain the original onFieldChange function
305
+ const onFieldChange = (id: string, val: string, type: string) => {
306
+ onFieldSetChange(0, id, val, type);
171
307
  };
172
308
 
173
309
  return html`
@@ -180,31 +316,111 @@ const DynamicTypedFields = (props: DynamicFieldsProps) => {
180
316
  Loading custom fields...
181
317
  </div> `
182
318
  : html`
183
- ${dynamicItems.map(
184
- (el) => html`
185
- <div class=${styles.dynamicTypedFieldWrapper}>
186
- <${MultipurposeField}
187
- id="${el.id}"
188
- title="${el.title}"
189
- placeholder="${dynamicField.placeholder}"
190
- isRequired="${dynamicField.is_required}"
191
- isEditable="${dynamicField.is_editable}"
192
- fieldId=${el.id}
193
- onChange=${(val: any) => {
194
- onFieldChange(el.id, val, el.type || 'string');
195
- }}
196
- value=${customFieldsData[el.id] || ''}
197
- activityOutputData=${activityOutputData}
198
- activityOutputDataRaw=${activityOutputDataRaw}
199
- type=${el.type || 'TEXTFIELD'}
200
- isMappable=${Object.entries(activityOutputData || {})
201
- .length > 0}
202
- allowTagsInText=${true}
203
- isDisabled=${isDisabled}
204
- ><//>
205
- </div>
206
- `,
207
- )}
319
+ ${isArray
320
+ ? dynamicItems.length > 0 &&
321
+ html`
322
+ ${fieldSets.map(
323
+ (fieldSet, setIndex) => html`
324
+ <div
325
+ class=${styles.dynamicTypedFieldSetContainer}
326
+ key=${`fieldset-${setIndex}`}
327
+ >
328
+ <div class=${styles.fieldSetHeader}>
329
+ <h4>${dynamicField.title} ${setIndex + 1}</h4>
330
+ ${setIndex > 0 &&
331
+ html`
332
+ <button
333
+ type="button"
334
+ class=${styles.removeButton}
335
+ onClick=${() => removeFieldSet(setIndex)}
336
+ disabled=${isDisabled}
337
+ >
338
+ Remove
339
+ </button>
340
+ `}
341
+ </div>
342
+ ${dynamicItems.map(
343
+ (el) => html`
344
+ <div
345
+ class=${styles.dynamicTypedFieldWrapper}
346
+ key=${`${setIndex}-${el.id}`}
347
+ >
348
+ <${MultipurposeField}
349
+ id="${`${setIndex}_${el.id}`}"
350
+ title="${el.title}"
351
+ placeholder="${dynamicField.placeholder}"
352
+ isRequired="${dynamicField.is_required}"
353
+ isEditable="${dynamicField.is_editable}"
354
+ fieldId=${`${setIndex}_${el.id}`}
355
+ onChange=${(val: any) => {
356
+ onFieldSetChange(
357
+ setIndex,
358
+ el.id,
359
+ val,
360
+ el.type || 'string',
361
+ );
362
+ }}
363
+ value=${fieldSet[el.id] || ''}
364
+ activityOutputData=${activityOutputData}
365
+ activityOutputDataRaw=${activityOutputDataRaw}
366
+ type=${el.type || 'TEXTFIELD'}
367
+ isMappable=${Object.entries(
368
+ activityOutputData || {},
369
+ ).length > 0}
370
+ allowTagsInText=${true}
371
+ isDisabled=${isDisabled}
372
+ ><//>
373
+ </div>
374
+ `,
375
+ )}
376
+ ${setIndex === fieldSets.length - 1 &&
377
+ html`
378
+ <div class=${styles.addButtonContainer}>
379
+ <button
380
+ type="button"
381
+ class=${styles.addButton}
382
+ onClick=${addNewFieldSet}
383
+ disabled=${isDisabled}
384
+ >
385
+ + Add Another ${dynamicField.title}
386
+ </button>
387
+ </div>
388
+ `}
389
+ </div>
390
+ `,
391
+ )}
392
+ `
393
+ : dynamicItems.length > 0 &&
394
+ html`
395
+ ${dynamicItems.map(
396
+ (el) => html`
397
+ <div
398
+ class=${styles.dynamicTypedFieldWrapper}
399
+ key=${el.id}
400
+ >
401
+ <${MultipurposeField}
402
+ id="${el.id}"
403
+ title="${el.title}"
404
+ placeholder="${dynamicField.placeholder}"
405
+ isRequired="${dynamicField.is_required}"
406
+ isEditable="${dynamicField.is_editable}"
407
+ fieldId=${el.id}
408
+ onChange=${(val: any) => {
409
+ onFieldChange(el.id, val, el.type || 'string');
410
+ }}
411
+ value=${customFieldsData[el.id] || ''}
412
+ activityOutputData=${activityOutputData}
413
+ activityOutputDataRaw=${activityOutputDataRaw}
414
+ type=${el.type || 'TEXTFIELD'}
415
+ isMappable=${Object.entries(activityOutputData || {})
416
+ .length > 0}
417
+ allowTagsInText=${true}
418
+ isDisabled=${isDisabled}
419
+ ><//>
420
+ </div>
421
+ `,
422
+ )}
423
+ `}
208
424
  `}
209
425
  ${!isErrorOnLoadingCustomFields
210
426
  ? ''
@@ -71,3 +71,65 @@ a.optionsRefresh {
71
71
  height: 37px;
72
72
  }
73
73
  }
74
+
75
+ // New styles for field sets
76
+ .dynamicTypedFieldSetContainer {
77
+ margin-bottom: 20px;
78
+ padding: 15px;
79
+ border: 1px solid #e0e0e0;
80
+ border-radius: 4px;
81
+ background-color: #f9f9f9;
82
+
83
+ .fieldSetHeader {
84
+ display: flex;
85
+ justify-content: space-between;
86
+ align-items: center;
87
+ margin-bottom: 10px;
88
+ padding-bottom: 10px;
89
+ border-bottom: 1px solid #e0e0e0;
90
+
91
+ h4 {
92
+ margin: 0;
93
+ font-size: 12px;
94
+ font-weight: 500;
95
+ }
96
+ }
97
+
98
+ .removeButton {
99
+ background-color: #f44336;
100
+ color: white;
101
+ border: none;
102
+ border-radius: 4px;
103
+ padding: 5px 10px;
104
+ cursor: pointer;
105
+ font-size: 12px;
106
+
107
+ &:hover {
108
+ background-color: #d32f2f;
109
+ }
110
+
111
+ &:disabled {
112
+ background-color: #cccccc;
113
+ cursor: not-allowed;
114
+ }
115
+ }
116
+
117
+ .addButtonContainer {
118
+ margin-top: 15px;
119
+ }
120
+
121
+ .addButton {
122
+ background-color: unset;
123
+ color: #007bff;
124
+ border: none;
125
+ border-radius: 4px;
126
+ padding: 8px 16px;
127
+ cursor: pointer;
128
+ font-size: 12px;
129
+
130
+ &:disabled {
131
+ background-color: #cccccc;
132
+ cursor: not-allowed;
133
+ }
134
+ }
135
+ }
@@ -52,6 +52,8 @@ interface ActionFormStateType {
52
52
  parentFields: any;
53
53
  parentFieldsChanged: boolean;
54
54
  aiAssistedFields: string[]; // New state to track fields with AI assist enabled
55
+ changedParentMachineName?: string; // New prop to track changes in parent machine name
56
+ parentChangeVersion?: number; // New prop to track changes in parent machine name
55
57
  }
56
58
 
57
59
  interface StepDataMapping {
@@ -78,6 +80,8 @@ class FunctionForm extends Component<
78
80
  parentFields: [],
79
81
  parentFieldsChanged: false,
80
82
  aiAssistedFields: [], // Initialize empty array for AI assisted fields
83
+ changedParentMachineName: '',
84
+ parentChangeVersion: 0,
81
85
  };
82
86
  }
83
87
 
@@ -411,7 +415,10 @@ class FunctionForm extends Component<
411
415
 
412
416
  // Check if the field is a parent field
413
417
  if (this.state.parentFields.includes(fieldId)) {
414
- this.setState({ parentFieldsChanged: !this.state.parentFieldsChanged });
418
+ this.setState({
419
+ changedParentMachineName: fieldId,
420
+ parentChangeVersion: Date.now(),
421
+ });
415
422
  }
416
423
 
417
424
  if (this.props.customSaveCallback) {
@@ -894,7 +901,12 @@ class FunctionForm extends Component<
894
901
  field.dataSourceBody,
895
902
  this.state.dynamicFieldDataState,
896
903
  )}
897
- parentFieldsChanged=${this.state.parentFieldsChanged}
904
+ parentFieldsChanged=${field.dependsOn.length > 0 &&
905
+ field.dependsOn.includes(
906
+ this.state.changedParentMachineName,
907
+ )
908
+ ? this.state.parentChangeVersion
909
+ : 0}
898
910
  activityOutputData=${arrayToNestedJSONWithFirstValue(
899
911
  JSONToActivityOutputData(this.props.variables || {}),
900
912
  JSONToDynamicFieldData(this.props.variables || {}),
@@ -904,6 +916,8 @@ class FunctionForm extends Component<
904
916
  )}
905
917
  value=${this.state.dynamicFieldDataState[field.id] || {}}
906
918
  isDisabled=${isAIAssisted}
919
+ dependsOn=${field.dependsOn}
920
+ isArray=${field.dataType === 'array'}
907
921
  />
908
922
  `,
909
923
  )}
package/src/index.ts CHANGED
@@ -609,7 +609,8 @@ export class IntegryJS {
609
609
  if (
610
610
  options.connectedAccountId &&
611
611
  !response.meta.app.connected_accounts.find(
612
- (account: any) => account.id === options.connectedAccountId,
612
+ (account: any) =>
613
+ Number(account.id) === Number(options.connectedAccountId),
613
614
  )
614
615
  ) {
615
616
  return reject(new Error('Connected account not found.'));