@integry/sdk 4.7.35 → 4.7.37

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.7.35",
3
+ "version": "4.7.37",
4
4
  "description": "Integry SDK",
5
5
  "main": "dist/umd/index.umd.js",
6
6
  "module": "dist/esm/index.csm.js",
@@ -2034,12 +2034,14 @@ class ActionForm extends Component<ActionFormPropsType, ActionFormStateType> {
2034
2034
  : null}
2035
2035
  showMenuOnLeft=${this.props
2036
2036
  .showMenuOnLeft || false}
2037
+ showFieldsLimit=${5}
2037
2038
  />
2038
2039
  </div>
2039
2040
  `;
2040
2041
  }
2041
2042
  case 'CUSTOM_FIELDS': {
2042
2043
  let fieldVal = null;
2044
+ let fieldMappedVal = null;
2043
2045
  let elParentFields = [];
2044
2046
  if (
2045
2047
  el.activity_field?.parent_fields &&
@@ -2066,6 +2068,20 @@ class ActionForm extends Component<ActionFormPropsType, ActionFormStateType> {
2066
2068
  } else {
2067
2069
  fieldVal = el.default_value;
2068
2070
  }
2071
+ } else {
2072
+ fieldMappedVal = this.props.stepDataMapping[
2073
+ selectedStep.id
2074
+ ]
2075
+ ? this.props.stepDataMapping[
2076
+ selectedStep.id
2077
+ ][el.id]
2078
+ : ({} as {
2079
+ objectValue: Record<
2080
+ string,
2081
+ string | number
2082
+ >;
2083
+ });
2084
+ fieldMappedVal = fieldMappedVal?.objectValue;
2069
2085
  }
2070
2086
 
2071
2087
  return html`
