@ram_28/kf-ai-sdk 1.0.19 → 1.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +45 -12
  2. package/dist/api/client.d.ts.map +1 -1
  3. package/dist/api/datetime.d.ts +59 -10
  4. package/dist/api/datetime.d.ts.map +1 -1
  5. package/dist/api/index.d.ts +3 -2
  6. package/dist/api/index.d.ts.map +1 -1
  7. package/dist/api.cjs +1 -1
  8. package/dist/api.d.ts +1 -1
  9. package/dist/api.d.ts.map +1 -1
  10. package/dist/api.mjs +43 -21
  11. package/dist/api.types.d.ts +2 -1
  12. package/dist/api.types.d.ts.map +1 -1
  13. package/dist/auth/AuthProvider.d.ts.map +1 -1
  14. package/dist/auth.cjs +1 -1
  15. package/dist/auth.mjs +34 -34
  16. package/dist/base-types.d.ts +1 -1
  17. package/dist/base-types.d.ts.map +1 -1
  18. package/dist/client-BIkaIr2y.js +217 -0
  19. package/dist/client-DxjRcEtN.cjs +1 -0
  20. package/dist/components/hooks/useFilter/types.d.ts +14 -11
  21. package/dist/components/hooks/useFilter/types.d.ts.map +1 -1
  22. package/dist/components/hooks/useFilter/useFilter.d.ts +1 -1
  23. package/dist/components/hooks/useFilter/useFilter.d.ts.map +1 -1
  24. package/dist/components/hooks/useForm/apiClient.d.ts +45 -4
  25. package/dist/components/hooks/useForm/apiClient.d.ts.map +1 -1
  26. package/dist/components/hooks/useForm/useForm.d.ts.map +1 -1
  27. package/dist/components/hooks/useKanban/types.d.ts +5 -22
  28. package/dist/components/hooks/useKanban/types.d.ts.map +1 -1
  29. package/dist/components/hooks/useKanban/useKanban.d.ts.map +1 -1
  30. package/dist/components/hooks/useTable/types.d.ts +19 -31
  31. package/dist/components/hooks/useTable/types.d.ts.map +1 -1
  32. package/dist/components/hooks/useTable/useTable.d.ts.map +1 -1
  33. package/dist/error-handling-CAoD0Kwb.cjs +1 -0
  34. package/dist/error-handling-CrhTtD88.js +14 -0
  35. package/dist/filter.cjs +1 -1
  36. package/dist/filter.mjs +1 -1
  37. package/dist/form.cjs +1 -1
  38. package/dist/form.mjs +736 -750
  39. package/dist/index.d.ts +18 -0
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/kanban.cjs +2 -2
  42. package/dist/kanban.mjs +333 -323
  43. package/dist/{metadata-0lZAfuTP.cjs → metadata-Bz8zJqC1.cjs} +1 -1
  44. package/dist/{metadata-B88D_pVS.js → metadata-VbQzyD2C.js} +1 -1
  45. package/dist/table.cjs +1 -1
  46. package/dist/table.mjs +113 -96
  47. package/dist/table.types.d.ts +1 -1
  48. package/dist/table.types.d.ts.map +1 -1
  49. package/dist/types/base-fields.d.ts +71 -17
  50. package/dist/types/base-fields.d.ts.map +1 -1
  51. package/dist/types/common.d.ts +26 -18
  52. package/dist/types/common.d.ts.map +1 -1
  53. package/dist/useFilter-DzpP_ag0.cjs +1 -0
  54. package/dist/useFilter-H5bgAZQF.js +120 -0
  55. package/dist/utils/api/buildListOptions.d.ts +43 -0
  56. package/dist/utils/api/buildListOptions.d.ts.map +1 -0
  57. package/dist/utils/api/index.d.ts +2 -0
  58. package/dist/utils/api/index.d.ts.map +1 -0
  59. package/dist/utils/error-handling.d.ts +41 -0
  60. package/dist/utils/error-handling.d.ts.map +1 -0
  61. package/dist/utils/index.d.ts +2 -0
  62. package/dist/utils/index.d.ts.map +1 -1
  63. package/docs/QUICK_REFERENCE.md +142 -420
  64. package/docs/useAuth.md +52 -340
  65. package/docs/useFilter.md +858 -162
  66. package/docs/useForm.md +712 -501
  67. package/docs/useKanban.md +534 -279
  68. package/docs/useTable.md +725 -214
  69. package/package.json +1 -1
  70. package/sdk/api/client.ts +3 -41
  71. package/sdk/api/datetime.ts +98 -14
  72. package/sdk/api/index.ts +12 -6
  73. package/sdk/api.ts +6 -3
  74. package/sdk/api.types.ts +3 -4
  75. package/sdk/auth/AuthProvider.tsx +22 -24
  76. package/sdk/base-types.ts +2 -0
  77. package/sdk/components/hooks/useFilter/types.ts +14 -11
  78. package/sdk/components/hooks/useFilter/useFilter.ts +20 -18
  79. package/sdk/components/hooks/useForm/apiClient.ts +120 -5
  80. package/sdk/components/hooks/useForm/useForm.ts +97 -61
  81. package/sdk/components/hooks/useKanban/types.ts +7 -23
  82. package/sdk/components/hooks/useKanban/useKanban.ts +54 -18
  83. package/sdk/components/hooks/useTable/types.ts +26 -32
  84. package/sdk/components/hooks/useTable/useTable.llm.txt +8 -22
  85. package/sdk/components/hooks/useTable/useTable.ts +70 -25
  86. package/sdk/index.ts +157 -10
  87. package/sdk/table.types.ts +3 -0
  88. package/sdk/types/base-fields.ts +71 -17
  89. package/sdk/types/common.ts +33 -19
  90. package/sdk/utils/api/buildListOptions.ts +120 -0
  91. package/sdk/utils/api/index.ts +2 -0
  92. package/sdk/utils/error-handling.ts +150 -0
  93. package/sdk/utils/index.ts +6 -0
  94. package/dist/client-DgtkT50N.cjs +0 -1
  95. package/dist/client-V-WzUb8H.js +0 -237
  96. package/dist/useFilter-Dofowpr_.cjs +0 -1
  97. package/dist/useFilter-Dv-mr9QW.js +0 -117
