@ram_28/kf-ai-sdk 2.0.12 → 2.0.14

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 (69) hide show
  1. package/dist/api/client.d.ts.map +1 -1
  2. package/dist/api.cjs +1 -1
  3. package/dist/api.mjs +2 -2
  4. package/dist/attachment-constants-B5jlqoKI.cjs +1 -0
  5. package/dist/attachment-constants-C2UHWxmp.js +63 -0
  6. package/dist/auth.cjs +1 -1
  7. package/dist/auth.mjs +1 -1
  8. package/dist/bdo/core/types.d.ts +4 -0
  9. package/dist/bdo/core/types.d.ts.map +1 -1
  10. package/dist/bdo/fields/NumberField.d.ts.map +1 -1
  11. package/dist/bdo/fields/ReferenceField.d.ts +3 -2
  12. package/dist/bdo/fields/ReferenceField.d.ts.map +1 -1
  13. package/dist/bdo/fields/SelectField.d.ts +1 -1
  14. package/dist/bdo/fields/SelectField.d.ts.map +1 -1
  15. package/dist/bdo/fields/UserField.d.ts +5 -0
  16. package/dist/bdo/fields/UserField.d.ts.map +1 -1
  17. package/dist/bdo.cjs +1 -1
  18. package/dist/bdo.mjs +107 -153
  19. package/dist/client-DnO2KKrw.cjs +1 -0
  20. package/dist/{client-CMERmrC-.js → client-iQTqFDNI.js} +34 -30
  21. package/dist/components/hooks/useForm/createItemProxy.d.ts +4 -0
  22. package/dist/components/hooks/useForm/createItemProxy.d.ts.map +1 -1
  23. package/dist/components/hooks/useForm/createResolver.d.ts.map +1 -1
  24. package/dist/components/hooks/useForm/useForm.d.ts +1 -0
  25. package/dist/components/hooks/useForm/useForm.d.ts.map +1 -1
  26. package/dist/form.cjs +1 -1
  27. package/dist/form.mjs +368 -203
  28. package/dist/{metadata-BfJtHz84.cjs → metadata-DgLSJkF5.cjs} +1 -1
  29. package/dist/{metadata-CwAo6a8e.js → metadata-DpfI3zRN.js} +1 -1
  30. package/dist/table.cjs +1 -1
  31. package/dist/table.mjs +1 -1
  32. package/dist/workflow/types.d.ts +3 -2
  33. package/dist/workflow/types.d.ts.map +1 -1
  34. package/dist/workflow.cjs +1 -1
  35. package/dist/workflow.d.ts +0 -2
  36. package/dist/workflow.d.ts.map +1 -1
  37. package/dist/workflow.mjs +204 -274
  38. package/dist/workflow.types.d.ts +0 -1
  39. package/dist/workflow.types.d.ts.map +1 -1
  40. package/docs/api.md +45 -253
  41. package/docs/bdo.md +130 -711
  42. package/docs/useAuth.md +42 -104
  43. package/docs/useFilter.md +117 -1591
  44. package/docs/useForm.md +266 -861
  45. package/docs/useTable.md +255 -1096
  46. package/docs/workflow.md +10 -155
  47. package/package.json +1 -1
  48. package/sdk/api/client.ts +18 -4
  49. package/sdk/bdo/core/types.ts +1 -0
  50. package/sdk/bdo/fields/NumberField.ts +2 -1
  51. package/sdk/bdo/fields/ReferenceField.ts +4 -3
  52. package/sdk/bdo/fields/SelectField.ts +2 -2
  53. package/sdk/bdo/fields/UserField.ts +14 -0
  54. package/sdk/components/hooks/useForm/createItemProxy.ts +221 -4
  55. package/sdk/components/hooks/useForm/createResolver.ts +16 -1
  56. package/sdk/components/hooks/useForm/useForm.ts +151 -50
  57. package/sdk/workflow/types.ts +3 -2
  58. package/sdk/workflow.ts +0 -7
  59. package/sdk/workflow.types.ts +0 -7
  60. package/dist/client-BnVxSHAm.cjs +0 -1
  61. package/dist/workflow/components/useActivityTable/index.d.ts +0 -4
  62. package/dist/workflow/components/useActivityTable/index.d.ts.map +0 -1
  63. package/dist/workflow/components/useActivityTable/types.d.ts +0 -53
  64. package/dist/workflow/components/useActivityTable/types.d.ts.map +0 -1
  65. package/dist/workflow/components/useActivityTable/useActivityTable.d.ts +0 -4
  66. package/dist/workflow/components/useActivityTable/useActivityTable.d.ts.map +0 -1
  67. package/sdk/workflow/components/useActivityTable/index.ts +0 -8
  68. package/sdk/workflow/components/useActivityTable/types.ts +0 -67
  69. package/sdk/workflow/components/useActivityTable/useActivityTable.ts +0 -145
package/docs/bdo.md CHANGED
@@ -5,801 +5,220 @@ Type-safe, role-based data access layer for business objects.
5
5
  ## Imports
6
6
 