@@ -2079,6 +2095,7 @@ class ActionForm extends Component<ActionFormPropsType, ActionFormStateType> {
2079
2095
  }`}
2080
2096
  >
2081
2097
  <${DynamicTypedField}
2098
+ stepId=${selectedStep.id}
2082
2099
  dynamicField=${el}
2083
2100
  endpointData=${JSON.stringify({
2084
2101
  authorization_id:
@@ -2161,7 +2178,7 @@ class ActionForm extends Component<ActionFormPropsType, ActionFormStateType> {
2161
2178
  JSONToActivityOutputData(
2162
2179
  this.props.tagsTree || {},
2163
2180
  )}
2164
- value=${fieldVal}
2181
+ value=${fieldVal || fieldMappedVal}
2165
2182
  allowWorkspaceConnectedAccounts=${!!this
2166
2183
  .props.onFieldChangeCallback}
2167
2184
  />
@@ -1,10 +1,11 @@
1
+ 'use client';
2
+
1
3
  import { html } from 'htm/preact';
2
4
  import { connect } from 'unistore/preact';
3
5
  import { useContext, useEffect, useState } from 'preact/hooks';
4
- import { TemplateField, NestedObject } from '@/interfaces';
6
+ import type { TemplateField, NestedObject } from '@/interfaces';
5
7
  import AppContext from '@/contexts/AppContext';
6
- import { StoreType } from '@/types/store';
7
- // import { Input } from '@/components/Input';
8
+ import type { StoreType } from '@/types/store';
8
9
  import { Loader } from '@/components/Loader';
9
10
  import { MultipurposeField } from '@/components/MultipurposeField';
10
11
  import { actionFunctions } from '@/store';
@@ -30,6 +31,7 @@ export type DynamicFieldsProps = {
30
31
  objectValue?: any;
31
32
  tagsTree?: any;
32
33
  showMenuOnLeft?: boolean;
34
+ showFieldsLimit?: 'all' | number; // New prop
33
35
  } & StoreType;
34
36
 
35
37
  interface DynamicDataItem {
@@ -59,7 +61,9 @@ const DynamicFields = (props: DynamicFieldsProps) => {
59
61
  objectValue = null,
60
62
  tagsTree = null,
61
63
  showMenuOnLeft = false,
64
+ showFieldsLimit = 'all', // Default to 'all'
62
65
  } = props;
66
+
63
67
  const [dynamicItems, setDynamicItems] = useState<DynamicDataItem[]>([]);
64
68
  const [loading, setLoading] = useState<boolean>(true);
65
69
  const [customFieldsData, setCustomFieldsData] = useState<any>(
@@ -70,13 +74,19 @@ const DynamicFields = (props: DynamicFieldsProps) => {
70
74
  setIsErrorOnLoadingCustomFields,
71
75
  ] = useState<boolean>(false);
72
76
 
77
+ // New state for field limiting functionality
78
+ const [visibleFieldIds, setVisibleFieldIds] = useState<Set<string>>(
79
+ new Set(),
80
+ );
81
+ const [showFieldSelector, setShowFieldSelector] = useState<boolean>(false);
82
+ const [searchTerm, setSearchTerm] = useState<string>('');
83
+
73
84
  const context = useContext(AppContext);
74
85
  const isReadOnly = context?.isReadOnly;
75
86
 
76
87
  const fetchDynamicFields = async () => {
77
88
  const { activity_field } = dynamicField;
78
- // if configMode is true, then don't fetch dynamic items
79
- // fetch dynamic items from endpoint
89
+
80
90
  if (sourceFlowIntegrataionInvocationUrl && selectedAuthId) {
81
91
  setLoading(true);
82
92
  context?.apiHandler
@@ -138,6 +148,45 @@ const DynamicFields = (props: DynamicFieldsProps) => {
138
148
  }
139
149
  };
140
150
 
151
+ // Initialize visible fields based on showFieldsLimit and existing data
152
+ useEffect(() => {
153
+ if (dynamicItems.length > 0) {
154
+ const parsedVal = objectValue
155
+ ? customFieldsData
156
+ : (props.stepDataMapping[stepId]?.[dynamicField.id]
157
+ ?.objectValue as Record<string, string | number>) || {};
158
+
159
+ // Get fields that have existing values (for edit mode)
160
+ const fieldsWithValues = dynamicItems
161
+ .filter(
162
+ (item) =>
163
+ parsedVal && parsedVal[item.id] && parsedVal[item.id] !== '',
164
+ )
165
+ .map((item) => item.id);
166
+
167
+ if (showFieldsLimit === 'all') {
168
+ // Show all fields
169
+ setVisibleFieldIds(new Set(dynamicItems.map((item) => item.id)));
170
+ } else if (typeof showFieldsLimit === 'number') {
171
+ // Show limited fields + fields with existing values
172
+ const fieldsToShow = new Set(fieldsWithValues);
173
+
174
+ // Add additional fields up to the limit if needed
175
+ let count = fieldsWithValues.length;
176
+ dynamicItems.some((item) => {
177
+ if (count >= showFieldsLimit) return true;
178
+ if (!fieldsToShow.has(item.id)) {
179
+ fieldsToShow.add(item.id);
180
+ count += 1;
181
+ }
182
+ return false;
183
+ });
184
+
185
+ setVisibleFieldIds(fieldsToShow);
186
+ }
187
+ }
188
+ }, [dynamicItems, showFieldsLimit, customFieldsData, objectValue]);
189
+
141
190
  useEffect(() => {
142
191
  fetchDynamicFields();
143
192
  }, [parentFieldChanged]);
@@ -171,17 +220,12 @@ const DynamicFields = (props: DynamicFieldsProps) => {
171
220
  });
172
221
 
173
222
  if (onChangeCallback && objectValue !== null) {
174
- // Use the functional update form of setState to ensure we're working with the latest state
175
223
  setCustomFieldsData((prevData: any) => {
176
224
  const updatedData = {
177
225
  ...prevData,
178
226
  [machineName]: val,
179
227
  };
180
-
181
- // Call the callback with the updated data
182
228
  onChangeCallback(JSON.stringify(updatedData));
183
-
184
- // Return the updated data to set the state
185
229
  return updatedData;
186
230
  });
187
231
  } else {
@@ -194,54 +238,138 @@ const DynamicFields = (props: DynamicFieldsProps) => {
194
238
  }
195
239
  };
196
240
 
241
+ const handleFieldSelect = (fieldId: string) => {
242
+ setVisibleFieldIds((prev) => new Set([...prev, fieldId]));
243
+ setShowFieldSelector(false);
244
+ setSearchTerm('');
245
+ };
246
+
247
+ const getAvailableFields = () =>
248
+ dynamicItems.filter(
249
+ (item) =>
250
+ !visibleFieldIds.has(item.id) &&
251
+ item.title.toLowerCase().includes(searchTerm.toLowerCase()),
252
+ );
253
+
254
+ const getVisibleFields = () =>
255
+ dynamicItems.filter((item) => visibleFieldIds.has(item.id));
256
+
257
+ const shouldShowAddButton = () =>
258
+ showFieldsLimit !== 'all' &&
259
+ typeof showFieldsLimit === 'number' &&
260
+ visibleFieldIds.size < dynamicItems.length;
261
+
197
262
  return html`