@@ -5,6 +5,7 @@
5
5
 
6
6
  import { api, getBdoSchema } from "../../../api";
7
7
  import type { BDOSchemaType, FormOperationType, SubmissionResultType } from "./types";
8
+ import { toError } from "../../../utils/error-handling";
8
9
 
9
10
  // ============================================================
10
11
  // SCHEMA FETCHING
@@ -47,7 +48,7 @@ export async function fetchFormSchemaWithRetry(
47
48
  try {
48
49
  return await fetchFormSchema(source);
49
50
  } catch (error) {
50
- lastError = error as Error;
51
+ lastError = toError(error);
51
52
 
52
53
  if (attempt < maxRetries) {
53
54
  // Wait before retrying (exponential backoff)
@@ -134,7 +135,62 @@ export async function submitFormData<T = any>(
134
135
  // ============================================================
135
136
 
136
137
  /**
137
- * Fetch reference field data
138
+ * Fetch reference field data using the fetchField API
139
+ * This correctly uses instance context for filter evaluation
140
+ */
141
+ export async function fetchReferenceFieldData(
142
+ boId: string,
143
+ instanceId: string,
144
+ fieldId: string
145
+ ): Promise<Array<{ Value: string; Label: string }>> {
146
+ try {
147
+ // Calls GET /{boId}/{instanceId}/field/{fieldId}/fetch
148
+ return await api(boId).fetchField(instanceId, fieldId);
149
+ } catch (error) {
150
+ console.error(`Reference data fetch error for ${fieldId}:`, error);
151
+ return [];
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Fetch all reference data for a form
157
+ * @param boId - The Business Object ID
158
+ * @param instanceId - The instance ID (draftId for create, recordId for update)
159
+ * @param referenceFieldIds - Array of reference field IDs to fetch
160
+ */
161
+ export async function fetchAllReferenceFieldData(
162
+ boId: string,
163
+ instanceId: string,
164
+ referenceFieldIds: string[]
165
+ ): Promise<Record<string, Array<{ Value: string; Label: string }>>> {
166
+ const referenceData: Record<string, Array<{ Value: string; Label: string }>> = {};
167
+
168
+ // Fetch all reference data in parallel
169
+ const fetchPromises = referenceFieldIds.map(async (fieldId) => {
170
+ try {
171
+ const data = await fetchReferenceFieldData(boId, instanceId, fieldId);
172
+ return [fieldId, data] as const;
173
+ } catch (error) {
174
+ console.warn(`Failed to fetch reference data for ${fieldId}:`, error);
175
+ return [fieldId, []] as const;
176
+ }
177
+ });
178
+
179
+ const results = await Promise.allSettled(fetchPromises);
180
+
181
+ results.forEach((result) => {
182
+ if (result.status === "fulfilled") {
183
+ const [fieldId, data] = result.value;
184
+ referenceData[fieldId] = [...data];
185
+ }
186
+ });
187
+
188
+ return referenceData;
189
+ }
190
+
191
+ /**
192
+ * @deprecated Use fetchReferenceFieldData instead
193
+ * Legacy function that fetches reference data using list() API
138
194
  */
139
195
  export async function fetchReferenceData(
140
196
  businessObject: string,
@@ -170,7 +226,8 @@ export async function fetchReferenceData(
170
226
  }
171
227
 
172
228
  /**
173
- * Fetch all reference data for a schema
229
+ * @deprecated Use fetchAllReferenceFieldData instead
230
+ * Legacy function that fetches all reference data using list() API
174
231
  */
175
232
  export async function fetchAllReferenceData(
176
233
  referenceFields: Record<string, any>
@@ -238,18 +295,26 @@ export function validateFormData<T>(
238
295
  * Clean form data before submission
239
296
  * - For create: returns all non-computed, non-undefined fields
240
297
  * - For update: returns only fields that changed from originalData
298
+ * - Transforms Reference fields to JSON format expected by backend
299
+ *
300
+ * @param data - Form data to clean
301
+ * @param computedFields - Array of computed field names to exclude
302
+ * @param operation - "create" or "update"
303
+ * @param originalData - Original data for comparison (update only)
304
+ * @param fieldTypes - Map of field names to their BDO types (e.g., { "SupplierInfo": "Reference" })
241
305
  */
242
306
  export function cleanFormData<T>(
243
307
  data: Partial<T>,
244
308
  computedFields: string[],
245
309
  operation: FormOperationType = "create",
246
- originalData?: Partial<T>
310
+ originalData?: Partial<T>,
311
+ fieldTypes?: Record<string, string>
247
312
  ): Partial<T> {
248
313
  const cleanedData: Partial<T> = {};
249
314
 
250
315
  Object.keys(data).forEach((key) => {
251
316
  const fieldKey = key as keyof T;
252
- const value = data[fieldKey];
317
+ let value = data[fieldKey];
253
318
 
254
319
  // Skip computed fields
255
320
  if (computedFields.includes(key)) {
@@ -261,6 +326,12 @@ export function cleanFormData<T>(
261
326
  return;
262
327
  }
263
328
 
329
+ // Transform Reference fields to JSON format expected by backend
330
+ // Backend expects: {"_id": "...", "_name": "..."} or a JSON string that parses to this
331
+ if (fieldTypes?.[key] === "Reference" && value !== null) {
332
+ value = transformReferenceValue(value) as any;
333
+ }
334
+
264
335
  // For create: include all non-computed, non-undefined fields
265
336
  if (operation === "create") {
266
337
  cleanedData[fieldKey] = value;
@@ -288,6 +359,50 @@ export function cleanFormData<T>(
288
359
  return cleanedData;
289
360
  }
290
361
 
362
+ /**
363
+ * Transform a Reference/Lookup field value to the format expected by backend
364
+ *
365
+ * Backend expects Reference fields as either:
366
+ * - A dict with at minimum {"_id": "..."}
367
+ * - For Lookup fields, the full referenced record can be passed
368
+ * - A JSON string that parses to this format
369
+ *
370
+ * This function handles various input formats:
371
+ * - Plain string ID: "SUPP-001" → {"_id": "SUPP-001"}
372
+ * - Object with _id: preserves ALL properties (for lookup fields with full data)
373
+ * - JSON string: '{"_id": "..."}' → parsed and validated
374
+ */
375
+ export function transformReferenceValue(value: any): Record<string, any> | null {
376
+ if (value === null || value === undefined || value === "") {
377
+ return null;
378
+ }
379
+
380
+ // Already an object with _id - preserve ALL properties (for lookup fields with full data)
381
+ if (typeof value === "object" && value._id) {
382
+ return { ...value };
383
+ }
384
+
385
+ // String value - could be plain ID or JSON string
386
+ if (typeof value === "string") {
387
+ // Try to parse as JSON first
388
+ try {
389
+ const parsed = JSON.parse(value);
390
+ if (typeof parsed === "object" && parsed._id) {
391
+ return { ...parsed };
392
+ }
393
+ // Parsed but not a valid reference object - treat original as ID
394
+ } catch {
395
+ // Not valid JSON - treat as plain ID string
396
+ }
397
+
398
+ // Plain string ID
399
+ return { _id: value };
400
+ }
401
+
402
+ // Unknown format - return as-is (let backend handle/reject it)
403
+ return value;
404
+ }
405
+
291
406
  // ============================================================
292
407
  // ERROR HANDLING
293
408
  // ============================================================
@@ -16,19 +16,20 @@ import type {
16
16
  FormFieldConfigType,
17
17
  } from "./types";
18
18
 
19
- import { processSchema, extractReferenceFields } from "./schemaParser.utils";
19
+ import { processSchema } from "./schemaParser.utils";
20
20
 
21
21
  import {
22
22
  fetchFormSchemaWithCache,
23
23
  fetchRecord,
24
24
  submitFormData,
25
- fetchAllReferenceData,
26
25
  cleanFormData,
26
+ transformReferenceValue,
27
27
  } from "./apiClient";
28
28
 
29
29
  import { api } from "../../../api";
30
30
 
31
31
  import { validateCrossField } from "./expressionValidator.utils";
32
+ import { toError } from "../../../utils/error-handling";
32
33
  import {
33
34
  validateFieldOptimized,
34
35
  getFieldDependencies,
@@ -39,7 +40,7 @@ import {
39
40
  // ============================================================
40
41
 
41
42
  export function useForm<T extends Record<string, any> = Record<string, any>>(
42
- options: UseFormOptionsType<T>
43
+ options: UseFormOptionsType<T>,
43
44
  ): UseFormReturnType<T> {
44
45
  const {
45
46
  source,
@@ -62,9 +63,13 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
62
63
  // STATE MANAGEMENT
63
64
  // ============================================================
64
65
 
65
- const [schemaConfig, setSchemaConfig] =
66
- useState<FormSchemaConfigType | null>(null);
67
- const [referenceData, setReferenceData] = useState<Record<string, any[]>>({});
66
+ const [schemaConfig, setSchemaConfig] = useState<FormSchemaConfigType | null>(
67
+ null,
68
+ );
69
+ // Reference data for cross-field validation - populated lazily by UI components if needed
70
+ const [referenceData, _setReferenceData] = useState<Record<string, any[]>>(
71
+ {},
72
+ );
68
73
  const [isSubmitting, setIsSubmitting] = useState(false);
69
74
  const [lastFormValues] = useState<Partial<T>>({});
70
75
 
@@ -175,20 +180,14 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
175
180
  const processed = processSchema(
176
181
  schema as any,
177
182
  {}, // Pass empty object - validation functions get live values from react-hook-form
178
- userRole
183
+ userRole,
179
184
  );
180
185
  setSchemaConfig(processed);
181
-
182
- // Fetch reference data for reference fields
183
- const refFields = extractReferenceFields(processed);
184
- if (Object.keys(refFields).length > 0) {
185
- fetchAllReferenceData(refFields)
186
- .then(setReferenceData)
187
- .catch(console.warn);
188
- }
186
+ // Reference data is fetched lazily by UI components when dropdowns are opened
187
+ // using the fetchField API with proper instance context (draftId/recordId)
189
188
  } catch (error) {
190
189
  console.error("Schema processing failed:", error);
191
- onSchemaErrorRef.current?.(error as Error);
190
+ onSchemaErrorRef.current?.(toError(error));
192
191
  }
193
192
  }
194
193
  }, [schema, userRole]); // Removed onSchemaError - using ref instead
@@ -213,7 +212,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
213
212
  !schemaConfig ||
214
213
  !enabled ||
215
214
  draftId ||
216
- draftCreationStartedRef.current // Prevent duplicate calls in React strict mode
215
+ draftCreationStartedRef.current // Prevent duplicate calls in React strict mode
217
216
  ) {
218
217
  return;
219
218
  }
@@ -221,6 +220,9 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
221
220
  // Mark as started immediately to prevent duplicate calls
222
221
  draftCreationStartedRef.current = true;
223
222
 
223
+ // Track if effect is still active (for cleanup/race condition handling)
224
+ let isActive = true;
225
+
224
226
  const createInitialDraft = async () => {
225
227
  setIsCreatingDraft(true);
226
228
  setDraftError(null);
@@ -230,6 +232,9 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
230
232
  // Call PATCH /{bdo_id}/draft with empty payload to get draft ID
231
233
  const response = await client.draftInteraction({});
232
234
 
235
+ // Check if effect is still active before setting state
236
+ if (!isActive) return;
237
+
233
238
  // Store the draft ID
234
239
  setDraftId(response._id);
235
240
 
@@ -249,16 +254,27 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
249
254
  });
250
255
  }
251
256
  } catch (error) {
257
+ // Check if effect is still active before setting state
258
+ if (!isActive) return;
259
+
252
260
  console.error("Failed to create initial draft:", error);
253
- setDraftError(error as Error);
261
+ setDraftError(toError(error));
254
262
  // Reset the ref on error so it can be retried
255
263
  draftCreationStartedRef.current = false;
256
264
  } finally {
257
- setIsCreatingDraft(false);
265
+ // Check if effect is still active before setting state
266
+ if (isActive) {
267
+ setIsCreatingDraft(false);
268
+ }
258
269
  }
259
270
  };
260
271
 
261
272
  createInitialDraft();
273
+
274
+ // Cleanup function to handle unmount during async operation
275
+ return () => {
276
+ isActive = false;
277
+ };
262
278
  }, [isInteractiveMode, operation, schemaConfig, enabled, draftId, source]);
263
279
  // Note: rhfForm removed from deps - we use ref pattern to avoid dependency loops
264
280
 
@@ -298,7 +314,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
298
314
  const field = schemaConfig.fields[fieldName];
299
315
  if (field._bdoField.Formula) {
300
316
  const fieldDeps = getFieldDependencies(
301
- field._bdoField.Formula.ExpressionTree
317
+ field._bdoField.Formula.ExpressionTree,
302
318
  );
303
319
  fieldDeps.forEach((dep) => {
304
320
  // Only add non-computed fields as dependencies
@@ -331,12 +347,14 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
331
347
  }
332
348
 
333
349
  // Determine if draft should be triggered based on interaction mode
334
- // Interactive mode: Always trigger draft API on blur
350
+ // For update mode, always behave as non-interactive (only trigger for computed deps)
351
+ // Interactive mode (create only): Always trigger draft API on blur
335
352
  // Non-interactive mode: Only trigger for computed field dependencies
336
- const shouldTrigger = isInteractiveMode
337
- ? true // Interactive mode: always trigger
338
- : (computedFieldDependencies.length > 0 &&
339
- computedFieldDependencies.includes(fieldName as Path<T>));
353
+ const shouldTrigger =
354
+ isInteractiveMode && operation !== "update"
355
+ ? true // Interactive mode (create only): always trigger
356
+ : computedFieldDependencies.length > 0 &&
357
+ computedFieldDependencies.includes(fieldName as Path<T>);
340
358
 
341
359
  if (!shouldTrigger) {
342
360
  return;
@@ -398,7 +416,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
398
416
 
399
417
  // Get computed field names to exclude from payload
400
418
  const computedFieldNames = new Set(
401
- schemaConfig.computedFields || []
419
+ schemaConfig.computedFields || [],
402
420
  );
403
421
 
404
422
  // Find fields that changed from baseline (excluding computed fields)
@@ -406,7 +424,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
406
424
  // Skip _id and computed fields
407
425
  if (key === "_id" || computedFieldNames.has(key)) return;
408
426
 
409
- const currentValue = (currentValues as any)[key];
427
+ let currentValue = (currentValues as any)[key];
410
428
  const baselineValue = (baseline as any)[key];
411
429
 
412
430
  // Include if value has changed (using JSON.stringify for deep comparison)
@@ -419,6 +437,11 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
419
437
  currentValue !== undefined;
420
438
 
421
439
  if (hasChanged && isNonEmpty) {
440
+ // Transform Reference fields to format expected by backend
441
+ const fieldConfig = schemaConfig.fields[key];
442
+ if (fieldConfig?._bdoField?.Type === "Reference") {
443
+ currentValue = transformReferenceValue(currentValue);
444
+ }
422
445
  (changedFields as any)[key] = currentValue;
423
446
  }
424
447
  });
@@ -446,7 +469,10 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
446
469
  let computedFieldsResponse;
447
470
  if (operation === "update" && recordId) {
448
471
  // Update mode: use draftPatch (both interactive and non-interactive)
449
- computedFieldsResponse = await client.draftPatch(recordId, payload);
472
+ computedFieldsResponse = await client.draftPatch(
473
+ recordId,
474
+ payload,
475
+ );
450
476
  } else if (isInteractiveMode && draftId) {
451
477
  // Interactive create: use draftInteraction with _id
452
478
  computedFieldsResponse = await client.draftInteraction(payload);
@@ -469,7 +495,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
469
495
  shouldValidate: false,
470
496
  });
471
497
  }
472
- }
498
+ },
473
499
  );
474
500
 
475
501
  // Update baseline with computed fields from successful API response
@@ -478,7 +504,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
478
504
  if (computedFieldNames.has(fieldName)) {
479
505
  (lastSyncedValuesRef.current as any)[fieldName] = value;
480
506
  }
481
- }
507
+ },
482
508
  );
