@ram_28/kf-ai-sdk 1.0.0

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 (126) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +840 -0
  3. package/dist/api/client.d.ts +78 -0
  4. package/dist/api/client.d.ts.map +1 -0
  5. package/dist/api/datetime.d.ts +21 -0
  6. package/dist/api/datetime.d.ts.map +1 -0
  7. package/dist/api/index.d.ts +7 -0
  8. package/dist/api/index.d.ts.map +1 -0
  9. package/dist/api/metadata.d.ts +75 -0
  10. package/dist/api/metadata.d.ts.map +1 -0
  11. package/dist/components/hooks/index.d.ts +8 -0
  12. package/dist/components/hooks/index.d.ts.map +1 -0
  13. package/dist/components/hooks/useFilter/index.d.ts +5 -0
  14. package/dist/components/hooks/useFilter/index.d.ts.map +1 -0
  15. package/dist/components/hooks/useFilter/payloadBuilder.utils.d.ts +33 -0
  16. package/dist/components/hooks/useFilter/payloadBuilder.utils.d.ts.map +1 -0
  17. package/dist/components/hooks/useFilter/types.d.ts +137 -0
  18. package/dist/components/hooks/useFilter/types.d.ts.map +1 -0
  19. package/dist/components/hooks/useFilter/useFilter.d.ts +3 -0
  20. package/dist/components/hooks/useFilter/useFilter.d.ts.map +1 -0
  21. package/dist/components/hooks/useFilter/validation.utils.d.ts +38 -0
  22. package/dist/components/hooks/useFilter/validation.utils.d.ts.map +1 -0
  23. package/dist/components/hooks/useForm/apiClient.d.ts +71 -0
  24. package/dist/components/hooks/useForm/apiClient.d.ts.map +1 -0
  25. package/dist/components/hooks/useForm/expressionValidator.utils.d.ts +28 -0
  26. package/dist/components/hooks/useForm/expressionValidator.utils.d.ts.map +1 -0
  27. package/dist/components/hooks/useForm/index.d.ts +6 -0
  28. package/dist/components/hooks/useForm/index.d.ts.map +1 -0
  29. package/dist/components/hooks/useForm/optimizedExpressionValidator.utils.d.ts +88 -0
  30. package/dist/components/hooks/useForm/optimizedExpressionValidator.utils.d.ts.map +1 -0
  31. package/dist/components/hooks/useForm/ruleClassifier.utils.d.ts +28 -0
  32. package/dist/components/hooks/useForm/ruleClassifier.utils.d.ts.map +1 -0
  33. package/dist/components/hooks/useForm/schemaParser.utils.d.ts +29 -0
  34. package/dist/components/hooks/useForm/schemaParser.utils.d.ts.map +1 -0
  35. package/dist/components/hooks/useForm/types.d.ts +412 -0
  36. package/dist/components/hooks/useForm/types.d.ts.map +1 -0
  37. package/dist/components/hooks/useForm/useForm.d.ts +3 -0
  38. package/dist/components/hooks/useForm/useForm.d.ts.map +1 -0
  39. package/dist/components/hooks/useKanban/apiClient.d.ts +99 -0
  40. package/dist/components/hooks/useKanban/apiClient.d.ts.map +1 -0
  41. package/dist/components/hooks/useKanban/context.d.ts +4 -0
  42. package/dist/components/hooks/useKanban/context.d.ts.map +1 -0
  43. package/dist/components/hooks/useKanban/dragDropManager.d.ts +27 -0
  44. package/dist/components/hooks/useKanban/dragDropManager.d.ts.map +1 -0
  45. package/dist/components/hooks/useKanban/index.d.ts +6 -0
  46. package/dist/components/hooks/useKanban/index.d.ts.map +1 -0
  47. package/dist/components/hooks/useKanban/types.d.ts +438 -0
  48. package/dist/components/hooks/useKanban/types.d.ts.map +1 -0
  49. package/dist/components/hooks/useKanban/useKanban.d.ts +3 -0
  50. package/dist/components/hooks/useKanban/useKanban.d.ts.map +1 -0
  51. package/dist/components/hooks/useKanban/useKanbanSimple.d.ts +62 -0
  52. package/dist/components/hooks/useKanban/useKanbanSimple.d.ts.map +1 -0
  53. package/dist/components/hooks/useTable/index.d.ts +3 -0
  54. package/dist/components/hooks/useTable/index.d.ts.map +1 -0
  55. package/dist/components/hooks/useTable/types.d.ts +107 -0
  56. package/dist/components/hooks/useTable/types.d.ts.map +1 -0
  57. package/dist/components/hooks/useTable/useTable.d.ts +8 -0
  58. package/dist/components/hooks/useTable/useTable.d.ts.map +1 -0
  59. package/dist/components/index.d.ts +3 -0
  60. package/dist/components/index.d.ts.map +1 -0
  61. package/dist/components/ui/index.d.ts +2 -0
  62. package/dist/components/ui/index.d.ts.map +1 -0
  63. package/dist/components/ui/kanban/Kanban.d.ts +12 -0
  64. package/dist/components/ui/kanban/Kanban.d.ts.map +1 -0
  65. package/dist/components/ui/kanban/index.d.ts +2 -0
  66. package/dist/components/ui/kanban/index.d.ts.map +1 -0
  67. package/dist/index.cjs +45 -0
  68. package/dist/index.d.ts +5 -0
  69. package/dist/index.d.ts.map +1 -0
  70. package/dist/index.mjs +6522 -0
  71. package/dist/types/base-fields.d.ts +182 -0
  72. package/dist/types/base-fields.d.ts.map +1 -0
  73. package/dist/types/common.d.ts +238 -0
  74. package/dist/types/common.d.ts.map +1 -0
  75. package/dist/types/index.d.ts +3 -0
  76. package/dist/types/index.d.ts.map +1 -0
  77. package/dist/utils/cn.d.ts +7 -0
  78. package/dist/utils/cn.d.ts.map +1 -0
  79. package/dist/utils/formatting.d.ts +52 -0
  80. package/dist/utils/formatting.d.ts.map +1 -0
  81. package/dist/utils/index.d.ts +3 -0
  82. package/dist/utils/index.d.ts.map +1 -0
  83. package/package.json +98 -0
  84. package/sdk/api/client.ts +447 -0
  85. package/sdk/api/datetime.ts +33 -0
  86. package/sdk/api/index.ts +61 -0
  87. package/sdk/api/metadata.ts +148 -0
  88. package/sdk/components/hooks/index.ts +34 -0
  89. package/sdk/components/hooks/useFilter/index.ts +37 -0
  90. package/sdk/components/hooks/useFilter/payloadBuilder.utils.ts +298 -0
  91. package/sdk/components/hooks/useFilter/types.ts +158 -0
  92. package/sdk/components/hooks/useFilter/useFilter.llm.txt +497 -0
  93. package/sdk/components/hooks/useFilter/useFilter.ts +494 -0
  94. package/sdk/components/hooks/useFilter/validation.utils.ts +401 -0
  95. package/sdk/components/hooks/useForm/apiClient.ts +441 -0
  96. package/sdk/components/hooks/useForm/expressionValidator.utils.ts +444 -0
  97. package/sdk/components/hooks/useForm/index.ts +64 -0
  98. package/sdk/components/hooks/useForm/optimizedExpressionValidator.utils.ts +482 -0
  99. package/sdk/components/hooks/useForm/ruleClassifier.utils.ts +424 -0
  100. package/sdk/components/hooks/useForm/schemaParser.utils.ts +519 -0
  101. package/sdk/components/hooks/useForm/types.ts +630 -0
  102. package/sdk/components/hooks/useForm/useForm.llm.txt +340 -0
  103. package/sdk/components/hooks/useForm/useForm.ts +821 -0
  104. package/sdk/components/hooks/useKanban/apiClient.ts +494 -0
  105. package/sdk/components/hooks/useKanban/context.ts +14 -0
  106. package/sdk/components/hooks/useKanban/dragDropManager.ts +529 -0
  107. package/sdk/components/hooks/useKanban/index.ts +63 -0
  108. package/sdk/components/hooks/useKanban/types.ts +606 -0
  109. package/sdk/components/hooks/useKanban/useKanban.llm.txt +482 -0
  110. package/sdk/components/hooks/useKanban/useKanban.ts +725 -0
  111. package/sdk/components/hooks/useKanban/useKanbanSimple.ts +389 -0
  112. package/sdk/components/hooks/useTable/index.ts +5 -0
  113. package/sdk/components/hooks/useTable/types.ts +154 -0
  114. package/sdk/components/hooks/useTable/useTable.llm.txt +344 -0
  115. package/sdk/components/hooks/useTable/useTable.ts +413 -0
  116. package/sdk/components/index.ts +15 -0
  117. package/sdk/components/ui/index.ts +2 -0
  118. package/sdk/components/ui/kanban/Kanban.tsx +134 -0
  119. package/sdk/components/ui/kanban/index.ts +11 -0
  120. package/sdk/index.ts +13 -0
  121. package/sdk/types/base-fields.ts +221 -0
  122. package/sdk/types/common.ts +306 -0
  123. package/sdk/types/index.ts +5 -0
  124. package/sdk/utils/cn.ts +10 -0
  125. package/sdk/utils/formatting.ts +212 -0
  126. package/sdk/utils/index.ts +5 -0
