@ram_28/kf-ai-sdk 1.0.14 → 1.0.15

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.
@@ -5,16 +5,21 @@
5
5
  The `useFilter` hook is a React hook for managing filter conditions with:
6
6
  - Type-safe filter condition management
7
7
  - Support for simple conditions and nested logical groups
8
- - Automatic validation
9
- - API payload generation
10
- - State import/export
11
- - Integrated with useTable hook
8
+ - API payload generation (id fields stripped automatically)
9
+ - Integrated with useTable and useKanban hooks
12
10
 
13
11
  ## Import
14
12
 
15
13
  ```typescript
16
- import { useFilter } from "kf-ai-sdk";
17
- // Or use through useTable's filter property
14
+ import { useFilter, isCondition, isConditionGroup } from "@ram_28/kf-ai-sdk/filter";
15
+ import type {
16
+ UseFilterOptionsType,
17
+ UseFilterReturnType,
18
+ ConditionType,
19
+ ConditionGroupType,
20
+ ConditionGroupOperatorType,
21
+ FilterType,
22
+ } from "@ram_28/kf-ai-sdk/filter/types";
18
23
  ```
19
24
 
20
25
  ## Basic Usage
@@ -22,28 +27,27 @@ import { useFilter } from "kf-ai-sdk";
22
27
  ### Standalone Usage
23
28
 
24
29
  ```typescript
25
- const filter = useFilter<ProductType>({
26
- initialLogicalOperator: "And",
27
- fieldDefinitions: {
28
- Price: {
29
- type: "number",
30
- allowedOperators: ["EQ", "GT", "LT", "GTE", "LTE", "Between"],
31
- },
32
- Category: {
33
- type: "string",
34
- allowedOperators: ["EQ", "NE", "Contains"],
35
- },
36
- },
37
- onValidationError: (errors) => console.log(errors),
30
+ const filter = useFilter({
31
+ initialOperator: "And",
38
32
  });
39
33
 
40
- // Add a condition
34
+ // Add a condition at root level
41
35
  const id = filter.addCondition({
42
- lhsField: "Price",
43
- operator: "GT",
44
- rhsValue: 100,
45
- rhsType: "Constant",
36
+ Operator: "GT",
37
+ LHSField: "Price",
38
+ RHSValue: 100,
39
+ RHSType: "Constant",
46
40
  });
41
+
42
+ // Add a nested group
43
+ const groupId = filter.addConditionGroup("Or");
44
+
45
+ // Add conditions to the group
46
+ filter.addCondition({
47
+ Operator: "EQ",
48
+ LHSField: "Category",
49
+ RHSValue: "Electronics",
50
+ }, groupId);
47
51
  ```
48
52
 
49
53
  ### Through useTable