483
509
  }
484
510
  } catch (error) {
@@ -507,7 +533,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
507
533
  computedFieldDependencies,
508
534
  isInteractiveMode,
509
535
  draftId,
510
- ]
536
+ ],
511
537
  );
512
538
 
513
539
  // ============================================================
@@ -529,18 +555,16 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
529
555
 
530
556
  // Cross-field validation
531
557
  // Transform ValidationRule[] to the format expected by validateCrossField
532
- const transformedRules = schemaConfig.crossFieldValidation.map(
533
- (rule) => ({
534
- Id: rule.Id,
535
- Condition: { ExpressionTree: rule.ExpressionTree },
536
- Message: rule.Message || `Validation failed for ${rule.Name}`,
537
- })
538
- );
558
+ const transformedRules = schemaConfig.crossFieldValidation.map((rule) => ({
559
+ Id: rule.Id,
560
+ Condition: { ExpressionTree: rule.ExpressionTree },
561
+ Message: rule.Message || `Validation failed for ${rule.Name}`,
562
+ }));
539
563
 
540
564
  const crossFieldErrors = validateCrossField(
541
565
  transformedRules,
542
566
  values as any,
543
- referenceData
567
+ referenceData,
544
568
  );
545
569
 
546
570
  if (crossFieldErrors.length > 0) {
@@ -579,11 +603,14 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
579
603
  */
580
604
  const handleSubmit = useCallback(
581
605
  (
582
- onSuccess?: (data: T, e?: React.BaseSyntheticEvent) => void | Promise<void>,
606
+ onSuccess?: (
607
+ data: T,
608
+ e?: React.BaseSyntheticEvent,
609
+ ) => void | Promise<void>,
583
610
  onError?: (
584
611
  error: import("react-hook-form").FieldErrors<T> | Error,
585
- e?: React.BaseSyntheticEvent
586
- ) => void | Promise<void>
612
+ e?: React.BaseSyntheticEvent,
613
+ ) => void | Promise<void>,
587
614
  ) => {
588
615
  return rhfForm.handleSubmit(
589
616
  // RHF onValid handler - validation passed, now do cross-field + API
@@ -603,13 +630,13 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
603
630
  Id: rule.Id,
604
631
  Condition: { ExpressionTree: rule.ExpressionTree },
605
632
  Message: rule.Message || `Validation failed for ${rule.Name}`,
606
- })
633
+ }),
607
634
  );