@@ -0,0 +1,494 @@
1
+ import { useState, useCallback, useMemo, useEffect } from "react";
2
+ import type { Filter, LogicalOperator } from "../../../types/common";
3
+ import type {
4
+ FilterConditionWithId,
5
+ TypedFilterConditionInput,
6
+ FieldDefinition,
7
+ ValidationResult,
8
+ ValidationError,
9
+ FilterState,
10
+ UseFilterOptions,
11
+ UseFilterReturn,
12
+ } from "./types";
13
+
14
+ // ============================================================
15
+ // VALIDATION HELPERS
16
+ // ============================================================
17
+
18
+ /**
19
+ * Generate a unique ID for conditions
20
+ */
21
+ const generateId = (): string => {
22
+ return crypto.randomUUID();
23
+ };
24
+
25
+ /**
26
+ * Helper to check if operator is a logical operator
27
+ */
28
+ const isLogicalOperator = (operator: string): operator is LogicalOperator => {
29
+ return operator === 'And' || operator === 'Or' || operator === 'Not';
30
+ };
31
+
32
+ /**
33
+ * Validate a filter condition (supports both simple conditions and nested logical groups)
34
+ */
35
+ const validateFilterCondition = <T>(
36
+ condition: Partial<FilterConditionWithId>,
37
+ fieldDefinitions?: Record<keyof T, FieldDefinition>
38
+ ): ValidationResult => {
39
+ const errors: string[] = [];
40
+
41
+ // Check required fields
42
+ if (!condition.operator) {
43
+ errors.push('Operator is required');
44
+ }
45
+
46
+ // Check if this is a logical operator (nested group)
47
+ if (condition.operator && isLogicalOperator(condition.operator)) {
48
+ // Validate logical group
49
+ if (!condition.children || !Array.isArray(condition.children)) {
50
+ errors.push('Logical operators require a children array');
51
+ } else if (condition.children.length === 0) {
52
+ errors.push('Logical operators require at least one child condition');
53
+ } else if (condition.operator === 'Not' && condition.children.length > 1) {
54
+ errors.push('Not operator can only have one child condition');
55
+ }
56
+
57
+ // Recursively validate children
58
+ if (condition.children && Array.isArray(condition.children)) {
59
+ condition.children.forEach((child, index) => {
60
+ const childValidation = validateFilterCondition(child, fieldDefinitions);
61
+ if (!childValidation.isValid) {
62
+ errors.push(...childValidation.errors.map(err => `Child ${index + 1}: ${err}`));
63
+ }
64
+ });
65
+ }
66
+
67
+ // Logical operators should not have lhsField or rhsValue
68
+ if (condition.lhsField) {
69
+ errors.push('Logical operators should not have lhsField');
70
+ }
71
+ if (condition.rhsValue !== undefined) {
72
+ errors.push('Logical operators should not have rhsValue');
73
+ }
74
+ } else {
75
+ // Validate simple condition
76
+ if (!condition.lhsField) {
77
+ errors.push('Field is required for condition operators');
78
+ }
79
+
80
+ // Validate operator-specific requirements
81
+ if (condition.operator && condition.rhsValue !== undefined) {
82
+ switch (condition.operator) {
83
+ case 'Between':
84
+ case 'NotBetween':
85
+ if (!Array.isArray(condition.rhsValue) || condition.rhsValue.length !== 2) {
86
+ errors.push('Between operators require an array of two values');
87
+ }
88
+ break;
89
+ case 'IN':
90
+ case 'NIN':
91
+ if (!Array.isArray(condition.rhsValue) || condition.rhsValue.length === 0) {
92
+ errors.push('IN/NIN operators require a non-empty array');
93
+ }
94
+ break;
95
+ case 'Empty':
96
+ case 'NotEmpty':
97
+ // These operators don't need RHS values
98
+ break;
99
+ default:
100
+ if (condition.rhsValue === null || condition.rhsValue === undefined || condition.rhsValue === '') {
101
+ errors.push('Value is required for this operator');
102
+ }
103
+ break;
104
+ }
105
+ }
106
+
107
+ // Field-specific validation
108
+ if (fieldDefinitions && condition.lhsField && condition.operator) {
109
+ const fieldDef = fieldDefinitions[condition.lhsField as keyof T];
110
+ if (fieldDef) {
111
+ // Check if operator is allowed for this field
112
+ if (!fieldDef.allowedOperators.includes(condition.operator as any)) {
113
+ errors.push(`Operator ${condition.operator} is not allowed for field ${condition.lhsField}`);
114
+ }
115
+
116
+ // Custom field validation
117
+ if (fieldDef.validateValue && condition.rhsValue !== undefined) {
118
+ const fieldValidation = fieldDef.validateValue(condition.rhsValue, condition.operator as any);
119
+ if (!fieldValidation.isValid) {
120
+ errors.push(...fieldValidation.errors);
121
+ }
122
+ }
123
+ }
124
+ }
125
+
126
+ // Simple conditions should not have children
127
+ if (condition.children && condition.children.length > 0) {
128
+ errors.push('Condition operators should not have children');
129
+ }
130
+ }
131
+
132
+ return {
133
+ isValid: errors.length === 0,
134
+ errors
135
+ };
136
+ };
137
+
138
+ /**
139
+ * Convert a single FilterConditionWithId to API format (FilterCondition or FilterLogical)
140
+ */
141
+ const convertConditionToAPI = (condition: FilterConditionWithId): any => {
142
+ // Check if this is a logical operator (nested group)
143
+ if (isLogicalOperator(condition.operator)) {
144
+ // Build nested logical filter
145
+ return {
146
+ Operator: condition.operator,
147
+ Condition: (condition.children || [])
148
+ .filter(child => child.isValid)
149
+ .map(child => convertConditionToAPI(child))
150
+ };
151
+ } else {
152
+ // Build simple condition
153
+ return {
154
+ Operator: condition.operator,
155
+ LHSField: condition.lhsField,
156
+ RHSValue: condition.rhsValue,
157
+ RHSType: condition.rhsType || "Constant"
158
+ };
159
+ }
160
+ };
161
+
162
+ /**
163
+ * Convert filter state to SDK Filter format
164
+ * Supports both flat and nested filter structures
165
+ */
166
+ const buildFilterPayload = (
167
+ conditions: FilterConditionWithId[],
168
+ logicalOperator: LogicalOperator
169
+ ): Filter | undefined => {
170
+ if (conditions.length === 0) {
171
+ return undefined;
172
+ }
173
+
174
+ const validConditions = conditions.filter(c => c.isValid);
175
+ if (validConditions.length === 0) {
176
+ return undefined;
177
+ }
178
+
179
+ return {
180
+ Operator: logicalOperator,
181
+ Condition: validConditions.map(c => convertConditionToAPI(c))
182
+ };
183
+ };
184
+
185
+ // ============================================================
186
+ // MAIN HOOK
187
+ // ============================================================
188
+
189
+ export function useFilter<T = any>(
190
+ options: UseFilterOptions<T> = {}
191
+ ): UseFilterReturn<T> {
192
+ // ============================================================
193
+ // STATE MANAGEMENT
194
+ // ============================================================
195
+
196
+ const [filterState, setFilterState] = useState<FilterState>({
197
+ logicalOperator: options.initialLogicalOperator || "And",
198
+ conditions: options.initialConditions || []
199
+ });
200
+
201
+ // Store initial state for reset functionality
202
+ const [initialState] = useState<FilterState>({
203
+ logicalOperator: options.initialLogicalOperator || "And",
204
+ conditions: options.initialConditions || []
205
+ });
206
+
207
+ // ============================================================
208
+ // VALIDATION
209
+ // ============================================================
210
+
211
+ const validateCondition = useCallback((condition: Partial<FilterConditionWithId>): ValidationResult => {
212
+ return validateFilterCondition(condition, options.fieldDefinitions);
213
+ }, [options.fieldDefinitions]);
214
+
215
+ const validateAllConditions = useCallback((): ValidationResult => {
216
+ const allErrors: string[] = [];
217
+
218
+ filterState.conditions.forEach(condition => {
219
+ const validation = validateCondition(condition);
220
+ if (!validation.isValid) {
221
+ allErrors.push(...validation.errors.map(err => `Condition ${condition.id}: ${err}`));
222
+ }
223
+ });
224
+
225
+ return {
226
+ isValid: allErrors.length === 0,
227
+ errors: allErrors
228
+ };
229
+ }, [filterState.conditions, validateCondition]);
230
+
231
+ // ============================================================
232
+ // CONDITION MANAGEMENT
233
+ // ============================================================
234
+
235
+ const addCondition = useCallback((condition: TypedFilterConditionInput<T>): string => {
236
+ const id = generateId();
237
+ // Convert typed input to internal format (using unknown for type narrowing)
238
+ const internalCondition = condition as unknown as Omit<FilterConditionWithId, 'id' | 'isValid'>;
239
+ const validation = validateCondition(internalCondition);
240
+
241
+ const newCondition: FilterConditionWithId = {
242
+ ...internalCondition,
243
+ id,
244
+ isValid: validation.isValid,
245
+ validationErrors: validation.errors
246
+ };
247
+
248
+ setFilterState(prev => ({
249
+ ...prev,
250
+ conditions: [...prev.conditions, newCondition]
251
+ }));
252
+
253
+ if (options.onConditionAdd) {
254
+ options.onConditionAdd(newCondition);
255
+ }
256
+
257
+ return id;
258
+ }, [validateCondition, options]);
259
+
260
+ const updateCondition = useCallback((id: string, updates: Partial<TypedFilterConditionInput<T>>): boolean => {
261
+ let found = false;
262
+ // Convert typed input to internal format (using unknown for type narrowing)
263
+ const internalUpdates = updates as unknown as Partial<FilterConditionWithId>;
264
+
265
+ setFilterState(prev => ({
266
+ ...prev,
267
+ conditions: prev.conditions.map(condition => {
268
+ if (condition.id === id) {
269
+ found = true;
270
+ const updatedCondition = { ...condition, ...internalUpdates };
271
+ const validation = validateCondition(updatedCondition);
272
+
273
+ const finalCondition = {
274
+ ...updatedCondition,
275
+ isValid: validation.isValid,
276
+ validationErrors: validation.errors
277
+ };
278
+
279
+ if (options.onConditionUpdate) {
280
+ options.onConditionUpdate(finalCondition);
281
+ }
282
+
283
+ return finalCondition;
284
+ }
285
+ return condition;
286
+ })
287
+ }));
288
+
289
+ return found;
290
+ }, [validateCondition, options]);
291
+
292
+ const removeCondition = useCallback((id: string): boolean => {
293
+ let found = false;
294
+
295
+ setFilterState(prev => ({
296
+ ...prev,
297
+ conditions: prev.conditions.filter(condition => {
298
+ if (condition.id === id) {
299
+ found = true;
300
+ if (options.onConditionRemove) {
301
+ options.onConditionRemove(id);
302
+ }
303
+ return false;
304
+ }
305
+ return true;
306
+ })
307
+ }));
308
+
309
+ return found;
310
+ }, [options]);
311
+
312
+ const clearConditions = useCallback(() => {
313
+ setFilterState(prev => ({
314
+ ...prev,
315
+ conditions: []
316
+ }));
317
+ }, []);
318
+
319
+ const getCondition = useCallback((id: string): FilterConditionWithId | undefined => {
320
+ return filterState.conditions.find(condition => condition.id === id);
321
+ }, [filterState.conditions]);
322
+
323
+ // ============================================================
324
+ // LOGICAL OPERATOR MANAGEMENT
325
+ // ============================================================
326
+
327
+ const setLogicalOperator = useCallback((operator: LogicalOperator) => {
328
+ setFilterState(prev => ({
329
+ ...prev,
330
+ logicalOperator: operator
331
+ }));
332
+ }, []);
333
+
334
+ // ============================================================
335
+ // BULK OPERATIONS
336
+ // ============================================================
337
+
338
+ const setConditions = useCallback((conditions: FilterConditionWithId[]) => {
339
+ const validatedConditions = conditions.map(condition => {
340
+ const validation = validateCondition(condition);
341
+ return {
342
+ ...condition,
343
+ isValid: validation.isValid,
344
+ validationErrors: validation.errors
345
+ };
346
+ });
347
+
348
+ setFilterState(prev => ({
349
+ ...prev,
350
+ conditions: validatedConditions
351
+ }));
352
+ }, [validateCondition]);
353
+
354
+ const replaceCondition = useCallback((id: string, newCondition: TypedFilterConditionInput<T>): boolean => {
355
+ let found = false;
356
+ // Convert typed input to internal format (using unknown for type narrowing)
357
+ const internalCondition = newCondition as unknown as Omit<FilterConditionWithId, 'id' | 'isValid'>;
358
+
359
+ setFilterState(prev => ({
360
+ ...prev,
361
+ conditions: prev.conditions.map(condition => {
362
+ if (condition.id === id) {
363
+ found = true;
364
+ const validation = validateCondition(internalCondition);
365
+ return {
366
+ ...internalCondition,
367
+ id,
368
+ isValid: validation.isValid,
369
+ validationErrors: validation.errors
370
+ };
371
+ }
372
+ return condition;
373
+ })
374
+ }));
375
+
376
+ return found;
377
+ }, [validateCondition]);
378
+
379
+ // ============================================================
380
+ // STATE MANAGEMENT
381
+ // ============================================================
382
+
383
+ const exportState = useCallback((): FilterState => ({
384
+ logicalOperator: filterState.logicalOperator,
385
+ conditions: filterState.conditions.map(condition => ({ ...condition })) // Deep copy
386
+ }), [filterState]);
387
+
388
+ const importState = useCallback((state: FilterState) => {
389
+ const validatedConditions = state.conditions.map(condition => {
390
+ const validation = validateCondition(condition);
391
+ return {
392
+ ...condition,
393
+ isValid: validation.isValid,
394
+ validationErrors: validation.errors
395
+ };
396
+ });
397
+
398
+ setFilterState({
399
+ logicalOperator: state.logicalOperator,
400
+ conditions: validatedConditions
401
+ });
402
+ }, [validateCondition]);
403
+
404
+ const resetToInitial = useCallback(() => {
405
+ setFilterState({ ...initialState });
406
+ }, [initialState]);
407
+
408
+ // ============================================================
409
+ // COMPUTED VALUES
410
+ // ============================================================
411
+
412
+ const filterPayload = useMemo(() =>
413
+ buildFilterPayload(filterState.conditions, filterState.logicalOperator),
414
+ [filterState.conditions, filterState.logicalOperator]
415
+ );
416
+
417
+ const validationErrors = useMemo((): ValidationError[] => {
418
+ const errors: ValidationError[] = [];
419
+
420
+ filterState.conditions.forEach(condition => {
421
+ if (!condition.isValid && condition.validationErrors) {
422
+ condition.validationErrors.forEach(error => {
423
+ errors.push({
424
+ conditionId: condition.id,
425
+ field: condition.lhsField || '', // Empty string for logical operators
426
+ message: error
427
+ });
428
+ });
429
+ }
430
+ });
431
+
432
+ return errors;
433
+ }, [filterState.conditions]);
434
+
435
+ const isValid = useMemo(() =>
436
+ filterState.conditions.every(condition => condition.isValid),
437
+ [filterState.conditions]
438
+ );
439
+
440
+ const getConditionCount = useCallback(() => filterState.conditions.length, [filterState.conditions]);
441
+ const hasConditions = useMemo(() => filterState.conditions.length > 0, [filterState.conditions]);
442
+ const canAddCondition = useMemo(() => true, []); // Can always add more conditions
443
+
444
+ // ============================================================
445
+ // VALIDATION ERROR CALLBACK
446
+ // ============================================================
447
+
448
+ useEffect(() => {
449
+ if (options.onValidationError && validationErrors.length > 0) {
450
+ options.onValidationError(validationErrors);
451
+ }
452
+ }, [validationErrors, options]);
453
+
454
+ // ============================================================
455
+ // RETURN OBJECT
456
+ // ============================================================
457
+
458
+ return {
459
+ // Current state
460
+ conditions: filterState.conditions,
461
+ logicalOperator: filterState.logicalOperator,
462
+ filterPayload,
463
+ isValid,
464
+ validationErrors,
465
+
466
+ // Condition management
467
+ addCondition,
468
+ updateCondition,
469
+ removeCondition,
470
+ clearConditions,
471
+ getCondition,
472
+
473
+ // Logical operator management
474
+ setLogicalOperator,
475
+
476
+ // Bulk operations
477
+ setConditions,
478
+ replaceCondition,
479
+
480
+ // Validation
481
+ validateCondition,
482
+ validateAllConditions,
483
+
484
+ // State management
485
+ exportState,
486
+ importState,
487
+ resetToInitial,
488
+
489
+ // Utilities
490
+ getConditionCount,
491
+ hasConditions,
492
+ canAddCondition
493
+ };
494
+ }