@ram_28/kf-ai-sdk 1.0.5 → 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 -4
- package/dist/components/hooks/useFilter/index.d.ts.map +1 -1
- package/dist/components/hooks/useFilter/types.d.ts +84 -127
- 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 -49
- 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 -35
- 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 +13 -13
- package/dist/index.mjs +2395 -2865
- 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 -31
- package/sdk/components/hooks/useFilter/types.ts +157 -138
- package/sdk/components/hooks/useFilter/useFilter.ts +259 -414
- package/sdk/components/hooks/useKanban/index.ts +0 -1
- package/sdk/components/hooks/useKanban/types.ts +8 -71
- package/sdk/components/hooks/useKanban/useKanban.ts +14 -77
- package/sdk/components/hooks/useTable/types.ts +7 -63
- package/sdk/components/hooks/useTable/useTable.ts +13 -122
- 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/dist/components/hooks/useFilter/validation.utils.d.ts +0 -38
- package/dist/components/hooks/useFilter/validation.utils.d.ts.map +0 -1
- package/sdk/components/hooks/useFilter/payloadBuilder.utils.ts +0 -298
- package/sdk/components/hooks/useFilter/validation.utils.ts +0 -401
|
@@ -1,494 +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
|
-
UseFilterReturn,
|
|
12
|
-
} 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";
|
|
13
10
|
|
|
14
11
|
// ============================================================
|
|
15
|
-
//
|
|
12
|
+
// HELPER FUNCTIONS
|
|
16
13
|
// ============================================================
|
|
17
14
|
|
|
15
|
+
let idCounter = 0;
|
|
16
|
+
|
|
18
17
|
/**
|
|
19
|
-
* Generate a unique ID for conditions
|
|
18
|
+
* Generate a unique ID for conditions and groups
|
|
20
19
|
*/
|
|
21
20
|
const generateId = (): string => {
|
|
22
|
-
return
|
|
21
|
+
return `filter_${Date.now()}_${++idCounter}`;
|
|
23
22
|
};
|
|
24
23
|
|
|
25
24
|
/**
|
|
26
|
-
*
|
|
25
|
+
* Ensure an item has an id
|
|
27
26
|
*/
|
|
28
|
-
const
|
|
29
|
-
|
|
27
|
+
const ensureId = <T extends Condition | ConditionGroup>(item: T): T => {
|
|
28
|
+
if (!item.id) {
|
|
29
|
+
return { ...item, id: generateId() };
|
|
30
|
+
}
|
|
31
|
+
return item;
|
|
30
32
|
};
|
|
31
33
|
|
|
32
34
|
/**
|
|
33
|
-
*
|
|
35
|
+
* Deep clone and ensure all items have ids
|
|
34
36
|
*/
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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');
|
|
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
|
+
};
|
|
55
47
|
}
|
|
48
|
+
return withId;
|
|
49
|
+
});
|
|
50
|
+
};
|
|
56
51
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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;
|
|
65
65
|
}
|
|
66
|
+
const { id, ...rest } = item;
|
|
67
|
+
return rest as Condition;
|
|
68
|
+
});
|
|
69
|
+
};
|
|
66
70
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
errors.push('Field is required for condition operators');
|
|
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;
|
|
78
81
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
}
|
|
82
|
+
if (isConditionGroup(item)) {
|
|
83
|
+
const found = findById(item.Condition, id);
|
|
84
|
+
if (found) return found;
|
|
105
85
|
}
|
|
86
|
+
}
|
|
87
|
+
return undefined;
|
|
88
|
+
};
|
|
106
89
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const fieldValidation = fieldDef.validateValue(condition.rhsValue, condition.operator as any);
|
|
119
|
-
if (!fieldValidation.isValid) {
|
|
120
|
-
errors.push(...fieldValidation.errors);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
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;
|
|
124
101
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
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;
|
|
129
105
|
}
|
|
130
106
|
}
|
|
131
|
-
|
|
132
|
-
return {
|
|
133
|
-
isValid: errors.length === 0,
|
|
134
|
-
errors
|
|
135
|
-
};
|
|
107
|
+
return null;
|
|
136
108
|
};
|
|
137
109
|
|
|
138
110
|
/**
|
|
139
|
-
*
|
|
111
|
+
* Update an item in the tree by id
|
|
140
112
|
*/
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
};
|
|
159
|
-
}
|
|
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
|
+
});
|
|
160
130
|
};
|
|
161
131
|
|
|
162
132
|
/**
|
|
163
|
-
*
|
|
164
|
-
* Supports both flat and nested filter structures
|
|
133
|
+
* Remove an item from the tree by id
|
|
165
134
|
*/
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
):
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
+
};
|
|
178
151
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
+
});
|
|
183
175
|
};
|
|
184
176
|
|
|
185
177
|
// ============================================================
|
|
186
|
-
//
|
|
178
|
+
// USE FILTER HOOK - Nested Filter Support
|
|
187
179
|
// ============================================================
|
|
188
180
|
|
|
189
|
-
export function useFilter
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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[] = [];
|
|
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
|
+
);
|
|
217
186
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
allErrors.push(...validation.errors.map(err => `Condition ${condition.id}: ${err}`));
|
|
222
|
-
}
|
|
223
|
-
});
|
|
187
|
+
const [operator, setOperatorState] = useState<ConditionGroupOperator>(
|
|
188
|
+
options.initialOperator || "And"
|
|
189
|
+
);
|
|
224
190
|
|
|
191
|
+
// Build payload for API (strip ids)
|
|
192
|
+
const payload = useMemo((): Filter | undefined => {
|
|
193
|
+
if (items.length === 0) return undefined;
|
|
225
194
|
return {
|
|
226
|
-
|
|
227
|
-
|
|
195
|
+
Operator: operator,
|
|
196
|
+
Condition: stripIds(items),
|
|
228
197
|
};
|
|
229
|
-
}, [
|
|
198
|
+
}, [items, operator]);
|
|
199
|
+
|
|
200
|
+
const hasConditions = items.length > 0;
|
|
230
201
|
|
|
231
202
|
// ============================================================
|
|
232
|
-
//
|
|
203
|
+
// ADD OPERATIONS
|
|
233
204
|
// ============================================================
|
|
234
205
|
|
|
235
|
-
const
|
|
206
|
+
const add = useCallback((condition: Omit<Condition, "id">): string => {
|
|
236
207
|
const id = generateId();
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
208
|
+
const newCondition: Condition = { ...condition, id };
|
|
209
|
+
setItems((prev) => [...prev, newCondition]);
|
|
210
|
+
return id;
|
|
211
|
+
}, []);
|
|
240
212
|
|
|
241
|
-
|
|
242
|
-
|
|
213
|
+
const addGroup = useCallback((groupOperator: ConditionGroupOperator): string => {
|
|
214
|
+
const id = generateId();
|
|
215
|
+
const newGroup: ConditionGroup = {
|
|
243
216
|
id,
|
|
244
|
-
|
|
245
|
-
|
|
217
|
+
Operator: groupOperator,
|
|
218
|
+
Condition: [],
|
|
246
219
|
};
|
|
247
|
-
|
|
248
|
-
setFilterState(prev => ({
|
|
249
|
-
...prev,
|
|
250
|
-
conditions: [...prev.conditions, newCondition]
|
|
251
|
-
}));
|
|
252
|
-
|
|
253
|
-
if (options.onConditionAdd) {
|
|
254
|
-
options.onConditionAdd(newCondition);
|
|
255
|
-
}
|
|
256
|
-
|
|
220
|
+
setItems((prev) => [...prev, newGroup]);
|
|
257
221
|
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
222
|
}, []);
|
|
318
223
|
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
setFilterState(prev => ({
|
|
329
|
-
...prev,
|
|
330
|
-
logicalOperator: operator
|
|
331
|
-
}));
|
|
332
|
-
}, []);
|
|
333
|
-
|
|
334
|
-
// ============================================================
|
|
335
|
-
// BULK OPERATIONS
|
|
336
|
-
// ============================================================
|
|
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
|
+
);
|
|
337
233
|
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
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: [],
|
|
345
241
|
};
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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]);
|
|
242
|
+
setItems((prev) => addToParent(prev, parentId, newGroup));
|
|
243
|
+
return id;
|
|
244
|
+
},
|
|
245
|
+
[]
|
|
246
|
+
);
|
|
378
247
|
|
|
379
248
|
// ============================================================
|
|
380
|
-
//
|
|
249
|
+
// UPDATE OPERATIONS
|
|
381
250
|
// ============================================================
|
|
382
251
|
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
setFilterState({
|
|
399
|
-
logicalOperator: state.logicalOperator,
|
|
400
|
-
conditions: validatedConditions
|
|
401
|
-
});
|
|
402
|
-
}, [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
|
+
);
|
|
403
265
|
|
|
404
|
-
const
|
|
405
|
-
|
|
406
|
-
|
|
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
|
+
);
|
|
407
279
|
|
|
408
280
|
// ============================================================
|
|
409
|
-
//
|
|
281
|
+
// REMOVE & ACCESS
|
|
410
282
|
// ============================================================
|
|
411
283
|
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
|
|
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]);
|
|
284
|
+
const remove = useCallback((id: string): void => {
|
|
285
|
+
setItems((prev) => removeFromTree(prev, id));
|
|
286
|
+
}, []);
|
|
434
287
|
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
|
|
288
|
+
const get = useCallback(
|
|
289
|
+
(id: string): Condition | ConditionGroup | undefined => {
|
|
290
|
+
return findById(items, id);
|
|
291
|
+
},
|
|
292
|
+
[items]
|
|
438
293
|
);
|
|
439
294
|
|
|
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
295
|
// ============================================================
|
|
445
|
-
//
|
|
296
|
+
// UTILITY
|
|
446
297
|
// ============================================================
|
|
447
298
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
299
|
+
const clear = useCallback((): void => {
|
|
300
|
+
setItems([]);
|
|
301
|
+
}, []);
|
|
302
|
+
|
|
303
|
+
const setOperator = useCallback((op: ConditionGroupOperator): void => {
|
|
304
|
+
setOperatorState(op);
|
|
305
|
+
}, []);
|
|
453
306
|
|
|
454
307
|
// ============================================================
|
|
455
|
-
// RETURN
|
|
308
|
+
// RETURN
|
|
456
309
|
// ============================================================
|
|
457
310
|
|
|
458
311
|
return {
|
|
459
|
-
//
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
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,
|
|
312
|
+
// State
|
|
313
|
+
operator,
|
|
314
|
+
items,
|
|
315
|
+
payload,
|
|
491
316
|
hasConditions,
|
|
492
|
-
|
|
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,
|
|
493
338
|
};
|
|
494
339
|
}
|