608
635
 
609
636
  const crossFieldErrors = validateCrossField(
610
637
  transformedRules,
611
638
  data as any,
612
- referenceData
639
+ referenceData,
613
640
  );
614
641
 
615
642
  if (crossFieldErrors.length > 0) {
@@ -625,12 +652,21 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
625
652
  return;
626
653
  }
627
654
 
655
+ // Extract field types from schema for Reference field transformation
656
+ const fieldTypes: Record<string, string> = {};
657
+ Object.entries(schemaConfig.fields).forEach(
658
+ ([fieldName, field]) => {
659
+ fieldTypes[fieldName] = field._bdoField.Type;
660
+ },
661
+ );
662
+
628
663
  // Clean data for submission
629
664
  const cleanedData = cleanFormData(
630
665
  data as any,
631
666
  schemaConfig.computedFields,
632
667
  operation,
633
- recordData as Partial<T> | undefined
668
+ recordData as Partial<T> | undefined,
669
+ fieldTypes,
634
670
  );
635
671
 
636
672
  let result;
@@ -643,7 +679,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
643
679
  // Interactive create: must have draftId
644
680
  if (!draftId) {
645
681
  throw new Error(
646
- "Interactive create mode requires a draft ID. Draft creation may have failed."
682
+ "Interactive create mode requires a draft ID. Draft creation may have failed.",
647
683
  );
648
684
  }
