@ram_28/kf-ai-sdk 2.0.19 → 2.0.20-beta.1

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.
Files changed (93) hide show
  1. package/README.md +8 -16
  2. package/dist/api.cjs +1 -1
  3. package/dist/api.mjs +1 -1
  4. package/dist/auth/authConfig.d.ts +1 -1
  5. package/dist/auth/types.d.ts +1 -1
  6. package/dist/auth/types.d.ts.map +1 -1
  7. package/dist/auth.cjs +1 -1
  8. package/dist/auth.mjs +1 -1
  9. package/dist/bdo/core/Item.d.ts.map +1 -1
  10. package/dist/bdo.cjs +1 -1
  11. package/dist/bdo.mjs +32 -32
  12. package/dist/components/hooks/useActivityForm/types.d.ts +5 -4
  13. package/dist/components/hooks/useActivityForm/types.d.ts.map +1 -1
  14. package/dist/components/hooks/useActivityForm/useActivityForm.d.ts.map +1 -1
  15. package/dist/components/hooks/useActivityTable/types.d.ts +4 -5
  16. package/dist/components/hooks/useActivityTable/types.d.ts.map +1 -1
  17. package/dist/components/hooks/useActivityTable/useActivityTable.d.ts.map +1 -1
  18. package/dist/components/hooks/useBDOForm/createItemProxy.d.ts +2 -2
  19. package/dist/components/hooks/useBDOForm/createItemProxy.d.ts.map +1 -1
  20. package/dist/components/hooks/useBDOTable/types.d.ts +12 -20
  21. package/dist/components/hooks/useBDOTable/types.d.ts.map +1 -1
  22. package/dist/components/hooks/useBDOTable/useBDOTable.d.ts +2 -2
  23. package/dist/components/hooks/useBDOTable/useBDOTable.d.ts.map +1 -1
  24. package/dist/{constants-Cyi942Yr.js → constants-ConHc1oS.js} +5 -5
  25. package/dist/constants-QX2RX-wu.cjs +1 -0
  26. package/dist/filter.cjs +1 -1
  27. package/dist/filter.mjs +1 -1
  28. package/dist/form.cjs +1 -1
  29. package/dist/form.mjs +1 -1
  30. package/dist/table.cjs +1 -1
  31. package/dist/table.mjs +16 -15
  32. package/dist/table.types.d.ts +1 -1
  33. package/dist/table.types.d.ts.map +1 -1
  34. package/dist/types/constants.d.ts +1 -1
  35. package/dist/workflow/Activity.d.ts +5 -8
  36. package/dist/workflow/Activity.d.ts.map +1 -1
  37. package/dist/workflow.cjs +1 -1
  38. package/dist/workflow.mjs +476 -461
  39. package/docs/api.md +95 -0
  40. package/docs/bdo.md +224 -0
  41. package/docs/gaps.md +360 -0
  42. package/docs/useActivityForm.md +393 -0
  43. package/docs/useActivityTable.md +418 -0
  44. package/docs/useBDOForm.md +498 -0
  45. package/docs/useBDOTable.md +284 -0
  46. package/docs/useFilter.md +188 -0
  47. package/docs/workflow.md +560 -0
  48. package/package.json +14 -15
  49. package/sdk/auth/authConfig.ts +1 -1
  50. package/sdk/auth/types.ts +1 -1
  51. package/sdk/bdo/core/Item.ts +1 -2
  52. package/sdk/components/hooks/useActivityForm/types.ts +6 -4
  53. package/sdk/components/hooks/useActivityForm/useActivityForm.ts +73 -10
  54. package/sdk/components/hooks/useActivityTable/types.ts +5 -4
  55. package/sdk/components/hooks/useActivityTable/useActivityTable.ts +8 -10
  56. package/sdk/components/hooks/useBDOForm/createItemProxy.ts +5 -9
  57. package/sdk/components/hooks/useBDOTable/types.ts +10 -20
  58. package/sdk/components/hooks/useBDOTable/useBDOTable.ts +8 -12
  59. package/sdk/table.types.ts +0 -2
  60. package/sdk/types/constants.ts +1 -1
  61. package/sdk/workflow/Activity.ts +7 -39
  62. package/dist/constants-DEmYwKfC.cjs +0 -1
  63. package/docs/README.md +0 -57
  64. package/docs/bdo/README.md +0 -161
  65. package/docs/bdo/api_reference.md +0 -281
  66. package/docs/examples/bdo/create-product.md +0 -69
  67. package/docs/examples/bdo/edit-product-dialog.md +0 -95
  68. package/docs/examples/bdo/filtered-product-table.md +0 -100
  69. package/docs/examples/bdo/product-listing.md +0 -73
  70. package/docs/examples/bdo/supplier-dropdown.md +0 -60
  71. package/docs/examples/fields/complex-fields.md +0 -248
  72. package/docs/examples/fields/primitive-fields.md +0 -217
  73. package/docs/examples/workflow/approve-leave-request.md +0 -76
  74. package/docs/examples/workflow/filtered-activity-table.md +0 -101
  75. package/docs/examples/workflow/my-pending-requests.md +0 -90
  76. package/docs/examples/workflow/start-new-workflow.md +0 -47
  77. package/docs/examples/workflow/submit-leave-request.md +0 -72
  78. package/docs/examples/workflow/workflow-progress.md +0 -49
  79. package/docs/fields/README.md +0 -141
  80. package/docs/fields/api_reference.md +0 -134
  81. package/docs/useActivityForm/README.md +0 -244
  82. package/docs/useActivityForm/api_reference.md +0 -279
  83. package/docs/useActivityTable/README.md +0 -263
  84. package/docs/useActivityTable/api_reference.md +0 -294
  85. package/docs/useBDOForm/README.md +0 -175
  86. package/docs/useBDOForm/api_reference.md +0 -244
  87. package/docs/useBDOTable/README.md +0 -242
  88. package/docs/useBDOTable/api_reference.md +0 -253
  89. package/docs/useFilter/README.md +0 -323
  90. package/docs/useFilter/api_reference.md +0 -228
  91. package/docs/workflow/README.md +0 -158
  92. package/docs/workflow/api_reference.md +0 -161
  93. /package/docs/{useAuth/README.md → useAuth.md} +0 -0
