@ram_28/kf-ai-sdk 2.0.16 → 2.0.18
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 +16 -8
- package/dist/{FileField-BWrSHNRq.js → FileField-CZjS2uLh.js} +3 -3
- package/dist/{FileField-eDeuzln8.cjs → FileField-DU4UWo_t.cjs} +1 -1
- package/dist/api.cjs +1 -1
- package/dist/api.mjs +1 -1
- package/dist/auth/authConfig.d.ts +1 -1
- package/dist/auth/types.d.ts +1 -1
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/auth.cjs +1 -1
- package/dist/auth.mjs +1 -1
- package/dist/bdo/core/Item.d.ts +0 -4
- package/dist/bdo/core/Item.d.ts.map +1 -1
- package/dist/bdo/fields/ReferenceField.d.ts +1 -1
- package/dist/bdo/fields/ReferenceField.d.ts.map +1 -1
- package/dist/bdo/fields/SelectField.d.ts +1 -1
- package/dist/bdo/fields/SelectField.d.ts.map +1 -1
- package/dist/bdo/fields/UserField.d.ts +1 -1
- package/dist/bdo/fields/UserField.d.ts.map +1 -1
- package/dist/bdo.cjs +1 -1
- package/dist/bdo.mjs +53 -62
- package/dist/components/hooks/useActivityForm/types.d.ts +4 -5
- package/dist/components/hooks/useActivityForm/types.d.ts.map +1 -1
- package/dist/components/hooks/useActivityForm/useActivityForm.d.ts.map +1 -1
- package/dist/components/hooks/useActivityTable/types.d.ts +5 -4
- package/dist/components/hooks/useActivityTable/types.d.ts.map +1 -1
- package/dist/components/hooks/useActivityTable/useActivityTable.d.ts.map +1 -1
- package/dist/components/hooks/useBDOForm/createItemProxy.d.ts +2 -3
- package/dist/components/hooks/useBDOForm/createItemProxy.d.ts.map +1 -1
- package/dist/components/hooks/useBDOTable/types.d.ts +20 -12
- package/dist/components/hooks/useBDOTable/types.d.ts.map +1 -1
- package/dist/components/hooks/useBDOTable/useBDOTable.d.ts +2 -2
- package/dist/components/hooks/useBDOTable/useBDOTable.d.ts.map +1 -1
- package/dist/{constants-ConHc1oS.js → constants-Cyi942Yr.js} +5 -5
- package/dist/constants-DEmYwKfC.cjs +1 -0
- package/dist/filter.cjs +1 -1
- package/dist/filter.mjs +1 -1
- package/dist/form.cjs +1 -1
- package/dist/form.mjs +226 -243
- package/dist/table.cjs +1 -1
- package/dist/table.mjs +15 -16
- package/dist/table.types.d.ts +1 -1
- package/dist/table.types.d.ts.map +1 -1
- package/dist/types/constants.d.ts +1 -1
- package/dist/workflow/Activity.d.ts +8 -5
- package/dist/workflow/Activity.d.ts.map +1 -1
- package/dist/workflow.cjs +1 -1
- package/dist/workflow.mjs +461 -476
- package/docs/README.md +57 -0
- package/docs/bdo/README.md +161 -0
- package/docs/bdo/api_reference.md +281 -0
- package/docs/examples/bdo/create-product.md +69 -0
- package/docs/examples/bdo/edit-product-dialog.md +95 -0
- package/docs/examples/bdo/filtered-product-table.md +100 -0
- package/docs/examples/bdo/product-listing.md +73 -0
- package/docs/examples/bdo/supplier-dropdown.md +60 -0
- package/docs/examples/fields/complex-fields.md +248 -0
- package/docs/examples/fields/primitive-fields.md +217 -0
- package/docs/examples/workflow/approve-leave-request.md +76 -0
- package/docs/examples/workflow/filtered-activity-table.md +101 -0
- package/docs/examples/workflow/my-pending-requests.md +90 -0
- package/docs/examples/workflow/start-new-workflow.md +47 -0
- package/docs/examples/workflow/submit-leave-request.md +72 -0
- package/docs/examples/workflow/workflow-progress.md +49 -0
- package/docs/fields/README.md +141 -0
- package/docs/fields/api_reference.md +134 -0
- package/docs/useActivityForm/README.md +244 -0
- package/docs/useActivityForm/api_reference.md +279 -0
- package/docs/useActivityTable/README.md +263 -0
- package/docs/useActivityTable/api_reference.md +294 -0
- package/docs/useBDOForm/README.md +175 -0
- package/docs/useBDOForm/api_reference.md +244 -0
- package/docs/useBDOTable/README.md +242 -0
- package/docs/useBDOTable/api_reference.md +253 -0
- package/docs/useFilter/README.md +323 -0
- package/docs/useFilter/api_reference.md +228 -0
- package/docs/workflow/README.md +158 -0
- package/docs/workflow/api_reference.md +161 -0
- package/package.json +1 -1
- package/sdk/auth/authConfig.ts +1 -1
- package/sdk/auth/types.ts +1 -1
- package/sdk/bdo/core/Item.ts +1 -10
- package/sdk/bdo/fields/ReferenceField.ts +1 -1
- package/sdk/bdo/fields/SelectField.ts +1 -1
- package/sdk/bdo/fields/UserField.ts +1 -1
- package/sdk/components/hooks/useActivityForm/types.ts +4 -6
- package/sdk/components/hooks/useActivityForm/useActivityForm.ts +10 -73
- package/sdk/components/hooks/useActivityTable/types.ts +4 -5
- package/sdk/components/hooks/useActivityTable/useActivityTable.ts +10 -8
- package/sdk/components/hooks/useBDOForm/createItemProxy.ts +17 -58
- package/sdk/components/hooks/useBDOTable/types.ts +20 -10
- package/sdk/components/hooks/useBDOTable/useBDOTable.ts +12 -8
- package/sdk/table.types.ts +2 -0
- package/sdk/types/constants.ts +1 -1
- package/sdk/workflow/Activity.ts +39 -7
- package/dist/constants-QX2RX-wu.cjs +0 -1
- package/docs/api.md +0 -95
- package/docs/bdo.md +0 -224
- package/docs/gaps.md +0 -360
- package/docs/useActivityForm.md +0 -393
- package/docs/useActivityTable.md +0 -418
- package/docs/useBDOForm.md +0 -376
- package/docs/useBDOTable.md +0 -284
- package/docs/useFilter.md +0 -188
- package/docs/workflow.md +0 -560
- /package/docs/{useAuth.md → useAuth/README.md} +0 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# useBDOTable API Reference
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
import { useBDOTable } from '@ram_28/kf-ai-sdk/table';
|
|
5
|
+
import type {
|
|
6
|
+
UseBDOTableOptionsType,
|
|
7
|
+
UseBDOTableReturnType,
|
|
8
|
+
PaginationStateType,
|
|
9
|
+
} from '@ram_28/kf-ai-sdk/table/types';
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Signature
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
const table: UseBDOTableReturnType<ProductBdo> = useBDOTable(options: UseBDOTableOptionsType<ProductBdo>);
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Options
|
|
19
|
+
|
|
20
|
+
`UseBDOTableOptionsType<B>`
|
|
21
|
+
|
|
22
|
+
- `bdo: B`
|
|
23
|
+
- **Required**
|
|
24
|
+
- BDO instance with `list()` and `count()` methods. Must be memoized with `useMemo()`.
|
|
25
|
+
- `initialState`
|
|
26
|
+
- **Optional**
|
|
27
|
+
- Object with:
|
|
28
|
+
- `sort?: SortType` — Initial sort configuration. Format: `[{ "fieldName": "ASC" }]`
|
|
29
|
+
- `pagination?: PaginationStateType` — Initial page and page size. Defaults to `{ pageNo: 1, pageSize: 10 }`.
|
|
30
|
+
- `filter?: UseFilterOptionsType<T>` — Initial filter conditions and operator.
|
|
31
|
+
- `onError?: (error: Error) => void`
|
|
32
|
+
- **Optional**
|
|
33
|
+
- Called when a list or count fetch fails.
|
|
34
|
+
- `onSuccess?: (data: T[]) => void`
|
|
35
|
+
- **Optional**
|
|
36
|
+
- Called with the current page rows after a successful list fetch.
|
|
37
|
+
|
|
38
|
+
## Return Value
|
|
39
|
+
|
|
40
|
+
`UseBDOTableReturnType<B>`
|
|
41
|
+
|
|
42
|
+
Alias for `UseTableReturnType<BDORowType<B>>`. `BDORowType<B>` resolves to `ItemType<TEditable, TReadonly>` — inferred from the BDO's `list()` return type. Rows are proxy objects with `.get()` accessors, not plain objects.
|
|
43
|
+
|
|
44
|
+
### Data
|
|
45
|
+
|
|
46
|
+
- `rows: ItemType<TEditable, TReadonly>[]`
|
|
47
|
+
- Array of `ItemType` instances for the current page. Access field values via `.get()` (e.g. `row.Title.get()`), not direct property access. Empty array during initial loading.
|
|
48
|
+
- `totalItems: number`
|
|
49
|
+
- Total number of records matching the current filters and search. `0` while count is loading.
|
|
50
|
+
|
|
51
|
+
### Loading & Error
|
|
52
|
+
|
|
53
|
+
- `isLoading: boolean`
|
|
54
|
+
- `true` during the initial load of both list and count queries.
|
|
55
|
+
- `isFetching: boolean`
|
|
56
|
+
- `true` during any fetch including background refetches for list or count.
|
|
57
|
+
- `error: Error | null`
|
|
58
|
+
- Most recent fetch error from either list or count query. `null` when no error.
|
|
59
|
+
|
|
60
|
+
### Search
|
|
61
|
+
|
|
62
|
+
- `search.query: string`
|
|
63
|
+
- Current search input value (updated immediately on `set()`).
|
|
64
|
+
- `search.field: keyof T | null`
|
|
65
|
+
- The field currently being searched. `null` when no search is active.
|
|
66
|
+
- `search.set(field: keyof T, query: string): void`
|
|
67
|
+
- Set the search field and query. The input value updates immediately; the API query is debounced by 300 ms. Pagination resets to page 1 after the debounce. Query is capped at 255 characters.
|
|
68
|
+
- `search.clear(): void`
|
|
69
|
+
- Clear the search field and query. Pagination resets to page 1.
|
|
70
|
+
|
|
71
|
+
### Sort
|
|
72
|
+
|
|
73
|
+
- `sort.field: keyof T | null`
|
|
74
|
+
- Currently sorted field. `null` when no sort is active.
|
|
75
|
+
- `sort.direction: "ASC" | "DESC" | null`
|
|
76
|
+
- Current sort direction. `null` when no sort is active.
|
|
77
|
+
- `sort.toggle(field: keyof T): void`
|
|
78
|
+
- Cycle the sort for a field: ASC -> DESC -> cleared. If a different field was sorted, starts at ASC.
|
|
79
|
+
- `sort.clear(): void`
|
|
80
|
+
- Remove all sorting.
|
|
81
|
+
- `sort.set(field: keyof T | null, direction: "ASC" | "DESC" | null): void`
|
|
82
|
+
- Explicitly set the sort field and direction.
|
|
83
|
+
|
|
84
|
+
### Filter
|
|
85
|
+
|
|
86
|
+
- `filter: UseFilterReturnType<T>`
|
|
87
|
+
- Full filter state and operations. See [useFilter reference](../useFilter/api_reference.md) for the complete API.
|
|
88
|
+
|
|
89
|
+
### Pagination
|
|
90
|
+
|
|
91
|
+
- `pagination.pageNo: number`
|
|
92
|
+
- Current page number (1-indexed).
|
|
93
|
+
- `pagination.pageSize: number`
|
|
94
|
+
- Current number of items per page.
|
|
95
|
+
- `pagination.totalPages: number`
|
|
96
|
+
- Total number of pages based on `totalItems` and `pageSize`.
|
|
97
|
+
- `pagination.totalItems: number`
|
|
98
|
+
- Same as the top-level `totalItems`.
|
|
99
|
+
- `pagination.canGoNext: boolean`
|
|
100
|
+
- `true` when a next page exists.
|
|
101
|
+
- `pagination.canGoPrevious: boolean`
|
|
102
|
+
- `true` when a previous page exists (i.e. `pageNo > 1`).
|
|
103
|
+
- `pagination.goToNext(): void`
|
|
104
|
+
- Navigate to the next page. No-op if already on the last page.
|
|
105
|
+
- `pagination.goToPrevious(): void`
|
|
106
|
+
- Navigate to the previous page. No-op if already on page 1.
|
|
107
|
+
- `pagination.goToPage(page: number): void`
|
|
108
|
+
- Navigate to a specific page. Clamped to `[1, totalPages]`.
|
|
109
|
+
- `pagination.setPageSize(size: number): void`
|
|
110
|
+
- Change the page size. Resets to page 1.
|
|
111
|
+
|
|
112
|
+
### Operations
|
|
113
|
+
|
|
114
|
+
- `refetch(): Promise<ListResponseType<T>>`
|
|
115
|
+
- Manually refetch both the list and count queries. Returns the list response.
|
|
116
|
+
|
|
117
|
+
## Exported Constants
|
|
118
|
+
|
|
119
|
+
Available from `@ram_28/kf-ai-sdk/table`:
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
import { SortDirection, TableDefaults } from '@ram_28/kf-ai-sdk/table';
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**SortDirection**
|
|
126
|
+
|
|
127
|
+
| Key | Value |
|
|
128
|
+
|-----|-------|
|
|
129
|
+
| `ASC` | `"ASC"` |
|
|
130
|
+
| `DESC` | `"DESC"` |
|
|
131
|
+
|
|
132
|
+
**TableDefaults**
|
|
133
|
+
|
|
134
|
+
| Key | Value | Description |
|
|
135
|
+
|-----|-------|-------------|
|
|
136
|
+
| `SEARCH_DEBOUNCE_MS` | `300` | Debounce delay for search (ms) |
|
|
137
|
+
| `PAGE_SIZE` | `10` | Default items per page |
|
|
138
|
+
| `PAGE` | `1` | Default page number (1-indexed) |
|
|
139
|
+
| `SEARCH_MAX_LENGTH` | `255` | Maximum search query length |
|
|
140
|
+
|
|
141
|
+
For filter constants (`ConditionOperator`, `GroupOperator`, `RHSType`), see [useFilter reference](../useFilter/api_reference.md).
|
|
142
|
+
|
|
143
|
+
## Types
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import type { ItemType } from '@ram_28/kf-ai-sdk/bdo/types';
|
|
147
|
+
|
|
148
|
+
// ============================================================
|
|
149
|
+
// Options
|
|
150
|
+
// ============================================================
|
|
151
|
+
|
|
152
|
+
interface UseBDOTableOptionsType<B extends BDOTableSourceType> {
|
|
153
|
+
bdo: B;
|
|
154
|
+
initialState?: {
|
|
155
|
+
sort?: SortType;
|
|
156
|
+
pagination?: PaginationStateType;
|
|
157
|
+
filter?: UseFilterOptionsType<BDORowType<B>>;
|
|
158
|
+
};
|
|
159
|
+
onError?: (error: Error) => void;
|
|
160
|
+
onSuccess?: (data: BDORowType<B>[]) => void;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ============================================================
|
|
164
|
+
// Return type
|
|
165
|
+
// ============================================================
|
|
166
|
+
|
|
167
|
+
type UseBDOTableReturnType<B extends BDOTableSourceType> =
|
|
168
|
+
UseTableReturnType<BDORowType<B>>;
|
|
169
|
+
|
|
170
|
+
// When used via UseBDOTableReturnType<B>, T = ItemType<TEditable, TReadonly>
|
|
171
|
+
// ItemType<TEditable, TReadonly> provides:
|
|
172
|
+
// row._id: string (direct access, no .get())
|
|
173
|
+
// row.Field.get(): value (read field value)
|
|
174
|
+
// row.Field.set(value): void (editable fields only)
|
|
175
|
+
// row.Field.validate(): ValidationResultType (single field)
|
|
176
|
+
// row.Field.label / .required / .readOnly / .defaultValue / .meta
|
|
177
|
+
// row.toJSON(): plain object
|
|
178
|
+
// row.validate(): ValidationResultType (all fields)
|
|
179
|
+
interface UseTableReturnType<T> {
|
|
180
|
+
// Data
|
|
181
|
+
rows: T[];
|
|
182
|
+
totalItems: number;
|
|
183
|
+
|
|
184
|
+
// Loading States
|
|
185
|
+
isLoading: boolean;
|
|
186
|
+
isFetching: boolean;
|
|
187
|
+
|
|
188
|
+
// Error Handling
|
|
189
|
+
error: Error | null;
|
|
190
|
+
|
|
191
|
+
// Search
|
|
192
|
+
search: {
|
|
193
|
+
query: string;
|
|
194
|
+
field: keyof T | null;
|
|
195
|
+
set: (field: keyof T, query: string) => void;
|
|
196
|
+
clear: () => void;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// Sorting
|
|
200
|
+
sort: {
|
|
201
|
+
field: keyof T | null;
|
|
202
|
+
direction: 'ASC' | 'DESC' | null;
|
|
203
|
+
toggle: (field: keyof T) => void;
|
|
204
|
+
clear: () => void;
|
|
205
|
+
set: (field: keyof T | null, direction: 'ASC' | 'DESC' | null) => void;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// Filter
|
|
209
|
+
filter: UseFilterReturnType<T>;
|
|
210
|
+
|
|
211
|
+
// Pagination
|
|
212
|
+
pagination: {
|
|
213
|
+
pageNo: number;
|
|
214
|
+
pageSize: number;
|
|
215
|
+
totalPages: number;
|
|
216
|
+
totalItems: number;
|
|
217
|
+
canGoNext: boolean;
|
|
218
|
+
canGoPrevious: boolean;
|
|
219
|
+
goToNext: () => void;
|
|
220
|
+
goToPrevious: () => void;
|
|
221
|
+
goToPage: (page: number) => void;
|
|
222
|
+
setPageSize: (size: number) => void;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
// Operations
|
|
226
|
+
refetch: () => Promise<ListResponseType<T>>;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ============================================================
|
|
230
|
+
// State types
|
|
231
|
+
// ============================================================
|
|
232
|
+
|
|
233
|
+
interface PaginationStateType {
|
|
234
|
+
pageNo: number;
|
|
235
|
+
pageSize: number;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ============================================================
|
|
239
|
+
// Sort
|
|
240
|
+
// ============================================================
|
|
241
|
+
|
|
242
|
+
type SortOptionType = Record<string, 'ASC' | 'DESC'>;
|
|
243
|
+
type SortType = SortOptionType[];
|
|
244
|
+
|
|
245
|
+
// Filter types — see useFilter reference (../useFilter/api_reference.md)
|
|
246
|
+
|
|
247
|
+
// ============================================================
|
|
248
|
+
// Row type inference
|
|
249
|
+
// ============================================================
|
|
250
|
+
|
|
251
|
+
type BDORowType<B extends BDOTableSourceType> =
|
|
252
|
+
B extends { list(opts?: any): Promise<(infer R)[]> } ? R : never;
|
|
253
|
+
```
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
# useFilter
|
|
2
|
+
|
|
3
|
+
Standalone filter state manager for building composable filter conditions with nested groups.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
|
|
7
|
+
**Use `useFilter` when:**
|
|
8
|
+
|
|
9
|
+
- Building a standalone filter panel or sidebar outside of a table
|
|
10
|
+
- You need a composable condition tree with nested AND/OR/NOT groups
|
|
11
|
+
- Managing filter state that feeds into a custom data fetch
|
|
12
|
+
|
|
13
|
+
**Use something else when:**
|
|
14
|
+
|
|
15
|
+
- Filtering a BDO table — use [`useBDOTable`](../useBDOTable/README.md) (includes useFilter at `table.filter`)
|
|
16
|
+
- Filtering an activity table — use [`useActivityTable`](../useActivityTable/README.md) (includes useFilter at `table.filter`)
|
|
17
|
+
|
|
18
|
+
## Imports
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import { useFilter, ConditionOperator, GroupOperator, FilterValueSource } from "@ram_28/kf-ai-sdk/filter";
|
|
22
|
+
import { isCondition, isConditionGroup } from "@ram_28/kf-ai-sdk/filter";
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import { useMemo } from "react";
|
|
29
|
+
import { useFilter, ConditionOperator, FilterValueSource } from "@ram_28/kf-ai-sdk/filter";
|
|
30
|
+
import { AdminProduct } from "@/bdo/admin/Product";
|
|
31
|
+
import type { AdminProductFieldType } from "@/bdo/admin/Product";
|
|
32
|
+
|
|
33
|
+
function ProductFilter() {
|
|
34
|
+
const bdo = useMemo(() => new AdminProduct(), []);
|
|
35
|
+
const filter = useFilter<AdminProductFieldType>();
|
|
36
|
+
|
|
37
|
+
const applyStatusFilter = (status: string) => {
|
|
38
|
+
filter.clearAllConditions();
|
|
39
|
+
if (status !== "all") {
|
|
40
|
+
filter.addCondition({
|
|
41
|
+
Operator: ConditionOperator.EQ,
|
|
42
|
+
LHSField: bdo.status.id,
|
|
43
|
+
RHSValue: status,
|
|
44
|
+
RHSType: FilterValueSource.Constant,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div>
|
|
51
|
+
<button onClick={() => applyStatusFilter("Active")}>Active</button>
|
|
52
|
+
<button onClick={() => applyStatusFilter("all")}>All</button>
|
|
53
|
+
<p>{filter.hasConditions ? "Filtering active" : "No filters"}</p>
|
|
54
|
+
{filter.payload && <pre>{JSON.stringify(filter.payload, null, 2)}</pre>}
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Usage Guide
|
|
61
|
+
|
|
62
|
+
### Adding Conditions
|
|
63
|
+
|
|
64
|
+
Add leaf conditions with `addCondition()`. Each condition uses PascalCase properties: `Operator`, `LHSField`, `RHSValue`, and optionally `RHSType`. Always use constants instead of string literals.
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
import { ConditionOperator, FilterValueSource } from "@ram_28/kf-ai-sdk/filter";
|
|
68
|
+
|
|
69
|
+
// Exact match
|
|
70
|
+
filter.addCondition({
|
|
71
|
+
Operator: ConditionOperator.EQ,
|
|
72
|
+
LHSField: bdo.category.id,
|
|
73
|
+
RHSValue: "Electronics",
|
|
74
|
+
RHSType: FilterValueSource.Constant,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Range filter
|
|
78
|
+
filter.addCondition({
|
|
79
|
+
Operator: ConditionOperator.Between,
|
|
80
|
+
LHSField: bdo.unit_price.id,
|
|
81
|
+
RHSValue: [100, 500],
|
|
82
|
+
RHSType: FilterValueSource.Constant,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// String search
|
|
86
|
+
filter.addCondition({
|
|
87
|
+
Operator: ConditionOperator.Contains,
|
|
88
|
+
LHSField: bdo.description.id,
|
|
89
|
+
RHSValue: "wireless",
|
|
90
|
+
RHSType: FilterValueSource.Constant,
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Tracking and Removing by ID
|
|
95
|
+
|
|
96
|
+
`addCondition()` returns an auto-generated ID string. Store the ID to later remove or look up the condition. IDs are internal state-management artifacts -- they are stripped from `payload` automatically.
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
const [statusId, setStatusId] = useState<string | null>(null);
|
|
100
|
+
|
|
101
|
+
const applyStatusFilter = (status: string) => {
|
|
102
|
+
// Remove existing status condition if present
|
|
103
|
+
if (statusId) {
|
|
104
|
+
filter.removeCondition(statusId);
|
|
105
|
+
setStatusId(null);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Add new condition and store its ID
|
|
109
|
+
if (status !== "all") {
|
|
110
|
+
const id = filter.addCondition({
|
|
111
|
+
Operator: ConditionOperator.EQ,
|
|
112
|
+
LHSField: bdo.status.id,
|
|
113
|
+
RHSValue: status,
|
|
114
|
+
RHSType: FilterValueSource.Constant,
|
|
115
|
+
});
|
|
116
|
+
setStatusId(id);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Updating Conditions
|
|
122
|
+
|
|
123
|
+
Update a leaf condition's properties without removing and re-adding it. Use `updateCondition()` for leaf conditions and `updateGroupOperator()` for groups:
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
// Change the comparison value on an existing condition
|
|
127
|
+
filter.updateCondition(conditionId, {
|
|
128
|
+
RHSValue: "Books",
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Change a group's operator from And to Or
|
|
132
|
+
filter.updateGroupOperator(groupId, GroupOperator.Or);
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Nested Groups
|
|
136
|
+
|
|
137
|
+
Build arbitrarily deep condition trees using `addConditionGroup()`. Pass a `parentId` to nest groups inside other groups, and pass a `parentId` to `addCondition()` to add leaf conditions inside a group.
|
|
138
|
+
|
|
139
|
+
This example constructs: `Status = "Active" AND (Category = "Electronics" AND Price < 500) OR (Category = "Books" AND Price < 50)`:
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
import { useFilter, ConditionOperator, GroupOperator, FilterValueSource } from "@ram_28/kf-ai-sdk/filter";
|
|
143
|
+
|
|
144
|
+
const filter = useFilter<AdminProductFieldType>();
|
|
145
|
+
|
|
146
|
+
// Root-level condition
|
|
147
|
+
filter.addCondition({
|
|
148
|
+
Operator: ConditionOperator.EQ,
|
|
149
|
+
LHSField: bdo.status.id,
|
|
150
|
+
RHSValue: "Active",
|
|
151
|
+
RHSType: FilterValueSource.Constant,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Create an OR group at root level -- capture its ID
|
|
155
|
+
const orGroupId = filter.addConditionGroup(GroupOperator.Or);
|
|
156
|
+
|
|
157
|
+
// Nest an AND group inside the OR group for Electronics
|
|
158
|
+
const electronicsGroupId = filter.addConditionGroup(GroupOperator.And, orGroupId);
|
|
159
|
+
filter.addCondition(
|
|
160
|
+
{
|
|
161
|
+
Operator: ConditionOperator.EQ,
|
|
162
|
+
LHSField: bdo.category.id,
|
|
163
|
+
RHSValue: "Electronics",
|
|
164
|
+
RHSType: FilterValueSource.Constant,
|
|
165
|
+
},
|
|
166
|
+
electronicsGroupId
|
|
167
|
+
);
|
|
168
|
+
filter.addCondition(
|
|
169
|
+
{
|
|
170
|
+
Operator: ConditionOperator.LT,
|
|
171
|
+
LHSField: bdo.unit_price.id,
|
|
172
|
+
RHSValue: 500,
|
|
173
|
+
RHSType: FilterValueSource.Constant,
|
|
174
|
+
},
|
|
175
|
+
electronicsGroupId
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
// Nest another AND group inside the OR group for Books
|
|
179
|
+
const booksGroupId = filter.addConditionGroup(GroupOperator.And, orGroupId);
|
|
180
|
+
filter.addCondition(
|
|
181
|
+
{
|
|
182
|
+
Operator: ConditionOperator.EQ,
|
|
183
|
+
LHSField: bdo.category.id,
|
|
184
|
+
RHSValue: "Books",
|
|
185
|
+
RHSType: FilterValueSource.Constant,
|
|
186
|
+
},
|
|
187
|
+
booksGroupId
|
|
188
|
+
);
|
|
189
|
+
filter.addCondition(
|
|
190
|
+
{
|
|
191
|
+
Operator: ConditionOperator.LT,
|
|
192
|
+
LHSField: bdo.unit_price.id,
|
|
193
|
+
RHSValue: 50,
|
|
194
|
+
RHSType: FilterValueSource.Constant,
|
|
195
|
+
},
|
|
196
|
+
booksGroupId
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
// Resulting payload:
|
|
200
|
+
// {
|
|
201
|
+
// Operator: "And",
|
|
202
|
+
// Condition: [
|
|
203
|
+
// { Operator: "EQ", LHSField: "status", RHSValue: "Active", RHSType: "Constant" },
|
|
204
|
+
// {
|
|
205
|
+
// Operator: "Or",
|
|
206
|
+
// Condition: [
|
|
207
|
+
// {
|
|
208
|
+
// Operator: "And",
|
|
209
|
+
// Condition: [
|
|
210
|
+
// { Operator: "EQ", LHSField: "category", RHSValue: "Electronics", RHSType: "Constant" },
|
|
211
|
+
// { Operator: "LT", LHSField: "unit_price", RHSValue: 500, RHSType: "Constant" },
|
|
212
|
+
// ],
|
|
213
|
+
// },
|
|
214
|
+
// {
|
|
215
|
+
// Operator: "And",
|
|
216
|
+
// Condition: [
|
|
217
|
+
// { Operator: "EQ", LHSField: "category", RHSValue: "Books", RHSType: "Constant" },
|
|
218
|
+
// { Operator: "LT", LHSField: "unit_price", RHSValue: 50, RHSType: "Constant" },
|
|
219
|
+
// ],
|
|
220
|
+
// },
|
|
221
|
+
// ],
|
|
222
|
+
// },
|
|
223
|
+
// ],
|
|
224
|
+
// }
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Root Operator
|
|
228
|
+
|
|
229
|
+
The root operator combines all top-level conditions. It defaults to `"And"` and can be set via the `operator` option or changed later with `setRootOperator()`:
|
|
230
|
+
|
|
231
|
+
```tsx
|
|
232
|
+
// Set at initialization
|
|
233
|
+
const filter = useFilter<AdminProductFieldType>({
|
|
234
|
+
operator: "Or",
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// Toggle between And and Or
|
|
238
|
+
const toggleOperator = () => {
|
|
239
|
+
const next = filter.operator === GroupOperator.And ? GroupOperator.Or : GroupOperator.And;
|
|
240
|
+
filter.setRootOperator(next);
|
|
241
|
+
};
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Reading State
|
|
245
|
+
|
|
246
|
+
- **`items`** -- current conditions with IDs populated (use for UI rendering)
|
|
247
|
+
- **`payload`** -- API-ready `FilterType` with IDs stripped. `undefined` when no conditions exist.
|
|
248
|
+
- **`hasConditions`** -- convenience boolean (`items.length > 0`)
|
|
249
|
+
- **`operator`** -- current root operator (`"And"`, `"Or"`, or `"Not"`)
|
|
250
|
+
|
|
251
|
+
Guard with a falsy check before sending the payload:
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
if (filter.payload) {
|
|
255
|
+
const results = await bdo.list({ Filter: filter.payload });
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Integration with Table Hooks
|
|
260
|
+
|
|
261
|
+
`useBDOTable` and `useActivityTable` embed a `useFilter` instance at `table.filter`. The API is identical -- `table.filter.addCondition()`, `table.filter.clearAllConditions()`, etc.
|
|
262
|
+
|
|
263
|
+
```tsx
|
|
264
|
+
const table = useBDOTable({
|
|
265
|
+
bdo: product,
|
|
266
|
+
initialState: {
|
|
267
|
+
filter: {
|
|
268
|
+
conditions: [
|
|
269
|
+
{ Operator: ConditionOperator.EQ, LHSField: "status", RHSValue: "Active" },
|
|
270
|
+
],
|
|
271
|
+
operator: "And",
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// Same API as standalone useFilter
|
|
277
|
+
table.filter.addCondition({ ... });
|
|
278
|
+
table.filter.clearAllConditions();
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
- Filter changes automatically reset table pagination to page 1.
|
|
282
|
+
- See [`useBDOTable`](../useBDOTable/README.md) and [`useActivityTable`](../useActivityTable/README.md) for full table documentation.
|
|
283
|
+
|
|
284
|
+
### Iterating with Type Guards
|
|
285
|
+
|
|
286
|
+
Use `isCondition()` and `isConditionGroup()` to distinguish between leaf conditions and groups when rendering the filter tree:
|
|
287
|
+
|
|
288
|
+
```tsx
|
|
289
|
+
import { isCondition, isConditionGroup } from "@ram_28/kf-ai-sdk/filter";
|
|
290
|
+
|
|
291
|
+
filter.items.map((item) => {
|
|
292
|
+
if (isCondition(item)) {
|
|
293
|
+
return (
|
|
294
|
+
<li key={item.id}>
|
|
295
|
+
{String(item.LHSField)} {item.Operator} {String(item.RHSValue)}
|
|
296
|
+
<button onClick={() => filter.removeCondition(item.id!)}>Remove</button>
|
|
297
|
+
</li>
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
if (isConditionGroup(item)) {
|
|
301
|
+
return (
|
|
302
|
+
<li key={item.id}>
|
|
303
|
+
{item.Operator} group ({item.Condition.length} children)
|
|
304
|
+
</li>
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
return null;
|
|
308
|
+
});
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Further Reading
|
|
312
|
+
|
|
313
|
+
- [API Reference](./api_reference.md) -- All options, return values, constants, and type definitions
|
|
314
|
+
- [Filtered Product Table](../examples/bdo/filtered-product-table.md) -- BDO table with category + price range filters
|
|
315
|
+
- [Filtered Activity Table](../examples/workflow/filtered-activity-table.md) -- Activity table with date range filter
|
|
316
|
+
|
|
317
|
+
## Common Mistakes
|
|
318
|
+
|
|
319
|
+
- **Don't read `filter.conditions`** -- the property is `filter.items`.
|
|
320
|
+
- **Don't expect `payload` to be an empty object when empty** -- it's `undefined`. Use a simple falsy check: `if (filter.payload) { ... }`.
|
|
321
|
+
- **Don't use string literals** -- use `ConditionOperator.EQ`, `GroupOperator.And`, `FilterValueSource.Constant` instead.
|
|
322
|
+
- **Don't expect IDs in `payload`** -- they're stripped automatically before the payload is built.
|
|
323
|
+
- **Don't mix PascalCase and camelCase** -- condition properties are PascalCase (`Operator`, `LHSField`, `RHSValue`, `RHSType`), while hook options are camelCase (`conditions`, `operator`).
|