649
685
  // POST /{bdo_id}/draft with _id in payload
@@ -653,8 +689,8 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
653
689
  } as any);
654
690
  result = { success: true, data: response };
655
691
  } else {
656
- // Interactive update: POST /{bdo_id}/{id}/draft
657
- const response = await client.draftUpdate(recordId!, cleanedData);
692
+ // Update operation - always use direct update API (non-interactive)
693
+ const response = await client.update(recordId!, cleanedData);
658
694
  result = { success: true, data: response };
659
695
  }
660
696
  } else {
@@ -663,7 +699,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
663
699
  source,
664
700
  operation,
665
701
  cleanedData,
666
- recordId
702
+ recordId,
667
703
  );
668
704
 
669
705
  if (!result.success) {
@@ -684,7 +720,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
684
720
  await onSuccess?.(result.data || data, event);
685
721
  } catch (error) {
686
722
  // API error - call onError with Error object
687
- onError?.(error as Error, event);
723
+ onError?.(toError(error), event);
688
724
  } finally {
689
725
  setIsSubmitting(false);
690
726
  }
@@ -692,7 +728,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
692
728
  // RHF onInvalid handler - validation failed
693
729
  (errors, event) => {
694
730
  onError?.(errors, event);
695
- }
731
+ },
696
732
  );
