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

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 (105) hide show
  1. package/README.md +8 -16
  2. package/dist/{FileField-CZjS2uLh.js → FileField-BWrSHNRq.js} +3 -3
  3. package/dist/{FileField-DU4UWo_t.cjs → FileField-eDeuzln8.cjs} +1 -1
  4. package/dist/api.cjs +1 -1
  5. package/dist/api.mjs +1 -1
  6. package/dist/auth/authConfig.d.ts +1 -1
  7. package/dist/auth/types.d.ts +1 -1
  8. package/dist/auth/types.d.ts.map +1 -1
  9. package/dist/auth.cjs +1 -1
  10. package/dist/auth.mjs +1 -1
  11. package/dist/bdo/core/Item.d.ts +4 -0
  12. package/dist/bdo/core/Item.d.ts.map +1 -1
  13. package/dist/bdo/fields/ReferenceField.d.ts +1 -1
  14. package/dist/bdo/fields/ReferenceField.d.ts.map +1 -1
  15. package/dist/bdo/fields/SelectField.d.ts +1 -1
  16. package/dist/bdo/fields/SelectField.d.ts.map +1 -1
  17. package/dist/bdo/fields/UserField.d.ts +1 -1
  18. package/dist/bdo/fields/UserField.d.ts.map +1 -1
  19. package/dist/bdo.cjs +1 -1
  20. package/dist/bdo.mjs +62 -53
  21. package/dist/components/hooks/useActivityForm/types.d.ts +5 -4
  22. package/dist/components/hooks/useActivityForm/types.d.ts.map +1 -1
  23. package/dist/components/hooks/useActivityForm/useActivityForm.d.ts.map +1 -1
  24. package/dist/components/hooks/useActivityTable/types.d.ts +4 -5
  25. package/dist/components/hooks/useActivityTable/types.d.ts.map +1 -1
  26. package/dist/components/hooks/useActivityTable/useActivityTable.d.ts.map +1 -1
  27. package/dist/components/hooks/useBDOForm/createItemProxy.d.ts +3 -2
  28. package/dist/components/hooks/useBDOForm/createItemProxy.d.ts.map +1 -1
  29. package/dist/components/hooks/useBDOTable/types.d.ts +12 -20
  30. package/dist/components/hooks/useBDOTable/types.d.ts.map +1 -1
  31. package/dist/components/hooks/useBDOTable/useBDOTable.d.ts +2 -2
  32. package/dist/components/hooks/useBDOTable/useBDOTable.d.ts.map +1 -1
  33. package/dist/{constants-Cyi942Yr.js → constants-ConHc1oS.js} +5 -5
  34. package/dist/constants-QX2RX-wu.cjs +1 -0
  35. package/dist/filter.cjs +1 -1
  36. package/dist/filter.mjs +1 -1
  37. package/dist/form.cjs +1 -1
  38. package/dist/form.mjs +243 -226
  39. package/dist/table.cjs +1 -1
  40. package/dist/table.mjs +16 -15
  41. package/dist/table.types.d.ts +1 -1
  42. package/dist/table.types.d.ts.map +1 -1
  43. package/dist/types/constants.d.ts +1 -1
  44. package/dist/workflow/Activity.d.ts +5 -8
  45. package/dist/workflow/Activity.d.ts.map +1 -1
  46. package/dist/workflow.cjs +1 -1
  47. package/dist/workflow.mjs +476 -461
  48. package/docs/api.md +95 -0
  49. package/docs/bdo.md +224 -0
  50. package/docs/gaps.md +360 -0
  51. package/docs/useActivityForm.md +393 -0
  52. package/docs/useActivityTable.md +418 -0
  53. package/docs/useBDOForm.md +498 -0
  54. package/docs/useBDOTable.md +284 -0
  55. package/docs/useFilter.md +188 -0
  56. package/docs/workflow.md +560 -0
  57. package/package.json +14 -15
  58. package/sdk/auth/authConfig.ts +1 -1
  59. package/sdk/auth/types.ts +1 -1
  60. package/sdk/bdo/core/Item.ts +10 -1
  61. package/sdk/bdo/fields/ReferenceField.ts +1 -1
  62. package/sdk/bdo/fields/SelectField.ts +1 -1
  63. package/sdk/bdo/fields/UserField.ts +1 -1
  64. package/sdk/components/hooks/useActivityForm/types.ts +6 -4
  65. package/sdk/components/hooks/useActivityForm/useActivityForm.ts +73 -10
  66. package/sdk/components/hooks/useActivityTable/types.ts +5 -4
  67. package/sdk/components/hooks/useActivityTable/useActivityTable.ts +8 -10
  68. package/sdk/components/hooks/useBDOForm/createItemProxy.ts +58 -17
  69. package/sdk/components/hooks/useBDOTable/types.ts +10 -20
  70. package/sdk/components/hooks/useBDOTable/useBDOTable.ts +8 -12
  71. package/sdk/table.types.ts +0 -2
  72. package/sdk/types/constants.ts +1 -1
  73. package/sdk/workflow/Activity.ts +7 -39
  74. package/dist/constants-DEmYwKfC.cjs +0 -1
  75. package/docs/README.md +0 -57
  76. package/docs/bdo/README.md +0 -161
  77. package/docs/bdo/api_reference.md +0 -281
  78. package/docs/examples/bdo/create-product.md +0 -69
  79. package/docs/examples/bdo/edit-product-dialog.md +0 -95
  80. package/docs/examples/bdo/filtered-product-table.md +0 -100
  81. package/docs/examples/bdo/product-listing.md +0 -73
  82. package/docs/examples/bdo/supplier-dropdown.md +0 -60
  83. package/docs/examples/fields/complex-fields.md +0 -248
  84. package/docs/examples/fields/primitive-fields.md +0 -217
  85. package/docs/examples/workflow/approve-leave-request.md +0 -76
  86. package/docs/examples/workflow/filtered-activity-table.md +0 -101
  87. package/docs/examples/workflow/my-pending-requests.md +0 -90
  88. package/docs/examples/workflow/start-new-workflow.md +0 -47
  89. package/docs/examples/workflow/submit-leave-request.md +0 -72
  90. package/docs/examples/workflow/workflow-progress.md +0 -49
  91. package/docs/fields/README.md +0 -141
  92. package/docs/fields/api_reference.md +0 -134
  93. package/docs/useActivityForm/README.md +0 -244
  94. package/docs/useActivityForm/api_reference.md +0 -279
  95. package/docs/useActivityTable/README.md +0 -263
  96. package/docs/useActivityTable/api_reference.md +0 -294
  97. package/docs/useBDOForm/README.md +0 -175
  98. package/docs/useBDOForm/api_reference.md +0 -244
  99. package/docs/useBDOTable/README.md +0 -242
  100. package/docs/useBDOTable/api_reference.md +0 -253
  101. package/docs/useFilter/README.md +0 -323
  102. package/docs/useFilter/api_reference.md +0 -228
  103. package/docs/workflow/README.md +0 -158
  104. package/docs/workflow/api_reference.md +0 -161
  105. /package/docs/{useAuth/README.md → useAuth.md} +0 -0
