@ram_28/kf-ai-sdk 1.0.8 → 1.0.10
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/dist/components/hooks/index.d.ts +2 -1
- package/dist/components/hooks/index.d.ts.map +1 -1
- package/dist/components/hooks/useForm/apiClient.d.ts +4 -4
- package/dist/components/hooks/useForm/apiClient.d.ts.map +1 -1
- package/dist/components/hooks/useForm/expressionValidator.utils.d.ts +3 -3
- package/dist/components/hooks/useForm/expressionValidator.utils.d.ts.map +1 -1
- package/dist/components/hooks/useForm/index.d.ts +7 -4
- package/dist/components/hooks/useForm/index.d.ts.map +1 -1
- package/dist/components/hooks/useForm/optimizedExpressionValidator.utils.d.ts +5 -5
- package/dist/components/hooks/useForm/optimizedExpressionValidator.utils.d.ts.map +1 -1
- package/dist/components/hooks/useForm/ruleClassifier.utils.d.ts +7 -6
- package/dist/components/hooks/useForm/ruleClassifier.utils.d.ts.map +1 -1
- package/dist/components/hooks/useForm/schemaParser.utils.d.ts +8 -8
- package/dist/components/hooks/useForm/schemaParser.utils.d.ts.map +1 -1
- package/dist/components/hooks/useForm/types.d.ts +130 -112
- package/dist/components/hooks/useForm/types.d.ts.map +1 -1
- package/dist/components/hooks/useForm/useForm.d.ts.map +1 -1
- package/dist/index.cjs +13 -13
- package/dist/index.mjs +2082 -2067
- package/package.json +1 -1
- package/sdk/components/hooks/index.ts +24 -4
- package/sdk/components/hooks/useForm/apiClient.ts +5 -5
- package/sdk/components/hooks/useForm/expressionValidator.utils.ts +11 -11
- package/sdk/components/hooks/useForm/index.ts +41 -45
- package/sdk/components/hooks/useForm/optimizedExpressionValidator.utils.ts +7 -7
- package/sdk/components/hooks/useForm/ruleClassifier.utils.ts +21 -20
- package/sdk/components/hooks/useForm/schemaParser.utils.ts +36 -41
- package/sdk/components/hooks/useForm/types.ts +156 -141
- package/sdk/components/hooks/useForm/useForm.ts +155 -142
|
@@ -11,9 +11,9 @@ import type { Path } from "react-hook-form";
|
|
|
11
11
|
import type {
|
|
12
12
|
UseFormOptions,
|
|
13
13
|
UseFormReturn,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
BDOSchema,
|
|
15
|
+
FormSchemaConfig,
|
|
16
|
+
FormFieldConfig,
|
|
17
17
|
} from "./types";
|
|
18
18
|
|
|
19
19
|
import { processSchema, extractReferenceFields } from "./schemaParser.utils";
|
|
@@ -49,10 +49,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
49
49
|
mode = "onBlur", // Validation mode - controls when errors are shown (see types.ts for details)
|
|
50
50
|
enabled = true,
|
|
51
51
|
userRole,
|
|
52
|
-
onSuccess,
|
|
53
|
-
onError,
|
|
54
52
|
onSchemaError,
|
|
55
|
-
onSubmitError,
|
|
56
53
|
skipSchemaFetch = false,
|
|
57
54
|
schema: manualSchema,
|
|
58
55
|
draftOnEveryChange = false,
|
|
@@ -62,10 +59,9 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
62
59
|
// STATE MANAGEMENT
|
|
63
60
|
// ============================================================
|
|
64
61
|
|
|
65
|
-
const [
|
|
66
|
-
useState<
|
|
62
|
+
const [schemaConfig, setSchemaConfig] =
|
|
63
|
+
useState<FormSchemaConfig | null>(null);
|
|
67
64
|
const [referenceData, setReferenceData] = useState<Record<string, any[]>>({});
|
|
68
|
-
const [submitError, setSubmitError] = useState<Error | null>(null);
|
|
69
65
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
70
66
|
const [lastFormValues] = useState<Partial<T>>({});
|
|
71
67
|
|
|
@@ -77,25 +73,10 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
77
73
|
// This allows us to detect changes since the last draft, not since form init
|
|
78
74
|
const lastSyncedValuesRef = useRef<Partial<T> | null>(null);
|
|
79
75
|
|
|
80
|
-
// Stable callback
|
|
81
|
-
const onSuccessRef = useRef(onSuccess);
|
|
82
|
-
const onErrorRef = useRef(onError);
|
|
83
|
-
const onSubmitErrorRef = useRef(onSubmitError);
|
|
76
|
+
// Stable callback ref to prevent dependency loops
|
|
84
77
|
const onSchemaErrorRef = useRef(onSchemaError);
|
|
85
78
|
|
|
86
|
-
// Update
|
|
87
|
-
useEffect(() => {
|
|
88
|
-
onSuccessRef.current = onSuccess;
|
|
89
|
-
}, [onSuccess]);
|
|
90
|
-
|
|
91
|
-
useEffect(() => {
|
|
92
|
-
onErrorRef.current = onError;
|
|
93
|
-
}, [onError]);
|
|
94
|
-
|
|
95
|
-
useEffect(() => {
|
|
96
|
-
onSubmitErrorRef.current = onSubmitError;
|
|
97
|
-
}, [onSubmitError]);
|
|
98
|
-
|
|
79
|
+
// Update ref when callback changes
|
|
99
80
|
useEffect(() => {
|
|
100
81
|
onSchemaErrorRef.current = onSchemaError;
|
|
101
82
|
}, [onSchemaError]);
|
|
@@ -153,8 +134,8 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
153
134
|
}
|
|
154
135
|
|
|
155
136
|
// Apply default values from schema
|
|
156
|
-
if (
|
|
157
|
-
for (const [fieldName, field] of Object.entries(
|
|
137
|
+
if (schemaConfig) {
|
|
138
|
+
for (const [fieldName, field] of Object.entries(schemaConfig.fields)) {
|
|
158
139
|
if (field.defaultValue !== undefined && !(fieldName in values)) {
|
|
159
140
|
(values as any)[fieldName] = field.defaultValue;
|
|
160
141
|
}
|
|
@@ -162,7 +143,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
162
143
|
}
|
|
163
144
|
|
|
164
145
|
return values;
|
|
165
|
-
}, [defaultValues, recordData, operation,
|
|
146
|
+
}, [defaultValues, recordData, operation, schemaConfig]);
|
|
166
147
|
|
|
167
148
|
const rhfForm = useReactHookForm<T>({
|
|
168
149
|
mode,
|
|
@@ -185,7 +166,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
185
166
|
{}, // Pass empty object - validation functions get live values from react-hook-form
|
|
186
167
|
userRole
|
|
187
168
|
);
|
|
188
|
-
|
|
169
|
+
setSchemaConfig(processed);
|
|
189
170
|
|
|
190
171
|
// Fetch reference data for reference fields
|
|
191
172
|
const refFields = extractReferenceFields(processed);
|
|
@@ -201,40 +182,34 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
201
182
|
}
|
|
202
183
|
}, [schema, userRole]); // Removed onSchemaError - using ref instead
|
|
203
184
|
|
|
204
|
-
// Handle schema
|
|
185
|
+
// Handle schema error
|
|
205
186
|
useEffect(() => {
|
|
206
187
|
if (schemaError) {
|
|
207
188
|
onSchemaErrorRef.current?.(schemaError);
|
|
208
189
|
}
|
|
209
190
|
}, [schemaError]);
|
|
210
191
|
|
|
211
|
-
useEffect(() => {
|
|
212
|
-
if (recordError) {
|
|
213
|
-
onErrorRef.current?.(recordError);
|
|
214
|
-
}
|
|
215
|
-
}, [recordError]);
|
|
216
|
-
|
|
217
192
|
// ============================================================
|
|
218
193
|
// COMPUTED FIELD DEPENDENCY TRACKING AND OPTIMIZATION
|
|
219
194
|
// ============================================================
|
|
220
195
|
|
|
221
196
|
// Extract computed field dependencies using optimized analyzer
|
|
222
197
|
const computedFieldDependencies = useMemo(() => {
|
|
223
|
-
if (!
|
|
198
|
+
if (!schemaConfig) return [];
|
|
224
199
|
|
|
225
200
|
const dependencies = new Set<string>();
|
|
226
|
-
const computedFieldNames = new Set(
|
|
201
|
+
const computedFieldNames = new Set(schemaConfig.computedFields);
|
|
227
202
|
|
|
228
203
|
// Analyze dependencies from computation rules
|
|
229
|
-
Object.entries(
|
|
204
|
+
Object.entries(schemaConfig.fieldRules).forEach(([fieldName, rules]) => {
|
|
230
205
|
rules.computation.forEach((ruleId) => {
|
|
231
|
-
const rule =
|
|
206
|
+
const rule = schemaConfig.rules.computation[ruleId];
|
|
232
207
|
if (rule?.ExpressionTree) {
|
|
233
208
|
const ruleDeps = getFieldDependencies(rule.ExpressionTree);
|
|
234
209
|
ruleDeps.forEach((dep) => {
|
|
235
210
|
// Only add non-computed fields as dependencies
|
|
236
211
|
if (
|
|
237
|
-
|
|
212
|
+
schemaConfig.fields[dep] &&
|
|
238
213
|
dep !== fieldName &&
|
|
239
214
|
!computedFieldNames.has(dep)
|
|
240
215
|
) {
|
|
@@ -246,16 +221,16 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
246
221
|
});
|
|
247
222
|
|
|
248
223
|
// Also check formulas (legacy support)
|
|
249
|
-
|
|
250
|
-
const field =
|
|
251
|
-
if (field.
|
|
224
|
+
schemaConfig.computedFields.forEach((fieldName: string) => {
|
|
225
|
+
const field = schemaConfig.fields[fieldName];
|
|
226
|
+
if (field._bdoField.Formula) {
|
|
252
227
|
const fieldDeps = getFieldDependencies(
|
|
253
|
-
field.
|
|
228
|
+
field._bdoField.Formula.ExpressionTree
|
|
254
229
|
);
|
|
255
230
|
fieldDeps.forEach((dep) => {
|
|
256
231
|
// Only add non-computed fields as dependencies
|
|
257
232
|
if (
|
|
258
|
-
|
|
233
|
+
schemaConfig.fields[dep] &&
|
|
259
234
|
dep !== fieldName &&
|
|
260
235
|
!computedFieldNames.has(dep)
|
|
261
236
|
) {
|
|
@@ -266,7 +241,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
266
241
|
});
|
|
267
242
|
|
|
268
243
|
return Array.from(dependencies) as Array<Path<T>>;
|
|
269
|
-
}, [
|
|
244
|
+
}, [schemaConfig]);
|
|
270
245
|
|
|
271
246
|
// Watch dependencies are tracked but not used for automatic computation
|
|
272
247
|
// Computation is triggered manually on blur after validation passes
|
|
@@ -278,7 +253,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
278
253
|
// Manual computation trigger - called on blur after validation passes
|
|
279
254
|
const triggerComputationAfterValidation = useCallback(
|
|
280
255
|
async (fieldName: string) => {
|
|
281
|
-
if (!
|
|
256
|
+
if (!schemaConfig || computedFieldDependencies.length === 0) {
|
|
282
257
|
return;
|
|
283
258
|
}
|
|
284
259
|
|
|
@@ -305,7 +280,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
305
280
|
|
|
306
281
|
computeTimeoutRef.current = setTimeout(() => {
|
|
307
282
|
// Additional safety check
|
|
308
|
-
if (!
|
|
283
|
+
if (!schemaConfig) return;
|
|
309
284
|
|
|
310
285
|
// Prevent concurrent API calls
|
|
311
286
|
if (isComputingRef.current) {
|
|
@@ -338,7 +313,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
338
313
|
|
|
339
314
|
// Get computed field names to exclude from payload
|
|
340
315
|
const computedFieldNames = new Set(
|
|
341
|
-
|
|
316
|
+
schemaConfig.computedFields || []
|
|
342
317
|
);
|
|
343
318
|
|
|
344
319
|
// Find fields that changed from baseline (excluding computed fields)
|
|
@@ -430,7 +405,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
430
405
|
}, 300); // 300ms debounce
|
|
431
406
|
},
|
|
432
407
|
[
|
|
433
|
-
|
|
408
|
+
schemaConfig,
|
|
434
409
|
operation,
|
|
435
410
|
recordId,
|
|
436
411
|
recordData,
|
|
@@ -446,7 +421,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
446
421
|
// ============================================================
|
|
447
422
|
|
|
448
423
|
const validateForm = useCallback(async (): Promise<boolean> => {
|
|
449
|
-
if (!
|
|
424
|
+
if (!schemaConfig) {
|
|
450
425
|
return false;
|
|
451
426
|
}
|
|
452
427
|
|
|
@@ -460,7 +435,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
460
435
|
|
|
461
436
|
// Cross-field validation
|
|
462
437
|
// Transform ValidationRule[] to the format expected by validateCrossField
|
|
463
|
-
const transformedRules =
|
|
438
|
+
const transformedRules = schemaConfig.crossFieldValidation.map(
|
|
464
439
|
(rule) => ({
|
|
465
440
|
Id: rule.Id,
|
|
466
441
|
Condition: { ExpressionTree: rule.ExpressionTree },
|
|
@@ -487,7 +462,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
487
462
|
|
|
488
463
|
return true;
|
|
489
464
|
}, [
|
|
490
|
-
|
|
465
|
+
schemaConfig,
|
|
491
466
|
rhfForm.getValues,
|
|
492
467
|
rhfForm.trigger,
|
|
493
468
|
rhfForm.setError,
|
|
@@ -495,122 +470,163 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
495
470
|
]);
|
|
496
471
|
|
|
497
472
|
// ============================================================
|
|
498
|
-
//
|
|
473
|
+
// HANDLE SUBMIT - RHF-style API with internal submission
|
|
499
474
|
// ============================================================
|
|
500
475
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
476
|
+
/**
|
|
477
|
+
* handleSubmit follows React Hook Form's signature pattern:
|
|
478
|
+
*
|
|
479
|
+
* handleSubmit(onSuccess?, onError?) => (e?) => Promise<void>
|
|
480
|
+
*
|
|
481
|
+
* Internal flow:
|
|
482
|
+
* 1. RHF validation + Cross-field validation → FAILS → onError(fieldErrors)
|
|
483
|
+
* 2. Clean data & call API → FAILS → onError(apiError)
|
|
484
|
+
* 3. SUCCESS → onSuccess(responseData)
|
|
485
|
+
*/
|
|
486
|
+
const handleSubmit = useCallback(
|
|
487
|
+
(
|
|
488
|
+
onSuccess?: (data: T, e?: React.BaseSyntheticEvent) => void | Promise<void>,
|
|
489
|
+
onError?: (
|
|
490
|
+
error: import("react-hook-form").FieldErrors<T> | Error,
|
|
491
|
+
e?: React.BaseSyntheticEvent
|
|
492
|
+
) => void | Promise<void>
|
|
493
|
+
) => {
|
|
494
|
+
return rhfForm.handleSubmit(
|
|
495
|
+
// RHF onValid handler - validation passed, now do cross-field + API
|
|
496
|
+
async (data, event) => {
|
|
497
|
+
if (!schemaConfig) {
|
|
498
|
+
const error = new Error("Schema not loaded");
|
|
499
|
+
onError?.(error, event);
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
508
502
|
|
|
509
|
-
|
|
510
|
-
// Validate form including cross-field validation
|
|
511
|
-
const isValid = await validateForm();
|
|
512
|
-
if (!isValid) {
|
|
513
|
-
throw new Error("Form validation failed");
|
|
514
|
-
}
|
|
503
|
+
setIsSubmitting(true);
|
|
515
504
|
|
|
516
|
-
|
|
505
|
+
try {
|
|
506
|
+
// Cross-field validation
|
|
507
|
+
const transformedRules = schemaConfig.crossFieldValidation.map(
|
|
508
|
+
(rule) => ({
|
|
509
|
+
Id: rule.Id,
|
|
510
|
+
Condition: { ExpressionTree: rule.ExpressionTree },
|
|
511
|
+
Message: rule.Message || `Validation failed for ${rule.Name}`,
|
|
512
|
+
})
|
|
513
|
+
);
|
|
517
514
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
processedSchema.computedFields,
|
|
524
|
-
operation,
|
|
525
|
-
recordData as Partial<T> | undefined
|
|
526
|
-
);
|
|
515
|
+
const crossFieldErrors = validateCrossField(
|
|
516
|
+
transformedRules,
|
|
517
|
+
data as any,
|
|
518
|
+
referenceData
|
|
519
|
+
);
|
|
527
520
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
521
|
+
if (crossFieldErrors.length > 0) {
|
|
522
|
+
// Set cross-field errors in form state
|
|
523
|
+
crossFieldErrors.forEach((error, index) => {
|
|
524
|
+
rhfForm.setError(`root.crossField${index}` as any, {
|
|
525
|
+
type: "validate",
|
|
526
|
+
message: error.message,
|
|
527
|
+
});
|
|
528
|
+
});
|
|
529
|
+
// Call onError with current form errors
|
|
530
|
+
onError?.(rhfForm.formState.errors, event);
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
535
533
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
534
|
+
// Clean data for submission
|
|
535
|
+
const cleanedData = cleanFormData(
|
|
536
|
+
data as any,
|
|
537
|
+
schemaConfig.computedFields,
|
|
538
|
+
operation,
|
|
539
|
+
recordData as Partial<T> | undefined
|
|
540
|
+
);
|
|
539
541
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
+
// Submit data to API
|
|
543
|
+
const result = await submitFormData<T>(
|
|
544
|
+
source,
|
|
545
|
+
operation,
|
|
546
|
+
cleanedData,
|
|
547
|
+
recordId
|
|
548
|
+
);
|
|
542
549
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
}
|
|
547
|
-
} catch (error) {
|
|
548
|
-
const submitError = error as Error;
|
|
549
|
-
setSubmitError(submitError);
|
|
550
|
-
onSubmitErrorRef.current?.(submitError);
|
|
551
|
-
onErrorRef.current?.(submitError);
|
|
552
|
-
throw error;
|
|
553
|
-
} finally {
|
|
554
|
-
setIsSubmitting(false);
|
|
555
|
-
}
|
|
556
|
-
}, [processedSchema, validateForm, rhfForm, source, operation, recordId, recordData]);
|
|
550
|
+
if (!result.success) {
|
|
551
|
+
throw result.error || new Error("Submission failed");
|
|
552
|
+
}
|
|
557
553
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
554
|
+
// Reset form for create operations
|
|
555
|
+
if (operation === "create") {
|
|
556
|
+
rhfForm.reset();
|
|
557
|
+
}
|
|
561
558
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
559
|
+
// Success callback with response data
|
|
560
|
+
await onSuccess?.(result.data || data, event);
|
|
561
|
+
} catch (error) {
|
|
562
|
+
// API error - call onError with Error object
|
|
563
|
+
onError?.(error as Error, event);
|
|
564
|
+
} finally {
|
|
565
|
+
setIsSubmitting(false);
|
|
566
|
+
}
|
|
567
|
+
},
|
|
568
|
+
// RHF onInvalid handler - validation failed
|
|
569
|
+
(errors, event) => {
|
|
570
|
+
onError?.(errors, event);
|
|
571
|
+
}
|
|
572
|
+
);
|
|
573
|
+
},
|
|
574
|
+
[
|
|
575
|
+
rhfForm,
|
|
576
|
+
schemaConfig,
|
|
577
|
+
referenceData,
|
|
578
|
+
source,
|
|
579
|
+
operation,
|
|
580
|
+
recordId,
|
|
581
|
+
recordData,
|
|
582
|
+
]
|
|
583
|
+
);
|
|
568
584
|
|
|
569
585
|
// ============================================================
|
|
570
586
|
// FIELD HELPERS
|
|
571
587
|
// ============================================================
|
|
572
588
|
|
|
573
589
|
const getField = useCallback(
|
|
574
|
-
<K extends keyof T>(fieldName: K):
|
|
575
|
-
return
|
|
590
|
+
<K extends keyof T>(fieldName: K): FormFieldConfig | null => {
|
|
591
|
+
return schemaConfig?.fields[fieldName as string] || null;
|
|
576
592
|
},
|
|
577
|
-
[
|
|
593
|
+
[schemaConfig]
|
|
578
594
|
);
|
|
579
595
|
|
|
580
|
-
const getFields = useCallback((): Record<keyof T,
|
|
581
|
-
if (!
|
|
596
|
+
const getFields = useCallback((): Record<keyof T, FormFieldConfig> => {
|
|
597
|
+
if (!schemaConfig) return {} as Record<keyof T, FormFieldConfig>;
|
|
582
598
|
|
|
583
|
-
const typedFields: Record<keyof T,
|
|
584
|
-
Object.entries(
|
|
599
|
+
const typedFields: Record<keyof T, FormFieldConfig> = {} as any;
|
|
600
|
+
Object.entries(schemaConfig.fields).forEach(([key, field]) => {
|
|
585
601
|
(typedFields as any)[key] = field;
|
|
586
602
|
});
|
|
587
603
|
|
|
588
604
|
return typedFields;
|
|
589
|
-
}, [
|
|
605
|
+
}, [schemaConfig]);
|
|
590
606
|
|
|
591
607
|
const hasField = useCallback(
|
|
592
608
|
<K extends keyof T>(fieldName: K): boolean => {
|
|
593
|
-
return !!
|
|
609
|
+
return !!schemaConfig?.fields[fieldName as string];
|
|
594
610
|
},
|
|
595
|
-
[
|
|
611
|
+
[schemaConfig]
|
|
596
612
|
);
|
|
597
613
|
|
|
598
614
|
const isFieldRequired = useCallback(
|
|
599
615
|
<K extends keyof T>(fieldName: K): boolean => {
|
|
600
616
|
return (
|
|
601
|
-
|
|
617
|
+
schemaConfig?.requiredFields.includes(fieldName as string) || false
|
|
602
618
|
);
|
|
603
619
|
},
|
|
604
|
-
[
|
|
620
|
+
[schemaConfig]
|
|
605
621
|
);
|
|
606
622
|
|
|
607
623
|
const isFieldComputed = useCallback(
|
|
608
624
|
<K extends keyof T>(fieldName: K): boolean => {
|
|
609
625
|
return (
|
|
610
|
-
|
|
626
|
+
schemaConfig?.computedFields.includes(fieldName as string) || false
|
|
611
627
|
);
|
|
612
628
|
},
|
|
613
|
-
[
|
|
629
|
+
[schemaConfig]
|
|
614
630
|
);
|
|
615
631
|
|
|
616
632
|
// ============================================================
|
|
@@ -623,7 +639,6 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
623
639
|
|
|
624
640
|
const clearErrors = useCallback((): void => {
|
|
625
641
|
rhfForm.clearErrors();
|
|
626
|
-
setSubmitError(null);
|
|
627
642
|
}, [rhfForm]);
|
|
628
643
|
|
|
629
644
|
// ============================================================
|
|
@@ -634,16 +649,16 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
634
649
|
isLoadingSchema || (operation === "update" && isLoadingRecord);
|
|
635
650
|
const isLoading = isLoadingInitialData || isSubmitting;
|
|
636
651
|
const loadError = schemaError || recordError;
|
|
637
|
-
const hasError = !!
|
|
652
|
+
const hasError = !!loadError;
|
|
638
653
|
|
|
639
654
|
const computedFields = useMemo<Array<keyof T>>(
|
|
640
|
-
() => (
|
|
641
|
-
[
|
|
655
|
+
() => (schemaConfig?.computedFields as Array<keyof T>) || [],
|
|
656
|
+
[schemaConfig]
|
|
642
657
|
);
|
|
643
658
|
|
|
644
659
|
const requiredFields = useMemo<Array<keyof T>>(
|
|
645
|
-
() => (
|
|
646
|
-
[
|
|
660
|
+
() => (schemaConfig?.requiredFields as Array<keyof T>) || [],
|
|
661
|
+
[schemaConfig]
|
|
647
662
|
);
|
|
648
663
|
|
|
649
664
|
// ============================================================
|
|
@@ -652,11 +667,11 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
652
667
|
|
|
653
668
|
// Create validation rules from processed schema (client-side only)
|
|
654
669
|
const validationRules = useMemo(() => {
|
|
655
|
-
if (!
|
|
670
|
+
if (!schemaConfig) return {};
|
|
656
671
|
|
|
657
672
|
const rules: Record<string, any> = {};
|
|
658
673
|
|
|
659
|
-
Object.entries(
|
|
674
|
+
Object.entries(schemaConfig.fields).forEach(([fieldName, field]) => {
|
|
660
675
|
const fieldRules: any = {};
|
|
661
676
|
|
|
662
677
|
// Required validation
|
|
@@ -689,7 +704,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
689
704
|
|
|
690
705
|
// Execute client-side validation rules with optimization
|
|
691
706
|
for (const ruleId of validationRuleIds) {
|
|
692
|
-
const rule =
|
|
707
|
+
const rule = schemaConfig.rules.validation[ruleId];
|
|
693
708
|
if (rule) {
|
|
694
709
|
const result = validateFieldOptimized<T>(
|
|
695
710
|
fieldName,
|
|
@@ -712,7 +727,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
712
727
|
});
|
|
713
728
|
|
|
714
729
|
return rules;
|
|
715
|
-
}, [
|
|
730
|
+
}, [schemaConfig, rhfForm, referenceData]);
|
|
716
731
|
|
|
717
732
|
/**
|
|
718
733
|
* Enhanced register function that wraps react-hook-form's register
|
|
@@ -796,12 +811,11 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
796
811
|
|
|
797
812
|
// Error handling
|
|
798
813
|
loadError: loadError as Error | null,
|
|
799
|
-
submitError,
|
|
800
814
|
hasError,
|
|
801
815
|
|
|
802
816
|
// Schema information
|
|
803
|
-
schema: schema as
|
|
804
|
-
|
|
817
|
+
schema: schema as BDOSchema | null,
|
|
818
|
+
schemaConfig,
|
|
805
819
|
computedFields,
|
|
806
820
|
requiredFields,
|
|
807
821
|
|
|
@@ -813,7 +827,6 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
813
827
|
isFieldComputed,
|
|
814
828
|
|
|
815
829
|
// Operations
|
|
816
|
-
submit,
|
|
817
830
|
refreshSchema,
|
|
818
831
|
validateForm,
|
|
819
832
|
clearErrors,
|