697
733
  },
698
734
  [
@@ -705,7 +741,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
705
741
  recordData,
706
742
  isInteractiveMode,
707
743
  draftId,
708
- ]
744
+ ],
709
745
  );
710
746
 
711
747
  // ============================================================
@@ -716,7 +752,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
716
752
  <K extends keyof T>(fieldName: K): FormFieldConfigType | null => {
717
753
  return schemaConfig?.fields[fieldName as string] || null;
718
754
  },
719
- [schemaConfig]
755
+ [schemaConfig],
720
756
  );
721
757
 
722
758
  const getFields = useCallback((): Record<keyof T, FormFieldConfigType> => {
@@ -734,7 +770,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
734
770
  <K extends keyof T>(fieldName: K): boolean => {
735
771
  return !!schemaConfig?.fields[fieldName as string];
736
772
  },
737
- [schemaConfig]
773
+ [schemaConfig],
738
774
  );
739
775
 
740
776
  const isFieldRequired = useCallback(
@@ -743,7 +779,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
743
779
  schemaConfig?.requiredFields.includes(fieldName as string) || false
744
780
  );
745
781
  },
746
- [schemaConfig]
782
+ [schemaConfig],
747
783
  );
748
784
 
749
785
  const isFieldComputed = useCallback(
@@ -752,7 +788,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
752
788
  schemaConfig?.computedFields.includes(fieldName as string) || false
753
789
  );
