@ram_28/kf-ai-sdk 2.0.27 → 2.0.29
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/docs/bdo/README.md +23 -3
- package/docs/bdo/api_reference.md +1 -1
- package/docs/examples/bdo/filtered-product-table.md +1 -1
- package/docs/examples/workflow/filtered-activity-table.md +1 -1
- package/docs/fields/README.md +4 -1
- package/docs/useActivityForm/README.md +1 -0
- package/docs/useActivityTable/README.md +5 -3
- package/docs/useBDOForm/README.md +27 -1
- package/docs/useBDOTable/README.md +13 -2
- package/package.json +1 -1
package/docs/bdo/README.md
CHANGED
|
@@ -25,6 +25,26 @@ import type { ListOptionsType } from "@ram_28/kf-ai-sdk/api/types";
|
|
|
25
25
|
```typescript
|
|
26
26
|
const product = useMemo(() => new AdminProduct(), []);
|
|
27
27
|
```
|
|
28
|
+
5. **`Filter` must be `ConditionGroupType`** — Always wrap conditions in `{ Operator: "And", Condition: [...] }`. Never pass a flat condition:
|
|
29
|
+
```typescript
|
|
30
|
+
// ❌ WRONG — flat ConditionType causes TS2322
|
|
31
|
+
await product.list({ Filter: { Operator: "EQ", LHSField: "Category", RHSValue: "Electronics" } });
|
|
32
|
+
// ✅ CORRECT — ConditionGroupType wrapper
|
|
33
|
+
await product.list({ Filter: { Operator: "And", Condition: [
|
|
34
|
+
{ Operator: "EQ", LHSField: "Category", RHSValue: "Electronics", RHSType: "Constant" }
|
|
35
|
+
] } });
|
|
36
|
+
```
|
|
37
|
+
6. **Use `PageSize` and `Page`, not `Take` or `Limit`** — `ListOptionsType` uses `PageSize` (number of items per page) and `Page` (1-indexed page number).
|
|
38
|
+
7. **Never spread ItemType proxies.** `list()`, `get()`, and `create()` return proxied `ItemType` objects. Spreading `{ ...item, extraProp }` copies enumerable keys but destroys the proxy — `.get()` and `.set()` stop working. Instead, wrap in a container object:
|
|
39
|
+
```typescript
|
|
40
|
+
// ❌ WRONG — proxy is destroyed, .get() fails at runtime
|
|
41
|
+
const enriched = items.map(item => ({ ...item, extra: fetchedData }));
|
|
42
|
+
enriched[0].Title.get(); // TypeError: get is not a function
|
|
43
|
+
|
|
44
|
+
// ✅ CORRECT — proxy preserved in container
|
|
45
|
+
const enriched = items.map(item => ({ entry: item, extra: fetchedData }));
|
|
46
|
+
enriched[0].entry.Title.get(); // works
|
|
47
|
+
```
|
|
28
48
|
|
|
29
49
|
## Quick Start
|
|
30
50
|
|
|
@@ -82,7 +102,7 @@ const filtered = await product.count({
|
|
|
82
102
|
Filter: {
|
|
83
103
|
Operator: "And",
|
|
84
104
|
Condition: [
|
|
85
|
-
{ Operator: "
|
|
105
|
+
{ Operator: "EQ", LHSField: "Category", RHSValue: "Electronics", RHSType: "Constant" },
|
|
86
106
|
],
|
|
87
107
|
},
|
|
88
108
|
});
|
|
@@ -125,8 +145,8 @@ const items = await product.list({
|
|
|
125
145
|
Filter: {
|
|
126
146
|
Operator: "And",
|
|
127
147
|
Condition: [
|
|
128
|
-
{ Operator: "
|
|
129
|
-
{ Operator: "
|
|
148
|
+
{ Operator: "EQ", LHSField: "Category", RHSValue: "Electronics", RHSType: "Constant" },
|
|
149
|
+
{ Operator: "GTE", LHSField: "Price", RHSValue: 10, RHSType: "Constant" },
|
|
130
150
|
],
|
|
131
151
|
},
|
|
132
152
|
Sort: [{ Price: "DESC" }],
|
|
@@ -140,7 +140,7 @@ interface ConditionGroupType {
|
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
interface ConditionType {
|
|
143
|
-
Operator: string; // "
|
|
143
|
+
Operator: string; // "EQ", "NE", "GT", "GTE", "LT", "LTE", "Contains", "StartsWith", "IN", "NIN", "Empty", "NotEmpty"
|
|
144
144
|
LHSField: string; // field name
|
|
145
145
|
RHSValue: any; // comparison value
|
|
146
146
|
RHSType?: string; // defaults to "Constant"
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { useState, useMemo } from "react";
|
|
7
7
|
import { useBDOTable } from "@ram_28/kf-ai-sdk/table";
|
|
8
8
|
import type { UseBDOTableReturnType } from "@ram_28/kf-ai-sdk/table/types";
|
|
9
|
-
import { ConditionOperator, RHSType } from "@ram_28/kf-ai-sdk/
|
|
9
|
+
import { ConditionOperator, RHSType } from "@ram_28/kf-ai-sdk/table";
|
|
10
10
|
import { BuyerProduct } from "@/bdo/buyer/Product";
|
|
11
11
|
|
|
12
12
|
export default function FilteredProductTable() {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { useState, useMemo } from "react";
|
|
7
7
|
import { useActivityTable, ActivityTableStatus } from "@ram_28/kf-ai-sdk/workflow";
|
|
8
8
|
import type { UseActivityTableReturnType } from "@ram_28/kf-ai-sdk/workflow";
|
|
9
|
-
import { ConditionOperator, RHSType } from "@ram_28/kf-ai-sdk/
|
|
9
|
+
import { ConditionOperator, RHSType } from "@ram_28/kf-ai-sdk/table";
|
|
10
10
|
import { EmployeeInputActivity } from "@/workflow/leave";
|
|
11
11
|
|
|
12
12
|
export default function FilteredActivityTable() {
|
package/docs/fields/README.md
CHANGED
|
@@ -64,8 +64,11 @@ This applies to ALL field types: `StringField.get()` → `string | undefined`, `
|
|
|
64
64
|
2. **`.get()` returns `T | undefined`** — Always null-guard before passing to `new Date()`, `format()`, template literals, or typed function parameters. See section above.
|
|
65
65
|
3. **Don't confuse `StringField` (class) with `StringFieldType` (type alias)** — Different modules.
|
|
66
66
|
4. **`fetchOptions()` requires a parent BDO** — Standalone fields will throw.
|
|
67
|
-
5. **SelectField meta `Type` is `"String"`** — `Constraint.Enum` differentiates it.
|
|
67
|
+
5. **SelectField meta `Type` is `"String"`** — `Constraint.Enum` differentiates it. Only `SelectField` has the `.options` getter. `StringField` (even with an enum constraint) does NOT have `.options` — you must hardcode the enum values from the BDO file.
|
|
68
68
|
6. **Always use pre-built components for File/Image** — `<FileUpload>`, `<ImageUpload>`, `<FilePreview>`, `<ImageThumbnail>`.
|
|
69
|
+
7. **`<ImageUpload>` / `<FileUpload>` ONLY work with `ImageField` / `FileField`** — Never use them for `TextField` or `StringField`, even if the field name contains "image" or "file". A `TextField` named `image_urls` is still a text field — render it with `<Textarea>` or `<Input>`, not `<ImageUpload>`. Determine the component from the **field class** in the BDO file, not the field name.
|
|
70
|
+
8. **`<ImageUpload>` / `<FileUpload>` `field` prop MUST be `item.Field`, NOT `bdo.Field`** — The `upload()` and `deleteAttachment()` methods live on the form item accessor (created by the Item proxy), not on the BDO field class. Passing `bdo.Field` causes silent upload failures.
|
|
71
|
+
9. **BooleanField: use `watch()` + `setValue()`, never `register()`** — `<Switch>` and `<Checkbox>` components don't fire native change events. Using `register()` means the value never updates on toggle.
|
|
69
72
|
|
|
70
73
|
## Quick Start
|
|
71
74
|
|
|
@@ -242,3 +242,4 @@ item.StartDate.readOnly; // is readonly?
|
|
|
242
242
|
- **Don't call `activity.update()` or `activity.complete()` manually.** `handleSubmit` handles the API calls. Calling them yourself will double-submit.
|
|
243
243
|
- **Don't render the form before `isLoading` is false.** The activity data and metadata are being fetched. Guard with `if (isLoading) return <Loading />`.
|
|
244
244
|
- **Don't forget `activity_instance_id` is required.** This is the activity instance ID (from `workflow.start()` or `getInProgressList()`), not a BDO record ID.
|
|
245
|
+
- **Don't pass a single options object.** `useActivityForm` takes 2 arguments: `useActivityForm(activity, { activity_instance_id })`. This is different from `useActivityTable` which takes a single object `useActivityTable({ activity, status })`. Passing `useActivityForm({ activity, activity_instance_id })` causes a type error.
|
|
@@ -161,14 +161,14 @@ Sorting is controlled through the `sort` object. Column headers typically use `t
|
|
|
161
161
|
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:
|
|
162
162
|
|
|
163
163
|
```tsx
|
|
164
|
-
import { ConditionOperator } from "@ram_28/kf-ai-sdk/table";
|
|
164
|
+
import { ConditionOperator, RHSType } from "@ram_28/kf-ai-sdk/table";
|
|
165
165
|
|
|
166
166
|
// Filter by leave type
|
|
167
167
|
table.filter.addCondition({
|
|
168
168
|
Operator: ConditionOperator.EQ,
|
|
169
169
|
LHSField: activity.LeaveType.id,
|
|
170
170
|
RHSValue: "PTO",
|
|
171
|
-
RHSType:
|
|
171
|
+
RHSType: RHSType.Constant,
|
|
172
172
|
});
|
|
173
173
|
|
|
174
174
|
// Date range filter
|
|
@@ -176,7 +176,7 @@ table.filter.addCondition({
|
|
|
176
176
|
Operator: ConditionOperator.GTE,
|
|
177
177
|
LHSField: activity.StartDate.id,
|
|
178
178
|
RHSValue: "2026-01-01",
|
|
179
|
-
RHSType:
|
|
179
|
+
RHSType: RHSType.Constant,
|
|
180
180
|
});
|
|
181
181
|
|
|
182
182
|
// Clear all filters
|
|
@@ -261,3 +261,5 @@ This is the standard pattern after any mutation (complete, update, save) that sh
|
|
|
261
261
|
- **Don't use 0-indexed pagination.** Pages start at 1. Passing `pageNo: 0` will produce incorrect results.
|
|
262
262
|
- **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.
|
|
263
263
|
- **Don't hardcode field names as strings.** Use the activity field IDs (`activity.StartDate.id`) instead of raw strings (`"StartDate"`). This keeps your code type-safe and resilient to field renames.
|
|
264
|
+
- **Don't use `table.data`** — the property is `table.rows`, same as `useBDOTable`.
|
|
265
|
+
- **`initialState.sort` format is `[{ fieldName: "ASC" }]`** — NOT `{ field, direction }`. Write `sort: [{ StartDate: "DESC" }]`, not `sort: [{ field: "StartDate", direction: "desc" }]`.
|
|
@@ -170,6 +170,32 @@ item.Title.readOnly; // is readonly?
|
|
|
170
170
|
- **Don't forget `useMemo` on the BDO.** `new BdoClass()` must be wrapped in `useMemo(() => ..., [])`. Re-creating the instance on every render breaks the hook.
|
|
171
171
|
- **Don't set date `defaultValues` to empty string.** Use `undefined` instead. Empty strings cause type validation errors for Date and DateTime fields.
|
|
172
172
|
- **Don't mix `register()` and `watch()`+`setValue()` for the same field.** Pick one approach per field. `register()` for native inputs, `watch()`+`setValue()` for custom components.
|
|
173
|
-
- **Don't use `register()` for select, checkbox, or reference components.** They don't fire native change events. Use `watch()` + `setValue()`.
|
|
173
|
+
- **Don't use `register()` for select, checkbox, switch, or reference components.** They don't fire native change events. Use `watch()` + `setValue()`.
|
|
174
174
|
- **Don't call `bdo.create()` or `bdo.update()` manually.** `handleSubmit` handles the API call. Calling them yourself will double-submit.
|
|
175
|
+
- **`handleSubmit` is curried — pass it directly to `onSubmit`, don't call it inside a handler.** It takes `(onSuccess, onError)` and returns an event handler. Write `<form onSubmit={handleSubmit(onSuccess, onError)}>`. NEVER write `<form onSubmit={(e) => handleSubmit(data)}>` or `await handleSubmit(data)` — this passes the wrong argument and the API call never fires.
|
|
176
|
+
```tsx
|
|
177
|
+
// ❌ WRONG — handleSubmit receives FormEvent, not form data. API never fires.
|
|
178
|
+
const onSubmit = async (data) => { await handleSubmit(data); };
|
|
179
|
+
<form onSubmit={onSubmit}>
|
|
180
|
+
|
|
181
|
+
// ✅ CORRECT — handleSubmit wraps your callback, validates, calls API, then invokes onSuccess
|
|
182
|
+
<form onSubmit={handleSubmit(
|
|
183
|
+
(result) => { toast.success("Created"); navigate("/products"); },
|
|
184
|
+
(error) => { toast.error("Failed"); }
|
|
185
|
+
)}>
|
|
186
|
+
```
|
|
175
187
|
- **Don't render the form before `isLoading` is false.** In create mode, the draft is being allocated. In update mode, the record is being fetched. Guard with `if (isLoading) return <Loading />`.
|
|
188
|
+
- **There is no `operation` field.** Create vs. update mode is determined solely by `recordId`: absent = create, present = update. Never pass `operation: "create"` or `operation: "update"`.
|
|
189
|
+
- **Guard `loadError` in edit forms.** After the `isLoading` check, add `if (loadError) return <ErrorMessage />` before rendering the form. Without this, `item` may be undefined in update mode.
|
|
190
|
+
```tsx
|
|
191
|
+
if (isLoading) return <Loading />;
|
|
192
|
+
if (loadError) return <p>Error: {loadError.message}</p>;
|
|
193
|
+
```
|
|
194
|
+
- **Don't pass `operation` even though the SDK type supports it.** The SDK has `UseBDOFormAutoOptionsType` that auto-infers create/update from `recordId`. Passing `operation: "update"` requires `recordId: string` (not `string | undefined`), causing TS errors with `useParams()`. Omit `operation` entirely:
|
|
195
|
+
```tsx
|
|
196
|
+
// ❌ WRONG — TS2345 when id is string | undefined from useParams
|
|
197
|
+
useBDOForm({ bdo, recordId: id, operation: "update" })
|
|
198
|
+
|
|
199
|
+
// ✅ CORRECT — auto mode handles string | undefined
|
|
200
|
+
useBDOForm({ bdo, recordId: id })
|
|
201
|
+
```
|
|
@@ -141,14 +141,14 @@ Sorting is controlled through the `sort` object. Column headers typically use `t
|
|
|
141
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
142
|
|
|
143
143
|
```tsx
|
|
144
|
-
import { ConditionOperator } from "@ram_28/kf-ai-sdk/table";
|
|
144
|
+
import { ConditionOperator, RHSType } from "@ram_28/kf-ai-sdk/table";
|
|
145
145
|
|
|
146
146
|
// Exact match
|
|
147
147
|
table.filter.addCondition({
|
|
148
148
|
Operator: ConditionOperator.EQ,
|
|
149
149
|
LHSField: product.Category.id,
|
|
150
150
|
RHSValue: "Electronics",
|
|
151
|
-
RHSType:
|
|
151
|
+
RHSType: RHSType.Constant,
|
|
152
152
|
});
|
|
153
153
|
|
|
154
154
|
// Range filter
|
|
@@ -156,6 +156,7 @@ table.filter.addCondition({
|
|
|
156
156
|
Operator: ConditionOperator.GTE,
|
|
157
157
|
LHSField: product.Price.id,
|
|
158
158
|
RHSValue: 100,
|
|
159
|
+
RHSType: RHSType.Constant,
|
|
159
160
|
});
|
|
160
161
|
|
|
161
162
|
// Clear all filters
|
|
@@ -236,7 +237,17 @@ This is the standard pattern after any mutation (create, update, delete) that sh
|
|
|
236
237
|
## Common Mistakes
|
|
237
238
|
|
|
238
239
|
- **Don't forget `useMemo` on the BDO.** `new BdoClass()` must be wrapped in `useMemo(() => ..., [])`. Re-creating the instance on every render causes infinite refetching.
|
|
240
|
+
- **Don't use `table.data`** — the property is `table.rows`. Other table libraries use `data`, but this SDK uses `rows`.
|
|
241
|
+
- **Don't use `table.setSearch()`** — the correct API is `table.search.set(field, query)` with two arguments: field ID and query string.
|
|
239
242
|
- **Don't access row fields directly.** `row.Title` is an accessor object, not the value. Use `row.Title.get()` to read the value.
|
|
240
243
|
- **Don't use 0-indexed pagination.** Pages start at 1. Passing `pageNo: 0` will produce incorrect results.
|
|
241
244
|
- **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
245
|
- **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.
|
|
246
|
+
- **`initialState.sort` format is `[{ fieldName: "ASC" }]`** — a single-key object, NOT `{ field, direction }`. Other libraries use `{ field: "Title", direction: "asc" }` but this SDK uses `[{ Title: "ASC" }]`. Note: the direction is uppercase `"ASC"` or `"DESC"`.
|
|
247
|
+
```tsx
|
|
248
|
+
// ❌ WRONG — { field, direction } format from other libraries
|
|
249
|
+
initialState: { sort: [{ field: product.Title.id, direction: "desc" }] }
|
|
250
|
+
// ✅ CORRECT — single-key object with uppercase direction
|
|
251
|
+
initialState: { sort: [{ Title: "DESC" }] }
|
|
252
|
+
```
|
|
253
|
+
- **Don't pass `bdo.field` where `bdo.field.id` is expected.** `bdo.Title` is a `StringField` object, not a string. Use `bdo.Title.id` to get the field ID string. Passing the field object causes TS2322 (`StringField` not assignable to `string`).
|