198
- <div>
263
+ <div class=${styles.dynamicFieldsWrapper}>
199
264
  ${loading
200
265
  ? html`<div className=${styles.loadingText}>
201
266
  <div class=${styles.actionFormLoader}>
202
267
  <${Loader} />
203
268
  </div>
204
269
  Loading custom fields...
205
- </div> `
206
- : html` ${dynamicItems.map((el) => {
207
- const ele = props.stepDataMapping[stepId]
208
- ? props.stepDataMapping[stepId][dynamicField.id]
209
- : ({} as { objectValue: Record<string, string | number> });
210
- let fieldVal = '';
211
- const parsedVal = objectValue
212
- ? customFieldsData
213
- : (ele.objectValue as Record<string, string | number>);
214
- if (parsedVal && parsedVal[el.id]) {
215
- fieldVal = `${parsedVal[el.id]}`;
216
- }
217
- return html`
218
- <div class=${styles.dynamicFieldWrapper}>
219
- <${MultipurposeField}
220
- title=${el.title}
221
- placeholder=${getPlaceholder()}
222
- value=${fieldVal}
223
- onChange=${(val: string) => {
224
- onChange(el.id, val);
225
- }}
226
- isReadOnly=${isReadOnly}
227
- type="TEXTFIELD"
228
- isMappable=${isMappable}
229
- isEditable=${isEditable}
230
- allowTagsInText=${allowTagsInText}
231
- activityOutputData=${activityOutputData}
232
- activityOutputDataRaw=${activityOutputDataRaw}
233
- refreshRootStepData=${refreshRootStepData}
234
- fieldId=${el.id || ''}
235
- tagsTree=${tagsTree}
236
- showMenuOnLeft=${showMenuOnLeft}
237
- />
238
- </div>
239
- `;
240
- })}`}
270
+ </div>`
271
+ : html`
272
+ ${getVisibleFields().map((el) => {
273
+ const ele = props.stepDataMapping[stepId]
274
+ ? props.stepDataMapping[stepId][dynamicField.id]
275
+ : ({} as { objectValue: Record<string, string | number> });
276
+ let fieldVal = '';
277
+ const parsedVal = objectValue
278
+ ? customFieldsData
279
+ : (ele.objectValue as Record<string, string | number>);
280
+ if (parsedVal && parsedVal[el.id]) {
281
+ fieldVal = `${parsedVal[el.id]}`;
282
+ }
283
+ return html`
284
+ <div class=${styles.dynamicFieldWrapper}>
285
+ <${MultipurposeField}
286
+ title=${el.title}
287
+ placeholder=${getPlaceholder()}
288
+ value=${fieldVal}
289
+ onChange=${(val: string) => {
290
+ onChange(el.id, val);
291
+ }}
292
+ isReadOnly=${isReadOnly}
293
+ type="TEXTFIELD"
294
+ isMappable=${isMappable}
295
+ isEditable=${isEditable}
296
+ allowTagsInText=${allowTagsInText}
297
+ activityOutputData=${activityOutputData}
298
+ activityOutputDataRaw=${activityOutputDataRaw}
299
+ refreshRootStepData=${refreshRootStepData}
300
+ fieldId=${el.id || ''}
301
+ tagsTree=${tagsTree}
302
+ showMenuOnLeft=${showMenuOnLeft}
303
+ />
304
+ </div>
305
+ `;
306
+ })}
307
+ ${shouldShowAddButton() && !showFieldSelector
308
+ ? html`
309
+ <div class=${styles.addFieldButtonWrapper}>
310
+ <button
311
+ class=${styles.addFieldButton}
312
+ onClick=${() => setShowFieldSelector(true)}
313
+ type="button"
314
+ >
315
+ <span class=${styles.addIcon}>+</span>
316
+ Add Field Mapping
317
+ </button>
318
+ </div>
319
+ `
320
+ : ''}
321
+ ${showFieldSelector
322
+ ? html`
323
+ <div class=${styles.fieldSelectorWrapper}>
324
+ <div class=${styles.searchInputWrapper}>
325
+ <input
326
+ type="text"
327
+ class=${styles.searchInput}
328
+ placeholder="Search fields to add..."
329
+ value=${searchTerm}
330
+ onInput=${(e: Event) =>
331
+ setSearchTerm((e.target as HTMLInputElement).value)}
332
+ autofocus
333
+ />
334
+ <button
335
+ class=${styles.cancelButton}
336
+ onClick=${() => {
337
+ setShowFieldSelector(false);
338
+ setSearchTerm('');
339
+ }}
340
+ type="button"
341
+ >
342
+ ×
343
+ </button>
344
+ </div>
345
+ ${getAvailableFields().length > 0
346
+ ? html`
347
+ <div class=${styles.fieldDropdown}>
348
+ ${getAvailableFields().map(
349
+ (field) => html`
350
+ <div
351
+ class=${styles.fieldOption}
352
+ onClick=${() => handleFieldSelect(field.id)}
353
+ >
354
+ ${field.title}
355
+ </div>
356
+ `,
357
+ )}
358
+ </div>
359
+ `
360
+ : html`
361
+ <div class=${styles.noFieldsMessage}>
362
+ No matching fields found
363
+ </div>
364
+ `}
365
+ </div>
366
+ `
367
+ : ''}
368
+ `}
241
369
  ${!isErrorOnLoadingCustomFields
242
370
  ? ''
243
- : html`<span className=${styles.noOptions}
244
- >Could not load custom fields.
371
+ : html`<span className=${styles.noOptions}>
372
+ Could not load custom fields.
245
373
  ${false
246
374
  ? html``
247
375
  : html` <a
