@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
|
@@ -18,44 +18,11 @@ interface SortingState<T> {
|
|
|
18
18
|
direction: "asc" | "desc" | null;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
interface FilteringState {
|
|
22
|
-
global: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
21
|
interface PaginationState {
|
|
26
22
|
pageNo: number; // 1-indexed page number
|
|
27
23
|
pageSize: number;
|
|
28
24
|
}
|
|
29
25
|
|
|
30
|
-
// ============================================================
|
|
31
|
-
// FIELD TYPE DETECTION
|
|
32
|
-
// ============================================================
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Detect field type for auto-formatting
|
|
36
|
-
* This will be enhanced with actual type introspection in the future
|
|
37
|
-
* Currently unused but kept for future auto-formatting implementation
|
|
38
|
-
*/
|
|
39
|
-
// function _detectFieldType<T>(fieldId: keyof T): string {
|
|
40
|
-
// const fieldName = String(fieldId);
|
|
41
|
-
//
|
|
42
|
-
// // Basic pattern matching - will be replaced with actual type introspection
|
|
43
|
-
// if (fieldName.includes('price') || fieldName.includes('cost') || fieldName.includes('amount')) {
|
|
44
|
-
// return 'currency';
|
|
45
|
-
// }
|
|
46
|
-
// if (fieldName.includes('date') || fieldName.includes('At') || fieldName === 'created' || fieldName === 'updated') {
|
|
47
|
-
// return 'datetime';
|
|
48
|
-
// }
|
|
49
|
-
// if (fieldName.includes('stock') || fieldName.includes('quantity') || fieldName.includes('count')) {
|
|
50
|
-
// return 'number';
|
|
51
|
-
// }
|
|
52
|
-
// if (fieldName.includes('status') || fieldName.includes('type') || fieldName.includes('category')) {
|
|
53
|
-
// return 'string';
|
|
54
|
-
// }
|
|
55
|
-
//
|
|
56
|
-
// return 'string'; // default
|
|
57
|
-
// }
|
|
58
|
-
|
|
59
26
|
// ============================================================
|
|
60
27
|
// MAIN HOOK
|
|
61
28
|
// ============================================================
|
|
@@ -76,12 +43,8 @@ export function useTable<T = any>(
|
|
|
76
43
|
direction: options.initialState?.sorting?.direction || null,
|
|
77
44
|
});
|
|
78
45
|
|
|
79
|
-
const [filtering, setFiltering] = useState<FilteringState>({
|
|
80
|
-
global: options.initialState?.globalFilter || "",
|
|
81
|
-
});
|
|
82
|
-
|
|
83
46
|
const [pagination, setPagination] = useState<PaginationState>({
|
|
84
|
-
pageNo: options.initialState?.pagination?.pageNo || 1,
|
|
47
|
+
pageNo: options.initialState?.pagination?.pageNo || 1,
|
|
85
48
|
pageSize: options.initialState?.pagination?.pageSize || 10,
|
|
86
49
|
});
|
|
87
50
|
|
|
@@ -89,23 +52,9 @@ export function useTable<T = any>(
|
|
|
89
52
|
// FILTER HOOK INTEGRATION
|
|
90
53
|
// ============================================================
|
|
91
54
|
|
|
92
|
-
const
|
|
55
|
+
const filter = useFilter({
|
|
93
56
|
initialConditions: options.initialState?.filters,
|
|
94
|
-
|
|
95
|
-
validateOnChange: true,
|
|
96
|
-
onValidationError: options.onFilterError,
|
|
97
|
-
onConditionAdd: () => {
|
|
98
|
-
// Reset to first page when adding filters
|
|
99
|
-
setPagination((prev) => ({ ...prev, pageNo: 1 }));
|
|
100
|
-
},
|
|
101
|
-
onConditionUpdate: () => {
|
|
102
|
-
// Reset to first page when updating filters
|
|
103
|
-
setPagination((prev) => ({ ...prev, pageNo: 1 }));
|
|
104
|
-
},
|
|
105
|
-
onConditionRemove: () => {
|
|
106
|
-
// Reset to first page when removing filters
|
|
107
|
-
setPagination((prev) => ({ ...prev, pageNo: 1 }));
|
|
108
|
-
},
|
|
57
|
+
initialOperator: options.initialState?.filterOperator || "And",
|
|
109
58
|
});
|
|
110
59
|
|
|
111
60
|
// ============================================================
|
|
@@ -122,12 +71,12 @@ export function useTable<T = any>(
|
|
|
122
71
|
}
|
|
123
72
|
|
|
124
73
|
// Add filter conditions (affects count)
|
|
125
|
-
if (
|
|
126
|
-
opts.Filter =
|
|
74
|
+
if (filter.payload) {
|
|
75
|
+
opts.Filter = filter.payload;
|
|
127
76
|
}
|
|
128
77
|
|
|
129
78
|
return opts;
|
|
130
|
-
}, [search.query,
|
|
79
|
+
}, [search.query, filter.payload]);
|
|
131
80
|
|
|
132
81
|
// Options for list query - includes all options
|
|
133
82
|
const apiOptions = useMemo((): ListOptions => {
|
|
@@ -144,7 +93,7 @@ export function useTable<T = any>(
|
|
|
144
93
|
|
|
145
94
|
// Add pagination
|
|
146
95
|
if (options.enablePagination) {
|
|
147
|
-
opts.Page = pagination.pageNo;
|
|
96
|
+
opts.Page = pagination.pageNo;
|
|
148
97
|
opts.PageSize = pagination.pageSize;
|
|
149
98
|
}
|
|
150
99
|
|
|
@@ -181,7 +130,7 @@ export function useTable<T = any>(
|
|
|
181
130
|
gcTime: 0,
|
|
182
131
|
});
|
|
183
132
|
|
|
184
|
-
// Count query for accurate total items
|
|
133
|
+
// Count query for accurate total items
|
|
185
134
|
const {
|
|
186
135
|
data: countData,
|
|
187
136
|
isLoading: isCountLoading,
|
|
@@ -222,14 +171,12 @@ export function useTable<T = any>(
|
|
|
222
171
|
const toggleSort = useCallback((field: keyof T) => {
|
|
223
172
|
setSorting((prev) => {
|
|
224
173
|
if (prev.field === field) {
|
|
225
|
-
// Same field - toggle direction or clear
|
|
226
174
|
if (prev.direction === "asc") {
|
|
227
175
|
return { field, direction: "desc" };
|
|
228
176
|
} else if (prev.direction === "desc") {
|
|
229
177
|
return { field: null, direction: null };
|
|
230
178
|
}
|
|
231
179
|
}
|
|
232
|
-
// New field or no current sort
|
|
233
180
|
return { field, direction: "asc" };
|
|
234
181
|
});
|
|
235
182
|
}, []);
|
|
@@ -251,7 +198,6 @@ export function useTable<T = any>(
|
|
|
251
198
|
|
|
252
199
|
const setSearchQuery = useCallback((value: string) => {
|
|
253
200
|
setSearch({ query: value });
|
|
254
|
-
// Reset to first page when searching
|
|
255
201
|
setPagination((prev) => ({ ...prev, pageNo: 1 }));
|
|
256
202
|
}, []);
|
|
257
203
|
|
|
@@ -259,20 +205,6 @@ export function useTable<T = any>(
|
|
|
259
205
|
setSearch({ query: "" });
|
|
260
206
|
}, []);
|
|
261
207
|
|
|
262
|
-
// ============================================================
|
|
263
|
-
// FILTERING OPERATIONS
|
|
264
|
-
// ============================================================
|
|
265
|
-
|
|
266
|
-
const setGlobalFilter = useCallback((value: string) => {
|
|
267
|
-
setFiltering((prev) => ({ ...prev, global: value }));
|
|
268
|
-
// Reset to first page when filtering
|
|
269
|
-
setPagination((prev) => ({ ...prev, pageNo: 1 }));
|
|
270
|
-
}, []);
|
|
271
|
-
|
|
272
|
-
const clearFilter = useCallback(() => {
|
|
273
|
-
setFiltering({ global: "" });
|
|
274
|
-
}, []);
|
|
275
|
-
|
|
276
208
|
// ============================================================
|
|
277
209
|
// PAGINATION OPERATIONS
|
|
278
210
|
// ============================================================
|
|
@@ -294,7 +226,7 @@ export function useTable<T = any>(
|
|
|
294
226
|
|
|
295
227
|
const goToPage = useCallback(
|
|
296
228
|
(page: number) => {
|
|
297
|
-
const pageNo = Math.max(1, Math.min(page, totalPages));
|
|
229
|
+
const pageNo = Math.max(1, Math.min(page, totalPages));
|
|
298
230
|
setPagination((prev) => ({ ...prev, pageNo }));
|
|
299
231
|
},
|
|
300
232
|
[totalPages]
|
|
@@ -304,7 +236,7 @@ export function useTable<T = any>(
|
|
|
304
236
|
setPagination((prev) => ({
|
|
305
237
|
...prev,
|
|
306
238
|
pageSize: size,
|
|
307
|
-
pageNo: 1,
|
|
239
|
+
pageNo: 1,
|
|
308
240
|
}));
|
|
309
241
|
}, []);
|
|
310
242
|
|
|
@@ -349,52 +281,12 @@ export function useTable<T = any>(
|
|
|
349
281
|
set: setSort,
|
|
350
282
|
},
|
|
351
283
|
|
|
352
|
-
//
|
|
353
|
-
|
|
354
|
-
value: filtering.global,
|
|
355
|
-
setValue: setGlobalFilter,
|
|
356
|
-
clear: clearFilter,
|
|
357
|
-
},
|
|
358
|
-
|
|
359
|
-
// Advanced Filtering (Filter Conditions)
|
|
360
|
-
filter: {
|
|
361
|
-
// State
|
|
362
|
-
conditions: filterHook.conditions,
|
|
363
|
-
logicalOperator: filterHook.logicalOperator,
|
|
364
|
-
isValid: filterHook.isValid,
|
|
365
|
-
validationErrors: filterHook.validationErrors,
|
|
366
|
-
hasConditions: filterHook.hasConditions,
|
|
367
|
-
|
|
368
|
-
// Condition Management
|
|
369
|
-
addCondition: filterHook.addCondition,
|
|
370
|
-
updateCondition: filterHook.updateCondition,
|
|
371
|
-
removeCondition: filterHook.removeCondition,
|
|
372
|
-
clearConditions: filterHook.clearConditions,
|
|
373
|
-
getCondition: filterHook.getCondition,
|
|
374
|
-
|
|
375
|
-
// Logical Operator
|
|
376
|
-
setLogicalOperator: filterHook.setLogicalOperator,
|
|
377
|
-
|
|
378
|
-
// Bulk Operations
|
|
379
|
-
setConditions: filterHook.setConditions,
|
|
380
|
-
replaceCondition: filterHook.replaceCondition,
|
|
381
|
-
|
|
382
|
-
// Validation
|
|
383
|
-
validateCondition: filterHook.validateCondition,
|
|
384
|
-
validateAllConditions: filterHook.validateAllConditions,
|
|
385
|
-
|
|
386
|
-
// State Management
|
|
387
|
-
exportState: filterHook.exportState,
|
|
388
|
-
importState: filterHook.importState,
|
|
389
|
-
resetToInitial: filterHook.resetToInitial,
|
|
390
|
-
|
|
391
|
-
// Utilities
|
|
392
|
-
getConditionCount: filterHook.getConditionCount,
|
|
393
|
-
},
|
|
284
|
+
// Filter (Simplified chainable API)
|
|
285
|
+
filter,
|
|
394
286
|
|
|
395
287
|
// Pagination (Flat Access)
|
|
396
288
|
pagination: {
|
|
397
|
-
currentPage: pagination.pageNo,
|
|
289
|
+
currentPage: pagination.pageNo,
|
|
398
290
|
pageSize: pagination.pageSize,
|
|
399
291
|
totalPages,
|
|
400
292
|
totalItems,
|
package/sdk/types/common.ts
CHANGED
|
@@ -15,18 +15,20 @@ export type SortOption = Record<string, SortDirection>;
|
|
|
15
15
|
export type Sort = SortOption[];
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
18
|
+
* Condition operators for individual conditions (leaf nodes)
|
|
19
|
+
* Used in Condition.Operator
|
|
19
20
|
*/
|
|
20
|
-
export type
|
|
21
|
+
export type ConditionOperator =
|
|
21
22
|
| "EQ" | "NE" | "GT" | "GTE" | "LT" | "LTE"
|
|
22
23
|
| "Between" | "NotBetween" | "IN" | "NIN"
|
|
23
24
|
| "Empty" | "NotEmpty" | "Contains" | "NotContains"
|
|
24
25
|
| "MinLength" | "MaxLength";
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
|
-
*
|
|
28
|
+
* Operators for combining conditions in a group (tree nodes)
|
|
29
|
+
* Used in ConditionGroup.Operator
|
|
28
30
|
*/
|
|
29
|
-
export type
|
|
31
|
+
export type ConditionGroupOperator = "And" | "Or" | "Not";
|
|
30
32
|
|
|
31
33
|
/**
|
|
32
34
|
* RHS value type for filter conditions
|
|
@@ -34,19 +36,13 @@ export type LogicalOperator = "And" | "Or" | "Not";
|
|
|
34
36
|
export type FilterRHSType = "Constant" | "BOField" | "AppVariable";
|
|
35
37
|
|
|
36
38
|
/**
|
|
37
|
-
*
|
|
39
|
+
* Leaf condition (actual field comparison)
|
|
38
40
|
*/
|
|
39
|
-
interface
|
|
40
|
-
/**
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Leaf filter condition (actual field comparison)
|
|
46
|
-
*/
|
|
47
|
-
export interface FilterCondition extends FilterNodeBase {
|
|
41
|
+
export interface Condition {
|
|
42
|
+
/** Optional ID for hook state management (omitted in API payload) */
|
|
43
|
+
id?: string;
|
|
48
44
|
/** Condition operator */
|
|
49
|
-
Operator:
|
|
45
|
+
Operator: ConditionOperator;
|
|
50
46
|
/** Left-hand side field name */
|
|
51
47
|
LHSField: string;
|
|
52
48
|
/** Right-hand side value */
|
|
@@ -56,25 +52,45 @@ export interface FilterCondition extends FilterNodeBase {
|
|
|
56
52
|
}
|
|
57
53
|
|
|
58
54
|
/**
|
|
59
|
-
*
|
|
55
|
+
* Group combining conditions (recursive structure)
|
|
60
56
|
*/
|
|
61
|
-
export interface
|
|
62
|
-
/**
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
|
|
57
|
+
export interface ConditionGroup {
|
|
58
|
+
/** Optional ID for hook state management (omitted in API payload) */
|
|
59
|
+
id?: string;
|
|
60
|
+
/** Group operator (And, Or, Not) */
|
|
61
|
+
Operator: ConditionGroupOperator;
|
|
62
|
+
/** Nested conditions (can be Condition or ConditionGroup) */
|
|
63
|
+
Condition: Array<Condition | ConditionGroup>;
|
|
66
64
|
}
|
|
67
65
|
|
|
68
66
|
/**
|
|
69
|
-
*
|
|
70
|
-
|
|
67
|
+
* Root filter type (alias for ConditionGroup)
|
|
68
|
+
*/
|
|
69
|
+
export type Filter = ConditionGroup;
|
|
70
|
+
|
|
71
|
+
// ============================================================
|
|
72
|
+
// LEGACY TYPE ALIASES (for backwards compatibility)
|
|
73
|
+
// ============================================================
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @deprecated Use `Condition` instead
|
|
77
|
+
*/
|
|
78
|
+
export type FilterCondition = Condition;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @deprecated Use `ConditionGroup` instead
|
|
82
|
+
*/
|
|
83
|
+
export type FilterLogical = ConditionGroup;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @deprecated Use `ConditionGroupOperator` instead
|
|
71
87
|
*/
|
|
72
|
-
export type
|
|
88
|
+
export type FilterOperator = ConditionGroupOperator;
|
|
73
89
|
|
|
74
90
|
/**
|
|
75
|
-
*
|
|
91
|
+
* @deprecated Use `Condition | ConditionGroup` instead
|
|
76
92
|
*/
|
|
77
|
-
export type FilterNode =
|
|
93
|
+
export type FilterNode = Condition | ConditionGroup;
|
|
78
94
|
|
|
79
95
|
/**
|
|
80
96
|
* DateTime encoding format used by the API
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import type { Filter, LogicalOperator } from "../../../types/common";
|
|
2
|
-
import type { FilterConditionWithId, FilterState } from "../useFilter";
|
|
3
|
-
/**
|
|
4
|
-
* Build SDK Filter payload from filter state
|
|
5
|
-
* Returns undefined if no valid conditions exist
|
|
6
|
-
* Supports both flat and nested filter structures
|
|
7
|
-
*/
|
|
8
|
-
export declare const buildFilterPayload: (conditions: FilterConditionWithId[], logicalOperator: LogicalOperator) => Filter | undefined;
|
|
9
|
-
/**
|
|
10
|
-
* Build filter payload from complete filter state
|
|
11
|
-
*/
|
|
12
|
-
export declare const buildFilterPayloadFromState: (state: FilterState) => Filter | undefined;
|
|
13
|
-
/**
|
|
14
|
-
* Validate that a filter payload is well-formed (supports nested filters)
|
|
15
|
-
*/
|
|
16
|
-
export declare const validateFilterPayload: (filter: Filter | undefined) => boolean;
|
|
17
|
-
/**
|
|
18
|
-
* Deep clone a filter payload (supports nested filters)
|
|
19
|
-
*/
|
|
20
|
-
export declare const cloneFilterPayload: (filter: Filter | undefined) => Filter | undefined;
|
|
21
|
-
/**
|
|
22
|
-
* Merge multiple filter payloads with a logical operator
|
|
23
|
-
*/
|
|
24
|
-
export declare const mergeFilterPayloads: (filters: (Filter | undefined)[], operator: LogicalOperator) => Filter | undefined;
|
|
25
|
-
/**
|
|
26
|
-
* Convert filter payload to a human-readable string for debugging (supports nested filters)
|
|
27
|
-
*/
|
|
28
|
-
export declare const filterPayloadToString: (filter: Filter | undefined) => string;
|
|
29
|
-
/**
|
|
30
|
-
* Check if two filter payloads are equivalent (supports nested filters)
|
|
31
|
-
*/
|
|
32
|
-
export declare const areFilterPayloadsEqual: (filter1: Filter | undefined, filter2: Filter | undefined) => boolean;
|
|
33
|
-
//# sourceMappingURL=payloadBuilder.utils.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"payloadBuilder.utils.d.ts","sourceRoot":"","sources":["../../../../sdk/components/hooks/useFilter/payloadBuilder.utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAkC,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACrG,OAAO,KAAK,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAqCvE;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAC7B,YAAY,qBAAqB,EAAE,EACnC,iBAAiB,eAAe,KAC/B,MAAM,GAAG,SAmBX,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,2BAA2B,GAAI,OAAO,WAAW,KAAG,MAAM,GAAG,SAEzE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAI,QAAQ,MAAM,GAAG,SAAS,KAAG,OAwClE,CAAC;AAwBF;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAAI,QAAQ,MAAM,GAAG,SAAS,KAAG,MAAM,GAAG,SASxE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAC9B,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,EAC/B,UAAU,eAAe,KACxB,MAAM,GAAG,SAoBX,CAAC;AA6BF;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAI,QAAQ,MAAM,GAAG,SAAS,KAAG,MAYlE,CAAC;AAyCF;;GAEG;AACH,eAAO,MAAM,sBAAsB,GACjC,SAAS,MAAM,GAAG,SAAS,EAC3B,SAAS,MAAM,GAAG,SAAS,KAC1B,OAyBF,CAAC"}
|
|
@@ -1,298 +0,0 @@
|
|
|
1
|
-
import type { Filter, FilterCondition, FilterLogical, LogicalOperator } from "../../../types/common";
|
|
2
|
-
import type { FilterConditionWithId, FilterState } from "../useFilter";
|
|
3
|
-
|
|
4
|
-
// ============================================================
|
|
5
|
-
// PAYLOAD BUILDING UTILITIES
|
|
6
|
-
// ============================================================
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Helper to check if operator is a logical operator
|
|
10
|
-
*/
|
|
11
|
-
const isLogicalOperator = (operator: string): operator is LogicalOperator => {
|
|
12
|
-
return operator === 'And' || operator === 'Or' || operator === 'Not';
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Convert a single FilterConditionWithId to API format (FilterCondition or FilterLogical)
|
|
17
|
-
*/
|
|
18
|
-
const buildCondition = (condition: FilterConditionWithId): FilterCondition | FilterLogical => {
|
|
19
|
-
// Check if this is a logical operator (nested group)
|
|
20
|
-
if (isLogicalOperator(condition.operator)) {
|
|
21
|
-
// Build nested logical filter
|
|
22
|
-
return {
|
|
23
|
-
Operator: condition.operator,
|
|
24
|
-
Condition: (condition.children || [])
|
|
25
|
-
.filter(child => child.isValid)
|
|
26
|
-
.map(child => buildCondition(child))
|
|
27
|
-
};
|
|
28
|
-
} else {
|
|
29
|
-
// Build simple condition
|
|
30
|
-
return {
|
|
31
|
-
Operator: condition.operator,
|
|
32
|
-
LHSField: condition.lhsField!,
|
|
33
|
-
RHSValue: condition.rhsValue,
|
|
34
|
-
RHSType: condition.rhsType || "Constant"
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Build SDK Filter payload from filter state
|
|
41
|
-
* Returns undefined if no valid conditions exist
|
|
42
|
-
* Supports both flat and nested filter structures
|
|
43
|
-
*/
|
|
44
|
-
export const buildFilterPayload = (
|
|
45
|
-
conditions: FilterConditionWithId[],
|
|
46
|
-
logicalOperator: LogicalOperator
|
|
47
|
-
): Filter | undefined => {
|
|
48
|
-
// Return undefined if no conditions
|
|
49
|
-
if (conditions.length === 0) {
|
|
50
|
-
return undefined;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Filter out invalid conditions
|
|
54
|
-
const validConditions = conditions.filter(condition => condition.isValid);
|
|
55
|
-
|
|
56
|
-
// Return undefined if no valid conditions
|
|
57
|
-
if (validConditions.length === 0) {
|
|
58
|
-
return undefined;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Build the filter payload
|
|
62
|
-
return {
|
|
63
|
-
Operator: logicalOperator,
|
|
64
|
-
Condition: validConditions.map(buildCondition)
|
|
65
|
-
};
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Build filter payload from complete filter state
|
|
70
|
-
*/
|
|
71
|
-
export const buildFilterPayloadFromState = (state: FilterState): Filter | undefined => {
|
|
72
|
-
return buildFilterPayload(state.conditions, state.logicalOperator);
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Validate that a filter payload is well-formed (supports nested filters)
|
|
77
|
-
*/
|
|
78
|
-
export const validateFilterPayload = (filter: Filter | undefined): boolean => {
|
|
79
|
-
if (!filter) {
|
|
80
|
-
return true; // undefined filter is valid (no filtering)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Check operator
|
|
84
|
-
if (!filter.Operator || !['And', 'Or', 'Not'].includes(filter.Operator)) {
|
|
85
|
-
return false;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Check conditions array
|
|
89
|
-
if (!Array.isArray(filter.Condition) || filter.Condition.length === 0) {
|
|
90
|
-
return false;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Not operator can only have one child
|
|
94
|
-
if (filter.Operator === 'Not' && filter.Condition.length !== 1) {
|
|
95
|
-
return false;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Validate each condition recursively
|
|
99
|
-
return filter.Condition.every(condition => {
|
|
100
|
-
if (!condition.Operator) {
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Check if this is a logical operator (nested)
|
|
105
|
-
if (isLogicalOperator(condition.Operator)) {
|
|
106
|
-
// Recursively validate nested filter
|
|
107
|
-
return validateFilterPayload(condition as FilterLogical);
|
|
108
|
-
} else {
|
|
109
|
-
// Validate simple condition
|
|
110
|
-
const simpleCondition = condition as FilterCondition;
|
|
111
|
-
return (
|
|
112
|
-
simpleCondition.LHSField !== undefined &&
|
|
113
|
-
simpleCondition.RHSValue !== undefined &&
|
|
114
|
-
(simpleCondition.RHSType === undefined || ['Constant', 'BOField', 'AppVariable'].includes(simpleCondition.RHSType))
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Deep clone a filter condition (supports nested filters)
|
|
122
|
-
*/
|
|
123
|
-
const cloneCondition = (condition: FilterCondition | FilterLogical): FilterCondition | FilterLogical => {
|
|
124
|
-
if (isLogicalOperator(condition.Operator)) {
|
|
125
|
-
// Clone logical filter recursively
|
|
126
|
-
return {
|
|
127
|
-
Operator: condition.Operator,
|
|
128
|
-
Condition: (condition as FilterLogical).Condition.map(cloneCondition)
|
|
129
|
-
};
|
|
130
|
-
} else {
|
|
131
|
-
// Clone simple condition
|
|
132
|
-
const simpleCondition = condition as FilterCondition;
|
|
133
|
-
return {
|
|
134
|
-
Operator: simpleCondition.Operator,
|
|
135
|
-
LHSField: simpleCondition.LHSField,
|
|
136
|
-
RHSValue: simpleCondition.RHSValue,
|
|
137
|
-
RHSType: simpleCondition.RHSType
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Deep clone a filter payload (supports nested filters)
|
|
144
|
-
*/
|
|
145
|
-
export const cloneFilterPayload = (filter: Filter | undefined): Filter | undefined => {
|
|
146
|
-
if (!filter) {
|
|
147
|
-
return undefined;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return {
|
|
151
|
-
Operator: filter.Operator,
|
|
152
|
-
Condition: filter.Condition.map(cloneCondition)
|
|
153
|
-
};
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Merge multiple filter payloads with a logical operator
|
|
158
|
-
*/
|
|
159
|
-
export const mergeFilterPayloads = (
|
|
160
|
-
filters: (Filter | undefined)[],
|
|
161
|
-
operator: LogicalOperator
|
|
162
|
-
): Filter | undefined => {
|
|
163
|
-
const validFilters = filters.filter((filter): filter is Filter =>
|
|
164
|
-
filter !== undefined && validateFilterPayload(filter)
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
if (validFilters.length === 0) {
|
|
168
|
-
return undefined;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if (validFilters.length === 1) {
|
|
172
|
-
return cloneFilterPayload(validFilters[0]);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Flatten all conditions
|
|
176
|
-
const allConditions = validFilters.flatMap(filter => filter.Condition);
|
|
177
|
-
|
|
178
|
-
return {
|
|
179
|
-
Operator: operator,
|
|
180
|
-
Condition: allConditions
|
|
181
|
-
};
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Convert filter condition to a human-readable string (supports nested filters)
|
|
186
|
-
*/
|
|
187
|
-
const conditionToString = (condition: FilterCondition | FilterLogical): string => {
|
|
188
|
-
if (isLogicalOperator(condition.Operator)) {
|
|
189
|
-
// Handle logical filter
|
|
190
|
-
const logicalFilter = condition as FilterLogical;
|
|
191
|
-
const childStrings = logicalFilter.Condition.map(conditionToString);
|
|
192
|
-
|
|
193
|
-
if (childStrings.length === 1) {
|
|
194
|
-
return condition.Operator === 'Not'
|
|
195
|
-
? `NOT (${childStrings[0]})`
|
|
196
|
-
: childStrings[0];
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return `(${childStrings.join(` ${condition.Operator} `)})`;
|
|
200
|
-
} else {
|
|
201
|
-
// Handle simple condition
|
|
202
|
-
const simpleCondition = condition as FilterCondition;
|
|
203
|
-
const rhsDisplay = Array.isArray(simpleCondition.RHSValue)
|
|
204
|
-
? `[${simpleCondition.RHSValue.join(', ')}]`
|
|
205
|
-
: String(simpleCondition.RHSValue);
|
|
206
|
-
|
|
207
|
-
return `${simpleCondition.LHSField} ${simpleCondition.Operator} ${rhsDisplay}`;
|
|
208
|
-
}
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Convert filter payload to a human-readable string for debugging (supports nested filters)
|
|
213
|
-
*/
|
|
214
|
-
export const filterPayloadToString = (filter: Filter | undefined): string => {
|
|
215
|
-
if (!filter) {
|
|
216
|
-
return "No filters";
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const conditionStrings = filter.Condition.map(conditionToString);
|
|
220
|
-
|
|
221
|
-
if (conditionStrings.length === 1) {
|
|
222
|
-
return conditionStrings[0];
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return `(${conditionStrings.join(` ${filter.Operator} `)})`;
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Check if two conditions are equivalent (supports nested filters)
|
|
230
|
-
*/
|
|
231
|
-
const areConditionsEqual = (
|
|
232
|
-
condition1: FilterCondition | FilterLogical,
|
|
233
|
-
condition2: FilterCondition | FilterLogical
|
|
234
|
-
): boolean => {
|
|
235
|
-
// Different operators
|
|
236
|
-
if (condition1.Operator !== condition2.Operator) {
|
|
237
|
-
return false;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Check if logical operators
|
|
241
|
-
if (isLogicalOperator(condition1.Operator)) {
|
|
242
|
-
const logical1 = condition1 as FilterLogical;
|
|
243
|
-
const logical2 = condition2 as FilterLogical;
|
|
244
|
-
|
|
245
|
-
// Different number of children
|
|
246
|
-
if (logical1.Condition.length !== logical2.Condition.length) {
|
|
247
|
-
return false;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Recursively compare all children
|
|
251
|
-
return logical1.Condition.every((child1, index) =>
|
|
252
|
-
areConditionsEqual(child1, logical2.Condition[index])
|
|
253
|
-
);
|
|
254
|
-
} else {
|
|
255
|
-
// Compare simple conditions
|
|
256
|
-
const simple1 = condition1 as FilterCondition;
|
|
257
|
-
const simple2 = condition2 as FilterCondition;
|
|
258
|
-
|
|
259
|
-
return (
|
|
260
|
-
simple1.LHSField === simple2.LHSField &&
|
|
261
|
-
JSON.stringify(simple1.RHSValue) === JSON.stringify(simple2.RHSValue) &&
|
|
262
|
-
simple1.RHSType === simple2.RHSType
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Check if two filter payloads are equivalent (supports nested filters)
|
|
269
|
-
*/
|
|
270
|
-
export const areFilterPayloadsEqual = (
|
|
271
|
-
filter1: Filter | undefined,
|
|
272
|
-
filter2: Filter | undefined
|
|
273
|
-
): boolean => {
|
|
274
|
-
// Both undefined
|
|
275
|
-
if (!filter1 && !filter2) {
|
|
276
|
-
return true;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// One undefined, one defined
|
|
280
|
-
if (!filter1 || !filter2) {
|
|
281
|
-
return false;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// Different operators
|
|
285
|
-
if (filter1.Operator !== filter2.Operator) {
|
|
286
|
-
return false;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// Different number of conditions
|
|
290
|
-
if (filter1.Condition.length !== filter2.Condition.length) {
|
|
291
|
-
return false;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// Compare each condition recursively
|
|
295
|
-
return filter1.Condition.every((condition1, index) =>
|
|
296
|
-
areConditionsEqual(condition1, filter2.Condition[index])
|
|
297
|
-
);
|
|
298
|
-
};
|