7
7
  ```typescript
8
- // BDO class (from your generated code)
9
- import { AdminProduct } from "../bdo/admin/Product";
10
-
11
- // Field types (from your generated code)
12
- import type {
13
- AdminProductEditableFieldType,
14
- AdminProductReadonlyFieldType,
15
- AdminProductFieldType,
16
- } from "../bdo/admin/Product";
8
+ // BDO class (from generated code)
9
+ import { AdminProduct } from "@/bdo/admin/Product";
10
+ import type { AdminProductEditableFieldType, AdminProductFieldType } from "@/bdo/admin/Product";
17
11
 
18
12
  // SDK types
19
- import type {
20
- ItemType,
21
- BdoMetaType,
22
- ValidationResultType,
23
- SelectOptionType,
24
- EditableFieldAccessorType,
25
- ReadonlyFieldAccessorType,
26
- BaseFieldMetaType,
27
- } from "@ram_28/kf-ai-sdk/bdo/types";
28
-
29
- // System fields
30
- import type { SystemFieldsType } from "@ram_28/kf-ai-sdk/bdo/types";
31
-
32
- // API types (for method params/responses)
33
- import type {
34
- ListOptionsType,
35
- CreateUpdateResponseType,
36
- DeleteResponseType,
37
- DraftResponseType,
38
- MetricOptionsType,
39
- MetricResponseType,
40
- PivotOptionsType,
41
- PivotResponseType,
42
- } from "@ram_28/kf-ai-sdk/api/types";
43
- ```
44
-
45
- ## BDO Meta
46
-
47
- Every BDO has a `meta` property identifying the business object.
48
-
49
- ```typescript
50
- const product = new AdminProduct();
51
-
52
- product.meta._id; // "BDO_Product"
53
- product.meta.name; // "Product"
54
- ```
55
-
56
- Use `meta._id` anywhere a source identifier is needed (e.g., `useTable`, `useFilter`).
57
-
58
- ---
59
-
60
- ## Field Definitions
61
-
62
- Fields are readonly properties on the BDO class, constructed with raw backend meta JSON.
63
-
64
- ```typescript
65
- // In the generated BDO class
66
- readonly Title = new StringField({
67
- _id: "Title",
68
- Name: "Product Title",
69
- Type: "String",
70
- Constraint: { Required: true, Length: 255 },
71
- });
72
- ```
73
-
74
- ### BaseField Getters
75
-
76
- Every field exposes these getters:
77
-
78
- | Getter | Type | Source |
79
- |--------|------|--------|
80
- | `field.id` | `string` | `_meta._id` |
81
- | `field.label` | `string` | `_meta.Name` |
82
- | `field.readOnly` | `boolean` | `_meta.ReadOnly` |
83
- | `field.required` | `boolean` | `_meta.Constraint.Required` or `_meta.Required` |
84
- | `field.defaultValue` | `unknown` | `_meta.DefaultValue` or `_meta.Constraint.DefaultValue` |
85
- | `field.primaryKey` | `boolean` | `_meta.Constraint.PrimaryKey` |
86
- | `field.meta` | `BaseFieldMetaType` | Full raw backend meta object |
87
-
88
- ### Usage
89
-
90
- ```typescript
91
- const product = new AdminProduct();
92
-
93
- // react-hook-form register
94
- register(product.Title.id);
95
-
96
- // UI labels
97
- <label>{product.Title.label}</label>
98
-
99
- // Required indicator
100
- {product.Title.required && <span>*</span>}
101
-
102
- // Filter by field
103
- const items = await product.list({
104
- Filter: {
105
- Operator: "And",
106
- Condition: [{
107
- LHSField: product.Price.id,
108
- Operator: "GT",
109
- RHSValue: 50,
110
- RHSType: "Constant",
111
- }],
112
- },
113
- });
114
- ```
115
-
116
- ---
117
-
118
- ## Methods
119
-
120
- All `BaseBdo` methods are `protected`. Role BDOs selectively re-expose them as `public` based on permissions. For full request/response type definitions, see [api.md](api.md).
121
-
122
- ### get
123
-
124
- Fetches a single record by ID.
125
-
126
- ```typescript
127
- async get(id: string): Promise<ItemType<TEditable, TReadonly>>
128
- ```
129
-
130
- Returns an [ItemType](#itemtype--runtime-accessor-pattern) with field accessors.
131
-
132
- ```typescript
133
- const item = await product.get("prod_abc123");
134
-
135
- console.log(item._id); // "prod_abc123"
136
- console.log(item.Title.get()); // "Wireless Headphones"
137
- console.log(item.Title.label); // "Product Title"
138
- ```
139
-
140
- ---
141
-
142
- ### list
143
-
144
- Fetches paginated records with optional filtering, sorting, and pagination.
145
-
146
- ```typescript
147
- async list(options?: ListOptionsType): Promise<ItemType<TEditable, TReadonly>[]>
148
- ```
149
-
150
- Returns an array of [ItemType](#itemtype--runtime-accessor-pattern).
151
-
152
- ```typescript
153
- const items = await product.list({
154
- Filter: {
155
- Operator: "And",
156
- Condition: [{
157
- LHSField: product.Category.id,
158
- Operator: "EQ",
159
- RHSValue: "Electronics",
160
- RHSType: "Constant",
161
- }],
162
- },
163
- Sort: [{ [product.Price.id]: "ASC" }],
164
- Page: 1,
165
- PageSize: 20,
166
- });
167
-
168
- items.forEach((item) => {
169
- console.log(item.Title.get(), item.Price.get());
170
- });
171
- ```
172
-
173
- ---
174
-
175
- ### count
176
-
177
- Returns the count of records matching filter criteria.
178
-
179
- ```typescript
180
- async count(options?: ListOptionsType): Promise<number>
181
- ```
182
-
183
- Uses the same `ListOptionsType` for filtering. Returns the count directly as a `number`.
184
-
185
- ```typescript
186
- const total = await product.count();
187
- console.log(`Total products: ${total}`);
188
-
189
- // With filter
190
- const lowStock = await product.count({
191
- Filter: {
192
- Operator: "And",
193
- Condition: [{
194
- LHSField: product.Stock.id,
195
- Operator: "LT",
196
- RHSValue: 10,
197
- RHSType: "Constant",
198
- }],
199
- },
200
- });
13
+ import type { ItemType, SystemFieldsType } from "@ram_28/kf-ai-sdk/bdo/types";
14
+ import type { ListOptionsType, CreateUpdateResponseType, DeleteResponseType } from "@ram_28/kf-ai-sdk/api/types";
201
15
  ```
202
16
 
203
17
  ---
204
18
 
205
- ### create
19
+ ## Common Mistakes (READ FIRST)
206
20
 
207
- Creates a new record.
208
-
209
- ```typescript
210
- async create(data: Partial<TEditable>): Promise<ItemType<TEditable, TReadonly>>
211
- ```
212
-
213
- Returns an [ItemType](#itemtype--runtime-accessor-pattern) with the new `_id` from the API response and the input data as field accessors.
214
-
215
- ```typescript
216
- const newItem = await product.create({
217
- Title: "Wireless Headphones",
218
- Price: 99.99,
219
- Category: "Electronics",
220
- });
221
-
222
- console.log(newItem._id); // New record ID
223
- console.log(newItem.Title.get()); // "Wireless Headphones"
224
- ```
225
-
226
- ---
227
-
228
- ### update
229
-
230
- Updates an existing record.
231
-
232
- ```typescript
233
- async update(id: string, data: Partial<TEditable>): Promise<CreateUpdateResponseType>
234
- ```
235
-
236
- Returns `{ _id: string }`.
237
-
238
- ```typescript
239
- const result = await product.update("prod_abc123", {
240
- Price: 79.99,
241
- Stock: 45,
242
- });
243
-
244
- console.log("Updated:", result._id);
245
- ```
246
-
247
- ---
248
-
249
- ### delete
250
-
251
- Deletes a record by ID.
252
-
253
- ```typescript
254
- async delete(id: string): Promise<DeleteResponseType>
255
- ```
21
+ ### 1. Guessing field names instead of reading BDO files
256
22
 
257
- Returns `{ status: "success" }`.
23
+ ALWAYS read `src/bdo/{role}/*.ts` BEFORE writing page code. Use EXACT field names from the class.
258
24
 
259
25
  ```typescript
260
- const result = await product.delete("prod_abc123");
26
+ // WRONG guessing
27
+ bdo.productName bdo.isActive bdo.unitPrice
261
28
 
262
- if (result.status === "success") {
263
- console.log("Deleted successfully");
264
- }
29
+ // CORRECT exact snake_case from BDO file
30
+ bdo.product_name bdo.is_active bdo.unit_price
265
31
  ```
266
32
 
267
- ---
268
-
269
- ### draft
270
-
271
- Previews computed field values for a new record without saving.
33
+ ### 2. Rendering ItemType fields directly in JSX without `.get()`
272
34
 
273
- ```typescript
274
- async draft(data: Partial<TEditable>): Promise<DraftResponseType>
275
- ```
35
+ `bdo.list()`, `bdo.get()`, `bdo.create()` return `ItemType` — each field is an accessor object, NOT a value.
276
36
 
277
- Returns computed field values as `{ [fieldName: string]: any }`.
278
-
279
- ```typescript
280
- const draftResult = await product.draft({
281
- Price: 100,
282
- DiscountPercent: 15,
283
- });
37
+ ```tsx
38
+ // ❌ WRONG — renders [object Object]
39
+ const item = await bdo.get(id);
40
+ <span>{item.product_name}</span>
284
41
 
285
- console.log("Computed discount:", draftResult.DiscountAmount);
286
- console.log("Computed final price:", draftResult.FinalPrice);
42
+ // CORRECT — call .get()
43
+ <span>{item.product_name.get()}</span>
44
+ <span>{item.product_name.get() ?? "—"}</span>
287
45
  ```
288
46
 
289
- ---
290
-
291
- ### draftPatch
47
+ **Key distinction:** `useTable().rows` are plain objects (access `row.field` directly). `bdo.get()`/`bdo.list()` return ItemType proxies (must call `item.field.get()`).
292
48
 
293
- Previews computed field values for an existing record being edited.
294
-
295
- ```typescript
296
- async draftPatch(id: string, data: Partial<TEditable>): Promise<DraftResponseType>
297
- ```
49
+ ### 3. Rendering Image/File/Reference values in detail pages
298
50
 
299
- ```typescript
300
- const draftResult = await product.draftPatch("prod_abc123", {
301
- Price: 120,
302
- DiscountPercent: 20,
303
- });
51
+ These field types return complex objects from `.get()` — NOT renderable as React children.
304
52
 
305
- console.log("Updated discount:", draftResult.DiscountAmount);
306
- ```
53
+ ```tsx
54
+ // ❌ WRONG — FileType/ImageFieldType are objects, not ReactNode
55
+ <span>{item.product_image.get()}</span> // ImageFieldType = FileType | null
56
+ <span>{item.attachments.get()}</span> // FileFieldType = FileType[]
57
+ <span>{item.category.get()}</span> // Reference = { _id, _name, ... }
307
58
 
308
- ---
59
+ // ✅ CORRECT — extract the renderable value
60
+ // Image: use ImageThumbnail component (NEVER use src="#")
61
+ import { ImageThumbnail } from "@/components/ui/image-thumbnail";
62
+ <ImageThumbnail boId={bdo.meta._id} instanceId={item._id} fieldId={bdo.product_image.id} value={item.product_image.get()} imgClassName="w-24 h-24 object-cover rounded" />
309
63
 
310
- ### draftInteraction
64
+ // Image: if you only need the filename as text
65
+ const img = item.product_image.get();
66
+ <span>{img ? (img as any).FileName : "No image"}</span>
311
67
 
312
- Creates/updates a draft without requiring an instance ID. Returns computed fields along with a temporary `_id`.
68
+ // File array: use FilePreview component
69
+ import { FilePreview } from "@/components/ui/file-preview";
70
+ <FilePreview boId={bdo.meta._id} instanceId={item._id} fieldId={bdo.attachments.id} value={item.attachments.get()} />
313
71
 
314
- ```typescript
315
- async draftInteraction(data: Partial<TEditable>): Promise<DraftResponseType & { _id: string }>
316
- ```
72
+ // Reference: access _name
73
+ const ref = item.category.get();
74
+ <span>{ref ? (ref as any)._name : "—"}</span>
317
75
 
318
- ```typescript
319
- const result = await product.draftInteraction({
320
- Price: 100,
321
- DiscountPercent: 10,
322
- });
76
+ // Number: format
77
+ <span>{(item.unit_price.get() ?? 0).toFixed(2)}</span>
323
78
 
324
- console.log(result._id); // Temporary draft ID
325
- console.log(result.DiscountAmount); // Computed value
79
+ // Boolean: display
80
+ <span>{item.is_active.get() ? "Yes" : "No"}</span>
326
81
  ```
327
82
 
328
- ---
83
+ ### 4. Calling protected methods
329
84
 
330
- ### createItem
331
-
332
- Creates an `ItemType` wrapper synchronously (no API call). Useful for creating an empty item for form binding.
333
-
334
- ```typescript
335
- createItem(data?: Partial<TEditable>): ItemType<TEditable, TReadonly>
336
- ```
85
+ Only methods the role has permission for are `public`. Check the BDO file.
337
86
 
338
87
  ```typescript
339
- const emptyItem = product.createItem();
340
- emptyItem.Title.set("Draft Product");
341
- emptyItem.Price.set(0);
88
+ // WRONG — if delete is not in the role's BDO class
89
+ await bdo.delete(id); // TS2445: Property 'delete' is protected
342
90
 
343
- const validation = emptyItem.validate();
344
- console.log(validation.valid); // false (required fields missing)
91
+ // CORRECT — use api() for methods not on the BDO class
92
+ import { api } from "@ram_28/kf-ai-sdk/api";
93
+ await api(bdo.meta._id).delete(id);
345
94
  ```
346
95
 
347
- ---
348
-
349
- ### metric
350
-
351
- Performs aggregation queries on records.
96
+ ### 5. Not handling `.get()` return type (undefined)
352
97
 
353
98
  ```typescript
354
- async metric(options: Omit<MetricOptionsType, "Type">): Promise<MetricResponseType>
355
- ```
356
-
357
- The `Type` field is added internally. Pass only `GroupBy`, `Metric`, and optional `Filter`.
99
+ // WRONG might be undefined
100
+ const title: string = item.product_name.get();
358
101
 
359
- ```typescript
360
- // Total count
361
- const response = await product.metric({
362
- GroupBy: [],
363
- Metric: [{ Field: "_id", Type: "Count" }],
364
- });
365
- console.log("Total:", response.Data[0]["count__id"]);
366
-
367
- // Group by category with multiple metrics
368
- const byCategory = await product.metric({
369
- GroupBy: ["Category"],
370
- Metric: [
371
- { Field: "Stock", Type: "Sum" },
372
- { Field: "Price", Type: "Avg" },
373
- ],
374
- });
375
- byCategory.Data.forEach((row) => {
376
- console.log(`${row.Category}: ${row["sum_Stock"]} stock, $${row["avg_Price"]} avg`);
377
- });
102
+ // ✅ CORRECT — provide fallback
103
+ const title = item.product_name.get() ?? "";
104
+ const price = item.unit_price.get() ?? 0;
378
105
  ```
379
106
 
380
107
  ---
381
108
 
382
- ### pivot
383
-
384
- Creates pivot table aggregations with row and column dimensions.
109
+ ## BDO Meta & Fields
385
110
 
386
111
  ```typescript
387
- async pivot(options: Omit<PivotOptionsType, "Type">): Promise<PivotResponseType>
388
- ```
389
-
390
- The `Type` field is added internally. Pass only `Row`, `Column`, `Metric`, and optional `Filter`.
112
+ const product = new AdminProduct();
113
+ product.meta._id; // "BDO_Product" — use as source in useTable, api()
114
+ product.meta.name; // "Product"
391
115
 
392
- ```typescript
393
- const response = await product.pivot({
394
- Row: ["Category"],
395
- Column: ["Region"],
396
- Metric: [{ Field: "Stock", Type: "Sum" }],
397
- });
398
-
399
- const { RowHeader, ColumnHeader, Value } = response.Data;
400
-
401
- RowHeader.forEach((row, ri) => {
402
- ColumnHeader.forEach((col, ci) => {
403
- console.log(`${row.Key} - ${col.Key}: ${Value[ri][ci]}`);
404
- });
405
- });
116
+ // Field getters (all field types)
117
+ product.product_name.id // "product_name" use in register(), watch(), setValue()
118
+ product.product_name.label // "product_name" — display label
119
+ product.product_name.required // boolean
120
+ product.product_name.readOnly // boolean
121
+ product.product_name.defaultValue // unknown
406
122
  ```
407
123
 
408
- ---
409
-
410
124
  ## Field Classes
411
125
 
412
- All field classes extend `BaseField<T>` and accept raw backend meta JSON as the constructor parameter.
126
+ | Class | Extra Getters | Notes |
127
+ |-------|--------------|-------|
128
+ | `StringField` | `length` | May have `Constraint.Enum` — see useForm Mistake #3 |
129
+ | `NumberField` | `integerPart`, `fractionPart` | |
130
+ | `BooleanField` | — | |
131
+ | `DateField` | — | Format: YYYY-MM-DD |
132
+ | `DateTimeField` | `precision` | Format: YYYY-MM-DDTHH:MM:SS |
133
+ | `SelectField` | **`options`**, `fetchOptions(instanceId)` | `.options` returns `{ value, label }[]` |
134
+ | `ReferenceField` | `fetchOptions(instanceId)`, `referenceBdo` | Value is object `{ _id, _name, ... }` |
135
+ | `TextField` | `format` | Long text |
136
+ | `UserField` | `fetchOptions(instanceId)` | Value: `{ _id, _name }` |
137
+ | `ImageField` | — | Value: `FileType \| null` |
138
+ | `FileField` | — | Value: `FileType[]` |
413
139
 
414
- | Class | Backend Type | Extra Getters |
415
- |-------|-------------|---------------|
416
- | `StringField` | `"String"` | `length` |
417
- | `NumberField` | `"Number"` | `integerPart`, `fractionPart` |
418
- | `BooleanField` | `"Boolean"` | — |
419
- | `DateField` | `"Date"` | — |
420
- | `DateTimeField` | `"DateTime"` | `precision` |
421
- | `SelectField<T>` | `"String"` | `options`, `fetchOptions()` |
422
- | `ReferenceField<T>` | `"Reference"` | `referenceBdo`, `referenceFields`, `searchFields`, `fetchOptions()` |
423
- | `TextField` | `"Text"` | `format` |
424
- | `UserField` | `"User"` | `businessEntity` |
425
- | `FileField` | `"File"` | — |
426
- | `ArrayField<T>` | `"Array"` | `elementType` |
427
- | `ObjectField<T>` | `"Object"` | `properties` |
428
-
429
- ### SelectField
430
-
431
- Options from `Constraint.Enum` are available synchronously via the `options` getter:
432
-
433
- ```typescript
434
- product.Status.options;
435
- // [{ value: "active", label: "active" }, { value: "inactive", label: "inactive" }]
436
- ```
437
-
438
- Dynamic options can be fetched from the backend via `fetchOptions()`:
439
-
440
- ```typescript
441
- async fetchOptions(instanceId?: string): Promise<SelectOptionType<T>[]>
442
- ```
443
-
444
- ```typescript
445
- const statusOptions = await product.Status.fetchOptions();
446
- // [{ value: "active", label: "Active" }, { value: "inactive", label: "Inactive" }]
447
- ```
448
-
449
- ### ReferenceField
450
-
451
- Reference configuration is derived from `View.DataObject` in the raw meta:
452
-
453
- ```typescript
454
- product.SupplierInfo.referenceBdo; // "BDO_Supplier"
455
- product.SupplierInfo.referenceFields; // ["_id", "SupplierName", "Email"]
456
- product.SupplierInfo.searchFields; // ["SupplierName"]
457
- ```
458
-
459
- Referenced records can be fetched via `fetchOptions()`:
460
-
461
- ```typescript
462
- async fetchOptions(instanceId?: string): Promise<TRef[]>
463
- ```
464
-
465
- ```typescript
466
- const suppliers = await product.SupplierInfo.fetchOptions();
467
- // [{ _id: "sup_1", SupplierName: "Acme Corp", Email: "contact@acme.com" }, ...]
468
- ```
140
+ **CRITICAL**: Only `SelectField` has `.options` getter. `StringField` with `Constraint.Enum` does NOT.
469
141
 
470
142
  ---
471
143
 
472
- ## ItemType — Runtime Accessor Pattern
473
-
474
- `ItemType<TEditable, TReadonly>` is what `get()`, `list()`, `create()`, and `createItem()` return. It wraps API response data with a Proxy, providing field accessors for every field.
475
-
476
- ### Field Accessor Access
477
-
478
- ```typescript
479
- const item = await product.get("prod_abc123");
480
-
481
- // Get value (NOT direct access — always use .get())
482
- item.Title.get(); // "Wireless Headphones"
483
-
484
- // Field metadata
485
- item.Title.label; // "Product Title"
486
- item.Title.required; // true
487
- item.Title.readOnly; // false
488
- item.Title.defaultValue; // undefined
489
- item.Title.meta; // full raw backend meta (BaseFieldMetaType)
490
-
491
- // Set value (only on editable fields)
492
- item.Title.set("New Title");
493
-
494
- // Validate (type + expression rules)
495
- const result = item.Title.validate();
496
- // { valid: true, errors: [] }
497
- ```
498
-
499
- ### Direct `_id` Access
144
+ ## Methods
500
145
 
501
- `_id` is the only field accessed directly (not as an accessor):
146
+ All methods are `protected` on `BaseBdo`. Role BDOs selectively expose them as `public`.
502
147
 
503
148
  ```typescript
504
- item._id; // "prod_abc123"
505
- ```
149
+ // Read
150
+ const item = await bdo.get(id); // ItemType — call .get() on fields
151
+ const items = await bdo.list(options?); // ItemType[] — call .get() on fields
152
+ const count = await bdo.count(options?); // number
506
153
 
507
- ### Editable vs Readonly Accessors
154
+ // Write
155
+ const created = await bdo.create(data); // ItemType
156
+ const result = await bdo.update(id, data); // { _id: string }
157
+ const deleted = await bdo.delete(id); // { status: "success" }
508
158
 
509
- Fields in `TEditable` get a `set()` method. Fields in `TReadonly` do not.
159
+ // Draft
160
+ const draft = await bdo.draft(data); // { [field]: computed_value }
161
+ const draftPatch = await bdo.draftPatch(id, data);
162
+ const draftInt = await bdo.draftInteraction(data); // { _id, ...computed }
510
163
 
511
- **Editable field accessor:**
164
+ // Analytics
165
+ const metric = await bdo.metric({ GroupBy: [], Metric: [{ Field: "_id", Type: "Count" }] });
166
+ // Access: metric.Data[0]["count__id"] (key pattern: {type}_{Field})
512
167
 
513
- ```typescript
514
- {
515
- label: string,
516
- required: boolean,
517
- readOnly: false, // always false for editable
518
- defaultValue: unknown,
519
- meta: BaseFieldMetaType,
520
- get(): T | undefined,
521
- set(value: T): void, // only on editable fields
522
- validate(): ValidationResultType,
523
- }
168
+ const pivot = await bdo.pivot({ Row: ["category"], Column: ["status"], Metric: [{ Field: "_id", Type: "Count" }] });
169
+ // Access: pivot.Data.RowHeader, pivot.Data.ColumnHeader, pivot.Data.Value[row][col]
524
170
  ```
525
171
 
526
- **Readonly field accessor:**
172
+ ### ListOptionsType
527
173
 
528
174
  ```typescript
529
- {
530
- label: string,
531
- required: boolean,
532
- readOnly: true, // always true for readonly
533
- defaultValue: unknown,
534
- meta: BaseFieldMetaType,
535
- get(): T | undefined,
536
- validate(): ValidationResultType,
537
- // no set()
175
+ interface ListOptionsType {
176
+ Field?: string[]; // Specific fields (omit for all)
177
+ Filter?: FilterType; // See useFilter docs
178
+ Sort?: Record<string, "ASC" | "DESC">[]; // [{ "field": "ASC" }]
179
+ Page?: number; // 1-indexed
180
+ PageSize?: number; // Default: 10
538
181
  }
539
182
  ```
540
183
 
541
- ### Item Methods
542
-
543
- ```typescript
544
- // Validate all non-readonly fields
545
- item.validate();
546
- // { valid: boolean, errors: string[] }
547
-
548
- // Convert to plain object
549
- item.toJSON();
550
- // Partial<T> — raw data without accessors
551
- ```
552
-
553
184
  ---
554
185
 
555
- ## Type Definitions
186
+ ## ItemType Accessor Pattern
556
187
 
557
- ### BdoMetaType
188
+ `get()`, `list()`, `create()` return `ItemType` with Proxy-wrapped field accessors.
558
189
 
559
190
  ```typescript
560
- interface BdoMetaType {
561
- readonly _id: string; // Business object ID (e.g., "BDO_Product")
562
- readonly name: string; // Display name (e.g., "Product")
563
- }
564
- ```
191
+ const item = await bdo.get("id123");
565
192
 
566
- ### ValidationResultType
567
-
568
- ```typescript
569
- interface ValidationResultType {
570
- valid: boolean;
571
- errors: string[];
572
- }
193
+ item._id; // "id123" — direct access (only _id)
194
+ item.product_name.get(); // string | undefined
195
+ item.product_name.set("New"); // editable fields only
196
+ item.product_name.label; // "product_name"
197
+ item.product_name.required; // boolean
198
+ item.toJSON(); // plain object
573
199
  ```
574
200
 
575
- ### SelectOptionType
576
-
577
- ```typescript
578
- interface SelectOptionType<T = string> {
579
- value: T;
580
- label: string;
581
- disabled?: boolean;
582
- }
583
- ```
584
-
585
- ### SystemFieldsType
201
+ ## SystemFieldsType
586
202
 
587
203
  ```typescript
588
204
  type SystemFieldsType = {
589
205
  _id: string;
590
- _created_at: DateTimeFieldType;
591
- _modified_at: DateTimeFieldType;
592
- _created_by: UserFieldType; // { _id: string; _name: string }
593
- _modified_by: UserFieldType;
206
+ _created_at: string; // "YYYY-MM-DDTHH:MM:SS"
207
+ _modified_at: string;
208
+ _created_by: { _id: string; _name: string };
209
+ _modified_by: { _id: string; _name: string };
594
210
  _version: string;
595
211
  _m_version: string;
596
212
  };
597
-
598
- // Union of system field names (for Omit<> operations)
599
- type SystemFields = keyof SystemFieldsType;
600
- ```
601
-
602
- ### BaseFieldMetaType
603
-
604
- The raw backend meta shape stored by every field:
605
-
606
- ```typescript
607
- interface BaseFieldMetaType {
608
- _id: string; // Field identifier
609
- Name: string; // Display name
610
- Type: string; // Field type ("String", "Number", "Reference", etc.)
611
- ReadOnly?: boolean;
612
- Required?: boolean;
613
- Constraint?: BaseConstraintType;
614
- DefaultValue?: unknown;
615
- }
616
- ```
617
-
618
- ### BaseConstraintType
619
-
620
- ```typescript
621
- interface BaseConstraintType {
622
- Required?: boolean;
623
- PrimaryKey?: boolean;
624
- DefaultValue?: unknown;
625
- }
626
- ```
627
-
628
- ### EditableFieldAccessorType
629
-
630
- ```typescript
631
- interface EditableFieldAccessorType<T> {
632
- readonly label: string;
633
- readonly required: boolean;
634
- readonly readOnly: boolean;
635
- readonly defaultValue: unknown;
636
- readonly meta: BaseFieldMetaType;
637
- get(): T | undefined;
638
- set(value: T): void;
639
- validate(): ValidationResultType;
640
- }
641
- ```
642
-
643
- ### ReadonlyFieldAccessorType
644
-
645
- ```typescript
646
- type ReadonlyFieldAccessorType<T> = {
647
- readonly label: string;
648
- readonly required: boolean;
649
- readonly readOnly: boolean;
650
- readonly defaultValue: unknown;
651
- readonly meta: BaseFieldMetaType;
652
- get(): T | undefined;
653
- validate(): ValidationResultType;
654
- };
655
- ```
656
-
657
- ---
658
-
659
- ## Complete Example
660
-
661
- ```typescript
662
- import { AdminProduct } from "../bdo/admin/Product";
663
- import type {
664
- AdminProductEditableFieldType,
665
- AdminProductFieldType,
666
- } from "../bdo/admin/Product";
667
-
668
- const product = new AdminProduct();
669
-
670
- // ---- Field metadata ----
671
- console.log(product.meta._id); // "BDO_Product"
672
- console.log(product.Title.label); // "Product Title"
673
- console.log(product.Title.required); // true
674
-
675
- // ---- Get a single item ----
676
- const item = await product.get("prod_abc123");
677
- console.log(item._id); // "prod_abc123"
678
- console.log(item.Title.get()); // "Wireless Headphones"
679
- console.log(item.Price.get()); // 99.99
680
-
681
- // ---- List items with filter ----
682
- const items = await product.list({
683
- Filter: {
684
- Operator: "And",
685
- Condition: [{
686
- LHSField: product.Category.id,
687
- Operator: "EQ",
688
- RHSValue: "Electronics",
689
- RHSType: "Constant",
690
- }],
691
- },
692
- Sort: [{ [product.Price.id]: "ASC" }],
693
- Page: 1,
694
- PageSize: 20,
695
- });
696
-
697
- for (const item of items) {
698
- console.log(item.Title.get(), item.Price.get());
699
- }
700
-
701
- // ---- Count ----
702
- const total = await product.count();
703
- console.log(`Total products: ${total}`);
704
-
705
- // ---- Create ----
706
- const newItem = await product.create({
707
- Title: "New Product",
708
- Price: 49.99,
709
- Category: "Books",
710
- });
711
- console.log(newItem._id); // New record ID
712
- console.log(newItem.Title.get()); // "New Product"
713
-
714
- // ---- Update ----
715
- const updateResult = await product.update("prod_abc123", {
716
- Price: 79.99,
717
- });
718
- console.log(updateResult._id); // "prod_abc123"
719
-
720
- // ---- Delete ----
721
- const deleteResult = await product.delete("prod_abc123");
722
- console.log(deleteResult.status); // "success"
723
-
724
- // ---- Validate ----
725
- const emptyItem = product.createItem();
726
- emptyItem.Title.set("");
727
- const validation = emptyItem.validate();
728
- console.log(validation.valid); // false
729
- console.log(validation.errors); // ["Product Title is required"]
730
-
731
- // ---- SelectField options ----
732
- const statusOptions = await product.Status.fetchOptions();
733
- statusOptions.forEach((opt) => {
734
- console.log(`${opt.value}: ${opt.label}`);
735
- });
736
-
737
- // ---- ReferenceField options ----
738
- const suppliers = await product.SupplierInfo.fetchOptions();
739
- suppliers.forEach((sup) => {
740
- console.log(`${sup._id}: ${sup.SupplierName}`);
741
- });
742
- ```
743
-
744
- ---
745
-
746
- ## Common Mistakes
747
-
748
- ### 1. Guessing field names instead of reading BDO files
749
-
750
- ALWAYS read the BDO `.ts` files (`src/bdo/{role}/*.ts`) BEFORE writing page code. Use the exact field names from the class — NEVER guess or invent field names.
751
-
752
- ```typescript
753
- // ❌ WRONG — guessing field names that don't exist
754
- bdo.used // TS2339: Property 'used' does not exist
755
- bdo.remaining // TS2339: Property 'remaining' does not exist
756
- bdo.meta_title // TS2339: actual field might be 'seo_title'
757
- bdo.tags // TS2339: no such field exists
758
-
759
- // ✅ CORRECT — read the BDO file first, use exact names
760
- // Check src/bdo/employee/LeaveBalance.ts for actual fields
761
- bdo.UsedLeaves // matches the actual field in the BDO class
762
- bdo.RemainingDays // matches the actual field in the BDO class
763
213
  ```
764
214
 
765
- ### 2. Inventing BDO classes that don't exist
766
-
767
- Check `src/bdo/{role}/index.ts` to see which BDO classes actually exist before importing.
215
+ ## API Types
768
216
 
769
217
  ```typescript
770
- // WRONG ShippingAddress BDO doesn't exist
771
- import { customerShippingAddress } from "@/bdo/customer/ShippingAddress";
772
-
773
- // CORRECT check index.ts first. shipping_address might just be a StringField on Order
774
- import { customerOrder } from "@/bdo/customer/Order";
775
- ```
776
-
777
- ### 3. Calling protected methods
778
-
779
- All `BaseBdo` methods are `protected` by default. Only the methods the role has permission for are re-exposed as `public` in the generated class. Read the BDO file to see which methods are available.
780
-
781
- ```typescript
782
- // ❌ WRONG — if delete is not in the role's BDO class
783
- await bdo.delete(id); // TS2445: Property 'delete' is protected
784
-
785
- // ✅ CORRECT — use api() for methods not exposed on the BDO class
786
- import { api } from "@ram_28/kf-ai-sdk/api";
787
- await api(bdo.meta._id).delete(id);
788
-
789
- // ✅ CORRECT — for create/update, prefer useForm
790
- const { handleSubmit } = useForm({ bdo });
791
- ```
792
-
793
- ### 4. Not handling `.get()` return type
794
-
795
- `ItemType` field `.get()` returns `T | undefined`. Always handle the undefined case.
796
-
797
- ```typescript
798
- // ❌ WRONG — might be undefined
799
- const title: string = item.Title.get();
800
-
801
- // ✅ CORRECT — provide fallback
802
- const title = item.Title.get() ?? "";
803
- const price = item.Price.get() ?? 0;
804
- const active = item.IsActive.get() ?? false;
218
+ interface CreateUpdateResponseType { _id: string; }
219
+ interface DeleteResponseType { status: "success"; }
220
+ interface FileType { _id: string; _name: string; FileName: string; FileExtension: string; Size: number; ContentType: string; }
221
+ type ImageFieldType = FileType | null;
222
+ type FileFieldType = FileType[];
223
+ type AggregationType = "Sum" | "Avg" | "Count" | "Max" | "Min" | "DistinctCount" | "BlankCount" | "NotBlankCount" | "Concat" | "DistinctConcat";
805
224
  ```