754
790
  },
755
- [schemaConfig]
791
+ [schemaConfig],
756
792
  );
757
793
 
758
794
  // ============================================================
@@ -782,12 +818,12 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
782
818
 
783
819
  const computedFields = useMemo<Array<keyof T>>(
784
820
  () => (schemaConfig?.computedFields as Array<keyof T>) || [],
785
- [schemaConfig]
821
+ [schemaConfig],
786
822
  );
787
823
 
788
824
  const requiredFields = useMemo<Array<keyof T>>(
789
825
  () => (schemaConfig?.requiredFields as Array<keyof T>) || [],
790
- [schemaConfig]
826
+ [schemaConfig],
791
827
  );
792
828
 
793
829
  // ============================================================
@@ -840,7 +876,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
840
876
  value,
841
877
  [rule],
842
878
  currentValues,
843
- lastFormValues as T | undefined
879
+ lastFormValues as T | undefined,
844
880
  );
845
881
  if (!result.isValid) {
846
882
  return result.message || rule.Message || "Invalid value";
@@ -912,7 +948,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
912
948
  onBlur: enhancedOnBlur,
913
949
  });
914
950
  },
915
- [rhfForm, validationRules, triggerComputationAfterValidation, mode]
951
+ [rhfForm, validationRules, triggerComputationAfterValidation, mode],
916
952
  );
