@ram_28/kf-ai-sdk 1.0.19 → 1.0.20
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/README.md +45 -12
- package/dist/components/hooks/useFilter/types.d.ts +14 -11
- 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/useForm/apiClient.d.ts.map +1 -1
- package/dist/components/hooks/useForm/useForm.d.ts.map +1 -1
- package/dist/components/hooks/useKanban/types.d.ts +5 -22
- 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 +19 -31
- package/dist/components/hooks/useTable/types.d.ts.map +1 -1
- package/dist/components/hooks/useTable/useTable.d.ts.map +1 -1
- package/dist/error-handling-CAoD0Kwb.cjs +1 -0
- package/dist/error-handling-CrhTtD88.js +14 -0
- package/dist/filter.cjs +1 -1
- package/dist/filter.mjs +1 -1
- package/dist/form.cjs +1 -1
- package/dist/form.mjs +338 -327
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/kanban.cjs +2 -2
- package/dist/kanban.mjs +332 -322
- package/dist/table.cjs +1 -1
- package/dist/table.mjs +113 -96
- package/dist/table.types.d.ts +1 -1
- package/dist/table.types.d.ts.map +1 -1
- package/dist/types/common.d.ts +26 -6
- package/dist/types/common.d.ts.map +1 -1
- package/dist/useFilter-DzpP_ag0.cjs +1 -0
- package/dist/useFilter-H5bgAZQF.js +120 -0
- package/dist/utils/api/buildListOptions.d.ts +43 -0
- package/dist/utils/api/buildListOptions.d.ts.map +1 -0
- package/dist/utils/api/index.d.ts +2 -0
- package/dist/utils/api/index.d.ts.map +1 -0
- package/dist/utils/error-handling.d.ts +41 -0
- package/dist/utils/error-handling.d.ts.map +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/docs/QUICK_REFERENCE.md +142 -420
- package/docs/useAuth.md +52 -340
- package/docs/useFilter.md +858 -162
- package/docs/useForm.md +712 -501
- package/docs/useKanban.md +534 -279
- package/docs/useTable.md +725 -214
- package/package.json +1 -1
- package/sdk/components/hooks/useFilter/types.ts +14 -11
- package/sdk/components/hooks/useFilter/useFilter.ts +20 -18
- package/sdk/components/hooks/useForm/apiClient.ts +2 -1
- package/sdk/components/hooks/useForm/useForm.ts +35 -11
- package/sdk/components/hooks/useKanban/types.ts +7 -23
- package/sdk/components/hooks/useKanban/useKanban.ts +54 -18
- package/sdk/components/hooks/useTable/types.ts +26 -32
- package/sdk/components/hooks/useTable/useTable.llm.txt +8 -22
- package/sdk/components/hooks/useTable/useTable.ts +70 -25
- package/sdk/index.ts +154 -10
- package/sdk/table.types.ts +3 -0
- package/sdk/types/common.ts +31 -6
- package/sdk/utils/api/buildListOptions.ts +120 -0
- package/sdk/utils/api/index.ts +2 -0
- package/sdk/utils/error-handling.ts +150 -0
- package/sdk/utils/index.ts +6 -0
- package/dist/useFilter-Dofowpr_.cjs +0 -1
- package/dist/useFilter-Dv-mr9QW.js +0 -117
package/docs/useFilter.md
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
# useFilter
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Build and manage filter conditions with support for nested groups and complex logic.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
- Provides a clean API for building complex filter payloads that match the backend API format
|
|
7
|
-
- Supports both flat conditions and nested condition groups for advanced filtering scenarios
|
|
8
|
-
- Includes type guards (`isCondition`, `isConditionGroup`) for safely working with the filter tree structure
|
|
9
|
-
|
|
10
|
-
## Type Reference
|
|
5
|
+
## Imports
|
|
11
6
|
|
|
12
7
|
```typescript
|
|
13
8
|
import { useFilter, isCondition, isConditionGroup } from "@ram_28/kf-ai-sdk/filter";
|
|
@@ -19,115 +14,523 @@ import type {
|
|
|
19
14
|
ConditionOperatorType,
|
|
20
15
|
ConditionGroupOperatorType,
|
|
21
16
|
FilterType,
|
|
22
|
-
FilterRHSTypeType,
|
|
23
17
|
} from "@ram_28/kf-ai-sdk/filter/types";
|
|
18
|
+
```
|
|
24
19
|
|
|
25
|
-
|
|
20
|
+
## Type Definitions
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
// Condition operators
|
|
26
24
|
type ConditionOperatorType =
|
|
27
|
-
| "EQ" | "NE"
|
|
25
|
+
| "EQ" | "NE" // Equal, Not Equal
|
|
26
|
+
| "GT" | "GTE" // Greater Than, Greater Than or Equal
|
|
27
|
+
| "LT" | "LTE" // Less Than, Less Than or Equal
|
|
28
28
|
| "Between" | "NotBetween"
|
|
29
|
-
| "IN" | "NIN"
|
|
29
|
+
| "IN" | "NIN" // In List, Not In List
|
|
30
30
|
| "Empty" | "NotEmpty"
|
|
31
31
|
| "Contains" | "NotContains"
|
|
32
32
|
| "MinLength" | "MaxLength";
|
|
33
33
|
|
|
34
|
-
// Group operators
|
|
34
|
+
// Group operators
|
|
35
35
|
type ConditionGroupOperatorType = "And" | "Or" | "Not";
|
|
36
36
|
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// Leaf condition (matches API format)
|
|
41
|
-
interface ConditionType {
|
|
37
|
+
// Single condition (generic for type-safe LHSField)
|
|
38
|
+
interface ConditionType<T = any> {
|
|
42
39
|
id?: string;
|
|
43
40
|
Operator: ConditionOperatorType;
|
|
44
|
-
LHSField: string;
|
|
41
|
+
LHSField: keyof T | string; // Type-safe when T is provided
|
|
45
42
|
RHSValue: any;
|
|
46
|
-
RHSType?:
|
|
43
|
+
RHSType?: "Constant" | "BOField" | "AppVariable";
|
|
47
44
|
}
|
|
48
45
|
|
|
49
|
-
// Condition group (
|
|
50
|
-
interface ConditionGroupType {
|
|
46
|
+
// Condition group (can contain conditions or nested groups)
|
|
47
|
+
interface ConditionGroupType<T = any> {
|
|
51
48
|
id?: string;
|
|
52
49
|
Operator: ConditionGroupOperatorType;
|
|
53
|
-
Condition: Array<ConditionType | ConditionGroupType
|
|
50
|
+
Condition: Array<ConditionType<T> | ConditionGroupType<T>>;
|
|
54
51
|
}
|
|
55
52
|
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
interface UseFilterOptionsType {
|
|
61
|
-
initialConditions?: Array<ConditionType | ConditionGroupType>;
|
|
62
|
-
initialOperator?: ConditionGroupOperatorType;
|
|
53
|
+
// Hook options (also used for initialState in useTable/useKanban)
|
|
54
|
+
interface UseFilterOptionsType<T = any> {
|
|
55
|
+
conditions?: Array<ConditionType<T> | ConditionGroupType<T>>;
|
|
56
|
+
operator?: ConditionGroupOperatorType;
|
|
63
57
|
}
|
|
64
58
|
|
|
65
|
-
// Hook return type
|
|
66
|
-
interface UseFilterReturnType {
|
|
67
|
-
// State (read-only)
|
|
59
|
+
// Hook return type (generic for type-safe field names)
|
|
60
|
+
interface UseFilterReturnType<T = any> {
|
|
68
61
|
operator: ConditionGroupOperatorType;
|
|
69
|
-
items: Array<ConditionType | ConditionGroupType
|
|
70
|
-
payload: FilterType | undefined;
|
|
62
|
+
items: Array<ConditionType<T> | ConditionGroupType<T>>;
|
|
63
|
+
payload: FilterType<T> | undefined;
|
|
71
64
|
hasConditions: boolean;
|
|
72
65
|
|
|
73
|
-
|
|
74
|
-
addCondition: (condition: Omit<ConditionType, "id">, parentId?: string) => string;
|
|
66
|
+
addCondition: (condition: Omit<ConditionType<T>, "id">, parentId?: string) => string;
|
|
75
67
|
addConditionGroup: (operator: ConditionGroupOperatorType, parentId?: string) => string;
|
|
76
|
-
|
|
77
|
-
// Update operations
|
|
78
|
-
updateCondition: (id: string, updates: Partial<Omit<ConditionType, "id">>) => void;
|
|
68
|
+
updateCondition: (id: string, updates: Partial<Omit<ConditionType<T>, "id">>) => void;
|
|
79
69
|
updateGroupOperator: (id: string, operator: ConditionGroupOperatorType) => void;
|
|
80
|
-
|
|
81
|
-
// Remove & access
|
|
82
70
|
removeCondition: (id: string) => void;
|
|
83
|
-
getCondition: (id: string) => ConditionType | ConditionGroupType | undefined;
|
|
84
|
-
|
|
85
|
-
// Utility
|
|
71
|
+
getCondition: (id: string) => ConditionType<T> | ConditionGroupType<T> | undefined;
|
|
86
72
|
clearAllConditions: () => void;
|
|
87
|
-
setRootOperator: (
|
|
73
|
+
setRootOperator: (operator: ConditionGroupOperatorType) => void;
|
|
88
74
|
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Operator Applicability
|
|
78
|
+
|
|
79
|
+
| Operator | Applicable Field Types |
|
|
80
|
+
|----------|----------------------|
|
|
81
|
+
| EQ, NE | All types |
|
|
82
|
+
| GT, GTE, LT, LTE | number, date, currency |
|
|
83
|
+
| Between, NotBetween | number, date, currency |
|
|
84
|
+
| IN, NIN | All types |
|
|
85
|
+
| Empty, NotEmpty | All types |
|
|
86
|
+
| Contains, NotContains | string only |
|
|
87
|
+
| MinLength, MaxLength | string only |
|
|
88
|
+
|
|
89
|
+
## Basic Example
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
Create a simple filter with one condition.
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
import { useFilter } from "@ram_28/kf-ai-sdk/filter";
|
|
95
|
+
|
|
96
|
+
function SimpleFilter() {
|
|
97
|
+
const filter = useFilter();
|
|
98
|
+
|
|
99
|
+
const addCategoryFilter = () => {
|
|
100
|
+
filter.addCondition({
|
|
101
|
+
Operator: "EQ",
|
|
102
|
+
LHSField: "Category",
|
|
103
|
+
RHSValue: "Electronics",
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<div>
|
|
109
|
+
<button onClick={addCategoryFilter}>Filter by Electronics</button>
|
|
110
|
+
<button onClick={filter.clearAllConditions} disabled={!filter.hasConditions}>
|
|
111
|
+
Clear
|
|
112
|
+
</button>
|
|
113
|
+
<p>Active filters: {filter.items.length}</p>
|
|
114
|
+
</div>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
93
117
|
```
|
|
94
118
|
|
|
95
|
-
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Type-Safe Filtering
|
|
122
|
+
|
|
123
|
+
Use the generic type parameter to get TypeScript validation on field names.
|
|
124
|
+
|
|
125
|
+
### With Generic Type
|
|
96
126
|
|
|
97
127
|
```tsx
|
|
98
|
-
import { useFilter
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
128
|
+
import { useFilter } from "@ram_28/kf-ai-sdk/filter";
|
|
129
|
+
|
|
130
|
+
interface Product {
|
|
131
|
+
_id: string;
|
|
132
|
+
Title: string;
|
|
133
|
+
Price: number;
|
|
134
|
+
Category: string;
|
|
135
|
+
Stock: number;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function TypeSafeFilter() {
|
|
139
|
+
// Pass the type parameter for type-safe LHSField
|
|
140
|
+
const filter = useFilter<Product>();
|
|
107
141
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
142
|
+
const addCategoryFilter = () => {
|
|
143
|
+
filter.addCondition({
|
|
144
|
+
Operator: "EQ",
|
|
145
|
+
LHSField: "Category", // TypeScript validates this field exists
|
|
146
|
+
RHSValue: "Electronics",
|
|
147
|
+
});
|
|
112
148
|
};
|
|
113
|
-
const filter: UseFilterReturnType = useFilter(options);
|
|
114
149
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
150
|
+
const addInvalidFilter = () => {
|
|
151
|
+
filter.addCondition({
|
|
152
|
+
Operator: "EQ",
|
|
153
|
+
LHSField: "InvalidField", // TypeScript error: not a key of Product
|
|
154
|
+
RHSValue: "test",
|
|
155
|
+
});
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
<div>
|
|
160
|
+
<button onClick={addCategoryFilter}>Filter by Category</button>
|
|
161
|
+
</div>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### UseFilterOptionsType for Initial State
|
|
167
|
+
|
|
168
|
+
`UseFilterOptionsType<T>` is used for:
|
|
169
|
+
- Initializing `useFilter` directly
|
|
170
|
+
- Setting `initialState.filter` in `useTable`
|
|
171
|
+
- Setting `initialState.filter` in `useKanban`
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
import { useFilter } from "@ram_28/kf-ai-sdk/filter";
|
|
175
|
+
import type { UseFilterOptionsType } from "@ram_28/kf-ai-sdk/filter/types";
|
|
176
|
+
|
|
177
|
+
interface Product {
|
|
178
|
+
_id: string;
|
|
179
|
+
Title: string;
|
|
180
|
+
Price: number;
|
|
181
|
+
Category: string;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Type-safe filter options
|
|
185
|
+
const initialFilter: UseFilterOptionsType<Product> = {
|
|
186
|
+
conditions: [
|
|
187
|
+
{ Operator: "EQ", LHSField: "Category", RHSValue: "Electronics" },
|
|
188
|
+
{ Operator: "GT", LHSField: "Price", RHSValue: 100 },
|
|
189
|
+
],
|
|
190
|
+
operator: "And",
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// Use with useFilter
|
|
194
|
+
const filter = useFilter<Product>(initialFilter);
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Condition Types
|
|
200
|
+
|
|
201
|
+
### Equality (EQ, NE)
|
|
202
|
+
|
|
203
|
+
Match or exclude exact values.
|
|
204
|
+
|
|
205
|
+
```tsx
|
|
206
|
+
function EqualityFilters() {
|
|
207
|
+
const filter = useFilter();
|
|
208
|
+
|
|
209
|
+
// Exact match
|
|
210
|
+
const filterByStatus = (status: string) => {
|
|
211
|
+
filter.addCondition({
|
|
212
|
+
Operator: "EQ",
|
|
213
|
+
LHSField: "Status",
|
|
214
|
+
RHSValue: status,
|
|
215
|
+
});
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// Exclude a value
|
|
219
|
+
const excludeStatus = (status: string) => {
|
|
220
|
+
filter.addCondition({
|
|
221
|
+
Operator: "NE",
|
|
222
|
+
LHSField: "Status",
|
|
223
|
+
RHSValue: status,
|
|
224
|
+
});
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
return (
|
|
228
|
+
<div>
|
|
229
|
+
<button onClick={() => filterByStatus("Active")}>Active Only</button>
|
|
230
|
+
<button onClick={() => excludeStatus("Archived")}>Exclude Archived</button>
|
|
231
|
+
</div>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Comparison (GT, GTE, LT, LTE)
|
|
237
|
+
|
|
238
|
+
Filter by numeric or date comparisons.
|
|
239
|
+
|
|
240
|
+
```tsx
|
|
241
|
+
function ComparisonFilters() {
|
|
242
|
+
const filter = useFilter();
|
|
243
|
+
|
|
244
|
+
// Price greater than
|
|
245
|
+
const filterByMinPrice = (minPrice: number) => {
|
|
246
|
+
filter.addCondition({
|
|
247
|
+
Operator: "GT",
|
|
248
|
+
LHSField: "Price",
|
|
249
|
+
RHSValue: minPrice,
|
|
250
|
+
});
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
// Stock less than or equal
|
|
254
|
+
const filterLowStock = (threshold: number) => {
|
|
255
|
+
filter.addCondition({
|
|
256
|
+
Operator: "LTE",
|
|
257
|
+
LHSField: "Stock",
|
|
258
|
+
RHSValue: threshold,
|
|
259
|
+
});
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
// Date comparison
|
|
263
|
+
const filterRecent = () => {
|
|
264
|
+
const lastWeek = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
|
|
265
|
+
filter.addCondition({
|
|
266
|
+
Operator: "GTE",
|
|
267
|
+
LHSField: "CreatedAt",
|
|
268
|
+
RHSValue: lastWeek,
|
|
269
|
+
});
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
return (
|
|
273
|
+
<div>
|
|
274
|
+
<button onClick={() => filterByMinPrice(100)}>Price > $100</button>
|
|
275
|
+
<button onClick={() => filterLowStock(10)}>Low Stock</button>
|
|
276
|
+
<button onClick={filterRecent}>Created This Week</button>
|
|
277
|
+
</div>
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Range (Between, NotBetween)
|
|
283
|
+
|
|
284
|
+
Filter values within or outside a range.
|
|
285
|
+
|
|
286
|
+
```tsx
|
|
287
|
+
function RangeFilters() {
|
|
288
|
+
const filter = useFilter();
|
|
289
|
+
|
|
290
|
+
// Price between range
|
|
291
|
+
const filterPriceRange = (min: number, max: number) => {
|
|
292
|
+
filter.addCondition({
|
|
293
|
+
Operator: "Between",
|
|
294
|
+
LHSField: "Price",
|
|
295
|
+
RHSValue: [min, max],
|
|
296
|
+
});
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
// Date range
|
|
300
|
+
const filterDateRange = (startDate: string, endDate: string) => {
|
|
301
|
+
filter.addCondition({
|
|
302
|
+
Operator: "Between",
|
|
303
|
+
LHSField: "OrderDate",
|
|
304
|
+
RHSValue: [startDate, endDate],
|
|
305
|
+
});
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
// Exclude range
|
|
309
|
+
const excludePriceRange = (min: number, max: number) => {
|
|
310
|
+
filter.addCondition({
|
|
311
|
+
Operator: "NotBetween",
|
|
312
|
+
LHSField: "Price",
|
|
313
|
+
RHSValue: [min, max],
|
|
314
|
+
});
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
return (
|
|
318
|
+
<div>
|
|
319
|
+
<button onClick={() => filterPriceRange(50, 200)}>$50 - $200</button>
|
|
320
|
+
<button onClick={() => excludePriceRange(0, 10)}>Exclude Under $10</button>
|
|
321
|
+
</div>
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### List (IN, NIN)
|
|
327
|
+
|
|
328
|
+
Match against a list of values.
|
|
329
|
+
|
|
330
|
+
```tsx
|
|
331
|
+
function ListFilters() {
|
|
332
|
+
const filter = useFilter();
|
|
333
|
+
|
|
334
|
+
// Match any in list
|
|
335
|
+
const filterByCategories = (categories: string[]) => {
|
|
336
|
+
filter.addCondition({
|
|
337
|
+
Operator: "IN",
|
|
338
|
+
LHSField: "Category",
|
|
339
|
+
RHSValue: categories,
|
|
340
|
+
});
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
// Exclude list
|
|
344
|
+
const excludeCategories = (categories: string[]) => {
|
|
345
|
+
filter.addCondition({
|
|
346
|
+
Operator: "NIN",
|
|
347
|
+
LHSField: "Category",
|
|
348
|
+
RHSValue: categories,
|
|
349
|
+
});
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
return (
|
|
353
|
+
<div>
|
|
354
|
+
<button onClick={() => filterByCategories(["Electronics", "Computers"])}>
|
|
355
|
+
Electronics & Computers
|
|
356
|
+
</button>
|
|
357
|
+
<button onClick={() => excludeCategories(["Clearance", "Discontinued"])}>
|
|
358
|
+
Exclude Clearance
|
|
359
|
+
</button>
|
|
360
|
+
</div>
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### Text (Contains, NotContains)
|
|
366
|
+
|
|
367
|
+
Search within text fields.
|
|
368
|
+
|
|
369
|
+
```tsx
|
|
370
|
+
function TextFilters() {
|
|
371
|
+
const filter = useFilter();
|
|
372
|
+
|
|
373
|
+
// Contains text
|
|
374
|
+
const filterByKeyword = (keyword: string) => {
|
|
375
|
+
filter.addCondition({
|
|
376
|
+
Operator: "Contains",
|
|
377
|
+
LHSField: "Title",
|
|
378
|
+
RHSValue: keyword,
|
|
379
|
+
});
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
// Does not contain
|
|
383
|
+
const excludeKeyword = (keyword: string) => {
|
|
384
|
+
filter.addCondition({
|
|
385
|
+
Operator: "NotContains",
|
|
386
|
+
LHSField: "Description",
|
|
387
|
+
RHSValue: keyword,
|
|
388
|
+
});
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
return (
|
|
392
|
+
<div>
|
|
393
|
+
<input
|
|
394
|
+
placeholder="Search..."
|
|
395
|
+
onKeyDown={(e) => {
|
|
396
|
+
if (e.key === "Enter") {
|
|
397
|
+
filterByKeyword((e.target as HTMLInputElement).value);
|
|
398
|
+
}
|
|
399
|
+
}}
|
|
400
|
+
/>
|
|
401
|
+
</div>
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Empty Checks (Empty, NotEmpty)
|
|
407
|
+
|
|
408
|
+
Filter by presence or absence of values.
|
|
409
|
+
|
|
410
|
+
```tsx
|
|
411
|
+
function EmptyFilters() {
|
|
412
|
+
const filter = useFilter();
|
|
413
|
+
|
|
414
|
+
// Has no value
|
|
415
|
+
const filterUnassigned = () => {
|
|
416
|
+
filter.addCondition({
|
|
417
|
+
Operator: "Empty",
|
|
418
|
+
LHSField: "AssignedTo",
|
|
419
|
+
RHSValue: null,
|
|
420
|
+
});
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
// Has a value
|
|
424
|
+
const filterAssigned = () => {
|
|
425
|
+
filter.addCondition({
|
|
426
|
+
Operator: "NotEmpty",
|
|
427
|
+
LHSField: "AssignedTo",
|
|
428
|
+
RHSValue: null,
|
|
429
|
+
});
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
return (
|
|
433
|
+
<div>
|
|
434
|
+
<button onClick={filterUnassigned}>Unassigned Tasks</button>
|
|
435
|
+
<button onClick={filterAssigned}>Assigned Tasks</button>
|
|
436
|
+
</div>
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
## Condition Groups
|
|
444
|
+
|
|
445
|
+
### AND Logic
|
|
446
|
+
|
|
447
|
+
All conditions must match.
|
|
448
|
+
|
|
449
|
+
```tsx
|
|
450
|
+
function AndFilter() {
|
|
451
|
+
const filter = useFilter({
|
|
452
|
+
operator: "And",
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
const applyFilters = () => {
|
|
456
|
+
filter.clearAllConditions();
|
|
457
|
+
|
|
458
|
+
// Category AND Price AND InStock - all must be true
|
|
459
|
+
filter.addCondition({
|
|
118
460
|
Operator: "EQ",
|
|
119
461
|
LHSField: "Category",
|
|
120
462
|
RHSValue: "Electronics",
|
|
121
|
-
RHSType: "Constant",
|
|
122
463
|
});
|
|
123
|
-
|
|
464
|
+
filter.addCondition({
|
|
465
|
+
Operator: "LTE",
|
|
466
|
+
LHSField: "Price",
|
|
467
|
+
RHSValue: 500,
|
|
468
|
+
});
|
|
469
|
+
filter.addCondition({
|
|
470
|
+
Operator: "GT",
|
|
471
|
+
LHSField: "Stock",
|
|
472
|
+
RHSValue: 0,
|
|
473
|
+
});
|
|
124
474
|
};
|
|
125
475
|
|
|
126
|
-
|
|
127
|
-
|
|
476
|
+
return (
|
|
477
|
+
<div>
|
|
478
|
+
<button onClick={applyFilters}>
|
|
479
|
+
Electronics under $500, In Stock
|
|
480
|
+
</button>
|
|
481
|
+
<p>Root operator: {filter.operator}</p>
|
|
482
|
+
</div>
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### OR Logic
|
|
488
|
+
|
|
489
|
+
Any condition can match.
|
|
490
|
+
|
|
491
|
+
```tsx
|
|
492
|
+
function OrFilter() {
|
|
493
|
+
const filter = useFilter({
|
|
494
|
+
operator: "Or",
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
const applyFilters = () => {
|
|
128
498
|
filter.clearAllConditions();
|
|
129
499
|
|
|
130
|
-
//
|
|
500
|
+
// High priority OR Overdue - either can match
|
|
501
|
+
filter.addCondition({
|
|
502
|
+
Operator: "EQ",
|
|
503
|
+
LHSField: "Priority",
|
|
504
|
+
RHSValue: "High",
|
|
505
|
+
});
|
|
506
|
+
filter.addCondition({
|
|
507
|
+
Operator: "LT",
|
|
508
|
+
LHSField: "DueDate",
|
|
509
|
+
RHSValue: new Date().toISOString(),
|
|
510
|
+
});
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
return (
|
|
514
|
+
<button onClick={applyFilters}>Urgent Tasks</button>
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
### Nested Groups
|
|
520
|
+
|
|
521
|
+
Combine AND and OR logic.
|
|
522
|
+
|
|
523
|
+
```tsx
|
|
524
|
+
function NestedFilter() {
|
|
525
|
+
const filter = useFilter({
|
|
526
|
+
operator: "And",
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
// Build: Category = "Electronics" AND (Price < 100 OR OnSale = true)
|
|
530
|
+
const applyComplexFilter = () => {
|
|
531
|
+
filter.clearAllConditions();
|
|
532
|
+
|
|
533
|
+
// Root level condition
|
|
131
534
|
filter.addCondition({
|
|
132
535
|
Operator: "EQ",
|
|
133
536
|
LHSField: "Category",
|
|
@@ -135,42 +538,120 @@ function ProductFilterBuilder() {
|
|
|
135
538
|
});
|
|
136
539
|
|
|
137
540
|
// Create nested OR group
|
|
138
|
-
const
|
|
541
|
+
const orGroupId = filter.addConditionGroup("Or");
|
|
139
542
|
|
|
140
|
-
// Add conditions to the group
|
|
543
|
+
// Add conditions to the OR group
|
|
141
544
|
filter.addCondition({
|
|
142
|
-
Operator: "
|
|
545
|
+
Operator: "LT",
|
|
143
546
|
LHSField: "Price",
|
|
144
547
|
RHSValue: 100,
|
|
145
|
-
},
|
|
548
|
+
}, orGroupId);
|
|
146
549
|
|
|
147
|
-
|
|
550
|
+
filter.addCondition({
|
|
148
551
|
Operator: "EQ",
|
|
149
552
|
LHSField: "OnSale",
|
|
150
553
|
RHSValue: true,
|
|
151
|
-
},
|
|
554
|
+
}, orGroupId);
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
return (
|
|
558
|
+
<button onClick={applyComplexFilter}>
|
|
559
|
+
Affordable Electronics
|
|
560
|
+
</button>
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
### Deep Nesting
|
|
152
566
|
|
|
153
|
-
|
|
154
|
-
filter.updateCondition(saleConditionId, { RHSValue: false });
|
|
567
|
+
Create multiple levels of nested groups.
|
|
155
568
|
|
|
156
|
-
|
|
157
|
-
|
|
569
|
+
```tsx
|
|
570
|
+
function DeepNestedFilter() {
|
|
571
|
+
const filter = useFilter();
|
|
572
|
+
|
|
573
|
+
// Build: (A AND B) OR (C AND D)
|
|
574
|
+
const buildFilter = () => {
|
|
575
|
+
filter.clearAllConditions();
|
|
576
|
+
filter.setRootOperator("Or");
|
|
577
|
+
|
|
578
|
+
// First AND group
|
|
579
|
+
const group1 = filter.addConditionGroup("And");
|
|
580
|
+
filter.addCondition({ Operator: "EQ", LHSField: "Type", RHSValue: "A" }, group1);
|
|
581
|
+
filter.addCondition({ Operator: "GT", LHSField: "Value", RHSValue: 10 }, group1);
|
|
582
|
+
|
|
583
|
+
// Second AND group
|
|
584
|
+
const group2 = filter.addConditionGroup("And");
|
|
585
|
+
filter.addCondition({ Operator: "EQ", LHSField: "Type", RHSValue: "B" }, group2);
|
|
586
|
+
filter.addCondition({ Operator: "LT", LHSField: "Value", RHSValue: 5 }, group2);
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
return <button onClick={buildFilter}>Apply Complex Filter</button>;
|
|
590
|
+
}
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
---
|
|
594
|
+
|
|
595
|
+
## Displaying Filters
|
|
596
|
+
|
|
597
|
+
### Render Active Filters
|
|
598
|
+
|
|
599
|
+
Show current filter conditions with remove buttons.
|
|
600
|
+
|
|
601
|
+
```tsx
|
|
602
|
+
import { isCondition, isConditionGroup } from "@ram_28/kf-ai-sdk/filter";
|
|
603
|
+
|
|
604
|
+
function ActiveFilters() {
|
|
605
|
+
const filter = useFilter();
|
|
606
|
+
|
|
607
|
+
const renderItem = (item: ConditionType | ConditionGroupType) => {
|
|
608
|
+
if (isCondition(item)) {
|
|
609
|
+
return (
|
|
610
|
+
<span key={item.id} className="filter-tag">
|
|
611
|
+
{item.LHSField} {item.Operator} {String(item.RHSValue)}
|
|
612
|
+
<button onClick={() => filter.removeCondition(item.id!)}>x</button>
|
|
613
|
+
</span>
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
if (isConditionGroup(item)) {
|
|
618
|
+
return (
|
|
619
|
+
<span key={item.id} className="filter-group">
|
|
620
|
+
{item.Operator} ({item.Condition.length} conditions)
|
|
621
|
+
<button onClick={() => filter.removeCondition(item.id!)}>x</button>
|
|
622
|
+
</span>
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
return null;
|
|
158
627
|
};
|
|
159
628
|
|
|
160
|
-
|
|
161
|
-
|
|
629
|
+
return (
|
|
630
|
+
<div className="active-filters">
|
|
631
|
+
{filter.items.map(renderItem)}
|
|
632
|
+
{filter.hasConditions && (
|
|
633
|
+
<button onClick={filter.clearAllConditions}>Clear All</button>
|
|
634
|
+
)}
|
|
635
|
+
</div>
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
### Recursive Tree Display
|
|
641
|
+
|
|
642
|
+
Display nested filter structure as a tree.
|
|
643
|
+
|
|
644
|
+
```tsx
|
|
645
|
+
function FilterTree() {
|
|
646
|
+
const filter = useFilter();
|
|
647
|
+
|
|
648
|
+
const renderNode = (item: ConditionType | ConditionGroupType, depth = 0) => {
|
|
162
649
|
const indent = { marginLeft: depth * 20 };
|
|
163
650
|
|
|
164
651
|
if (isCondition(item)) {
|
|
165
652
|
return (
|
|
166
653
|
<div key={item.id} style={indent} className="filter-condition">
|
|
167
|
-
|
|
168
|
-
{item.LHSField} {item.Operator} {String(item.RHSValue)}
|
|
169
|
-
</span>
|
|
170
|
-
<button onClick={() => filter.removeCondition(item.id!)}>Remove</button>
|
|
171
|
-
<button onClick={() => filter.updateCondition(item.id!, { RHSValue: "Updated" })}>
|
|
172
|
-
Update
|
|
173
|
-
</button>
|
|
654
|
+
{item.LHSField} {item.Operator} {JSON.stringify(item.RHSValue)}
|
|
174
655
|
</div>
|
|
175
656
|
);
|
|
176
657
|
}
|
|
@@ -178,96 +659,311 @@ function ProductFilterBuilder() {
|
|
|
178
659
|
if (isConditionGroup(item)) {
|
|
179
660
|
return (
|
|
180
661
|
<div key={item.id} style={indent} className="filter-group">
|
|
181
|
-
<
|
|
182
|
-
|
|
183
|
-
value={item.Operator}
|
|
184
|
-
onChange={(e) =>
|
|
185
|
-
filter.updateGroupOperator(item.id!, e.target.value as ConditionGroupOperatorType)
|
|
186
|
-
}
|
|
187
|
-
>
|
|
188
|
-
<option value="And">AND</option>
|
|
189
|
-
<option value="Or">OR</option>
|
|
190
|
-
<option value="Not">NOT</option>
|
|
191
|
-
</select>
|
|
192
|
-
<button onClick={() => filter.addCondition({ Operator: "EQ", LHSField: "Field", RHSValue: "" }, item.id!)}>
|
|
193
|
-
+ Condition
|
|
194
|
-
</button>
|
|
195
|
-
<button onClick={() => filter.addConditionGroup("And", item.id!)}>+ Group</button>
|
|
196
|
-
<button onClick={() => filter.removeCondition(item.id!)}>Remove Group</button>
|
|
197
|
-
</div>
|
|
198
|
-
<div className="group-conditions">
|
|
199
|
-
{item.Condition.map((child) => renderFilterItem(child, depth + 1))}
|
|
200
|
-
</div>
|
|
662
|
+
<strong>{item.Operator}</strong>
|
|
663
|
+
{item.Condition.map((child) => renderNode(child, depth + 1))}
|
|
201
664
|
</div>
|
|
202
665
|
);
|
|
203
666
|
}
|
|
204
667
|
|
|
205
|
-
return
|
|
668
|
+
return null;
|
|
206
669
|
};
|
|
207
670
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
671
|
+
return (
|
|
672
|
+
<div className="filter-tree">
|
|
673
|
+
<div className="root">
|
|
674
|
+
<strong>Root: {filter.operator}</strong>
|
|
675
|
+
</div>
|
|
676
|
+
{filter.items.map((item) => renderNode(item, 1))}
|
|
677
|
+
</div>
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
---
|
|
683
|
+
|
|
684
|
+
## Modifying Filters
|
|
685
|
+
|
|
686
|
+
### Update a Condition
|
|
687
|
+
|
|
688
|
+
Change an existing condition's values.
|
|
689
|
+
|
|
690
|
+
```tsx
|
|
691
|
+
function EditableFilter() {
|
|
692
|
+
const filter = useFilter();
|
|
693
|
+
const [conditionId, setConditionId] = useState<string | null>(null);
|
|
694
|
+
|
|
695
|
+
const addFilter = () => {
|
|
696
|
+
const id = filter.addCondition({
|
|
697
|
+
Operator: "GT",
|
|
698
|
+
LHSField: "Price",
|
|
699
|
+
RHSValue: 50,
|
|
700
|
+
});
|
|
701
|
+
setConditionId(id);
|
|
702
|
+
};
|
|
703
|
+
|
|
704
|
+
const updateValue = (newValue: number) => {
|
|
705
|
+
if (conditionId) {
|
|
706
|
+
filter.updateCondition(conditionId, { RHSValue: newValue });
|
|
219
707
|
}
|
|
220
708
|
};
|
|
221
709
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
if (item) {
|
|
226
|
-
console.log("Found item:", item);
|
|
710
|
+
const updateOperator = (operator: ConditionOperatorType) => {
|
|
711
|
+
if (conditionId) {
|
|
712
|
+
filter.updateCondition(conditionId, { Operator: operator });
|
|
227
713
|
}
|
|
228
714
|
};
|
|
229
715
|
|
|
230
716
|
return (
|
|
231
|
-
<div
|
|
232
|
-
{
|
|
233
|
-
|
|
234
|
-
<
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
value=
|
|
238
|
-
|
|
239
|
-
>
|
|
240
|
-
<option value="And">AND</option>
|
|
241
|
-
<option value="Or">OR</option>
|
|
717
|
+
<div>
|
|
718
|
+
<button onClick={addFilter}>Add Price Filter</button>
|
|
719
|
+
{conditionId && (
|
|
720
|
+
<div>
|
|
721
|
+
<select onChange={(e) => updateOperator(e.target.value as ConditionOperatorType)}>
|
|
722
|
+
<option value="GT">Greater Than</option>
|
|
723
|
+
<option value="LT">Less Than</option>
|
|
724
|
+
<option value="EQ">Equals</option>
|
|
242
725
|
</select>
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
726
|
+
<input
|
|
727
|
+
type="number"
|
|
728
|
+
defaultValue={50}
|
|
729
|
+
onChange={(e) => updateValue(Number(e.target.value))}
|
|
730
|
+
/>
|
|
731
|
+
</div>
|
|
732
|
+
)}
|
|
733
|
+
</div>
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
```
|
|
251
737
|
|
|
252
|
-
|
|
253
|
-
<div className="filter-tree">
|
|
254
|
-
<h3>Filter Tree ({filter.items.length} root items)</h3>
|
|
255
|
-
{filter.items.map((item) => renderFilterItem(item))}
|
|
256
|
-
</div>
|
|
738
|
+
### Change Group Operator
|
|
257
739
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
740
|
+
Toggle between AND/OR for a group.
|
|
741
|
+
|
|
742
|
+
```tsx
|
|
743
|
+
function ToggleableGroupOperator() {
|
|
744
|
+
const filter = useFilter();
|
|
745
|
+
const [groupId, setGroupId] = useState<string | null>(null);
|
|
746
|
+
|
|
747
|
+
const createGroup = () => {
|
|
748
|
+
const id = filter.addConditionGroup("And");
|
|
749
|
+
filter.addCondition({ Operator: "EQ", LHSField: "A", RHSValue: 1 }, id);
|
|
750
|
+
filter.addCondition({ Operator: "EQ", LHSField: "B", RHSValue: 2 }, id);
|
|
751
|
+
setGroupId(id);
|
|
752
|
+
};
|
|
753
|
+
|
|
754
|
+
const toggleOperator = () => {
|
|
755
|
+
if (groupId) {
|
|
756
|
+
const group = filter.getCondition(groupId) as ConditionGroupType;
|
|
757
|
+
const newOp = group.Operator === "And" ? "Or" : "And";
|
|
758
|
+
filter.updateGroupOperator(groupId, newOp);
|
|
759
|
+
}
|
|
760
|
+
};
|
|
761
|
+
|
|
762
|
+
return (
|
|
763
|
+
<div>
|
|
764
|
+
<button onClick={createGroup}>Create Group</button>
|
|
765
|
+
{groupId && (
|
|
766
|
+
<button onClick={toggleOperator}>Toggle AND/OR</button>
|
|
767
|
+
)}
|
|
768
|
+
</div>
|
|
769
|
+
);
|
|
770
|
+
}
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
---
|
|
774
|
+
|
|
775
|
+
## Using Filter Payload
|
|
776
|
+
|
|
777
|
+
### Get API-Ready Payload
|
|
778
|
+
|
|
779
|
+
Access the filter structure for API calls.
|
|
780
|
+
|
|
781
|
+
```tsx
|
|
782
|
+
function FilterWithApi() {
|
|
783
|
+
const filter = useFilter();
|
|
784
|
+
|
|
785
|
+
const fetchFiltered = async () => {
|
|
786
|
+
const payload = filter.payload;
|
|
787
|
+
|
|
788
|
+
if (!payload) {
|
|
789
|
+
console.log("No filters applied");
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// Payload is ready for API - no internal IDs included
|
|
794
|
+
const response = await fetch("/api/products", {
|
|
795
|
+
method: "POST",
|
|
796
|
+
headers: { "Content-Type": "application/json" },
|
|
797
|
+
body: JSON.stringify({ Filter: payload }),
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
return response.json();
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
return (
|
|
804
|
+
<div>
|
|
805
|
+
<button onClick={() => filter.addCondition({
|
|
806
|
+
Operator: "EQ",
|
|
807
|
+
LHSField: "Status",
|
|
808
|
+
RHSValue: "Active",
|
|
809
|
+
})}>
|
|
810
|
+
Add Filter
|
|
811
|
+
</button>
|
|
812
|
+
<button onClick={fetchFiltered}>Fetch Data</button>
|
|
813
|
+
|
|
814
|
+
<pre>{JSON.stringify(filter.payload, null, 2)}</pre>
|
|
815
|
+
</div>
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
### Initialize with Existing Filters
|
|
821
|
+
|
|
822
|
+
Load filters from saved state.
|
|
265
823
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
824
|
+
```tsx
|
|
825
|
+
function FilterWithInitialState() {
|
|
826
|
+
const savedFilters: Array<ConditionType | ConditionGroupType> = [
|
|
827
|
+
{ Operator: "EQ", LHSField: "Status", RHSValue: "Active" },
|
|
828
|
+
{ Operator: "GT", LHSField: "Price", RHSValue: 100 },
|
|
829
|
+
];
|
|
830
|
+
|
|
831
|
+
const filter = useFilter({
|
|
832
|
+
conditions: savedFilters,
|
|
833
|
+
operator: "And",
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
return (
|
|
837
|
+
<div>
|
|
838
|
+
<p>Loaded {filter.items.length} filters</p>
|
|
839
|
+
{/* filter UI */}
|
|
270
840
|
</div>
|
|
271
841
|
);
|
|
272
842
|
}
|
|
273
843
|
```
|
|
844
|
+
|
|
845
|
+
---
|
|
846
|
+
|
|
847
|
+
## Payload Structure
|
|
848
|
+
|
|
849
|
+
The `filter.payload` property returns the filter structure ready for API consumption. Here are examples of what the JSON looks like for different scenarios.
|
|
850
|
+
|
|
851
|
+
### Single Condition
|
|
852
|
+
|
|
853
|
+
```tsx
|
|
854
|
+
filter.addCondition({
|
|
855
|
+
Operator: "EQ",
|
|
856
|
+
LHSField: "Status",
|
|
857
|
+
RHSValue: "Active",
|
|
858
|
+
});
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
Produces:
|
|
862
|
+
|
|
863
|
+
```json
|
|
864
|
+
{
|
|
865
|
+
"Operator": "And",
|
|
866
|
+
"Condition": [
|
|
867
|
+
{
|
|
868
|
+
"Operator": "EQ",
|
|
869
|
+
"LHSField": "Status",
|
|
870
|
+
"RHSValue": "Active",
|
|
871
|
+
"RHSType": "Constant"
|
|
872
|
+
}
|
|
873
|
+
]
|
|
874
|
+
}
|
|
875
|
+
```
|
|
876
|
+
|
|
877
|
+
### Multiple Conditions (AND)
|
|
878
|
+
|
|
879
|
+
```tsx
|
|
880
|
+
filter.addCondition({ Operator: "EQ", LHSField: "Category", RHSValue: "Electronics" });
|
|
881
|
+
filter.addCondition({ Operator: "GT", LHSField: "Price", RHSValue: 100 });
|
|
882
|
+
filter.addCondition({ Operator: "LTE", LHSField: "Stock", RHSValue: 50 });
|
|
883
|
+
```
|
|
884
|
+
|
|
885
|
+
Produces:
|
|
886
|
+
|
|
887
|
+
```json
|
|
888
|
+
{
|
|
889
|
+
"Operator": "And",
|
|
890
|
+
"Condition": [
|
|
891
|
+
{ "Operator": "EQ", "LHSField": "Category", "RHSValue": "Electronics", "RHSType": "Constant" },
|
|
892
|
+
{ "Operator": "GT", "LHSField": "Price", "RHSValue": 100, "RHSType": "Constant" },
|
|
893
|
+
{ "Operator": "LTE", "LHSField": "Stock", "RHSValue": 50, "RHSType": "Constant" }
|
|
894
|
+
]
|
|
895
|
+
}
|
|
896
|
+
```
|
|
897
|
+
|
|
898
|
+
### Nested Groups
|
|
899
|
+
|
|
900
|
+
```tsx
|
|
901
|
+
// Root: AND
|
|
902
|
+
// Category = "Electronics" AND (Price < 100 OR OnSale = true)
|
|
903
|
+
filter.addCondition({ Operator: "EQ", LHSField: "Category", RHSValue: "Electronics" });
|
|
904
|
+
const orGroupId = filter.addConditionGroup("Or");
|
|
905
|
+
filter.addCondition({ Operator: "LT", LHSField: "Price", RHSValue: 100 }, orGroupId);
|
|
906
|
+
filter.addCondition({ Operator: "EQ", LHSField: "OnSale", RHSValue: true }, orGroupId);
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
Produces:
|
|
910
|
+
|
|
911
|
+
```json
|
|
912
|
+
{
|
|
913
|
+
"Operator": "And",
|
|
914
|
+
"Condition": [
|
|
915
|
+
{ "Operator": "EQ", "LHSField": "Category", "RHSValue": "Electronics", "RHSType": "Constant" },
|
|
916
|
+
{
|
|
917
|
+
"Operator": "Or",
|
|
918
|
+
"Condition": [
|
|
919
|
+
{ "Operator": "LT", "LHSField": "Price", "RHSValue": 100, "RHSType": "Constant" },
|
|
920
|
+
{ "Operator": "EQ", "LHSField": "OnSale", "RHSValue": true, "RHSType": "Constant" }
|
|
921
|
+
]
|
|
922
|
+
}
|
|
923
|
+
]
|
|
924
|
+
}
|
|
925
|
+
```
|
|
926
|
+
|
|
927
|
+
### Range Values (Between)
|
|
928
|
+
|
|
929
|
+
```tsx
|
|
930
|
+
filter.addCondition({
|
|
931
|
+
Operator: "Between",
|
|
932
|
+
LHSField: "Price",
|
|
933
|
+
RHSValue: [50, 200],
|
|
934
|
+
});
|
|
935
|
+
```
|
|
936
|
+
|
|
937
|
+
Produces:
|
|
938
|
+
|
|
939
|
+
```json
|
|
940
|
+
{
|
|
941
|
+
"Operator": "And",
|
|
942
|
+
"Condition": [
|
|
943
|
+
{ "Operator": "Between", "LHSField": "Price", "RHSValue": [50, 200], "RHSType": "Constant" }
|
|
944
|
+
]
|
|
945
|
+
}
|
|
946
|
+
```
|
|
947
|
+
|
|
948
|
+
### List Values (IN)
|
|
949
|
+
|
|
950
|
+
```tsx
|
|
951
|
+
filter.addCondition({
|
|
952
|
+
Operator: "IN",
|
|
953
|
+
LHSField: "Category",
|
|
954
|
+
RHSValue: ["Electronics", "Computers", "Accessories"],
|
|
955
|
+
});
|
|
956
|
+
```
|
|
957
|
+
|
|
958
|
+
Produces:
|
|
959
|
+
|
|
960
|
+
```json
|
|
961
|
+
{
|
|
962
|
+
"Operator": "And",
|
|
963
|
+
"Condition": [
|
|
964
|
+
{ "Operator": "IN", "LHSField": "Category", "RHSValue": ["Electronics", "Computers", "Accessories"], "RHSType": "Constant" }
|
|
965
|
+
]
|
|
966
|
+
}
|
|
967
|
+
```
|
|
968
|
+
|
|
969
|
+
> **Note:** The `RHSType` field defaults to `"Constant"` and is automatically added to the payload. Other possible values are `"BOField"` (reference another field) and `"AppVariable"` (reference an application variable).
|