@@ -1,161 +0,0 @@
1
- # BDO (Business Data Object)
2
-
3
- Type-safe, role-based data access layer. Each BDO class extends `BaseBdo<TEntity, TEditable, TReadonly>` and exposes CRUD methods filtered by role permissions.
4
-
5
- ## Imports
6
-
7
- ```typescript
8
- // Page component — import the generated role-specific BDO class
9
- import { AdminProduct } from "../bdo/admin/Product";
10
- ```
11
-
12
- ```typescript
13
- // BDO class definition — used by the js_sdk generator
14
- import { BaseBdo, StringField, NumberField, BooleanField, DateField, SelectField, ReferenceField } from "@ram_28/kf-ai-sdk/bdo";
15
- import type { StringFieldType, NumberFieldType, BooleanFieldType, DateFieldType, SelectFieldType, ReferenceFieldType, SystemFieldsType } from "@ram_28/kf-ai-sdk/types";
16
- import type { ListOptionsType } from "@ram_28/kf-ai-sdk/api/types";
17
- ```
18
-
19
- ## Common Mistakes (READ FIRST)
20
-
21
- 1. **`create()` returns `ItemType`, not `{ _id }`** — It returns a proxy-wrapped item with field accessors, not `CreateUpdateResponseType`. Use `item._id` to get the ID.
22
- 2. **Use `.get()` to read field values** — `item.Title` returns a field accessor object, not the value. Use `item.Title.get()`.
23
- 3. **Field names use `_name` not `name`** — System fields like `_created_by` return `{ _id, _name }`. Access the display name with `item._created_by.get()?._name`.
24
- 4. **Memoize BDO instances in React** — Wrap in `useMemo` to prevent re-creation on every render:
25
- ```typescript
26
- const product = useMemo(() => new AdminProduct(), []);
27
- ```
28
-
29
- ## Quick Start
30
-
31
- ```typescript
32
- const product = new AdminProduct();
33
-
34
- // Create — returns ItemType (proxy with field accessors)
35
- const item = await product.create({ Title: "Widget", Price: 29.99 });
36
- item._id; // "abc123"
37
- item.Title.get(); // "Widget"
38
- item.Price.get(); // 29.99
39
-
40
- // List
41
- const items = await product.list({ Page: 1, PageSize: 10 });
42
- items[0].Title.get();
43
-
44
- // Get single item
45
- const found = await product.get("abc123");
46
- found.Title.get(); // "Widget"
47
-
48
- // Update — returns { _id: string }
49
- await product.update("abc123", { Price: 39.99 });
50
-
51
- // Delete — returns { status: string }
52
- await product.delete("abc123");
53
- ```
54
-
55
- ## Usage Guide
56
-
57
- ### CRUD Operations
58
-
59
- ```typescript
60
- const product = new AdminProduct();
61
-
62
- // Create — returns ItemType with field accessors
63
- const item = await product.create({
64
- Title: "Widget",
65
- Price: 29.99,
66
- Category: "Electronics",
67
- });
68
- item._id; // direct string access
69
-
70
- // Get single item
71
- const item = await product.get("abc123");
72
-
73
- // Update — returns { _id: string }
74
- const { _id } = await product.update("abc123", { Price: 39.99 });
75
-
76
- // Delete — returns { status: string }
77
- const { status } = await product.delete("abc123");
78
-
79
- // Count
80
- const total = await product.count();
81
- const filtered = await product.count({
82
- Filter: {
83
- Operator: "And",
84
- Condition: [
85
- { Operator: "eq", LHSField: "Category", RHSValue: "Electronics" },
86
- ],
87
- },
88
- });
89
- ```
90
-
91
- ### ItemType Field Accessors
92
-
93
- Every `get()`, `list()`, and `create()` call returns `ItemType` — a proxy with typed field accessors.
94
-
95
- ```typescript
96
- const item = await product.get("abc123");
97
-
98
- // Read values
99
- item._id; // string (direct, not an accessor)
100
- item.Title.get(); // string | undefined
101
- item.Price.getOrDefault(0); // number (never undefined)
102
-
103
- // Write values (editable fields only)
104
- item.Title.set("New Title");
105
-
106
- // Field metadata
107
- item.Title.label; // "Product Title"
108
- item.Title.required; // true
109
- item.Title.readOnly; // false
110
- item.Title.defaultValue; // undefined
111
- item.Title.meta; // raw backend field meta
112
-
113
- // Validation
114
- item.Title.validate(); // { valid: boolean, errors: string[] }
115
- item.validate(); // validates all fields, returns { valid, errors }
116
-
117
- // Serialize
118
- item.toJSON(); // { Title: "New Title", Price: 29.99, ... }
119
- ```
120
-
121
- ### List with Filter, Sort, and Pagination
122
-
123
- ```typescript
124
- const items = await product.list({
125
- Filter: {
126
- Operator: "And",
127
- Condition: [
128
- { Operator: "eq", LHSField: "Category", RHSValue: "Electronics" },
129
- { Operator: "gte", LHSField: "Price", RHSValue: 10 },
130
- ],
131
- },
132
- Sort: [{ Price: "DESC" }],
133
- Page: 1,
134
- PageSize: 20,
135
- });
136
- ```
137
-
138
- ### Metric (Aggregation)
139
-
140
- ```typescript
141
- const result = await product.metric({
142
- GroupBy: ["Category"],
143
- Metric: [{ Field: "Price", Type: "Avg" }],
144
- });
145
- // result.Data = [{ Category: "Electronics", Price: 299.5 }, ...]
146
- ```
147
-
148
- ### Pivot (Cross-Tabulation)
149
-
150
- ```typescript
151
- const result = await product.pivot({
152
- Row: ["Category"],
153
- Column: ["IsActive"],
154
- Metric: [{ Field: "_id", Type: "Count" }],
155
- });
156
- // result.Data = { RowHeader: [...], ColumnHeader: [...], Value: [[...]] }
157
- ```
158
-
159
- ## Further Reading
160
-
161
- - [API Reference](./api_reference.md) — full method signatures, parameter types, and return types
@@ -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