917
953
 
918
954
  return {
@@ -940,7 +976,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
940
976
  isCreatingDraft,
941
977
 
942
978
  // Error handling
943
- loadError: loadError as Error | null,
979
+ loadError: loadError ? toError(loadError) : null,
944
980
  hasError,
945
981
 
946
982
  // Schema information
@@ -4,8 +4,11 @@
4
4
  // Core TypeScript interfaces for the kanban board functionality
5
5
  // Following patterns from useTable and useForm
6
6
 
7
- import type { ConditionType, ConditionGroupType, UseFilterReturnType } from "../useFilter";
8
- import type { ConditionGroupOperatorType } from "../../../types/common";
7
+ import type { UseFilterReturnType, UseFilterOptionsType } from "../useFilter";
8
+ import type { ColumnDefinitionType } from "../../../types/common";
9
+
10
+ // Re-export ColumnDefinitionType for backwards compatibility
11
+ export type { ColumnDefinitionType };
9
12
 
10
13
  // ============================================================
11
14
  // CORE DATA STRUCTURES
@@ -95,23 +98,6 @@ export interface KanbanColumnType<T = Record<string, any>> {
95
98
  _modified_at?: Date;
96
99
  }
97
100
 
98
- /**
99
- * Column definition for display and behavior
100
- * Similar to ColumnDefinition in useTable
101
- */
102
- export interface ColumnDefinitionType<T> {
103
- /** Field name from the card type */
104
- fieldId: keyof T;
105
- /** Display label (optional, defaults to fieldId) */
106
- label?: string;
107
- /** Enable sorting for this field */
108
- enableSorting?: boolean;
109
- /** Enable filtering for this field */
110
- enableFiltering?: boolean;
111
- /** Custom transform function (overrides auto-formatting) */
112
- transform?: (value: any, card: T) => React.ReactNode;
113
- }
114
-
115
101
  // ============================================================
116
102
  // DRAG & DROP TYPES
117
103
  // ============================================================
@@ -195,10 +181,8 @@ export interface UseKanbanOptionsType<T> {
195
181
 
196
182
  /** Initial state */
197
183
  initialState?: {
198
- /** Initial filter conditions */
199
- filters?: Array<ConditionType | ConditionGroupType>;
200
- /** Initial filter operator for combining filter conditions */
201
- filterOperator?: ConditionGroupOperatorType;
184
+ /** Initial filter configuration: { conditions, operator } */
185
+ filter?: UseFilterOptionsType;
202
186
  /** Initial search query */
203
187
  search?: string;
204
188
  /** Initial column order */