@ram_28/kf-ai-sdk 1.0.7 → 1.0.8
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/api/index.d.ts +1 -1
- package/dist/api/index.d.ts.map +1 -1
- package/dist/components/hooks/useFilter/index.d.ts +3 -3
- package/dist/components/hooks/useFilter/index.d.ts.map +1 -1
- package/dist/components/hooks/useFilter/types.d.ts +83 -109
- package/dist/components/hooks/useFilter/types.d.ts.map +1 -1
- package/dist/components/hooks/useFilter/useFilter.d.ts +1 -1
- package/dist/components/hooks/useFilter/useFilter.d.ts.map +1 -1
- package/dist/components/hooks/useKanban/index.d.ts +1 -1
- package/dist/components/hooks/useKanban/index.d.ts.map +1 -1
- package/dist/components/hooks/useKanban/types.d.ts +6 -46
- package/dist/components/hooks/useKanban/types.d.ts.map +1 -1
- package/dist/components/hooks/useKanban/useKanban.d.ts.map +1 -1
- package/dist/components/hooks/useTable/types.d.ts +5 -33
- package/dist/components/hooks/useTable/types.d.ts.map +1 -1
- package/dist/components/hooks/useTable/useTable.d.ts +0 -5
- package/dist/components/hooks/useTable/useTable.d.ts.map +1 -1
- package/dist/index.cjs +12 -12
- package/dist/index.mjs +2130 -2368
- package/dist/types/common.d.ts +35 -26
- package/dist/types/common.d.ts.map +1 -1
- package/package.json +1 -1
- package/sdk/api/index.ts +7 -3
- package/sdk/components/hooks/useFilter/index.ts +19 -18
- package/sdk/components/hooks/useFilter/types.ts +157 -123
- package/sdk/components/hooks/useFilter/useFilter.ts +259 -393
- package/sdk/components/hooks/useKanban/index.ts +0 -1
- package/sdk/components/hooks/useKanban/types.ts +8 -66
- package/sdk/components/hooks/useKanban/useKanban.ts +14 -75
- package/sdk/components/hooks/useTable/types.ts +7 -60
- package/sdk/components/hooks/useTable/useTable.ts +13 -121
- package/sdk/types/common.ts +42 -26
- package/dist/components/hooks/useFilter/payloadBuilder.utils.d.ts +0 -33
- package/dist/components/hooks/useFilter/payloadBuilder.utils.d.ts.map +0 -1
- package/sdk/components/hooks/useFilter/payloadBuilder.utils.ts +0 -298
|
@@ -1,473 +1,339 @@
|
|
|
1
|
-
import { useState, useCallback, useMemo
|
|
2
|
-
import type { Filter, LogicalOperator } from "../../../types/common";
|
|
1
|
+
import { useState, useCallback, useMemo } from "react";
|
|
3
2
|
import type {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
} from "./types";
|
|
3
|
+
Condition,
|
|
4
|
+
ConditionGroup,
|
|
5
|
+
ConditionGroupOperator,
|
|
6
|
+
Filter,
|
|
7
|
+
} from "../../../types/common";
|
|
8
|
+
import type { UseFilterOptions, UseFilterReturn } from "./types";
|
|
9
|
+
import { isConditionGroup } from "./types";
|
|
12
10
|
|
|
13
11
|
// ============================================================
|
|
14
|
-
//
|
|
12
|
+
// HELPER FUNCTIONS
|
|
15
13
|
// ============================================================
|
|
16
14
|
|
|
15
|
+
let idCounter = 0;
|
|
16
|
+
|
|
17
17
|
/**
|
|
18
|
-
* Generate a unique ID for conditions
|
|
18
|
+
* Generate a unique ID for conditions and groups
|
|
19
19
|
*/
|
|
20
20
|
const generateId = (): string => {
|
|
21
|
-
return
|
|
21
|
+
return `filter_${Date.now()}_${++idCounter}`;
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
25
|
+
* Ensure an item has an id
|
|
26
26
|
*/
|
|
27
|
-
const
|
|
28
|
-
|
|
27
|
+
const ensureId = <T extends Condition | ConditionGroup>(item: T): T => {
|
|
28
|
+
if (!item.id) {
|
|
29
|
+
return { ...item, id: generateId() };
|
|
30
|
+
}
|
|
31
|
+
return item;
|
|
29
32
|
};
|
|
30
33
|
|
|
31
34
|
/**
|
|
32
|
-
*
|
|
35
|
+
* Deep clone and ensure all items have ids
|
|
33
36
|
*/
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
):
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// Check if this is a logical operator (nested group)
|
|
45
|
-
if (condition.operator && isLogicalOperator(condition.operator)) {
|
|
46
|
-
// Validate logical group
|
|
47
|
-
if (!condition.children || !Array.isArray(condition.children)) {
|
|
48
|
-
errors.push('Logical operators require a children array');
|
|
49
|
-
} else if (condition.children.length === 0) {
|
|
50
|
-
errors.push('Logical operators require at least one child condition');
|
|
51
|
-
} else if (condition.operator === 'Not' && condition.children.length > 1) {
|
|
52
|
-
errors.push('Not operator can only have one child condition');
|
|
37
|
+
const cloneWithIds = (
|
|
38
|
+
items: Array<Condition | ConditionGroup>
|
|
39
|
+
): Array<Condition | ConditionGroup> => {
|
|
40
|
+
return items.map((item) => {
|
|
41
|
+
const withId = ensureId(item);
|
|
42
|
+
if (isConditionGroup(withId)) {
|
|
43
|
+
return {
|
|
44
|
+
...withId,
|
|
45
|
+
Condition: cloneWithIds(withId.Condition),
|
|
46
|
+
};
|
|
53
47
|
}
|
|
48
|
+
return withId;
|
|
49
|
+
});
|
|
50
|
+
};
|
|
54
51
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Strip id fields from items for API payload
|
|
54
|
+
*/
|
|
55
|
+
const stripIds = (
|
|
56
|
+
items: Array<Condition | ConditionGroup>
|
|
57
|
+
): Array<Condition | ConditionGroup> => {
|
|
58
|
+
return items.map((item) => {
|
|
59
|
+
if (isConditionGroup(item)) {
|
|
60
|
+
const { id, ...rest } = item;
|
|
61
|
+
return {
|
|
62
|
+
...rest,
|
|
63
|
+
Condition: stripIds(item.Condition),
|
|
64
|
+
} as ConditionGroup;
|
|
63
65
|
}
|
|
66
|
+
const { id, ...rest } = item;
|
|
67
|
+
return rest as Condition;
|
|
68
|
+
});
|
|
69
|
+
};
|
|
64
70
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
/**
|
|
72
|
+
* Find an item by id in a tree structure
|
|
73
|
+
*/
|
|
74
|
+
const findById = (
|
|
75
|
+
items: Array<Condition | ConditionGroup>,
|
|
76
|
+
id: string
|
|
77
|
+
): Condition | ConditionGroup | undefined => {
|
|
78
|
+
for (const item of items) {
|
|
79
|
+
if (item.id === id) {
|
|
80
|
+
return item;
|
|
71
81
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
errors.push('Field is required for condition operators');
|
|
82
|
+
if (isConditionGroup(item)) {
|
|
83
|
+
const found = findById(item.Condition, id);
|
|
84
|
+
if (found) return found;
|
|
76
85
|
}
|
|
86
|
+
}
|
|
87
|
+
return undefined;
|
|
88
|
+
};
|
|
77
89
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (!Array.isArray(condition.rhsValue) || condition.rhsValue.length === 0) {
|
|
90
|
-
errors.push('IN/NIN operators require a non-empty array');
|
|
91
|
-
}
|
|
92
|
-
break;
|
|
93
|
-
case 'Empty':
|
|
94
|
-
case 'NotEmpty':
|
|
95
|
-
// These operators don't need RHS values
|
|
96
|
-
break;
|
|
97
|
-
default:
|
|
98
|
-
if (condition.rhsValue === null || condition.rhsValue === undefined || condition.rhsValue === '') {
|
|
99
|
-
errors.push('Value is required for this operator');
|
|
100
|
-
}
|
|
101
|
-
break;
|
|
102
|
-
}
|
|
90
|
+
/**
|
|
91
|
+
* Find a parent group by child id
|
|
92
|
+
*/
|
|
93
|
+
const findParentById = (
|
|
94
|
+
items: Array<Condition | ConditionGroup>,
|
|
95
|
+
childId: string,
|
|
96
|
+
parent: ConditionGroup | null = null
|
|
97
|
+
): ConditionGroup | null => {
|
|
98
|
+
for (const item of items) {
|
|
99
|
+
if (item.id === childId) {
|
|
100
|
+
return parent;
|
|
103
101
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
errors.push('Condition operators should not have children');
|
|
102
|
+
if (isConditionGroup(item)) {
|
|
103
|
+
const found = findParentById(item.Condition, childId, item);
|
|
104
|
+
if (found !== null) return found;
|
|
108
105
|
}
|
|
109
106
|
}
|
|
110
|
-
|
|
111
|
-
return {
|
|
112
|
-
isValid: errors.length === 0,
|
|
113
|
-
errors
|
|
114
|
-
};
|
|
107
|
+
return null;
|
|
115
108
|
};
|
|
116
109
|
|
|
117
110
|
/**
|
|
118
|
-
*
|
|
111
|
+
* Update an item in the tree by id
|
|
119
112
|
*/
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
};
|
|
138
|
-
}
|
|
113
|
+
const updateInTree = (
|
|
114
|
+
items: Array<Condition | ConditionGroup>,
|
|
115
|
+
id: string,
|
|
116
|
+
updater: (item: Condition | ConditionGroup) => Condition | ConditionGroup
|
|
117
|
+
): Array<Condition | ConditionGroup> => {
|
|
118
|
+
return items.map((item) => {
|
|
119
|
+
if (item.id === id) {
|
|
120
|
+
return updater(item);
|
|
121
|
+
}
|
|
122
|
+
if (isConditionGroup(item)) {
|
|
123
|
+
return {
|
|
124
|
+
...item,
|
|
125
|
+
Condition: updateInTree(item.Condition, id, updater),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return item;
|
|
129
|
+
});
|
|
139
130
|
};
|
|
140
131
|
|
|
141
132
|
/**
|
|
142
|
-
*
|
|
143
|
-
* Supports both flat and nested filter structures
|
|
133
|
+
* Remove an item from the tree by id
|
|
144
134
|
*/
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
):
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
135
|
+
const removeFromTree = (
|
|
136
|
+
items: Array<Condition | ConditionGroup>,
|
|
137
|
+
id: string
|
|
138
|
+
): Array<Condition | ConditionGroup> => {
|
|
139
|
+
return items
|
|
140
|
+
.filter((item) => item.id !== id)
|
|
141
|
+
.map((item) => {
|
|
142
|
+
if (isConditionGroup(item)) {
|
|
143
|
+
return {
|
|
144
|
+
...item,
|
|
145
|
+
Condition: removeFromTree(item.Condition, id),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return item;
|
|
149
|
+
});
|
|
150
|
+
};
|
|
157
151
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
152
|
+
/**
|
|
153
|
+
* Add an item to a specific parent in the tree
|
|
154
|
+
*/
|
|
155
|
+
const addToParent = (
|
|
156
|
+
items: Array<Condition | ConditionGroup>,
|
|
157
|
+
parentId: string,
|
|
158
|
+
newItem: Condition | ConditionGroup
|
|
159
|
+
): Array<Condition | ConditionGroup> => {
|
|
160
|
+
return items.map((item) => {
|
|
161
|
+
if (item.id === parentId && isConditionGroup(item)) {
|
|
162
|
+
return {
|
|
163
|
+
...item,
|
|
164
|
+
Condition: [...item.Condition, newItem],
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
if (isConditionGroup(item)) {
|
|
168
|
+
return {
|
|
169
|
+
...item,
|
|
170
|
+
Condition: addToParent(item.Condition, parentId, newItem),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
return item;
|
|
174
|
+
});
|
|
162
175
|
};
|
|
163
176
|
|
|
164
177
|
// ============================================================
|
|
165
|
-
//
|
|
178
|
+
// USE FILTER HOOK - Nested Filter Support
|
|
166
179
|
// ============================================================
|
|
167
180
|
|
|
168
|
-
export function useFilter
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
// ============================================================
|
|
174
|
-
|
|
175
|
-
const [filterState, setFilterState] = useState<FilterState>({
|
|
176
|
-
logicalOperator: options.initialLogicalOperator || "And",
|
|
177
|
-
conditions: options.initialConditions || []
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
// Store initial state for reset functionality
|
|
181
|
-
const [initialState] = useState<FilterState>({
|
|
182
|
-
logicalOperator: options.initialLogicalOperator || "And",
|
|
183
|
-
conditions: options.initialConditions || []
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
// ============================================================
|
|
187
|
-
// VALIDATION
|
|
188
|
-
// ============================================================
|
|
189
|
-
|
|
190
|
-
const validateCondition = useCallback((condition: Partial<FilterConditionWithId>): ValidationResult => {
|
|
191
|
-
return validateFilterCondition(condition);
|
|
192
|
-
}, []);
|
|
193
|
-
|
|
194
|
-
const validateAllConditions = useCallback((): ValidationResult => {
|
|
195
|
-
const allErrors: string[] = [];
|
|
181
|
+
export function useFilter(options: UseFilterOptions = {}): UseFilterReturn {
|
|
182
|
+
// Initialize items with ids
|
|
183
|
+
const [items, setItems] = useState<Array<Condition | ConditionGroup>>(() =>
|
|
184
|
+
cloneWithIds(options.initialConditions || [])
|
|
185
|
+
);
|
|
196
186
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
allErrors.push(...validation.errors.map(err => `Condition ${condition.id}: ${err}`));
|
|
201
|
-
}
|
|
202
|
-
});
|
|
187
|
+
const [operator, setOperatorState] = useState<ConditionGroupOperator>(
|
|
188
|
+
options.initialOperator || "And"
|
|
189
|
+
);
|
|
203
190
|
|
|
191
|
+
// Build payload for API (strip ids)
|
|
192
|
+
const payload = useMemo((): Filter | undefined => {
|
|
193
|
+
if (items.length === 0) return undefined;
|
|
204
194
|
return {
|
|
205
|
-
|
|
206
|
-
|
|
195
|
+
Operator: operator,
|
|
196
|
+
Condition: stripIds(items),
|
|
207
197
|
};
|
|
208
|
-
}, [
|
|
198
|
+
}, [items, operator]);
|
|
199
|
+
|
|
200
|
+
const hasConditions = items.length > 0;
|
|
209
201
|
|
|
210
202
|
// ============================================================
|
|
211
|
-
//
|
|
203
|
+
// ADD OPERATIONS
|
|
212
204
|
// ============================================================
|
|
213
205
|
|
|
214
|
-
const
|
|
206
|
+
const add = useCallback((condition: Omit<Condition, "id">): string => {
|
|
215
207
|
const id = generateId();
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
208
|
+
const newCondition: Condition = { ...condition, id };
|
|
209
|
+
setItems((prev) => [...prev, newCondition]);
|
|
210
|
+
return id;
|
|
211
|
+
}, []);
|
|
219
212
|
|
|
220
|
-
|
|
221
|
-
|
|
213
|
+
const addGroup = useCallback((groupOperator: ConditionGroupOperator): string => {
|
|
214
|
+
const id = generateId();
|
|
215
|
+
const newGroup: ConditionGroup = {
|
|
222
216
|
id,
|
|
223
|
-
|
|
224
|
-
|
|
217
|
+
Operator: groupOperator,
|
|
218
|
+
Condition: [],
|
|
225
219
|
};
|
|
226
|
-
|
|
227
|
-
setFilterState(prev => ({
|
|
228
|
-
...prev,
|
|
229
|
-
conditions: [...prev.conditions, newCondition]
|
|
230
|
-
}));
|
|
231
|
-
|
|
232
|
-
if (options.onConditionAdd) {
|
|
233
|
-
options.onConditionAdd(newCondition);
|
|
234
|
-
}
|
|
235
|
-
|
|
220
|
+
setItems((prev) => [...prev, newGroup]);
|
|
236
221
|
return id;
|
|
237
|
-
}, [validateCondition, options]);
|
|
238
|
-
|
|
239
|
-
const updateCondition = useCallback((id: string, updates: Partial<TypedFilterConditionInput<T>>): boolean => {
|
|
240
|
-
let found = false;
|
|
241
|
-
// Convert typed input to internal format (using unknown for type narrowing)
|
|
242
|
-
const internalUpdates = updates as unknown as Partial<FilterConditionWithId>;
|
|
243
|
-
|
|
244
|
-
setFilterState(prev => ({
|
|
245
|
-
...prev,
|
|
246
|
-
conditions: prev.conditions.map(condition => {
|
|
247
|
-
if (condition.id === id) {
|
|
248
|
-
found = true;
|
|
249
|
-
const updatedCondition = { ...condition, ...internalUpdates };
|
|
250
|
-
const validation = validateCondition(updatedCondition);
|
|
251
|
-
|
|
252
|
-
const finalCondition = {
|
|
253
|
-
...updatedCondition,
|
|
254
|
-
isValid: validation.isValid,
|
|
255
|
-
validationErrors: validation.errors
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
if (options.onConditionUpdate) {
|
|
259
|
-
options.onConditionUpdate(finalCondition);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return finalCondition;
|
|
263
|
-
}
|
|
264
|
-
return condition;
|
|
265
|
-
})
|
|
266
|
-
}));
|
|
267
|
-
|
|
268
|
-
return found;
|
|
269
|
-
}, [validateCondition, options]);
|
|
270
|
-
|
|
271
|
-
const removeCondition = useCallback((id: string): boolean => {
|
|
272
|
-
let found = false;
|
|
273
|
-
|
|
274
|
-
setFilterState(prev => ({
|
|
275
|
-
...prev,
|
|
276
|
-
conditions: prev.conditions.filter(condition => {
|
|
277
|
-
if (condition.id === id) {
|
|
278
|
-
found = true;
|
|
279
|
-
if (options.onConditionRemove) {
|
|
280
|
-
options.onConditionRemove(id);
|
|
281
|
-
}
|
|
282
|
-
return false;
|
|
283
|
-
}
|
|
284
|
-
return true;
|
|
285
|
-
})
|
|
286
|
-
}));
|
|
287
|
-
|
|
288
|
-
return found;
|
|
289
|
-
}, [options]);
|
|
290
|
-
|
|
291
|
-
const clearConditions = useCallback(() => {
|
|
292
|
-
setFilterState(prev => ({
|
|
293
|
-
...prev,
|
|
294
|
-
conditions: []
|
|
295
|
-
}));
|
|
296
222
|
}, []);
|
|
297
223
|
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
setFilterState(prev => ({
|
|
308
|
-
...prev,
|
|
309
|
-
logicalOperator: operator
|
|
310
|
-
}));
|
|
311
|
-
}, []);
|
|
312
|
-
|
|
313
|
-
// ============================================================
|
|
314
|
-
// BULK OPERATIONS
|
|
315
|
-
// ============================================================
|
|
224
|
+
const addTo = useCallback(
|
|
225
|
+
(parentId: string, condition: Omit<Condition, "id">): string => {
|
|
226
|
+
const id = generateId();
|
|
227
|
+
const newCondition: Condition = { ...condition, id };
|
|
228
|
+
setItems((prev) => addToParent(prev, parentId, newCondition));
|
|
229
|
+
return id;
|
|
230
|
+
},
|
|
231
|
+
[]
|
|
232
|
+
);
|
|
316
233
|
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
234
|
+
const addGroupTo = useCallback(
|
|
235
|
+
(parentId: string, groupOperator: ConditionGroupOperator): string => {
|
|
236
|
+
const id = generateId();
|
|
237
|
+
const newGroup: ConditionGroup = {
|
|
238
|
+
id,
|
|
239
|
+
Operator: groupOperator,
|
|
240
|
+
Condition: [],
|
|
324
241
|
};
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
}));
|
|
331
|
-
}, [validateCondition]);
|
|
332
|
-
|
|
333
|
-
const replaceCondition = useCallback((id: string, newCondition: TypedFilterConditionInput<T>): boolean => {
|
|
334
|
-
let found = false;
|
|
335
|
-
// Convert typed input to internal format (using unknown for type narrowing)
|
|
336
|
-
const internalCondition = newCondition as unknown as Omit<FilterConditionWithId, 'id' | 'isValid'>;
|
|
337
|
-
|
|
338
|
-
setFilterState(prev => ({
|
|
339
|
-
...prev,
|
|
340
|
-
conditions: prev.conditions.map(condition => {
|
|
341
|
-
if (condition.id === id) {
|
|
342
|
-
found = true;
|
|
343
|
-
const validation = validateCondition(internalCondition);
|
|
344
|
-
return {
|
|
345
|
-
...internalCondition,
|
|
346
|
-
id,
|
|
347
|
-
isValid: validation.isValid,
|
|
348
|
-
validationErrors: validation.errors
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
return condition;
|
|
352
|
-
})
|
|
353
|
-
}));
|
|
354
|
-
|
|
355
|
-
return found;
|
|
356
|
-
}, [validateCondition]);
|
|
242
|
+
setItems((prev) => addToParent(prev, parentId, newGroup));
|
|
243
|
+
return id;
|
|
244
|
+
},
|
|
245
|
+
[]
|
|
246
|
+
);
|
|
357
247
|
|
|
358
248
|
// ============================================================
|
|
359
|
-
//
|
|
249
|
+
// UPDATE OPERATIONS
|
|
360
250
|
// ============================================================
|
|
361
251
|
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
setFilterState({
|
|
378
|
-
logicalOperator: state.logicalOperator,
|
|
379
|
-
conditions: validatedConditions
|
|
380
|
-
});
|
|
381
|
-
}, [validateCondition]);
|
|
252
|
+
const update = useCallback(
|
|
253
|
+
(id: string, updates: Partial<Omit<Condition, "id">>): void => {
|
|
254
|
+
setItems((prev) =>
|
|
255
|
+
updateInTree(prev, id, (item) => {
|
|
256
|
+
if (!isConditionGroup(item)) {
|
|
257
|
+
return { ...item, ...updates };
|
|
258
|
+
}
|
|
259
|
+
return item;
|
|
260
|
+
})
|
|
261
|
+
);
|
|
262
|
+
},
|
|
263
|
+
[]
|
|
264
|
+
);
|
|
382
265
|
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
266
|
+
const updateOperator = useCallback(
|
|
267
|
+
(id: string, newOperator: ConditionGroupOperator): void => {
|
|
268
|
+
setItems((prev) =>
|
|
269
|
+
updateInTree(prev, id, (item) => {
|
|
270
|
+
if (isConditionGroup(item)) {
|
|
271
|
+
return { ...item, Operator: newOperator };
|
|
272
|
+
}
|
|
273
|
+
return item;
|
|
274
|
+
})
|
|
275
|
+
);
|
|
276
|
+
},
|
|
277
|
+
[]
|
|
278
|
+
);
|
|
386
279
|
|
|
387
280
|
// ============================================================
|
|
388
|
-
//
|
|
281
|
+
// REMOVE & ACCESS
|
|
389
282
|
// ============================================================
|
|
390
283
|
|
|
391
|
-
const
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
);
|
|
395
|
-
|
|
396
|
-
const validationErrors = useMemo((): ValidationError[] => {
|
|
397
|
-
const errors: ValidationError[] = [];
|
|
398
|
-
|
|
399
|
-
filterState.conditions.forEach(condition => {
|
|
400
|
-
if (!condition.isValid && condition.validationErrors) {
|
|
401
|
-
condition.validationErrors.forEach(error => {
|
|
402
|
-
errors.push({
|
|
403
|
-
conditionId: condition.id,
|
|
404
|
-
field: condition.lhsField || '', // Empty string for logical operators
|
|
405
|
-
message: error
|
|
406
|
-
});
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
return errors;
|
|
412
|
-
}, [filterState.conditions]);
|
|
284
|
+
const remove = useCallback((id: string): void => {
|
|
285
|
+
setItems((prev) => removeFromTree(prev, id));
|
|
286
|
+
}, []);
|
|
413
287
|
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
288
|
+
const get = useCallback(
|
|
289
|
+
(id: string): Condition | ConditionGroup | undefined => {
|
|
290
|
+
return findById(items, id);
|
|
291
|
+
},
|
|
292
|
+
[items]
|
|
417
293
|
);
|
|
418
294
|
|
|
419
|
-
const getConditionCount = useCallback(() => filterState.conditions.length, [filterState.conditions]);
|
|
420
|
-
const hasConditions = useMemo(() => filterState.conditions.length > 0, [filterState.conditions]);
|
|
421
|
-
const canAddCondition = useMemo(() => true, []); // Can always add more conditions
|
|
422
|
-
|
|
423
295
|
// ============================================================
|
|
424
|
-
//
|
|
296
|
+
// UTILITY
|
|
425
297
|
// ============================================================
|
|
426
298
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
299
|
+
const clear = useCallback((): void => {
|
|
300
|
+
setItems([]);
|
|
301
|
+
}, []);
|
|
302
|
+
|
|
303
|
+
const setOperator = useCallback((op: ConditionGroupOperator): void => {
|
|
304
|
+
setOperatorState(op);
|
|
305
|
+
}, []);
|
|
432
306
|
|
|
433
307
|
// ============================================================
|
|
434
|
-
// RETURN
|
|
308
|
+
// RETURN
|
|
435
309
|
// ============================================================
|
|
436
310
|
|
|
437
311
|
return {
|
|
438
|
-
//
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
isValid,
|
|
443
|
-
validationErrors,
|
|
444
|
-
|
|
445
|
-
// Condition management
|
|
446
|
-
addCondition,
|
|
447
|
-
updateCondition,
|
|
448
|
-
removeCondition,
|
|
449
|
-
clearConditions,
|
|
450
|
-
getCondition,
|
|
451
|
-
|
|
452
|
-
// Logical operator management
|
|
453
|
-
setLogicalOperator,
|
|
454
|
-
|
|
455
|
-
// Bulk operations
|
|
456
|
-
setConditions,
|
|
457
|
-
replaceCondition,
|
|
458
|
-
|
|
459
|
-
// Validation
|
|
460
|
-
validateCondition,
|
|
461
|
-
validateAllConditions,
|
|
462
|
-
|
|
463
|
-
// State management
|
|
464
|
-
exportState,
|
|
465
|
-
importState,
|
|
466
|
-
resetToInitial,
|
|
467
|
-
|
|
468
|
-
// Utilities
|
|
469
|
-
getConditionCount,
|
|
312
|
+
// State
|
|
313
|
+
operator,
|
|
314
|
+
items,
|
|
315
|
+
payload,
|
|
470
316
|
hasConditions,
|
|
471
|
-
|
|
317
|
+
|
|
318
|
+
// Add operations
|
|
319
|
+
add,
|
|
320
|
+
addGroup,
|
|
321
|
+
addTo,
|
|
322
|
+
addGroupTo,
|
|
323
|
+
|
|
324
|
+
// Update operations
|
|
325
|
+
update,
|
|
326
|
+
updateOperator,
|
|
327
|
+
|
|
328
|
+
// Remove & access
|
|
329
|
+
remove,
|
|
330
|
+
get,
|
|
331
|
+
|
|
332
|
+
// Utility
|
|
333
|
+
clear,
|
|
334
|
+
setOperator,
|
|
335
|
+
|
|
336
|
+
// Legacy API
|
|
337
|
+
conditions: items,
|
|
472
338
|
};
|
|
473
339
|
}
|