@@ -39,7 +39,7 @@
39
39
  .loadingText {
40
40
  font-weight: 400;
41
41
  font-size: 12px;
42
- line-height: 15px;
42
+ // line-height: 15px;
43
43
  margin-bottom: 12px;
44
44
  color: #999;
45
45
  display: flex;
@@ -65,3 +65,202 @@ a.optionsRefresh {
65
65
  cursor: pointer;
66
66
  text-decoration: none;
67
67
  }
68
+ .dynamicFieldsWrapper {
69
+ // Existing styles plus new ones for the field selector
70
+
71
+ .loadingText {
72
+ display: flex;
73
+ align-items: center;
74
+ gap: 8px;
75
+ color: #666;
76
+ font-size: 12px;
77
+ }
78
+
79
+ .actionFormLoader {
80
+ display: inline-block;
81
+ }
82
+
83
+ .dynamicFieldWrapper {
84
+ margin-bottom: 16px;
85
+ }
86
+
87
+ .noOptions {
88
+ color: #e74c3c;
89
+ font-size: 12px;
90
+ }
91
+
92
+ .optionsRefresh {
93
+ color: #3498db;
94
+ text-decoration: none;
95
+
96
+ &:hover {
97
+ text-decoration: underline;
98
+ }
99
+ }
100
+
101
+ // New styles for field limiting functionality
102
+ .addFieldButtonWrapper {
103
+ margin-top: 16px;
104
+ margin-bottom: 16px;
105
+ }
106
+
107
+ .addFieldButton {
108
+ display: flex;
109
+ align-items: center;
110
+ gap: 8px;
111
+ padding: 12px 16px;
112
+ background: transparent;
113
+ border: 2px dashed #d1d5db;
114
+ border-radius: 2px;
115
+ color: #6b7280;
116
+ font-size: 12px;
117
+ font-weight: 500;
118
+ cursor: pointer;
119
+ transition: all 0.2s ease;
120
+ width: 100%;
121
+ justify-content: center;
122
+
123
+ &:hover {
124
+ border-color: #9ca3af;
125
+ color: #4b5563;
126
+ background: #f9fafb;
127
+ }
128
+
129
+ &:focus {
130
+ outline: none;
131
+ border-color: #3b82f6;
132
+ color: #3b82f6;
133
+ }
134
+ }
135
+
136
+ .addIcon {
137
+ font-size: 16px;
138
+ font-weight: bold;
139
+ }
140
+
141
+ .fieldSelectorWrapper {
142
+ position: relative;
143
+ margin-top: 16px;
144
+ margin-bottom: 16px;
145
+
146
+ &.dropdownTop {
147
+ // When dropdown opens upward, we need extra space above
148
+ margin-top: 220px; // Max dropdown height + buffer
149
+ margin-bottom: 16px;
150
+ }
151
+ }
152
+
153
+ .searchInputWrapper {
154
+ position: relative;
155
+ display: flex;
156
+ align-items: center;
157
+ }
158
+
159
+ .searchInput {
160
+ width: 100%;
161
+ padding: 12px 16px;
162
+ padding-right: 40px;
163
+ border: 1px solid #d1d5db;
164
+ border-radius: 2px;
165
+ font-size: 12px;
166
+ background: white;
167
+
168
+ &:focus {
169
+ outline: none;
170
+ border-color: #3b82f6;
171
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
172
+ }
173
+
174
+ &::placeholder {
175
+ color: #9ca3af;
176
+ }
177
+ }
178
+
179
+ .cancelButton {
180
+ position: absolute;
181
+ right: 12px;
182
+ background: none;
183
+ border: none;
184
+ font-size: 18px;
185
+ color: #9ca3af;
186
+ cursor: pointer;
187
+ padding: 0;
188
+ width: 20px;
189
+ height: 20px;
190
+ display: flex;
191
+ align-items: center;
192
+ justify-content: center;
193
+
194
+ &:hover {
195
+ color: #6b7280;
196
+ }
197
+ }
198
+
199
+ .fieldDropdown {
200
+ // position: absolute;
201
+ top: 100%;
202
+ left: 0;
203
+ right: 0;
204
+ background: white;
205
+ border: 1px solid #d1d5db;
206
+ border-radius: 2px;
207
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
208
+ 0 2px 4px -1px rgba(0, 0, 0, 0.06);
209
+ max-height: 200px;
210
+ overflow-y: auto;
211
+ z-index: 1000;
212
+ margin-top: 4px;
213
+
214
+ &.fieldDropdownTop {
215
+ top: auto;
216
+ bottom: 100%;
217
+ margin-top: 0;
218
+ margin-bottom: 4px;
219
+ }
220
+ }
221
+
222
+ .fieldOption {
223
+ padding: 12px 16px;
224
+ cursor: pointer;
225
+ font-size: 11px;
226
+ border-bottom: 1px solid #f3f4f6;
227
+
228
+ &:last-child {
229
+ border-bottom: none;
230
+ }
231
+
232
+ &:hover {
233
+ background: #f9fafb;
234
+ }
235
+
236
+ &:active {
237
+ background: #f3f4f6;
238
+ }
239
+ }
240
+
241
+ .noFieldsMessage {
242
+ padding: 16px;
243
+ text-align: center;
244
+ color: #9ca3af;
245
+ font-size: 12px;
246
+ font-style: italic;
247
+ position: absolute;
248
+ top: 100%;
249
+ left: 0;
250
+ right: 0;
251
+ background: white;
252
+ border: 1px solid #d1d5db;
253
+ border-radius: 2px;
254
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
255
+ 0 2px 4px -1px rgba(0, 0, 0, 0.06);
256
+ z-index: 1000;
257
+ margin-top: 4px;
258
+
259
+ &.noFieldsMessageTop {
260
+ top: auto;
261
+ bottom: 100%;
262
+ margin-top: 0;
263
+ margin-bottom: 4px;
264
+ }
265
+ }
266
+ }
@@ -62,6 +62,7 @@ interface ApiResponse {
62
62
 
63
63
  const DynamicTypedFields = (props: DynamicFieldsProps) => {
64
64
  const {
65
+ stepId,
65
66
  dynamicField,
66
67
  sourceFlowIntegrataionInvocationUrl = '',
67
68
  selectedAuthId = '',
@@ -314,6 +315,13 @@ const DynamicTypedFields = (props: DynamicFieldsProps) => {
314
315
  onChangeCallback(
315
316
  JSON.stringify(isArray ? newFieldSets : newFieldSets[0]),
316
317
  );
318
+ } else {
319
+ props.setStepMappingData({
320
+ stepId,
321
+ fieldId: dynamicField.id,
322
+ id: fieldId,
323
+ value: val,
324
+ });
317
325
  }
318
326
 
319
327
  return newFieldSets;