@@ -1,281 +0,0 @@
1
- ```typescript
2
- import { BaseBdo, StringField, NumberField, BooleanField, DateField, SelectField, ReferenceField } from "@ram_28/kf-ai-sdk/bdo";
3
- import type {
4
- ItemType,
5
- EditableFieldAccessorType,
6
- ReadonlyFieldAccessorType,
7
- ValidationResultType,
8
- BdoMetaType,
9
- } from "@ram_28/kf-ai-sdk/bdo/types";
10
- import type {
11
- ListOptionsType,
12
- FilterType,
13
- ConditionGroupType,
14
- ConditionType,
15
- SortType,
16
- CreateUpdateResponseType,
17
- DeleteResponseType,
18
- MetricOptionsType,
19
- MetricResponseType,
20
- PivotOptionsType,
21
- PivotResponseType,
22
- } from "@ram_28/kf-ai-sdk/api/types";
23
- import type {
24
- StringFieldType,
25
- NumberFieldType,
26
- BooleanFieldType,
27
- DateFieldType,
28
- DateTimeFieldType,
29
- TextFieldType,
30
- SelectFieldType,
31
- ReferenceFieldType,
32
- UserFieldType,
33
- FileFieldType,
34
- ImageFieldType,
35
- ArrayFieldType,
36
- ObjectFieldType,
37
- SystemFieldsType,
38
- } from "@ram_28/kf-ai-sdk/types";
39
- ```
40
-
41
- ## Methods
42
-
43
- All methods are `protected` on `BaseBdo`. Subclasses expose them as `public` per role.
44
-
45
- | Method | Params | Returns |
46
- |--------|--------|---------|
47
- | `get(id)` | `id: string` | `Promise<ItemType<TEditable, TReadonly>>` |
48
- | `list(options?)` | `options?: ListOptionsType` | `Promise<ItemType<TEditable, TReadonly>[]>` |
49
- | `count(options?)` | `options?: ListOptionsType` | `Promise<number>` |
50
- | `create(data)` | `data: Partial<TEditable>` | `Promise<ItemType<TEditable, TReadonly>>` |
51
- | `update(id, data)` | `id: string, data: Partial<TEditable>` | `Promise<CreateUpdateResponseType>` |
52
- | `delete(id)` | `id: string` | `Promise<DeleteResponseType>` |
53
- | `metric(options)` | `options: Omit<MetricOptionsType, "Type">` | `Promise<MetricResponseType>` |
54
- | `pivot(options)` | `options: Omit<PivotOptionsType, "Type">` | `Promise<PivotResponseType>` |
55
-
56
- ## Types
57
-
58
- ### ItemType\<TEditable, TReadonly\>
59
-
60
- Proxy-wrapped record returned by `get()`, `list()`, and `create()`. Provides typed field accessors for each field.
61
-
62
- ```typescript
63
- type ItemType<TEditable, TReadonly> = {
64
- readonly _id: string; // direct string access
65
-
66
- // Editable fields → EditableFieldAccessorType<T>
67
- // Readonly fields → ReadonlyFieldAccessorType<T>
68
-
69
- validate(): ValidationResultType; // validates all fields
70
- toJSON(): Partial<TEditable & TReadonly>;
71
- };
72
- ```
73
-
74
- ### EditableFieldAccessorType\<T\>
75
-
76
- Accessor for fields the current role can write.
77
-
78
- ```typescript
79
- interface EditableFieldAccessorType<T> {
80
- get(): T | undefined;
81
- getOrDefault(fallback: T): T;
82
- set(value: T): void;
83
- validate(): ValidationResultType;
84
- readonly label: string;
85
- readonly required: boolean;
86
- readonly readOnly: false;
87
- readonly defaultValue: unknown;
88
- readonly meta: BaseFieldMetaType;
89
- }
90
- ```
91
-
92
- ### ReadonlyFieldAccessorType\<T\>
93
-
94
- Accessor for fields the current role can only read. Same as editable but no `set()`.
95
-
96
- ```typescript
97
- interface ReadonlyFieldAccessorType<T> {
98
- get(): T | undefined;
99
- getOrDefault(fallback: T): T;
100
- validate(): ValidationResultType;
101
- readonly label: string;
102
- readonly required: boolean;
103
- readonly readOnly: true;
104
- readonly defaultValue: unknown;
105
- readonly meta: BaseFieldMetaType;
106
- }
107
- ```
108
-
109
- ### ValidationResultType
110
-
111
- ```typescript
112
- interface ValidationResultType {
113
- valid: boolean;
114
- errors: string[];
115
- }
116
- ```
117
-
118
- ### ListOptionsType
119
-
120
- ```typescript
121
- interface ListOptionsType {
122
- Filter?: FilterType;
123
- Sort?: SortType;
124
- Page?: number; // 1-indexed
125
- PageSize?: number;
126
- Search?: string;
127
- Field?: string[]; // specific fields to return
128
- }
129
- ```
130
-
131
- ### FilterType / ConditionGroupType / ConditionType
132
-
133
- ```typescript
134
- // Root filter — alias for ConditionGroupType
135
- type FilterType = ConditionGroupType;
136
-
137
- interface ConditionGroupType {
138
- Operator: "And" | "Or" | "Not";
139
- Condition: Array<ConditionType | ConditionGroupType>;
140
- }
141
-
142
- interface ConditionType {
143
- Operator: string; // "eq", "neq", "gt", "gte", "lt", "lte", "contains", "startswith", etc.
144
- LHSField: string; // field name
145
- RHSValue: any; // comparison value
146
- RHSType?: string; // defaults to "Constant"
147
- }
148
- ```
149
-
150
- ### SortType
151
-
152
- ```typescript
153
- type SortType = Record<string, "ASC" | "DESC">[];
154
- // Example: [{ "Price": "DESC" }, { "Title": "ASC" }]
155
- ```
156
-
157
- ### CreateUpdateResponseType
158
-
159
- ```typescript
160
- interface CreateUpdateResponseType {
161
- _id: string;
162
- }
163
- ```
164
-
165
- ### DeleteResponseType
166
-
167
- ```typescript
168
- interface DeleteResponseType {
169
- status: string;
170
- }
171
- ```
172
-
173
- ### MetricOptionsType
174
-
175
- Omit `Type` when calling `bdo.metric()` — it is added automatically.
176
-
177
- ```typescript
178
- interface MetricOptionsType {
179
- Type: "Metric"; // omit when calling bdo.metric()
180
- GroupBy: string[];
181
- Metric: MetricFieldType[];
182
- Filter?: FilterType;
183
- }
184
-
185
- interface MetricFieldType {
186
- Field: string;
187
- Type: string; // "Count" | "Sum" | "Avg" | "Max" | "Min"
188
- }
189
- ```
190
-
191
- ### MetricResponseType
192
-
193
- ```typescript
194
- interface MetricResponseType {
195
- Data: Record<string, any>[];
196
- }
197
- ```
198
-
199
- ### PivotOptionsType
200
-
201
- Omit `Type` when calling `bdo.pivot()` — it is added automatically.
202
-
203
- ```typescript
204
- interface PivotOptionsType {
205
- Type: "Pivot"; // omit when calling bdo.pivot()
206
- Row: string[];
207
- Column: string[];
208
- Metric: MetricFieldType[];
209
- Filter?: FilterType;
210
- }
211
- ```
212
-
213
- ### PivotResponseType
214
-
215
- ```typescript
216
- interface PivotResponseType {
217
- Data: {
218
- RowHeader: PivotHeaderItemType[];
219
- ColumnHeader: PivotHeaderItemType[];
220
- Value: (number | string | null)[][];
221
- };
222
- }
223
-
224
- interface PivotHeaderItemType {
225
- Key: string;
226
- Children?: PivotHeaderItemType[] | null;
227
- }
228
- ```
229
-
230
- ### SystemFieldsType
231
-
232
- Seven system-managed fields inherited from `BaseBdo`. Never redeclare in subclasses.
233
-
234
- ```typescript
235
- type SystemFieldsType = {
236
- _id: StringFieldType;
237
- _created_at: DateTimeFieldType;
238
- _modified_at: DateTimeFieldType;
239
- _created_by: UserFieldType;
240
- _modified_by: UserFieldType;
241
- _version: StringFieldType;
242
- _m_version: StringFieldType;
243
- };
244
- ```
245
-
246
- ### Field Value Types
247
-
248
- | Type | Resolves To |
249
- |------|-------------|
250
- | `StringFieldType` | `string` |
251
- | `TextFieldType` | `string` |
252
- | `NumberFieldType` | `number` |
253
- | `BooleanFieldType` | `boolean` |
254
- | `DateFieldType` | `"YYYY-MM-DD"` |
255
- | `DateTimeFieldType` | `"YYYY-MM-DDThh:mm:ssZ"` |
256
- | `SelectFieldType<T>` | `T` |
257
- | `ReferenceFieldType<T>` | `T` |
258
- | `UserFieldType` | `{ _id: string; _name: string }` |
259
- | `ImageFieldType` | `FileType \| null` |
260
- | `FileFieldType` | `FileType[]` |
261
- | `ArrayFieldType<T>` | `T[]` |
262
- | `ObjectFieldType<T>` | `T` |
263
-
264
- ### Generated Type Patterns
265
-
266
- The js_sdk generator creates these types per entity:
267
-
268
- ```typescript
269
- // entities/Product.ts
270
- export type ProductType = {
271
- ProductId: StringFieldType;
272
- Title: StringFieldType;
273
- Price: NumberFieldType;
274
- // ... business fields
275
- } & SystemFieldsType;
276
-
277
- // admin/Product.ts
278
- type AdminProductEditableFieldType = Omit<ProductType, keyof SystemFieldsType>;
279
- type AdminProductReadonlyFieldType = Record<string, never>;
280
- // or Pick<ProductType, "Field1" | "Field2"> for limited access
281
- ```
@@ -1,69 +0,0 @@
1
- # Create Product
2
-
3
- > Create a new product using `useBDOForm` with validation, select fields, and submit handling.
4
-
5
- ```tsx
6
- import { useMemo } from "react";
7
- import { useBDOForm } from "@ram_28/kf-ai-sdk/form";
8
- import type { UseBDOFormReturnType } from "@ram_28/kf-ai-sdk/form/types";
9
- import { BuyerProduct } from "@/bdo/buyer/Product";
10
- import type { BuyerProductEntityType } from "@/bdo/buyer/Product";
11
- import type { FieldErrors } from "react-hook-form";
12
-
13
- export default function CreateProductForm() {
14
- const product = useMemo(() => new BuyerProduct(), []);
15
-
16
- const { register, handleSubmit, errors, isLoading, isSubmitting, watch, setValue }: UseBDOFormReturnType<BuyerProduct> =
17
- useBDOForm({ bdo: product, defaultValues: { Title: "", Price: 0 } });
18
-
19
- if (isLoading) return <p>Loading...</p>;
20
-
21
- const onSuccess = (data: { _id: string }) => console.log("Created:", data._id);
22
- const onError = (err: FieldErrors<BuyerProductEntityType> | Error) => {
23
- if (err instanceof Error) console.error(err.message);
24
- };
25
-
26
- return (
27
- <form onSubmit={handleSubmit(onSuccess, onError)}>
28
- {/* Text input with register() */}
29
- <label>{product.Title.label} {product.Title.required && <span>*</span>}</label>
30
- <input {...register(product.Title.id)} />
31
- {errors.Title && <p>{errors.Title.message}</p>}
32
-
33
- <label>{product.Description.label}</label>
34
- <textarea {...register(product.Description.id)} rows={3} />
35
- {errors.Description && <p>{errors.Description.message}</p>}
36
-
37
- {/* Number input with register() */}
38
- <label>{product.Price.label} {product.Price.required && <span>*</span>}</label>
39
- <input type="number" step="0.01" {...register(product.Price.id)} />
40
- {errors.Price && <p>{errors.Price.message}</p>}
41
-
42
- {/* Select field — watch/setValue, not register */}
43
- <label>{product.Category.label} {product.Category.required && <span>*</span>}</label>
44
- <select
45
- value={watch(product.Category.id) ?? ""}
46
- onChange={(e) => setValue(product.Category.id, e.target.value)}
47
- >
48
- <option value="">Select category</option>
49
- {product.Category.options.map((opt) => (
50
- <option key={opt.value} value={opt.value}>{opt.label}</option>
51
- ))}
52
- </select>
53
- {errors.Category && <p>{errors.Category.message}</p>}
54
-
55
- <button type="submit" disabled={isSubmitting}>
56
- {isSubmitting ? "Saving..." : "Add Product"}
57
- </button>
58
- </form>
59
- );
60
- }
61
- ```
62
-
63
- ## Key Patterns
64
-
65
- - **Create mode** -- no `recordId` passed to `useBDOForm`; a draft is allocated on mount
66
- - **`register()`** -- for text and number inputs that fire native change events
67
- - **`watch()` + `setValue()`** -- for select fields and other custom components
68
- - **`handleSubmit(onSuccess, onError)`** -- validates, filters to editable fields, calls the API; never call `bdo.create()` manually
69
- - **Validation errors** -- `errors.FieldName.message` shows constraint violations automatically
@@ -1,95 +0,0 @@
1
- # Edit Product Dialog
2
-
3
- > Click a table row to open an edit form in a dialog, save, and refetch the table.
4
-
5
- ```tsx
6
- import { useState, useMemo } from "react";
7
- import { useBDOTable } from "@ram_28/kf-ai-sdk/table";
8
- import type { UseBDOTableReturnType } from "@ram_28/kf-ai-sdk/table/types";
9
- import { useBDOForm } from "@ram_28/kf-ai-sdk/form";
10
- import type { UseBDOFormReturnType } from "@ram_28/kf-ai-sdk/form/types";
11
- import { BuyerProduct } from "@/bdo/buyer/Product";
12
- import type { BuyerProductEntityType } from "@/bdo/buyer/Product";
13
- import type { FieldErrors } from "react-hook-form";
14
-
15
- export default function ProductPage() {
16
- const [showForm, setShowForm] = useState(false);
17
- const [selectedId, setSelectedId] = useState<string | null>(null);
18
-
19
- const product = useMemo(() => new BuyerProduct(), []);
20
-
21
- // ── Table ────────────────────────────────────────────────────
22
- const table: UseBDOTableReturnType<BuyerProduct> = useBDOTable({
23
- bdo: product,
24
- initialState: { sort: [{ Title: "ASC" }], pagination: { pageNo: 1, pageSize: 10 } },
25
- });
26
-
27
- // ── Form (create when selectedId is null, edit when string) ──
28
- const { register, handleSubmit, errors, isLoading: formLoading, isSubmitting, watch, setValue, isDirty }: UseBDOFormReturnType<BuyerProduct> =
29
- useBDOForm({ bdo: product, recordId: selectedId ?? undefined });
30
-
31
- const handleCreate = () => { setSelectedId(null); setShowForm(true); };
32
- const handleEdit = (id: string) => { setSelectedId(id); setShowForm(true); };
33
- const onSuccess = () => { setShowForm(false); setSelectedId(null); table.refetch(); };
34
- const onError = (err: FieldErrors<BuyerProductEntityType> | Error) => {
35
- if (err instanceof Error) console.error(err.message);
36
- };
37
-
38
- if (table.isLoading) return <p>Loading...</p>;
39
-
40
- return (
41
- <div>
42
- <button onClick={handleCreate}>Add Product</button>
43
-
44
- <table>
45
- <thead>
46
- <tr>
47
- <th>{product.Title.label}</th>
48
- <th>{product.Price.label}</th>
49
- </tr>
50
- </thead>
51
- <tbody>
52
- {table.rows.map((row) => (
53
- <tr key={row._id} onClick={() => handleEdit(row._id)} style={{ cursor: "pointer" }}>
54
- <td>{row.Title.get()}</td>
55
- <td>${row.Price.get()?.toFixed(2)}</td>
56
- </tr>
57
- ))}
58
- </tbody>
59
- </table>
60
-
61
- {/* Dialog form */}
62
- {showForm && (
63
- <dialog open>
64
- <h2>{selectedId ? "Edit Product" : "Add Product"}</h2>
65
- {formLoading ? (
66
- <p>Loading form...</p>
67
- ) : (
68
- <form onSubmit={handleSubmit(onSuccess, onError)}>
69
- <label>{product.Title.label}</label>
70
- <input {...register(product.Title.id)} />
71
- {errors.Title && <p>{errors.Title.message}</p>}
72
-
73
- <label>{product.Price.label}</label>
74
- <input type="number" step="0.01" {...register(product.Price.id)} />
75
- {errors.Price && <p>{errors.Price.message}</p>}
76
-
77
- <button type="button" onClick={() => setShowForm(false)}>Cancel</button>
78
- <button type="submit" disabled={isSubmitting || (!!selectedId && !isDirty)}>
79
- {isSubmitting ? "Saving..." : selectedId ? "Save Changes" : "Add Product"}
80
- </button>
81
- </form>
82
- )}
83
- </dialog>
84
- )}
85
- </div>
86
- );
87
- }
88
- ```
89
-
90
- ## Key Patterns
91
-
92
- - **Shared BDO instance** -- `useMemo(() => new BuyerProduct(), [])` is passed to both `useBDOTable` and `useBDOForm`
93
- - **Create vs edit mode** -- `selectedId` is `null` for create (no `recordId`), or a string for edit
94
- - **`table.refetch()` after save** -- called in `onSuccess` to refresh the table data
95
- - **`isDirty` guard** -- disables the submit button in edit mode when no changes have been made
@@ -1,100 +0,0 @@
1
- # Filtered Product Table
2
-
3
- > Filter products by category and price range using the integrated `table.filter` API.
4
-
5
- ```tsx
6
- import { useState, useMemo } from "react";
7
- import { useBDOTable } from "@ram_28/kf-ai-sdk/table";
8
- import type { UseBDOTableReturnType } from "@ram_28/kf-ai-sdk/table/types";
9
- import { ConditionOperator, RHSType } from "@ram_28/kf-ai-sdk/filter";
10
- import { BuyerProduct } from "@/bdo/buyer/Product";
11
-
12
- export default function FilteredProductTable() {
13
- const product = useMemo(() => new BuyerProduct(), []);
14
- const table: UseBDOTableReturnType<BuyerProduct> = useBDOTable({ bdo: product });
15
-
16
- // ── Category filter (single EQ condition) ─────────────────────
17
- const [categoryFilter, setCategoryFilter] = useState("");
18
- const [categoryConditionId, setCategoryConditionId] = useState<string | null>(null);
19
-
20
- const handleCategoryFilter = (value: string) => {
21
- if (categoryConditionId) {
22
- table.filter.removeCondition(categoryConditionId);
23
- setCategoryConditionId(null);
24
- }
25
- setCategoryFilter(value);
26
- if (value && value !== "all") {
27
- const id = table.filter.addCondition({
28
- LHSField: "Category",
29
- Operator: ConditionOperator.EQ,
30
- RHSType: RHSType.Constant,
31
- RHSValue: value,
32
- });
33
- setCategoryConditionId(id);
34
- }
35
- };
36
-
37
- // ── Price range filter (GTE + LTE conditions) ─────────────────
38
- const [minPrice, setMinPrice] = useState("");
39
- const [maxPrice, setMaxPrice] = useState("");
40
- const [minPriceConditionId, setMinPriceConditionId] = useState<string | null>(null);
41
- const [maxPriceConditionId, setMaxPriceConditionId] = useState<string | null>(null);
42
-
43
- const applyPriceFilter = () => {
44
- if (minPriceConditionId) { table.filter.removeCondition(minPriceConditionId); setMinPriceConditionId(null); }
45
- if (maxPriceConditionId) { table.filter.removeCondition(maxPriceConditionId); setMaxPriceConditionId(null); }
46
-
47
- if (minPrice !== "") {
48
- const id = table.filter.addCondition({
49
- LHSField: "Price", Operator: ConditionOperator.GTE, RHSType: RHSType.Constant, RHSValue: Number(minPrice),
50
- });
51
- setMinPriceConditionId(id);
52
- }
53
- if (maxPrice !== "") {
54
- const id = table.filter.addCondition({
55
- LHSField: "Price", Operator: ConditionOperator.LTE, RHSType: RHSType.Constant, RHSValue: Number(maxPrice),
56
- });
57
- setMaxPriceConditionId(id);
58
- }
59
- };
60
-
61
- if (table.isLoading) return <p>Loading...</p>;
62
-
63
- return (
64
- <div>
65
- {/* Category dropdown */}
66
- <select value={categoryFilter} onChange={(e) => handleCategoryFilter(e.target.value)}>
67
- <option value="all">All categories</option>
68
- {product.Category.options.map((opt) => (
69
- <option key={opt.value} value={opt.value}>{opt.label}</option>
70
- ))}
71
- </select>
72
-
73
- {/* Price range */}
74
- <input type="number" placeholder="Min" value={minPrice} onChange={(e) => setMinPrice(e.target.value)} />
75
- <input type="number" placeholder="Max" value={maxPrice} onChange={(e) => setMaxPrice(e.target.value)} />
76
- <button onClick={applyPriceFilter}>Apply</button>
77
-
78
- {/* Table */}
79
- <table>
80
- <tbody>
81
- {table.rows.map((row) => (
82
- <tr key={row._id}>
83
- <td>{row.Title.get()}</td>
84
- <td>${row.Price.get()?.toFixed(2)}</td>
85
- <td>{row.Category.get()}</td>
86
- </tr>
87
- ))}
88
- </tbody>
89
- </table>
90
- </div>
91
- );
92
- }
93
- ```
94
-
95
- ## Key Patterns
96
-
97
- - **`addCondition()` returns an ID** -- store it in state to remove the condition later with `removeCondition(id)`
98
- - **Category filter** -- single `ConditionOperator.EQ` condition; remove-then-add on every change
99
- - **Price range** -- two separate conditions (`GTE` and `LTE`), each tracked by its own ID
100
- - **Filter changes auto-reset pagination** -- when conditions change, the table resets to page 1
@@ -1,73 +0,0 @@
1
- # Product Listing
2
-
3
- > Browse products with search, sortable columns, and pagination using `useBDOTable`.
4
-
5
- ```tsx
6
- import { useMemo } from "react";
7
- import { useBDOTable } from "@ram_28/kf-ai-sdk/table";
8
- import type { UseBDOTableReturnType } from "@ram_28/kf-ai-sdk/table/types";
9
- import { BuyerProduct } from "@/bdo/buyer/Product";
10
-
11
- export default function ProductListingPage() {
12
- const product = useMemo(() => new BuyerProduct(), []);
13
- const table: UseBDOTableReturnType<BuyerProduct> = useBDOTable({
14
- bdo: product,
15
- initialState: { sort: [{ _created_at: "DESC" }], pagination: { pageNo: 1, pageSize: 10 } },
16
- });
17
-
18
- if (table.isLoading) return <p>Loading...</p>;
19
- if (table.error) return <p>Error: {table.error.message}</p>;
20
-
21
- return (
22
- <div>
23
- {/* Search */}
24
- <input
25
- placeholder="Search by title..."
26
- value={table.search.query}
27
- onChange={(e) => table.search.set("Title", e.target.value)}
28
- />
29
- {table.search.query && <button onClick={table.search.clear}>Clear</button>}
30
-
31
- {/* Table with sortable headers */}
32
- <table>
33
- <thead>
34
- <tr>
35
- <th onClick={() => table.sort.toggle("Title")} style={{ cursor: "pointer" }}>
36
- {product.Title.label}
37
- {table.sort.field === "Title" && (table.sort.direction === "ASC" ? " ↑" : " ↓")}
38
- </th>
39
- <th onClick={() => table.sort.toggle("Price")} style={{ cursor: "pointer" }}>
40
- {product.Price.label}
41
- {table.sort.field === "Price" && (table.sort.direction === "ASC" ? " ↑" : " ↓")}
42
- </th>
43
- <th>{product.Category.label}</th>
44
- </tr>
45
- </thead>
46
- <tbody>
47
- {table.rows.map((row) => (
48
- <tr key={row._id}>
49
- <td>{row.Title.get()}</td>
50
- <td>${row.Price.get()?.toFixed(2)}</td>
51
- <td>{row.Category.get()}</td>
52
- </tr>
53
- ))}
54
- </tbody>
55
- </table>
56
-
57
- {/* Pagination */}
58
- <div>
59
- <button onClick={table.pagination.goToPrevious} disabled={!table.pagination.canGoPrevious}>Previous</button>
60
- <span>Page {table.pagination.pageNo} of {table.pagination.totalPages}</span>
61
- <button onClick={table.pagination.goToNext} disabled={!table.pagination.canGoNext}>Next</button>
62
- </div>
63
- </div>
64
- );
65
- }
66
- ```
67
-
68
- ## Key Patterns
69
-
70
- - **`row.Field.get()`** -- rows are `ItemType` proxies; always use `.get()` to read values
71
- - **`search.set(field, query)`** -- updates the input instantly, debounces the API call (300ms)
72
- - **`sort.toggle(field)`** -- cycles ASC → DESC → cleared on each click
73
- - **Pagination** -- `canGoNext`/`canGoPrevious` disable buttons at boundaries; `pageNo`, `totalPages` for display
@@ -1,60 +0,0 @@
1
- # Supplier Dropdown
2
-
3
- > Lazy-load ReferenceField options when the dropdown opens using `useQuery`.
4
-
5
- ```tsx
6
- import { useState, useMemo } from "react";
7
- import { useQuery } from "@tanstack/react-query";
8
- import { useBDOForm } from "@ram_28/kf-ai-sdk/form";
9
- import type { UseBDOFormReturnType } from "@ram_28/kf-ai-sdk/form/types";
10
- import { BuyerProduct } from "@/bdo/buyer/Product";
11
- import type { ProductSupplierRefType } from "@/bdo/buyer/Product";
12
-
13
- export default function SupplierDropdown({ recordId }: { recordId?: string }) {
14
- const [dropdownOpen, setDropdownOpen] = useState(false);
15
- const product = useMemo(() => new BuyerProduct(), []);
16
-
17
- const { watch, setValue, item }: UseBDOFormReturnType<BuyerProduct> = useBDOForm({ bdo: product, recordId });
18
-
19
- // Lazy-load options only when dropdown opens and item has an _id
20
- const { data: suppliers = [], isFetching } = useQuery<ProductSupplierRefType[]>({
21
- queryKey: ["supplier-options", item._id],
22
- queryFn: () => product.SupplierInfo.fetchOptions(item._id!),
23
- enabled: dropdownOpen && !!item._id,
24
- staleTime: Infinity,
25
- });
26
-
27
- const currentSupplier = watch(product.SupplierInfo.id);
28
-
29
- return (
30
- <div>
31
- <label>{product.SupplierInfo.label}</label>
32
- <select
33
- value={currentSupplier?._id ?? ""}
34
- onFocus={() => setDropdownOpen(true)}
35
- onChange={(e) => {
36
- const supplier = suppliers.find((s) => s._id === e.target.value);
37
- if (supplier) setValue(product.SupplierInfo.id, supplier);
38
- }}
39
- >
40
- <option value="">{currentSupplier?.SupplierName ?? "Select supplier"}</option>
41
- {isFetching ? (
42
- <option disabled>Loading...</option>
43
- ) : (
44
- suppliers.map((s) => (
45
- <option key={s._id} value={s._id}>{s.SupplierName}</option>
46
- ))
47
- )}
48
- </select>
49
- </div>
50
- );
51
- }
52
- ```
53
-
54
- ## Key Patterns
55
-
56
- - **`enabled: dropdownOpen && !!item._id`** -- options aren't fetched until the dropdown opens and the draft/record has an ID
57
- - **`fetchOptions(instanceId)`** -- loads reference options for the current record context
58
- - **`watch()` + `setValue()`** -- reads/writes the entire reference object (`{ _id, SupplierName }`)
59
- - **`referenceFields`** -- `product.SupplierInfo.referenceFields` lists the fields fetched from the referenced BDO
60
- - **`staleTime: Infinity`** -- options are cached and not refetched on re-focus