@@ -57,125 +61,102 @@ const table = useTable<ProductType>({
57
61
 
58
62
  // Use table.filter for all filter operations
59
63
  table.filter.addCondition({
60
- lhsField: "Category",
61
- operator: "EQ",
62
- rhsValue: "Electronics",
63
- rhsType: "Constant",
64
+ Operator: "EQ",
65
+ LHSField: "Category",
66
+ RHSValue: "Electronics",
67
+ RHSType: "Constant",
64
68
  });
69
+
70
+ // Clear all filters
71
+ table.filter.clearAllConditions();
65
72
  ```
66
73
 
67
74
  ## Configuration Options
68
75
 
69
- ### UseFilterOptions<T>
76
+ ### UseFilterOptionsType
70
77
 
71
78
  | Property | Type | Required | Default | Description |
72
79
  |----------|------|----------|---------|-------------|
73
- | `initialConditions` | `FilterConditionWithId[]` | No | `[]` | Initial filter conditions |
74
- | `initialLogicalOperator` | `LogicalOperator` | No | `"And"` | Initial logical operator |
75
- | `fieldDefinitions` | `Record<keyof T, FieldDefinition>` | No | - | Field definitions for validation |
76
- | `validateOnChange` | `boolean` | No | `true` | Validate conditions on change |
77
- | `onConditionAdd` | `(condition) => void` | No | - | Callback when condition added |
78
- | `onConditionUpdate` | `(condition) => void` | No | - | Callback when condition updated |
79
- | `onConditionRemove` | `(conditionId) => void` | No | - | Callback when condition removed |
80
- | `onValidationError` | `(errors) => void` | No | - | Callback for validation errors |
81
-
82
- ### FieldDefinition
83
-
84
- | Property | Type | Description |
85
- |----------|------|-------------|
86
- | `type` | `'string' \| 'number' \| 'date' \| 'boolean' \| 'currency' \| 'select'` | Field data type |
87
- | `allowedOperators` | `FilterOperator[]` | Operators allowed for this field |
88
- | `validateValue` | `(value, operator) => ValidationResult` | Custom validation function |
89
- | `transformValue` | `(value) => any` | Value transformation function |
90
- | `selectOptions` | `Array<{label, value}>` | Options for select fields |
80
+ | `initialConditions` | `Array<ConditionType \| ConditionGroupType>` | No | `[]` | Initial filter conditions |
81
+ | `initialOperator` | `ConditionGroupOperatorType` | No | `"And"` | Initial root operator |
91
82
 
92
83
  ## Return Value
93
84
 
94
- ### UseFilterReturn<T>
85
+ ### UseFilterReturnType
95
86
 
96
- #### Current State
87
+ #### State (read-only)
97
88
  | Property | Type | Description |
98
89
  |----------|------|-------------|
99
- | `conditions` | `FilterConditionWithId[]` | Current filter conditions |
100
- | `logicalOperator` | `LogicalOperator` | Current logical operator ("And" \| "Or") |
101
- | `filterPayload` | `Filter \| undefined` | SDK-formatted filter for API |
102
- | `isValid` | `boolean` | All conditions are valid |
103
- | `validationErrors` | `ValidationError[]` | Current validation errors |
90
+ | `operator` | `ConditionGroupOperatorType` | Current root operator ("And" \| "Or" \| "Not") |
91
+ | `items` | `Array<ConditionType \| ConditionGroupType>` | Current filter items (with id populated) |
92
+ | `payload` | `FilterType \| undefined` | Ready-to-use API payload (id stripped, undefined if no conditions) |
93
+ | `hasConditions` | `boolean` | Whether any conditions exist |
104
94
 
105
- #### Condition Management
106
- | Property | Type | Description |
107
- |----------|------|-------------|
108
- | `addCondition` | `(condition) => string` | Add condition, returns ID |
109
- | `updateCondition` | `(id, updates) => boolean` | Update condition |
110
- | `removeCondition` | `(id) => boolean` | Remove condition |
111
- | `clearConditions` | `() => void` | Clear all conditions |
112
- | `getCondition` | `(id) => FilterConditionWithId` | Get specific condition |
95
+ #### Add Operations
96
+ | Method | Type | Description |
97
+ |--------|------|-------------|
98
+ | `addCondition` | `(condition: Omit<ConditionType, "id">, parentId?: string) => string` | Add a leaf condition. If parentId omitted, adds at root level. Returns the id of the created condition. |
99
+ | `addConditionGroup` | `(operator: ConditionGroupOperatorType, parentId?: string) => string` | Add a condition group. If parentId omitted, adds at root level. Returns the id of the created group. |
113
100
 
114
- #### Logical Operator
115
- | Property | Type | Description |
116
- |----------|------|-------------|
117
- | `setLogicalOperator` | `(operator) => void` | Set logical operator |
101
+ #### Update Operations
102
+ | Method | Type | Description |
103
+ |--------|------|-------------|
104
+ | `updateCondition` | `(id: string, updates: Partial<Omit<ConditionType, "id">>) => void` | Update a leaf condition by id |
105
+ | `updateGroupOperator` | `(id: string, operator: ConditionGroupOperatorType) => void` | Update a condition group's operator by id |
118
106
 
119
- #### Bulk Operations
120
- | Property | Type | Description |
121
- |----------|------|-------------|
122
- | `setConditions` | `(conditions) => void` | Replace all conditions |
123
- | `replaceCondition` | `(id, newCondition) => boolean` | Replace specific condition |
107
+ #### Remove & Access
108
+ | Method | Type | Description |
109
+ |--------|------|-------------|
110
+ | `removeCondition` | `(id: string) => void` | Remove a condition or group by id |
111
+ | `getCondition` | `(id: string) => ConditionType \| ConditionGroupType \| undefined` | Get a condition or group by id |
124
112
 
125
- #### Validation
126
- | Property | Type | Description |
127
- |----------|------|-------------|
128
- | `validateCondition` | `(condition) => ValidationResult` | Validate single condition |
129
- | `validateAllConditions` | `() => ValidationResult` | Validate all conditions |
113
+ #### Utility
114
+ | Method | Type | Description |
115
+ |--------|------|-------------|
116
+ | `clearAllConditions` | `() => void` | Clear all conditions |
117
+ | `setRootOperator` | `(op: ConditionGroupOperatorType) => void` | Set the root operator for combining conditions |
130
118
 
131
- #### State Management
132
- | Property | Type | Description |
133
- |----------|------|-------------|
134
- | `exportState` | `() => FilterState` | Export current state |
135
- | `importState` | `(state) => void` | Import state |
136
- | `resetToInitial` | `() => void` | Reset to initial state |
119
+ ## Types
137
120
 
138
- #### Utilities
139
- | Property | Type | Description |
140
- |----------|------|-------------|
141
- | `getConditionCount` | `() => number` | Get number of conditions |
142
- | `hasConditions` | `boolean` | Whether any conditions exist |
143
- | `canAddCondition` | `boolean` | Whether more can be added (always true) |
121
+ ### ConditionType (Leaf Condition)
144
122
 
145
- ## Filter Condition Types
123
+ ```typescript
124
+ interface ConditionType {
125
+ id?: string; // Auto-generated unique identifier
126
+ Operator: ConditionOperatorType;
127
+ LHSField: string; // Field name to filter on
128
+ RHSValue: any; // Value to compare against
129
+ RHSType?: FilterRHSTypeType; // "Constant" | "BOField" | "AppVariable"
130
+ }
131
+ ```
146
132
 
147
- ### FilterConditionWithId
133
+ ### ConditionGroupType (Nested Group)
148
134
 
149
135
  ```typescript
150
- interface FilterConditionWithId {
151
- id: string; // Unique identifier
152
- operator: FilterOperator | LogicalOperator;
153
- lhsField?: string; // Field name (for condition operators)
154
- rhsValue?: any; // Value to compare
155
- rhsType?: FilterRHSType; // "Constant" | "Field"
156
- children?: FilterConditionWithId[]; // Nested conditions (for logical operators)
157
- isValid: boolean; // Validation state
158
- validationErrors?: string[]; // Specific errors
136
+ interface ConditionGroupType {
137
+ id?: string; // Auto-generated unique identifier
138
+ Operator: ConditionGroupOperatorType; // "And" | "Or" | "Not"
139
+ Condition: Array<ConditionType | ConditionGroupType>; // Nested items
159
140
  }
160
141
  ```
161
142
 
162
- ### TypedFilterConditionInput<T>
163
-
164
- Type-safe input for addCondition:
143
+ ### Type Guards
165
144
 
166
145
  ```typescript
167
- interface TypedFilterConditionInput<T> {
168
- operator: FilterOperator | LogicalOperator;
169
- lhsField?: keyof T & string; // Constrained to keys of T
170
- rhsValue?: T[keyof T] | T[keyof T][] | any;
171
- rhsType?: FilterRHSType;
172
- children?: TypedFilterConditionInput<T>[];
146
+ // Check if item is a leaf condition
147
+ if (isCondition(item)) {
148
+ console.log(item.LHSField, item.Operator, item.RHSValue);
149
+ }
150
+
151
+ // Check if item is a condition group
152
+ if (isConditionGroup(item)) {
153
+ console.log(item.Operator, item.Condition.length, "nested items");
173
154
  }
174
155
  ```
175
156
 
176
157
  ## Filter Operators
177
158
 
178
- ### Comparison Operators
159
+ ### Comparison Operators (ConditionOperatorType)
179
160
  | Operator | Description | Example |
180
161
  |----------|-------------|---------|
181
162
  | `EQ` | Equal | `Price = 100` |
@@ -202,8 +183,6 @@ interface TypedFilterConditionInput<T> {
202
183
  |----------|-------------|
203
184
  | `Contains` | Contains substring |
204
185
  | `NotContains` | Does not contain substring |
205
- | `StartsWith` | Starts with string |
206
- | `EndsWith` | Ends with string |
207
186
 
208
187
  ### Null Operators
209
188
  | Operator | Description | RHS Required |
@@ -211,26 +190,26 @@ interface TypedFilterConditionInput<T> {
211
190
  | `Empty` | Is empty/null | No |
212
191
  | `NotEmpty` | Is not empty/null | No |
213
192
 
214
- ### Logical Operators (for nesting)
193
+ ### Group Operators (ConditionGroupOperatorType)
215
194
  | Operator | Description |
216
195
  |----------|-------------|
217
- | `And` | All children must match |
218
- | `Or` | Any child must match |
219
- | `Not` | Negate child (single child only) |
196
+ | `And` | All conditions must match |
197
+ | `Or` | Any condition must match |
198
+ | `Not` | Negate conditions |
220
199
 
221
- ## E-Commerce App Usage Examples
200
+ ## Usage Examples
222
201
 
223
- ### Category Filter (BuyerProductListPage.tsx)
202
+ ### Simple Category Filter
224
203
 
225
204
  ```typescript
226
- const handleCategoryChange = (category: string) => {
227
- table.filter.clearConditions();
205
+ const filterByCategory = (category: string) => {
206
+ table.filter.clearAllConditions();
228
207
  if (category !== "all") {
229
208
  table.filter.addCondition({
230
- lhsField: "Category",
231
- operator: "EQ",
232
- rhsValue: category,
233
- rhsType: "Constant",
209
+ Operator: "EQ",
210
+ LHSField: "Category",
211
+ RHSValue: category,
212
+ RHSType: "Constant",
234
213
  });
235
214
  }
236
215
  };
@@ -239,230 +218,126 @@ const handleCategoryChange = (category: string) => {
239
218
  ### Price Range Filter
240
219
 
241
220
  ```typescript
242
- const PRICE_RANGES = [
243
- { label: "Under $25", min: 0, max: 25 },
244
- { label: "$25 to $50", min: 25, max: 50 },
245
- { label: "$50 to $100", min: 50, max: 100 },
246
- { label: "$100 to $200", min: 100, max: 200 },
247
- { label: "$200 & Above", min: 200, max: null },
248
- ];
249
-
250
- const handlePriceChange = (rangeLabel: string) => {
251
- const range = PRICE_RANGES.find((r) => r.label === rangeLabel);
252
- if (range) {
253
- if (range.max === null) {
254
- // "$200 & Above" - use GTE
255
- table.filter.addCondition({
256
- lhsField: "Price",
257
- operator: "GTE",
258
- rhsValue: range.min,
259
- rhsType: "Constant",
260
- });
261
- } else if (range.min === 0) {
262
- // "Under $25" - use LT
263
- table.filter.addCondition({
264
- lhsField: "Price",
265
- operator: "LT",
266
- rhsValue: range.max,
267
- rhsType: "Constant",
268
- });
269
- } else {
270
- // Range like "$25 to $50" - use Between
271
- table.filter.addCondition({
272
- lhsField: "Price",
273
- operator: "Between",
274
- rhsValue: [range.min, range.max],
275
- rhsType: "Constant",
276
- });
277
- }
221
+ const handlePriceRange = (min: number, max: number | null) => {
222
+ if (max === null) {
223
+ // Open-ended range: $200 & Above
224
+ table.filter.addCondition({
225
+ Operator: "GTE",
226
+ LHSField: "Price",
227
+ RHSValue: min,
228
+ RHSType: "Constant",
229
+ });
230
+ } else {
231
+ // Closed range: $25 to $50
232
+ table.filter.addCondition({
233
+ Operator: "Between",
234
+ LHSField: "Price",
235
+ RHSValue: [min, max],
236
+ RHSType: "Constant",
237
+ });
278
238
  }
279
239
  };
280
240
  ```
281
241
 
282
- ### Multiple Filters Combined
242
+ ### Complex Nested Filter
283
243
 
284
244
  ```typescript
285
- // Apply both category and price filters
286
- const applyFilters = (category: string, priceRange: string | null) => {
287
- table.filter.clearConditions();
288
-
289
- // Apply category filter
290
- if (category !== "all") {
291
- table.filter.addCondition({
292
- lhsField: "Category",
293
- operator: "EQ",
294
- rhsValue: category,
295
- rhsType: "Constant",
296
- });
297
- }
298
-
299
- // Apply price filter
300
- if (priceRange) {
301
- const range = PRICE_RANGES.find((r) => r.label === priceRange);
302
- if (range) {
303
- table.filter.addCondition({
304
- lhsField: "Price",
305
- operator: "Between",
306
- rhsValue: [range.min, range.max],
307
- rhsType: "Constant",
308
- });
309
- }
310
- }
245
+ // Build: (Category = "Electronics") AND (Price > 100 OR OnSale = true)
246
+ const buildComplexFilter = () => {
247
+ filter.clearAllConditions();
248
+
249
+ // Add root condition
250
+ filter.addCondition({
251
+ Operator: "EQ",
252
+ LHSField: "Category",
253
+ RHSValue: "Electronics",
254
+ });
255
+
256
+ // Create nested OR group
257
+ const groupId = filter.addConditionGroup("Or");
258
+
259
+ // Add conditions to the group
260
+ filter.addCondition({
261
+ Operator: "GT",
262
+ LHSField: "Price",
263
+ RHSValue: 100,
264
+ }, groupId);
265
+
266
+ filter.addCondition({
267
+ Operator: "EQ",
268
+ LHSField: "OnSale",
269
+ RHSValue: true,
270
+ }, groupId);
311
271
  };
312
272
  ```
313
273
 
314
- ### Clear All Filters
274
+ ### Toggle Root Operator
315
275
 
316
276
  ```typescript
317
- <Button
318
- onClick={() => {
319
- table.search.clear();
320
- table.filter.clearConditions();
321
- setSelectedCategory("all");
322
- setSelectedPriceRange(null);
323
- }}
324
- >
325
- Clear All Filters
326
- </Button>
277
+ const toggleFilterLogic = () => {
278
+ const next = filter.operator === "And" ? "Or" : "And";
279
+ filter.setRootOperator(next);
280
+ };
327
281
  ```
328
282
 
329
- ## Nested Logical Groups
330
-
331
- Support for complex filter expressions:
283
+ ### Render Filter Tree
332
284
 
333
285
  ```typescript
334
- // (Category = "Electronics" AND Price > 100) OR Stock = 0
335
- filter.addCondition({
336
- operator: "Or",
337
- children: [
338
- {
339
- operator: "And",
340
- children: [
341
- { operator: "EQ", lhsField: "Category", rhsValue: "Electronics", rhsType: "Constant" },
342
- { operator: "GT", lhsField: "Price", rhsValue: 100, rhsType: "Constant" },
343
- ],
344
- },
345
- { operator: "EQ", lhsField: "Stock", rhsValue: 0, rhsType: "Constant" },
346
- ],
347
- });
286
+ const renderFilterItem = (item: ConditionType | ConditionGroupType) => {
287
+ if (isCondition(item)) {
288
+ return (
289
+ <div key={item.id}>
290
+ {item.LHSField} {item.Operator} {String(item.RHSValue)}
291
+ <button onClick={() => filter.removeCondition(item.id!)}>Remove</button>
292
+ </div>
293
+ );
294
+ }
295
+
296
+ if (isConditionGroup(item)) {
297
+ return (
298
+ <div key={item.id}>
299
+ <span>{item.Operator} Group</span>
300
+ <button onClick={() => filter.addCondition({ Operator: "EQ", LHSField: "Field", RHSValue: "" }, item.id!)}>
301
+ + Condition
302
+ </button>
303
+ <button onClick={() => filter.addConditionGroup("And", item.id!)}>+ Group</button>
304
+ {item.Condition.map((child) => renderFilterItem(child))}
305
+ </div>
306
+ );
307
+ }
308
+ };
348
309
  ```
349
310
 
350
311
  ## API Payload Format
351
312
 
352
- The hook generates payloads compatible with the backend API:
313
+ The `payload` property automatically strips id fields for API compatibility:
353
314
 
354
315
  ```typescript
355
- // Single condition
356
- {
357
- Operator: "And",
358
- Condition: [
359
- {
360
- Operator: "EQ",
361
- LHSField: "Category",
362
- RHSValue: "Electronics",
363
- RHSType: "Constant"
364
- }
365
- ]
366
- }
367
-
368
- // Multiple conditions
316
+ // With conditions
369
317
  {
370
318
  Operator: "And",
371
319
  Condition: [
372
320
  { Operator: "EQ", LHSField: "Category", RHSValue: "Electronics", RHSType: "Constant" },
373
- { Operator: "GTE", LHSField: "Price", RHSValue: 50, RHSType: "Constant" }
374
- ]
375
- }
376
-
377
- // Nested logical groups
378
- {
379
- Operator: "Or",
380
- Condition: [
381
321
  {
382
- Operator: "And",
383
- Condition: [...]
384
- },
385
- { Operator: "EQ", LHSField: "Stock", RHSValue: 0, RHSType: "Constant" }
322
+ Operator: "Or",
323
+ Condition: [
324
+ { Operator: "GT", LHSField: "Price", RHSValue: 100 },
325
+ { Operator: "EQ", LHSField: "OnSale", RHSValue: true }
326
+ ]
327
+ }
386
328
  ]
387
329
  }
388
- ```
389
-
390
- ## Validation
391
-
392
- ### Automatic Validation
393
330
 
394
- Conditions are validated on add/update:
395
- - Operator is required
396
- - lhsField is required for condition operators
397
- - rhsValue format matches operator requirements
398
- - Field-specific validation if fieldDefinitions provided
399
-
400
- ### Validation Errors
401
-
402
- ```typescript
403
- interface ValidationError {
404
- conditionId: string;
405
- field: string;
406
- message: string;
407
- }
408
-
409
- // Access validation errors
410
- filter.validationErrors.forEach((error) => {
411
- console.log(`${error.field}: ${error.message}`);
412
- });
331
+ // No conditions returns undefined
332
+ filter.payload // undefined when items.length === 0
413
333
  ```
414
334
 
415
- ### Custom Field Validation
335
+ ## Integration with useTable/useKanban
416
336
 
417
- ```typescript
418
- const filter = useFilter<Product>({
419
- fieldDefinitions: {
420
- Price: {
421
- type: "number",
422
- allowedOperators: ["EQ", "GT", "LT", "GTE", "LTE", "Between"],
423
- validateValue: (value, operator) => {
424
- if (value < 0) {
425
- return { isValid: false, errors: ["Price cannot be negative"] };
426
- }
427
- return { isValid: true, errors: [] };
428
- },
429
- },
430
- },
431
- });
432
- ```
433
-
434
- ## State Management
435
-
436
- ### Export/Import State
437
-
438
- ```typescript
439
- // Export current state
440
- const savedState = filter.exportState();
441
- localStorage.setItem("filters", JSON.stringify(savedState));
442
-
443
- // Import saved state
444
- const savedState = JSON.parse(localStorage.getItem("filters"));
445
- filter.importState(savedState);
446
- ```
447
-
448
- ### Reset to Initial
449
-
450
- ```typescript
451
- filter.resetToInitial();
452
- ```
453
-
454
- ## Key Behaviors
455
-
456
- 1. **Auto-validation**: Conditions validated on add/update
457
- 2. **Type-safe lhsField**: Constrained to `keyof T` at compile time
458
- 3. **Only valid in payload**: Only valid conditions included in filterPayload
459
- 4. **UUID generation**: Uses `crypto.randomUUID()` for condition IDs
460
- 5. **Deep copy on export**: Exported state is a deep copy
461
- 6. **Nested group support**: Full support for And/Or/Not logical nesting
462
-
463
- ## Integration with useTable
464
-
465
- The useFilter hook is automatically integrated into useTable:
337
+ Filter changes automatically:
338
+ - Reset pagination to page 1
339
+ - Trigger data refetch
340
+ - Update count query
466
341
 
467
342
  ```typescript
468
343
  const table = useTable<Product>({
@@ -472,26 +347,19 @@ const table = useTable<Product>({
472
347
  filters: [...],
473
348
  filterOperator: "And",
474
349
  },
475
- onFilterError: (errors) => console.log(errors),
476
350
  });
477
351
 
478
352
  // Access filter through table.filter
479
353
  table.filter.addCondition({...});
480
- table.filter.clearConditions();
481
- table.filter.conditions;
482
- table.filter.isValid;
354
+ table.filter.clearAllConditions();
355
+ table.filter.hasConditions;
356
+ table.filter.payload;
483
357
  ```
484
358
 
485
- Filter changes automatically:
486
- - Reset pagination to page 1
487
- - Trigger data refetch
488
- - Update count query
489
-
490
- ## Architecture Notes
359
+ ## Key Behaviors
491
360
 
492
- - Uses `useState` for internal state management
493
- - Generates UUIDs for condition tracking
494
- - Recursive validation for nested groups
495
- - Memoized filterPayload computation
496
- - Type-safe with full TypeScript generics
497
- - Integrates seamlessly with useTable
361
+ 1. **Auto-generated IDs**: All conditions get unique IDs via `generateId()`
362
+ 2. **Payload strips IDs**: The `payload` property excludes id fields for API calls
363
+ 3. **Recursive operations**: All operations work on nested groups
364
+ 4. **Type guards**: Use `isCondition()` and `isConditionGroup()` for type-safe operations
365
+ 5. **Optional parentId**: `addCondition` and `addConditionGroup` add to root when parentId is omitted