@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,244 @@
|
|
|
1
|
+
# useBDOForm API Reference
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
import { useBDOForm } from "@ram_28/kf-ai-sdk/form";
|
|
5
|
+
import type {
|
|
6
|
+
UseBDOFormOptionsType,
|
|
7
|
+
UseBDOFormReturnType,
|
|
8
|
+
} from "@ram_28/kf-ai-sdk/form/types";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Signature
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
const form: UseBDOFormReturnType<SellerProduct> = useBDOForm(options: UseBDOFormOptionsType<SellerProduct>);
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Options
|
|
18
|
+
|
|
19
|
+
`UseBDOFormOptionsType<B>`
|
|
20
|
+
|
|
21
|
+
- `bdo: B extends BaseBdo<any, any, any>`
|
|
22
|
+
- **Required**
|
|
23
|
+
- BDO instance. Must be memoized with `useMemo()`.
|
|
24
|
+
- `recordId: string | undefined`
|
|
25
|
+
- **Optional** · Defaults to `undefined`
|
|
26
|
+
- Record ID to edit. When present, the hook fetches the record and enters update mode. When absent, enters create mode.
|
|
27
|
+
|
|
28
|
+
## Return Value
|
|
29
|
+
|
|
30
|
+
`UseBDOFormReturnType<B>`
|
|
31
|
+
|
|
32
|
+
### Instance
|
|
33
|
+
|
|
34
|
+
- `item: FormItemType<TEditable, TReadonly>`
|
|
35
|
+
- The current record instance. Each field is an accessor with methods to read, write, and validate. See [Instance and Field Accessors](#instance-and-field-accessors).
|
|
36
|
+
- `bdo: B`
|
|
37
|
+
- The BDO instance passed in.
|
|
38
|
+
|
|
39
|
+
### Form Methods
|
|
40
|
+
|
|
41
|
+
- `register: FormRegisterType<TEditable, TReadonly>`
|
|
42
|
+
- Registers an input field. Auto-disables readonly fields (`{ disabled: true }`).
|
|
43
|
+
- `handleSubmit: HandleSubmitType<CreateUpdateResponseType>`
|
|
44
|
+
- Validates, calls API, invokes callbacks. See [handleSubmit](#handlesubmit).
|
|
45
|
+
- `watch: UseFormWatch<AllFieldsType<B>>`
|
|
46
|
+
- Standard RHF `watch`. Typed to all fields (editable + readonly + system).
|
|
47
|
+
- `setValue: UseFormSetValue<ExtractEditableType<B>>`
|
|
48
|
+
- Standard RHF `setValue`. Typed to editable fields only.
|
|
49
|
+
- `getValues: UseFormGetValues<AllFieldsType<B>>`
|
|
50
|
+
- Returns all field values.
|
|
51
|
+
- `reset: UseFormReset<AllFieldsType<B>>`
|
|
52
|
+
- Resets form to given values or defaults.
|
|
53
|
+
- `trigger: UseFormTrigger<AllFieldsType<B>>`
|
|
54
|
+
- Manually triggers validation for specific or all fields.
|
|
55
|
+
- `control: Control<AllFieldsType<B>>`
|
|
56
|
+
- RHF control for `Controller` components.
|
|
57
|
+
|
|
58
|
+
### Form State
|
|
59
|
+
|
|
60
|
+
- `formState: FormState<AllFieldsType<B>>`
|
|
61
|
+
- Full RHF form state object.
|
|
62
|
+
- `errors: FieldErrors<AllFieldsType<B>>`
|
|
63
|
+
- Validation errors by field name. Each entry has `type` and `message`.
|
|
64
|
+
- `isDirty: boolean`
|
|
65
|
+
- `true` if any field has been modified.
|
|
66
|
+
- `isValid: boolean`
|
|
67
|
+
- `true` if all fields pass validation.
|
|
68
|
+
- `isSubmitting: boolean`
|
|
69
|
+
- `true` during submission (from button click to API response).
|
|
70
|
+
- `isSubmitSuccessful: boolean`
|
|
71
|
+
- `true` after a successful submission.
|
|
72
|
+
- `dirtyFields: Partial<Record<keyof AllFieldsType<B>, boolean>>`
|
|
73
|
+
- Which fields have been modified.
|
|
74
|
+
|
|
75
|
+
### Loading & Error
|
|
76
|
+
|
|
77
|
+
- `isLoading: boolean`
|
|
78
|
+
- `true` while fetching the record (update mode) or creating the draft (create mode). Guard form render on this.
|
|
79
|
+
- `isFetching: boolean`
|
|
80
|
+
- `true` during refetches in update mode (including background refetches).
|
|
81
|
+
- `loadError: Error | null`
|
|
82
|
+
- Error from record fetch or draft creation.
|
|
83
|
+
- `draftId: string | undefined`
|
|
84
|
+
- Draft record ID in create mode.
|
|
85
|
+
- `isCreatingDraft: boolean | undefined`
|
|
86
|
+
- `true` while the initial draft is being created.
|
|
87
|
+
|
|
88
|
+
## handleSubmit
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
type HandleSubmitType<TRead> = (
|
|
92
|
+
onSuccess?: (data: TRead, e?: React.BaseSyntheticEvent) => void | Promise<void>,
|
|
93
|
+
onError?: (error: FieldErrors | Error, e?: React.BaseSyntheticEvent) => void | Promise<void>,
|
|
94
|
+
) => (e?: React.BaseSyntheticEvent) => Promise<void>;
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Curried function. Pass to `<form onSubmit={handleSubmit(onSuccess, onError)}>`.
|
|
98
|
+
|
|
99
|
+
- `onSuccess(data, e?)`
|
|
100
|
+
- Called with `CreateUpdateResponseType` (`{ _id: string }`) after a successful API call.
|
|
101
|
+
- `onError(error, e?)`
|
|
102
|
+
- Called with `FieldErrors` if validation fails, or `Error` if the API call fails.
|
|
103
|
+
|
|
104
|
+
## Instance and Field Accessors
|
|
105
|
+
|
|
106
|
+
`item` represents the current record. Each field on `item` is an accessor object.
|
|
107
|
+
|
|
108
|
+
### Instance Members (`item`)
|
|
109
|
+
|
|
110
|
+
- `_id: string | undefined`
|
|
111
|
+
- Current record ID.
|
|
112
|
+
- `toJSON(): Partial<TEditable & TReadonly>`
|
|
113
|
+
- All form values as a plain object.
|
|
114
|
+
- `validate(): Promise<boolean>`
|
|
115
|
+
- Triggers validation for all fields. Returns `true` if valid.
|
|
116
|
+
|
|
117
|
+
### Editable Field (`item.FieldName`)
|
|
118
|
+
|
|
119
|
+
- `get(): T | undefined`
|
|
120
|
+
- Current value from form state.
|
|
121
|
+
- `getOrDefault(fallback: T): T`
|
|
122
|
+
- Value or fallback if null/undefined.
|
|
123
|
+
- `set(value: T): void`
|
|
124
|
+
- Sets the field value.
|
|
125
|
+
- `validate(): ValidationResultType`
|
|
126
|
+
- Validates the current value. Returns `{ valid: boolean, errors: string[] }`.
|
|
127
|
+
- `label: string`
|
|
128
|
+
- Display label from field metadata.
|
|
129
|
+
- `required: boolean`
|
|
130
|
+
- Whether the field is required.
|
|
131
|
+
- `readOnly: boolean`
|
|
132
|
+
- Always `false` for editable fields.
|
|
133
|
+
- `defaultValue: unknown`
|
|
134
|
+
- Default value from metadata.
|
|
135
|
+
- `meta: BaseFieldMetaType`
|
|
136
|
+
- Raw field metadata.
|
|
137
|
+
|
|
138
|
+
### Readonly Field (`item.FieldName`)
|
|
139
|
+
|
|
140
|
+
Same as editable except: no `set()` method, `readOnly` is always `true`.
|
|
141
|
+
|
|
142
|
+
## Types
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
// Options
|
|
146
|
+
interface UseBDOFormOptionsType<B extends BaseBdo<any, any, any>> {
|
|
147
|
+
bdo: B;
|
|
148
|
+
recordId?: string;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Return type
|
|
152
|
+
interface UseBDOFormReturnType<B extends BaseBdo<any, any, any>> {
|
|
153
|
+
item: FormItemType<ExtractEditableType<B>, ExtractReadonlyType<B>>;
|
|
154
|
+
bdo: B;
|
|
155
|
+
|
|
156
|
+
register: FormRegisterType<ExtractEditableType<B>, ExtractReadonlyType<B>>;
|
|
157
|
+
handleSubmit: HandleSubmitType<CreateUpdateResponseType>;
|
|
158
|
+
watch: UseFormWatch<AllFieldsType<B>>;
|
|
159
|
+
setValue: UseFormSetValue<ExtractEditableType<B>>;
|
|
160
|
+
getValues: UseFormGetValues<AllFieldsType<B>>;
|
|
161
|
+
reset: UseFormReset<AllFieldsType<B>>;
|
|
162
|
+
trigger: UseFormTrigger<AllFieldsType<B>>;
|
|
163
|
+
control: Control<AllFieldsType<B>>;
|
|
164
|
+
|
|
165
|
+
formState: FormState<AllFieldsType<B>>;
|
|
166
|
+
errors: FieldErrors<AllFieldsType<B>>;
|
|
167
|
+
isDirty: boolean;
|
|
168
|
+
isValid: boolean;
|
|
169
|
+
isSubmitting: boolean;
|
|
170
|
+
isSubmitSuccessful: boolean;
|
|
171
|
+
dirtyFields: Partial<Record<keyof AllFieldsType<B>, boolean>>;
|
|
172
|
+
|
|
173
|
+
isLoading: boolean;
|
|
174
|
+
isFetching: boolean;
|
|
175
|
+
loadError: Error | null;
|
|
176
|
+
|
|
177
|
+
draftId?: string;
|
|
178
|
+
isCreatingDraft?: boolean;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// handleSubmit signature
|
|
182
|
+
type HandleSubmitType<TRead = unknown> = (
|
|
183
|
+
onSuccess?: (data: TRead, e?: React.BaseSyntheticEvent) => void | Promise<void>,
|
|
184
|
+
onError?: (error: FieldErrors | Error, e?: React.BaseSyntheticEvent) => void | Promise<void>,
|
|
185
|
+
) => (e?: React.BaseSyntheticEvent) => Promise<void>;
|
|
186
|
+
|
|
187
|
+
// Instance type
|
|
188
|
+
type FormItemType<TEditable, TReadonly> = {
|
|
189
|
+
[K in keyof TEditable]: EditableFormFieldAccessorType<TEditable[K]>;
|
|
190
|
+
} & {
|
|
191
|
+
[K in keyof TReadonly]: ReadonlyFormFieldAccessorType<TReadonly[K]>;
|
|
192
|
+
} & {
|
|
193
|
+
readonly _id: string | undefined;
|
|
194
|
+
toJSON(): Partial<TEditable & TReadonly>;
|
|
195
|
+
validate(): Promise<boolean>;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
interface EditableFormFieldAccessorType<T> {
|
|
199
|
+
readonly label: string;
|
|
200
|
+
readonly required: boolean;
|
|
201
|
+
readonly readOnly: boolean;
|
|
202
|
+
readonly defaultValue: unknown;
|
|
203
|
+
readonly meta: BaseFieldMetaType;
|
|
204
|
+
get(): T | undefined;
|
|
205
|
+
getOrDefault(fallback: T): T;
|
|
206
|
+
set(value: T): void;
|
|
207
|
+
validate(): ValidationResultType;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
interface ReadonlyFormFieldAccessorType<T> {
|
|
211
|
+
readonly label: string;
|
|
212
|
+
readonly required: boolean;
|
|
213
|
+
readonly readOnly: boolean;
|
|
214
|
+
readonly defaultValue: unknown;
|
|
215
|
+
readonly meta: BaseFieldMetaType;
|
|
216
|
+
get(): T | undefined;
|
|
217
|
+
getOrDefault(fallback: T): T;
|
|
218
|
+
validate(): ValidationResultType;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Smart register
|
|
222
|
+
type FormRegisterType<TEditable, TReadonly> = <
|
|
223
|
+
K extends keyof TEditable | keyof TReadonly | string
|
|
224
|
+
>(
|
|
225
|
+
name: K & string,
|
|
226
|
+
options?: RegisterOptions,
|
|
227
|
+
) => K extends keyof TReadonly
|
|
228
|
+
? UseFormRegisterReturn & { disabled: true }
|
|
229
|
+
: UseFormRegisterReturn;
|
|
230
|
+
|
|
231
|
+
// Helpers
|
|
232
|
+
type ExtractEditableType<B> = B extends BaseBdo<any, infer E, any> ? E : never;
|
|
233
|
+
type ExtractReadonlyType<B> = B extends BaseBdo<any, any, infer R> ? R : never;
|
|
234
|
+
type AllFieldsType<B> = ExtractEditableType<B> & ExtractReadonlyType<B> & SystemFieldsType;
|
|
235
|
+
|
|
236
|
+
interface ValidationResultType {
|
|
237
|
+
valid: boolean;
|
|
238
|
+
errors: string[];
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
interface CreateUpdateResponseType {
|
|
242
|
+
_id: string;
|
|
243
|
+
}
|
|
244
|
+
```
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# useBDOTable
|
|
2
|
+
|
|
3
|
+
BDO-integrated table hook with data fetching, search, sorting, filtering, and pagination.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
|
|
7
|
+
**Use `useBDOTable` when:**
|
|
8
|
+
|
|
9
|
+
- Displaying a list of BDO records in a table
|
|
10
|
+
- You need search, sort, filter, and/or pagination
|
|
11
|
+
|
|
12
|
+
**Use something else when:**
|
|
13
|
+
|
|
14
|
+
- Displaying workflow/activity records — use [`useActivityTable`](../useActivityTable/README.md) instead
|
|
15
|
+
- Building a form — use [`useBDOForm`](../useBDOForm/README.md) instead
|
|
16
|
+
|
|
17
|
+
## Imports
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { useBDOTable } from "@ram_28/kf-ai-sdk/table";
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import { useMemo } from "react";
|
|
27
|
+
import { useBDOTable } from "@ram_28/kf-ai-sdk/table";
|
|
28
|
+
import { BuyerProduct } from "@/bdo/buyer/Product";
|
|
29
|
+
|
|
30
|
+
function ProductsPage() {
|
|
31
|
+
const product = useMemo(() => new BuyerProduct(), []);
|
|
32
|
+
|
|
33
|
+
const table = useBDOTable({
|
|
34
|
+
bdo: product,
|
|
35
|
+
initialState: {
|
|
36
|
+
sort: [{ Title: "ASC" }],
|
|
37
|
+
pagination: { pageNo: 1, pageSize: 10 },
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (table.isLoading) return <p>Loading...</p>;
|
|
42
|
+
if (table.error) return <p>Error: {table.error.message}</p>;
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<div>
|
|
46
|
+
<ul>
|
|
47
|
+
{table.rows.map((row) => (
|
|
48
|
+
<li key={row._id}>
|
|
49
|
+
{row.Title.get()} - ${row.Price.get()}
|
|
50
|
+
</li>
|
|
51
|
+
))}
|
|
52
|
+
</ul>
|
|
53
|
+
|
|
54
|
+
<button
|
|
55
|
+
onClick={table.pagination.goToPrevious}
|
|
56
|
+
disabled={!table.pagination.canGoPrevious}
|
|
57
|
+
>
|
|
58
|
+
Previous
|
|
59
|
+
</button>
|
|
60
|
+
<span>
|
|
61
|
+
Page {table.pagination.pageNo} of {table.pagination.totalPages}
|
|
62
|
+
</span>
|
|
63
|
+
<button
|
|
64
|
+
onClick={table.pagination.goToNext}
|
|
65
|
+
disabled={!table.pagination.canGoNext}
|
|
66
|
+
>
|
|
67
|
+
Next
|
|
68
|
+
</button>
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Usage Guide
|
|
75
|
+
|
|
76
|
+
### Accessing Row Data
|
|
77
|
+
|
|
78
|
+
Rows are `ItemType` instances, not plain objects. Access field values through the `.get()` method on each field accessor:
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
table.rows.map((row) => (
|
|
82
|
+
<tr key={row._id}>
|
|
83
|
+
<td>{row.Title.get()}</td>
|
|
84
|
+
<td>{row.Category.get()}</td>
|
|
85
|
+
<td>${row.Price.get()}</td>
|
|
86
|
+
</tr>
|
|
87
|
+
));
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
- `row._id` is a direct string (no `.get()` needed)
|
|
91
|
+
- `row.Title.get()` returns the field's current value
|
|
92
|
+
- `row.toJSON()` converts the entire row to a plain object
|
|
93
|
+
- Editable rows also have `.set()`, but this is typically not used in a table context
|
|
94
|
+
|
|
95
|
+
### Search
|
|
96
|
+
|
|
97
|
+
Search filters results by matching a query string against a specific field. The input value updates immediately for a responsive UI, while the API call is debounced.
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
<input
|
|
101
|
+
type="text"
|
|
102
|
+
placeholder={`Search by ${product.Title.label}...`}
|
|
103
|
+
value={table.search.query}
|
|
104
|
+
onChange={(e) => table.search.set(product.Title.id, e.target.value)}
|
|
105
|
+
/>
|
|
106
|
+
{table.search.query && (
|
|
107
|
+
<button onClick={table.search.clear}>Clear</button>
|
|
108
|
+
)}
|
|
109
|
+
{table.isFetching && <span>Searching...</span>}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
- **`search.set(field, query)`** -- set the field and query text. The API call is debounced by 300ms. Pagination resets to page 1.
|
|
113
|
+
- **`search.clear()`** -- clear the search field and query. Pagination resets to page 1.
|
|
114
|
+
- **`search.query`** -- current query string (updates immediately, API call debounced)
|
|
115
|
+
- **`search.field`** -- field currently being searched, or `null`
|
|
116
|
+
- Queries longer than 255 characters are silently ignored.
|
|
117
|
+
|
|
118
|
+
### Sorting
|
|
119
|
+
|
|
120
|
+
Sorting is controlled through the `sort` object. Column headers typically use `toggle()` for click-to-sort behavior:
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
<th
|
|
124
|
+
onClick={() => table.sort.toggle(product.Title.id)}
|
|
125
|
+
style={{ cursor: "pointer" }}
|
|
126
|
+
>
|
|
127
|
+
{product.Title.label}
|
|
128
|
+
{table.sort.field === product.Title.id &&
|
|
129
|
+
(table.sort.direction === "ASC" ? " ↑" : " ↓")}
|
|
130
|
+
</th>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
- **`sort.toggle(field)`** -- cycles through ASC, DESC, then cleared. Toggling a different field starts at ASC.
|
|
134
|
+
- **`sort.set(field, direction)`** -- set the sort field and direction directly. Pass `null` for both to clear.
|
|
135
|
+
- **`sort.clear()`** -- remove sorting entirely.
|
|
136
|
+
- **`sort.field`** / **`sort.direction`** -- current sort state, or `null` when no sort is active.
|
|
137
|
+
- **`initialState.sort`** format is an array of single-key objects: `[{ fieldName: "ASC" }]`.
|
|
138
|
+
|
|
139
|
+
### Filtering
|
|
140
|
+
|
|
141
|
+
The table includes an integrated [`useFilter`](../useFilter/README.md) instance at `table.filter`. Use it to build filter conditions that narrow down the table results:
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
import { ConditionOperator } from "@ram_28/kf-ai-sdk/table";
|
|
145
|
+
|
|
146
|
+
// Exact match
|
|
147
|
+
table.filter.addCondition({
|
|
148
|
+
Operator: ConditionOperator.EQ,
|
|
149
|
+
LHSField: product.Category.id,
|
|
150
|
+
RHSValue: "Electronics",
|
|
151
|
+
RHSType: "Constant",
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Range filter
|
|
155
|
+
table.filter.addCondition({
|
|
156
|
+
Operator: ConditionOperator.GTE,
|
|
157
|
+
LHSField: product.Price.id,
|
|
158
|
+
RHSValue: 100,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Clear all filters
|
|
162
|
+
table.filter.clearAllConditions();
|
|
163
|
+
|
|
164
|
+
// Check if any filters are active
|
|
165
|
+
if (table.filter.hasConditions) {
|
|
166
|
+
// ...
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
- **`table.filter.addCondition(condition)`** -- add a filter condition
|
|
171
|
+
- **`table.filter.clearAllConditions()`** -- remove all filter conditions
|
|
172
|
+
- **`table.filter.hasConditions`** -- `true` if any conditions are active
|
|
173
|
+
- Filter changes automatically reset pagination to page 1, preventing empty pages after narrowing results.
|
|
174
|
+
- See the [useFilter documentation](../useFilter/README.md) for the full filter API.
|
|
175
|
+
|
|
176
|
+
### Pagination
|
|
177
|
+
|
|
178
|
+
Pages are 1-indexed (they start at 1, not 0). The default page is 1 and the default page size is 10.
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
<div>
|
|
182
|
+
<button
|
|
183
|
+
onClick={table.pagination.goToPrevious}
|
|
184
|
+
disabled={!table.pagination.canGoPrevious}
|
|
185
|
+
>
|
|
186
|
+
Previous
|
|
187
|
+
</button>
|
|
188
|
+
<span>
|
|
189
|
+
Page {table.pagination.pageNo} of {table.pagination.totalPages}
|
|
190
|
+
{" "}({table.pagination.totalItems} total)
|
|
191
|
+
</span>
|
|
192
|
+
<button
|
|
193
|
+
onClick={table.pagination.goToNext}
|
|
194
|
+
disabled={!table.pagination.canGoNext}
|
|
195
|
+
>
|
|
196
|
+
Next
|
|
197
|
+
</button>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
<select
|
|
201
|
+
value={table.pagination.pageSize}
|
|
202
|
+
onChange={(e) => table.pagination.setPageSize(Number(e.target.value))}
|
|
203
|
+
>
|
|
204
|
+
<option value={10}>10</option>
|
|
205
|
+
<option value={25}>25</option>
|
|
206
|
+
<option value={50}>50</option>
|
|
207
|
+
</select>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
- **`pagination.goToNext()`** / **`pagination.goToPrevious()`** -- navigate forward/backward. No-op at boundaries.
|
|
211
|
+
- **`pagination.goToPage(n)`** -- jump to a specific page. Clamped to `[1, totalPages]`.
|
|
212
|
+
- **`pagination.canGoNext`** / **`pagination.canGoPrevious`** -- whether navigation is possible.
|
|
213
|
+
- **`pagination.setPageSize(n)`** -- change the page size. Resets to page 1.
|
|
214
|
+
- **`pagination.pageNo`** / **`pagination.pageSize`** / **`pagination.totalPages`** / **`pagination.totalItems`** -- current pagination state.
|
|
215
|
+
|
|
216
|
+
### Refetching
|
|
217
|
+
|
|
218
|
+
Call `refetch()` to manually refresh the table data. This refetches both the list and the count:
|
|
219
|
+
|
|
220
|
+
```tsx
|
|
221
|
+
const handleDelete = async (id: string) => {
|
|
222
|
+
await product.delete(id);
|
|
223
|
+
table.refetch();
|
|
224
|
+
};
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
This is the standard pattern after any mutation (create, update, delete) that should be reflected in the table.
|
|
228
|
+
|
|
229
|
+
## Further Reading
|
|
230
|
+
|
|
231
|
+
- [API Reference](./api_reference.md) -- All options, return values, and type definitions
|
|
232
|
+
- [Product Listing](../examples/bdo/product-listing.md) -- Table with search, sorting, pagination
|
|
233
|
+
- [Edit Product Dialog](../examples/bdo/edit-product-dialog.md) -- Table + edit dialog + refetch
|
|
234
|
+
- [Filtered Product Table](../examples/bdo/filtered-product-table.md) -- Category + price range filters
|
|
235
|
+
|
|
236
|
+
## Common Mistakes
|
|
237
|
+
|
|
238
|
+
- **Don't forget `useMemo` on the BDO.** `new BdoClass()` must be wrapped in `useMemo(() => ..., [])`. Re-creating the instance on every render causes infinite refetching.
|
|
239
|
+
- **Don't access row fields directly.** `row.Title` is an accessor object, not the value. Use `row.Title.get()` to read the value.
|
|
240
|
+
- **Don't use 0-indexed pagination.** Pages start at 1. Passing `pageNo: 0` will produce incorrect results.
|
|
241
|
+
- **Don't forget to guard on `isLoading` before rendering rows.** While loading, `rows` is an empty array. Guard with `if (table.isLoading) return <Loading />` to avoid rendering an empty table.
|
|
242
|
+
- **Don't hardcode field names as strings.** Use the BDO field IDs (`product.Title.id`) instead of raw strings (`"Title"`). This keeps your code type-safe and resilient to field renames.
|