@ram_28/kf-ai-sdk 2.0.16 → 2.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -8
- package/dist/{FileField-BWrSHNRq.js → FileField-CZjS2uLh.js} +3 -3
- package/dist/{FileField-eDeuzln8.cjs → FileField-DU4UWo_t.cjs} +1 -1
- package/dist/api.cjs +1 -1
- package/dist/api.mjs +1 -1
- package/dist/auth/authConfig.d.ts +1 -1
- package/dist/auth/types.d.ts +1 -1
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/auth.cjs +1 -1
- package/dist/auth.mjs +1 -1
- package/dist/bdo/core/Item.d.ts +0 -4
- package/dist/bdo/core/Item.d.ts.map +1 -1
- package/dist/bdo/fields/ReferenceField.d.ts +1 -1
- package/dist/bdo/fields/ReferenceField.d.ts.map +1 -1
- package/dist/bdo/fields/SelectField.d.ts +1 -1
- package/dist/bdo/fields/SelectField.d.ts.map +1 -1
- package/dist/bdo/fields/UserField.d.ts +1 -1
- package/dist/bdo/fields/UserField.d.ts.map +1 -1
- package/dist/bdo.cjs +1 -1
- package/dist/bdo.mjs +53 -62
- package/dist/components/hooks/useActivityForm/types.d.ts +4 -5
- package/dist/components/hooks/useActivityForm/types.d.ts.map +1 -1
- package/dist/components/hooks/useActivityForm/useActivityForm.d.ts.map +1 -1
- package/dist/components/hooks/useActivityTable/types.d.ts +5 -4
- package/dist/components/hooks/useActivityTable/types.d.ts.map +1 -1
- package/dist/components/hooks/useActivityTable/useActivityTable.d.ts.map +1 -1
- package/dist/components/hooks/useBDOForm/createItemProxy.d.ts +2 -3
- package/dist/components/hooks/useBDOForm/createItemProxy.d.ts.map +1 -1
- package/dist/components/hooks/useBDOTable/types.d.ts +20 -12
- package/dist/components/hooks/useBDOTable/types.d.ts.map +1 -1
- package/dist/components/hooks/useBDOTable/useBDOTable.d.ts +2 -2
- package/dist/components/hooks/useBDOTable/useBDOTable.d.ts.map +1 -1
- package/dist/{constants-ConHc1oS.js → constants-Cyi942Yr.js} +5 -5
- package/dist/constants-DEmYwKfC.cjs +1 -0
- package/dist/filter.cjs +1 -1
- package/dist/filter.mjs +1 -1
- package/dist/form.cjs +1 -1
- package/dist/form.mjs +226 -243
- package/dist/table.cjs +1 -1
- package/dist/table.mjs +15 -16
- package/dist/table.types.d.ts +1 -1
- package/dist/table.types.d.ts.map +1 -1
- package/dist/types/constants.d.ts +1 -1
- package/dist/workflow/Activity.d.ts +8 -5
- package/dist/workflow/Activity.d.ts.map +1 -1
- package/dist/workflow.cjs +1 -1
- package/dist/workflow.mjs +461 -476
- package/docs/README.md +57 -0
- package/docs/bdo/README.md +161 -0
- package/docs/bdo/api_reference.md +281 -0
- package/docs/examples/bdo/create-product.md +69 -0
- package/docs/examples/bdo/edit-product-dialog.md +95 -0
- package/docs/examples/bdo/filtered-product-table.md +100 -0
- package/docs/examples/bdo/product-listing.md +73 -0
- package/docs/examples/bdo/supplier-dropdown.md +60 -0
- package/docs/examples/fields/complex-fields.md +248 -0
- package/docs/examples/fields/primitive-fields.md +217 -0
- package/docs/examples/workflow/approve-leave-request.md +76 -0
- package/docs/examples/workflow/filtered-activity-table.md +101 -0
- package/docs/examples/workflow/my-pending-requests.md +90 -0
- package/docs/examples/workflow/start-new-workflow.md +47 -0
- package/docs/examples/workflow/submit-leave-request.md +72 -0
- package/docs/examples/workflow/workflow-progress.md +49 -0
- package/docs/fields/README.md +141 -0
- package/docs/fields/api_reference.md +134 -0
- package/docs/useActivityForm/README.md +244 -0
- package/docs/useActivityForm/api_reference.md +279 -0
- package/docs/useActivityTable/README.md +263 -0
- package/docs/useActivityTable/api_reference.md +294 -0
- package/docs/useBDOForm/README.md +175 -0
- package/docs/useBDOForm/api_reference.md +244 -0
- package/docs/useBDOTable/README.md +242 -0
- package/docs/useBDOTable/api_reference.md +253 -0
- package/docs/useFilter/README.md +323 -0
- package/docs/useFilter/api_reference.md +228 -0
- package/docs/workflow/README.md +158 -0
- package/docs/workflow/api_reference.md +161 -0
- package/package.json +1 -1
- package/sdk/auth/authConfig.ts +1 -1
- package/sdk/auth/types.ts +1 -1
- package/sdk/bdo/core/Item.ts +1 -10
- package/sdk/bdo/fields/ReferenceField.ts +1 -1
- package/sdk/bdo/fields/SelectField.ts +1 -1
- package/sdk/bdo/fields/UserField.ts +1 -1
- package/sdk/components/hooks/useActivityForm/types.ts +4 -6
- package/sdk/components/hooks/useActivityForm/useActivityForm.ts +10 -73
- package/sdk/components/hooks/useActivityTable/types.ts +4 -5
- package/sdk/components/hooks/useActivityTable/useActivityTable.ts +10 -8
- package/sdk/components/hooks/useBDOForm/createItemProxy.ts +17 -58
- package/sdk/components/hooks/useBDOTable/types.ts +20 -10
- package/sdk/components/hooks/useBDOTable/useBDOTable.ts +12 -8
- package/sdk/table.types.ts +2 -0
- package/sdk/types/constants.ts +1 -1
- package/sdk/workflow/Activity.ts +39 -7
- package/dist/constants-QX2RX-wu.cjs +0 -1
- package/docs/api.md +0 -95
- package/docs/bdo.md +0 -224
- package/docs/gaps.md +0 -360
- package/docs/useActivityForm.md +0 -393
- package/docs/useActivityTable.md +0 -418
- package/docs/useBDOForm.md +0 -376
- package/docs/useBDOTable.md +0 -284
- package/docs/useFilter.md +0 -188
- package/docs/workflow.md +0 -560
- /package/docs/{useAuth.md → useAuth/README.md} +0 -0
package/docs/useBDOForm.md
DELETED
|
@@ -1,376 +0,0 @@
|
|
|
1
|
-
# useBDOForm
|
|
2
|
-
|
|
3
|
-
React hook for BDO forms with validation, API integration, and typed field handling.
|
|
4
|
-
|
|
5
|
-
## Imports
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
import { useBDOForm } from "@ram_28/kf-ai-sdk/form";
|
|
9
|
-
import { ValidationMode, FormOperation } from "@ram_28/kf-ai-sdk/form";
|
|
10
|
-
import type { UseBDOFormOptionsType, UseBDOFormReturnType, FormItemType, FormRegisterType, HandleSubmitType } from "@ram_28/kf-ai-sdk/form/types";
|
|
11
|
-
import type { CreateUpdateResponseType } from "@ram_28/kf-ai-sdk/api/types";
|
|
12
|
-
|
|
13
|
-
// Pre-built components for special field types
|
|
14
|
-
import { ReferenceSelect } from "@/components/ui/reference-select";
|
|
15
|
-
import { ImageUpload } from "@/components/ui/image-upload";
|
|
16
|
-
import { FileUpload } from "@/components/ui/file-upload";
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Common Mistakes (READ FIRST)
|
|
22
|
-
|
|
23
|
-
### 1. Missing `operation` in useBDOForm options (TS2345)
|
|
24
|
-
|
|
25
|
-
ALWAYS include `operation`. Without it, TypeScript cannot resolve the union type.
|
|
26
|
-
|
|
27
|
-
```typescript
|
|
28
|
-
// ❌ WRONG — missing operation (TS2345)
|
|
29
|
-
useBDOForm({ bdo: product, mode: ValidationMode.OnBlur });
|
|
30
|
-
|
|
31
|
-
// ✅ CORRECT — always include operation
|
|
32
|
-
useBDOForm({ bdo: product, operation: FormOperation.Create, mode: ValidationMode.OnBlur });
|
|
33
|
-
useBDOForm({ bdo: product, operation: FormOperation.Update, recordId: id, mode: ValidationMode.OnBlur });
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
### 2. Annotating ternary with UseBDOFormOptionsType (TS2322)
|
|
37
|
-
|
|
38
|
-
`UseBDOFormOptionsType` is a discriminated union. Type-annotating a variable prevents TS narrowing. NEVER annotate — call useBDOForm inline in each branch.
|
|
39
|
-
|
|
40
|
-
```typescript
|
|
41
|
-
// ❌ WRONG — type annotation prevents union narrowing (TS2322)
|
|
42
|
-
const options: UseBDOFormOptionsType<typeof bdo> = id
|
|
43
|
-
? { bdo, operation: FormOperation.Update, recordId: id, mode: ValidationMode.OnBlur }
|
|
44
|
-
: { bdo, operation: FormOperation.Create, mode: ValidationMode.OnBlur };
|
|
45
|
-
const formResult = useBDOForm(options);
|
|
46
|
-
|
|
47
|
-
// ✅ CORRECT — call useBDOForm inline, no type annotation
|
|
48
|
-
const formResult = id
|
|
49
|
-
? useBDOForm({ bdo, operation: FormOperation.Update, recordId: id, mode: ValidationMode.OnBlur })
|
|
50
|
-
: useBDOForm({ bdo, operation: FormOperation.Create, mode: ValidationMode.OnBlur });
|
|
51
|
-
const { register, handleSubmit, watch, setValue, item, formState: { errors, isSubmitting }, isLoading } = formResult;
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### 3. Using `.options` on StringField (TS2339)
|
|
55
|
-
|
|
56
|
-
ONLY `SelectField` has `.options` getter. `StringField` with `Constraint.Enum` does NOT — use hardcoded `<option>` values from the BDO file.
|
|
57
|
-
|
|
58
|
-
```tsx
|
|
59
|
-
// Check the BDO class to determine field type:
|
|
60
|
-
// new SelectField({...}) → has .options → use bdo.field.options.map()
|
|
61
|
-
// new StringField({..."Constraint": {"Enum": [...]}}) → NO .options → hardcode from Enum array
|
|
62
|
-
|
|
63
|
-
// ❌ WRONG — StringField has no .options (TS2339)
|
|
64
|
-
// Given: readonly status = new StringField({... "Constraint": { "Enum": ["Active", "Discontinued"] }})
|
|
65
|
-
<select {...register(bdo.status.id)}>
|
|
66
|
-
{bdo.status.options.map((opt) => ( // TS2339: Property 'options' does not exist on StringField
|
|
67
|
-
<option key={opt.value} value={opt.value}>{opt.label}</option>
|
|
68
|
-
))}
|
|
69
|
-
</select>
|
|
70
|
-
|
|
71
|
-
// ✅ CORRECT for StringField with Enum — hardcode options from BDO Constraint.Enum array
|
|
72
|
-
<select {...register(bdo.status.id)}>
|
|
73
|
-
<option value="">Select {bdo.status.label}</option>
|
|
74
|
-
<option value="Active">Active</option>
|
|
75
|
-
<option value="Discontinued">Discontinued</option>
|
|
76
|
-
</select>
|
|
77
|
-
|
|
78
|
-
// ✅ CORRECT for SelectField — use .options getter
|
|
79
|
-
// Given: readonly status = new SelectField({...})
|
|
80
|
-
<select {...register(bdo.status.id)}>
|
|
81
|
-
<option value="">Select {bdo.status.label}</option>
|
|
82
|
-
{bdo.status.options.map((opt) => (
|
|
83
|
-
<option key={opt.value} value={opt.value}>{opt.label}</option>
|
|
84
|
-
))}
|
|
85
|
-
</select>
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
### 4. Using register() for BooleanField
|
|
89
|
-
|
|
90
|
-
HTML checkboxes don't work with register(). Use watch/setValue.
|
|
91
|
-
|
|
92
|
-
```tsx
|
|
93
|
-
// ❌ WRONG
|
|
94
|
-
<input type="checkbox" {...register(bdo.is_active.id)} />
|
|
95
|
-
|
|
96
|
-
// ✅ CORRECT
|
|
97
|
-
<Checkbox
|
|
98
|
-
checked={Boolean(watch(bdo.is_active.id))}
|
|
99
|
-
onCheckedChange={(v) => setValue(bdo.is_active.id, v as boolean, { shouldDirty: true })}
|
|
100
|
-
/>
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### 5. Using `<input>` for ReferenceField (should use `<ReferenceSelect>`)
|
|
104
|
-
|
|
105
|
-
ReferenceField stores an object `{ _id, _name, ... }`, not a string. Use the pre-built component.
|
|
106
|
-
|
|
107
|
-
```tsx
|
|
108
|
-
// ❌ WRONG — text input for reference field
|
|
109
|
-
<input {...register(bdo.category.id)} />
|
|
110
|
-
|
|
111
|
-
// ✅ CORRECT
|
|
112
|
-
<ReferenceSelect
|
|
113
|
-
bdoField={bdo.category}
|
|
114
|
-
instanceId={id || String(watch("_id") ?? "")}
|
|
115
|
-
value={watch(bdo.category.id)}
|
|
116
|
-
onChange={(val) => setValue(bdo.category.id, val, { shouldDirty: true })}
|
|
117
|
-
/>
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### 6. Using custom Image/File upload (should use template components)
|
|
121
|
-
|
|
122
|
-
Use `<ImageUpload>` and `<FileUpload>`. CRITICAL: `instanceId` must work for BOTH edit and create mode.
|
|
123
|
-
|
|
124
|
-
```tsx
|
|
125
|
-
// ❌ WRONG — instanceId={id} is undefined in create mode
|
|
126
|
-
<ImageUpload field={item.icon} value={watch(bdo.icon.id)} boId={bdo.meta._id} instanceId={id} fieldId={bdo.icon.id} />
|
|
127
|
-
|
|
128
|
-
// ✅ CORRECT — ImageUpload (single image)
|
|
129
|
-
<ImageUpload
|
|
130
|
-
field={item.product_image}
|
|
131
|
-
value={watch(bdo.product_image.id)}
|
|
132
|
-
boId={bdo.meta._id}
|
|
133
|
-
instanceId={id || String(watch("_id") ?? "")}
|
|
134
|
-
fieldId={bdo.product_image.id}
|
|
135
|
-
/>
|
|
136
|
-
|
|
137
|
-
// ✅ CORRECT — FileUpload (multi-file)
|
|
138
|
-
<FileUpload
|
|
139
|
-
field={item.specification_document}
|
|
140
|
-
value={watch(bdo.specification_document.id)}
|
|
141
|
-
boId={bdo.meta._id}
|
|
142
|
-
instanceId={id || String(watch("_id") ?? "")}
|
|
143
|
-
fieldId={bdo.specification_document.id}
|
|
144
|
-
/>
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
Props: `field` = item accessor (`item.fieldName`), `value` = `watch(bdo.field.id)`, `boId` = `bdo.meta._id`, `instanceId` = `id || String(watch("_id") ?? "")`, `fieldId` = `bdo.field.id`. Parent component MUST have `"use no memo"` directive.
|
|
148
|
-
|
|
149
|
-
### 7. Wrong handleSubmit onSuccess type
|
|
150
|
-
|
|
151
|
-
```typescript
|
|
152
|
-
// ❌ WRONG
|
|
153
|
-
const onSuccess = (data: unknown) => { ... };
|
|
154
|
-
|
|
155
|
-
// ✅ CORRECT — CreateUpdateResponseType = { _id: string }
|
|
156
|
-
import type { CreateUpdateResponseType } from "@ram_28/kf-ai-sdk/api/types";
|
|
157
|
-
const onSuccess = (data: CreateUpdateResponseType) => { toast.success("Saved"); navigate("/list"); };
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### 8. Passing FieldType instead of BDO instance
|
|
161
|
-
|
|
162
|
-
```typescript
|
|
163
|
-
// ❌ WRONG
|
|
164
|
-
useBDOForm<ProductFieldType>({ ... });
|
|
165
|
-
|
|
166
|
-
// ✅ CORRECT — pass BDO class instance
|
|
167
|
-
const product = useMemo(() => new SellerProduct(), []);
|
|
168
|
-
useBDOForm({ bdo: product, operation: FormOperation.Create, mode: ValidationMode.OnBlur });
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
### 9. Wrong default values for date fields
|
|
172
|
-
|
|
173
|
-
```typescript
|
|
174
|
-
// ❌ WRONG — empty string causes type errors
|
|
175
|
-
defaultValues: { start_date: "" }
|
|
176
|
-
|
|
177
|
-
// ✅ CORRECT
|
|
178
|
-
defaultValues: { start_date: undefined }
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
---
|
|
182
|
-
|
|
183
|
-
## Complete Form Example (Create + Edit)
|
|
184
|
-
|
|
185
|
-
```tsx
|
|
186
|
-
"use no memo";
|
|
187
|
-
|
|
188
|
-
import { useMemo } from "react";
|
|
189
|
-
import { useNavigate, useParams } from "react-router-dom";
|
|
190
|
-
import { useBDOForm, ValidationMode, FormOperation } from "@ram_28/kf-ai-sdk/form";
|
|
191
|
-
import type { CreateUpdateResponseType } from "@ram_28/kf-ai-sdk/api/types";
|
|
192
|
-
import { AdminProduct } from "@/bdo/admin/Product";
|
|
193
|
-
import { ReferenceSelect } from "@/components/ui/reference-select";
|
|
194
|
-
import { ImageUpload } from "@/components/ui/image-upload";
|
|
195
|
-
import { FileUpload } from "@/components/ui/file-upload";
|
|
196
|
-
import { Checkbox } from "@/components/ui/checkbox";
|
|
197
|
-
import { toast } from "sonner";
|
|
198
|
-
|
|
199
|
-
export default function ProductForm() {
|
|
200
|
-
const { id } = useParams<{ id: string }>();
|
|
201
|
-
const navigate = useNavigate();
|
|
202
|
-
const bdo = useMemo(() => new AdminProduct(), []);
|
|
203
|
-
|
|
204
|
-
// Create/Edit ternary — NO type annotation on result
|
|
205
|
-
const formResult = id
|
|
206
|
-
? useBDOForm({ bdo, operation: FormOperation.Update, recordId: id, mode: ValidationMode.OnBlur })
|
|
207
|
-
: useBDOForm({ bdo, operation: FormOperation.Create, mode: ValidationMode.OnBlur });
|
|
208
|
-
|
|
209
|
-
const { register, handleSubmit, watch, setValue, item, formState: { errors, isSubmitting }, isLoading } = formResult;
|
|
210
|
-
|
|
211
|
-
// Loading guard AFTER all hooks
|
|
212
|
-
if (isLoading) return <div className="flex items-center justify-center h-full"><div className="animate-spin w-8 h-8 border-4 border-primary border-t-transparent rounded-full" /></div>;
|
|
213
|
-
|
|
214
|
-
const onSuccess = (data: CreateUpdateResponseType) => {
|
|
215
|
-
toast.success(id ? "Updated" : "Created");
|
|
216
|
-
navigate("/products");
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
const onError = (error: any) => {
|
|
220
|
-
toast.error(error instanceof Error ? error.message : "Please fix errors above");
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
return (
|
|
224
|
-
<form onSubmit={handleSubmit(onSuccess, onError)} className="space-y-6">
|
|
225
|
-
{/* StringField — register */}
|
|
226
|
-
<div>
|
|
227
|
-
<label>{bdo.product_name.label}{bdo.product_name.required && <span className="text-red-500"> *</span>}</label>
|
|
228
|
-
<input {...register(bdo.product_name.id)} className="w-full border rounded px-3 py-2" />
|
|
229
|
-
{errors.product_name && <p className="text-red-600 text-sm">{errors.product_name.message}</p>}
|
|
230
|
-
</div>
|
|
231
|
-
|
|
232
|
-
{/* NumberField — register */}
|
|
233
|
-
<div>
|
|
234
|
-
<label>{bdo.unit_price.label}</label>
|
|
235
|
-
<input type="number" step="0.01" {...register(bdo.unit_price.id)} className="w-full border rounded px-3 py-2" />
|
|
236
|
-
{errors.unit_price && <p className="text-red-600 text-sm">{errors.unit_price.message}</p>}
|
|
237
|
-
</div>
|
|
238
|
-
|
|
239
|
-
{/* StringField with Constraint.Enum — hardcoded options (NO .options getter) */}
|
|
240
|
-
<div>
|
|
241
|
-
<label>{bdo.status.label}</label>
|
|
242
|
-
<select {...register(bdo.status.id)} className="w-full border rounded px-3 py-2">
|
|
243
|
-
<option value="">Select {bdo.status.label}</option>
|
|
244
|
-
<option value="Active">Active</option>
|
|
245
|
-
<option value="Discontinued">Discontinued</option>
|
|
246
|
-
</select>
|
|
247
|
-
{errors.status && <p className="text-red-600 text-sm">{errors.status.message}</p>}
|
|
248
|
-
</div>
|
|
249
|
-
|
|
250
|
-
{/* ReferenceField — ReferenceSelect component */}
|
|
251
|
-
<div>
|
|
252
|
-
<label>{bdo.category.label}</label>
|
|
253
|
-
<ReferenceSelect
|
|
254
|
-
bdoField={bdo.category}
|
|
255
|
-
instanceId={id || String(watch("_id") ?? "")}
|
|
256
|
-
value={watch(bdo.category.id)}
|
|
257
|
-
onChange={(val) => setValue(bdo.category.id, val, { shouldDirty: true })}
|
|
258
|
-
/>
|
|
259
|
-
{errors.category && <p className="text-red-600 text-sm">{String(errors.category.message ?? "")}</p>}
|
|
260
|
-
</div>
|
|
261
|
-
|
|
262
|
-
{/* BooleanField — watch + setValue */}
|
|
263
|
-
<div className="flex items-center gap-2">
|
|
264
|
-
<Checkbox
|
|
265
|
-
checked={Boolean(watch(bdo.is_active.id))}
|
|
266
|
-
onCheckedChange={(v) => setValue(bdo.is_active.id, v as boolean, { shouldDirty: true })}
|
|
267
|
-
/>
|
|
268
|
-
<label>{bdo.is_active.label}</label>
|
|
269
|
-
</div>
|
|
270
|
-
|
|
271
|
-
{/* ImageField — ImageUpload component */}
|
|
272
|
-
<div>
|
|
273
|
-
<label>{bdo.product_image.label}</label>
|
|
274
|
-
<ImageUpload
|
|
275
|
-
field={item.product_image}
|
|
276
|
-
value={watch(bdo.product_image.id)}
|
|
277
|
-
boId={bdo.meta._id}
|
|
278
|
-
instanceId={id || String(watch("_id") ?? "")}
|
|
279
|
-
fieldId={bdo.product_image.id}
|
|
280
|
-
/>
|
|
281
|
-
</div>
|
|
282
|
-
|
|
283
|
-
{/* FileField — FileUpload component */}
|
|
284
|
-
<div>
|
|
285
|
-
<label>{bdo.specification_document.label}</label>
|
|
286
|
-
<FileUpload
|
|
287
|
-
field={item.specification_document}
|
|
288
|
-
value={watch(bdo.specification_document.id)}
|
|
289
|
-
boId={bdo.meta._id}
|
|
290
|
-
instanceId={id || String(watch("_id") ?? "")}
|
|
291
|
-
fieldId={bdo.specification_document.id}
|
|
292
|
-
/>
|
|
293
|
-
</div>
|
|
294
|
-
|
|
295
|
-
{/* TextField — textarea with register */}
|
|
296
|
-
<div>
|
|
297
|
-
<label>{bdo.description.label}</label>
|
|
298
|
-
<textarea {...register(bdo.description.id)} rows={4} className="w-full border rounded px-3 py-2" />
|
|
299
|
-
</div>
|
|
300
|
-
|
|
301
|
-
<button type="submit" disabled={isSubmitting} className="px-4 py-2 bg-primary text-white rounded">
|
|
302
|
-
{isSubmitting ? "Saving..." : id ? "Update" : "Create"}
|
|
303
|
-
</button>
|
|
304
|
-
</form>
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
---
|
|
310
|
-
|
|
311
|
-
## Type Definitions
|
|
312
|
-
|
|
313
|
-
### UseBDOFormOptionsType (Discriminated Union)
|
|
314
|
-
|
|
315
|
-
```typescript
|
|
316
|
-
type UseBDOFormOptionsType<B extends BaseBdo<any, any, any>> =
|
|
317
|
-
| { bdo: B; operation: "create"; defaultValues?: Partial<EditableFieldType>; mode?: ValidationModeType; }
|
|
318
|
-
| { bdo: B; operation: "update"; recordId: string; mode?: ValidationModeType; }
|
|
319
|
-
| { bdo: B; recordId?: string; defaultValues?: Partial<EditableFieldType>; mode?: ValidationModeType; };
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
### UseBDOFormReturnType
|
|
323
|
-
|
|
324
|
-
```typescript
|
|
325
|
-
interface UseBDOFormReturnType<B> {
|
|
326
|
-
item: FormItemType<EditableFieldType, ReadonlyFieldType>; // Field accessors (.get(), .set(), .upload())
|
|
327
|
-
register: FormRegisterType; // Auto-disables readonly fields
|
|
328
|
-
handleSubmit: HandleSubmitType; // Auto-calls bdo.create() or bdo.update()
|
|
329
|
-
watch: UseFormWatch; // Watch field values by bdo.field.id
|
|
330
|
-
setValue: UseFormSetValue; // Set field values by bdo.field.id
|
|
331
|
-
getValues: UseFormGetValues;
|
|
332
|
-
control: Control;
|
|
333
|
-
formState: FormState;
|
|
334
|
-
errors: FieldErrors;
|
|
335
|
-
isLoading: boolean; // Fetching record data (edit mode)
|
|
336
|
-
isSubmitting: boolean;
|
|
337
|
-
isDirty: boolean;
|
|
338
|
-
loadError: Error | null;
|
|
339
|
-
}
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
### File & Image Types
|
|
343
|
-
|
|
344
|
-
```typescript
|
|
345
|
-
interface FileType { _id: string; _name: string; FileName: string; FileExtension: string; Size: number; ContentType: string; }
|
|
346
|
-
type ImageFieldType = FileType | null; // Single image, nullable
|
|
347
|
-
type FileFieldType = FileType[]; // Array of files
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
Image accessor: `item.field.get()` returns `FileType | null`. Has `upload(file: File)`, `deleteAttachment()`, `getDownloadUrl()`.
|
|
351
|
-
File accessor: `item.field.get()` returns `FileType[]`. Has `upload(files: File[])`, `deleteAttachment(id)`, `getDownloadUrl(id)`.
|
|
352
|
-
|
|
353
|
-
### Constants
|
|
354
|
-
|
|
355
|
-
```typescript
|
|
356
|
-
FormOperation.Create // "create"
|
|
357
|
-
FormOperation.Update // "update"
|
|
358
|
-
ValidationMode.OnBlur / .OnChange / .OnSubmit / .OnTouched / .All
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
### Field-Type to UI Component Mapping
|
|
362
|
-
|
|
363
|
-
| BDO Field Class | UI Pattern |
|
|
364
|
-
|---|---|
|
|
365
|
-
| `StringField` | `<input {...register(bdo.field.id)} />` |
|
|
366
|
-
| `StringField` with `Constraint.Enum` | `<select {...register(bdo.field.id)}>` with hardcoded `<option>` from Enum array |
|
|
367
|
-
| `SelectField` | `<select {...register(bdo.field.id)}>` with `bdo.field.options.map()` |
|
|
368
|
-
| `TextField` | `<textarea {...register(bdo.field.id)} />` |
|
|
369
|
-
| `NumberField` | `<input type="number" {...register(bdo.field.id)} />` |
|
|
370
|
-
| `BooleanField` | `<Checkbox checked={watch()} onCheckedChange={v => setValue()} />` |
|
|
371
|
-
| `DateField` | `<input type="date" {...register(bdo.field.id)} />` |
|
|
372
|
-
| `DateTimeField` | `<input type="datetime-local" {...register(bdo.field.id)} />` |
|
|
373
|
-
| `ReferenceField` | `<ReferenceSelect bdoField={bdo.field} instanceId={id \|\| String(watch("_id") ?? "")} value={watch()} onChange={...} />` |
|
|
374
|
-
| `UserField` | `<ReferenceSelect bdoField={bdo.field} instanceId={id \|\| String(watch("_id") ?? "")} value={watch()} onChange={...} />` |
|
|
375
|
-
| `ImageField` | `<ImageUpload field={item.field} value={watch()} boId={} instanceId={} fieldId={} />` |
|
|
376
|
-
| `FileField` | `<FileUpload field={item.field} value={watch()} boId={} instanceId={} fieldId={} />` |
|
package/docs/useBDOTable.md
DELETED
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
# useBDOTable
|
|
2
|
-
|
|
3
|
-
Hook for BDO (Business Data Object) tables with search, sort, filter, and pagination. Pass a BDO instance and the hook handles the rest.
|
|
4
|
-
|
|
5
|
-
## Imports
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
import { useBDOTable } from "@ram_28/kf-ai-sdk/table";
|
|
9
|
-
import { ConditionOperator, RHSType } from "@ram_28/kf-ai-sdk/table";
|
|
10
|
-
import type { UseBDOTableOptionsType, UseBDOTableReturnType } from "@ram_28/kf-ai-sdk/table/types";
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## Common Mistakes (READ FIRST)
|
|
16
|
-
|
|
17
|
-
### 1. Passing `source` instead of `bdo`
|
|
18
|
-
|
|
19
|
-
`useBDOTable` takes a `bdo` instance, NOT a `source` string.
|
|
20
|
-
|
|
21
|
-
```typescript
|
|
22
|
-
// ❌ WRONG — source is not a valid property on useBDOTable
|
|
23
|
-
useBDOTable({ source: product.meta._id });
|
|
24
|
-
|
|
25
|
-
// ✅ CORRECT — pass the BDO instance
|
|
26
|
-
useBDOTable({ bdo: product });
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
### 2. Passing a raw object instead of a BDO instance
|
|
30
|
-
|
|
31
|
-
The `bdo` property expects an object with a `meta` property containing `_id` and `name`. A plain object or entity type will not work.
|
|
32
|
-
|
|
33
|
-
```typescript
|
|
34
|
-
// ❌ WRONG — plain object without meta
|
|
35
|
-
useBDOTable({ bdo: { _id: 'BO_Product', name: 'Product' } });
|
|
36
|
-
|
|
37
|
-
// ✅ CORRECT — a BDO class instance
|
|
38
|
-
const product = useMemo(() => new BuyerProduct(), []);
|
|
39
|
-
useBDOTable({ bdo: product });
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
### 3. Recreating the BDO on every render
|
|
43
|
-
|
|
44
|
-
Constructing the BDO inside the component body without `useMemo` creates a new instance on every render, which destabilizes the query key and causes infinite refetching.
|
|
45
|
-
|
|
46
|
-
```typescript
|
|
47
|
-
// ❌ WRONG — new instance every render
|
|
48
|
-
function ProductsTable() {
|
|
49
|
-
const product = new BuyerProduct();
|
|
50
|
-
const table = useBDOTable({ bdo: product }); // infinite refetch loop
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// ✅ CORRECT — memoize the instance
|
|
54
|
-
function ProductsTable() {
|
|
55
|
-
const product = useMemo(() => new BuyerProduct(), []);
|
|
56
|
-
const table = useBDOTable({ bdo: product });
|
|
57
|
-
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### 4. Using `columns` with useBDOTable
|
|
61
|
-
|
|
62
|
-
`useBDOTable` does not accept a `columns` property. Column definitions are a UI concern handled in your rendering logic, not in the hook options.
|
|
63
|
-
|
|
64
|
-
```typescript
|
|
65
|
-
// ❌ WRONG — columns is not a hook option
|
|
66
|
-
useBDOTable({
|
|
67
|
-
bdo: product,
|
|
68
|
-
columns: [{ fieldId: 'Title', label: 'Title' }],
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
// ✅ CORRECT — define columns separately for rendering
|
|
72
|
-
const columns = [
|
|
73
|
-
{ fieldId: product.Title.id, label: product.Title.label },
|
|
74
|
-
{ fieldId: product.Price.id, label: product.Price.label },
|
|
75
|
-
];
|
|
76
|
-
const table = useBDOTable({ bdo: product });
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
### 5. Calling `.get()` on table rows
|
|
80
|
-
|
|
81
|
-
Table `rows` are plain objects, NOT `ItemType`. The `.get()` accessor is only available on items returned by `bdo.get()`, `bdo.create()`, or the `useBDOForm` item proxy.
|
|
82
|
-
|
|
83
|
-
```typescript
|
|
84
|
-
// ❌ WRONG — rows are plain objects, not ItemType
|
|
85
|
-
table.rows.map((row) => row.Title.get());
|
|
86
|
-
|
|
87
|
-
// ✅ CORRECT — access properties directly
|
|
88
|
-
table.rows.map((row) => row.Title);
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
### 6. Wrong initialState property names
|
|
92
|
-
|
|
93
|
-
This is not react-table. Do not use react-table naming conventions.
|
|
94
|
-
|
|
95
|
-
```typescript
|
|
96
|
-
// ❌ WRONG — sorting and pageIndex are react-table names
|
|
97
|
-
useBDOTable({
|
|
98
|
-
bdo: product,
|
|
99
|
-
initialState: { sorting: [...], pagination: { pageIndex: 0, pageSize: 10 } },
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
// ✅ CORRECT — use sort and pageNo (1-indexed)
|
|
103
|
-
useBDOTable({
|
|
104
|
-
bdo: product,
|
|
105
|
-
initialState: {
|
|
106
|
-
sort: [{ [product.Title.id]: 'ASC' }],
|
|
107
|
-
pagination: { pageNo: 1, pageSize: 10 },
|
|
108
|
-
},
|
|
109
|
-
});
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
---
|
|
113
|
-
|
|
114
|
-
## Complete Example
|
|
115
|
-
|
|
116
|
-
A product listing page with search, filter, sort, and pagination.
|
|
117
|
-
|
|
118
|
-
```tsx
|
|
119
|
-
import { useMemo, useState } from "react";
|
|
120
|
-
import { useBDOTable } from "@ram_28/kf-ai-sdk/table";
|
|
121
|
-
import { ConditionOperator, RHSType } from "@ram_28/kf-ai-sdk/table";
|
|
122
|
-
import type { UseBDOTableReturnType } from "@ram_28/kf-ai-sdk/table/types";
|
|
123
|
-
import { BuyerProduct } from "../bdo/buyer/Product";
|
|
124
|
-
import type { BuyerProductFieldType } from "../bdo/buyer/Product";
|
|
125
|
-
|
|
126
|
-
function ProductListPage() {
|
|
127
|
-
const productBdo = useMemo(() => new BuyerProduct(), []);
|
|
128
|
-
const [selectedCategory, setSelectedCategory] = useState("all");
|
|
129
|
-
|
|
130
|
-
const table: UseBDOTableReturnType<BuyerProductFieldType> =
|
|
131
|
-
useBDOTable<BuyerProductFieldType>({
|
|
132
|
-
bdo: productBdo,
|
|
133
|
-
initialState: {
|
|
134
|
-
sort: [{ [productBdo.Title.id]: "ASC" }],
|
|
135
|
-
pagination: { pageNo: 1, pageSize: 10 },
|
|
136
|
-
},
|
|
137
|
-
onError: (error) => {
|
|
138
|
-
console.error("Table fetch failed:", error.message);
|
|
139
|
-
},
|
|
140
|
-
onSuccess: (data) => {
|
|
141
|
-
console.log("Loaded", data.length, "rows");
|
|
142
|
-
},
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
const handleCategoryChange = (category: string) => {
|
|
146
|
-
setSelectedCategory(category);
|
|
147
|
-
table.filter.clearAllConditions();
|
|
148
|
-
if (category !== "all") {
|
|
149
|
-
table.filter.addCondition({
|
|
150
|
-
LHSField: productBdo.Category.id,
|
|
151
|
-
Operator: ConditionOperator.EQ,
|
|
152
|
-
RHSValue: category,
|
|
153
|
-
RHSType: RHSType.Constant,
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
if (table.isLoading) return <div>Loading...</div>;
|
|
159
|
-
if (table.error) {
|
|
160
|
-
return (
|
|
161
|
-
<div>
|
|
162
|
-
<p>Error: {table.error.message}</p>
|
|
163
|
-
<button onClick={() => table.refetch()}>Retry</button>
|
|
164
|
-
</div>
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
return (
|
|
169
|
-
<div>
|
|
170
|
-
{/* Search */}
|
|
171
|
-
<input
|
|
172
|
-
type="text"
|
|
173
|
-
placeholder="Search products..."
|
|
174
|
-
value={table.search.query}
|
|
175
|
-
onChange={(e) => table.search.set(productBdo.Title.id, e.target.value)}
|
|
176
|
-
/>
|
|
177
|
-
{table.search.query && (
|
|
178
|
-
<button onClick={table.search.clear}>Clear Search</button>
|
|
179
|
-
)}
|
|
180
|
-
|
|
181
|
-
{/* Category Filter */}
|
|
182
|
-
<select
|
|
183
|
-
value={selectedCategory}
|
|
184
|
-
onChange={(e) => handleCategoryChange(e.target.value)}
|
|
185
|
-
>
|
|
186
|
-
<option value="all">All Categories</option>
|
|
187
|
-
<option value="Electronics">Electronics</option>
|
|
188
|
-
<option value="Books">Books</option>
|
|
189
|
-
</select>
|
|
190
|
-
|
|
191
|
-
{/* Sort */}
|
|
192
|
-
<select
|
|
193
|
-
onChange={(e) => {
|
|
194
|
-
const [field, dir] = e.target.value.split(":");
|
|
195
|
-
table.sort.set(field, dir as "ASC" | "DESC");
|
|
196
|
-
}}
|
|
197
|
-
>
|
|
198
|
-
<option value={`${productBdo.Title.id}:ASC`}>Name A-Z</option>
|
|
199
|
-
<option value={`${productBdo.Price.id}:ASC`}>Price: Low to High</option>
|
|
200
|
-
<option value={`${productBdo.Price.id}:DESC`}>Price: High to Low</option>
|
|
201
|
-
</select>
|
|
202
|
-
|
|
203
|
-
{/* Results */}
|
|
204
|
-
<p>{table.totalItems} results</p>
|
|
205
|
-
<div>
|
|
206
|
-
{table.rows.map((row) => (
|
|
207
|
-
<div key={row._id}>
|
|
208
|
-
<h3>{row.Title}</h3>
|
|
209
|
-
<p>${row.Price}</p>
|
|
210
|
-
<p>{row.Category}</p>
|
|
211
|
-
</div>
|
|
212
|
-
))}
|
|
213
|
-
</div>
|
|
214
|
-
|
|
215
|
-
{/* Pagination */}
|
|
216
|
-
<div>
|
|
217
|
-
<button
|
|
218
|
-
onClick={table.pagination.goToPrevious}
|
|
219
|
-
disabled={!table.pagination.canGoPrevious}
|
|
220
|
-
>
|
|
221
|
-
Previous
|
|
222
|
-
</button>
|
|
223
|
-
<span>
|
|
224
|
-
Page {table.pagination.pageNo} of {table.pagination.totalPages}
|
|
225
|
-
</span>
|
|
226
|
-
<button
|
|
227
|
-
onClick={table.pagination.goToNext}
|
|
228
|
-
disabled={!table.pagination.canGoNext}
|
|
229
|
-
>
|
|
230
|
-
Next
|
|
231
|
-
</button>
|
|
232
|
-
</div>
|
|
233
|
-
</div>
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
---
|
|
239
|
-
|
|
240
|
-
## Type Definitions
|
|
241
|
-
|
|
242
|
-
### UseBDOTableOptionsType
|
|
243
|
-
|
|
244
|
-
```typescript
|
|
245
|
-
export interface UseBDOTableOptionsType<T> {
|
|
246
|
-
/** BDO instance — only meta._id is used (for API routing) */
|
|
247
|
-
bdo: {
|
|
248
|
-
meta: { readonly _id: string; readonly name: string };
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
/** Initial state */
|
|
252
|
-
initialState?: {
|
|
253
|
-
sort?: SortType;
|
|
254
|
-
pagination?: PaginationStateType;
|
|
255
|
-
filter?: UseFilterOptionsType<T>;
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
/** Error callback */
|
|
259
|
-
onError?: (error: Error) => void;
|
|
260
|
-
|
|
261
|
-
/** Success callback — receives rows from current page */
|
|
262
|
-
onSuccess?: (data: T[]) => void;
|
|
263
|
-
}
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
### UseBDOTableReturnType
|
|
267
|
-
|
|
268
|
-
```typescript
|
|
269
|
-
export type UseBDOTableReturnType<T> = UseTableReturnType<T>;
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
The return type is identical to `UseTableReturnType<T>`. All properties — `rows`, `totalItems`, `isLoading`, `isFetching`, `error`, `search`, `sort`, `filter`, `pagination`, and `refetch` — behave the same.
|
|
273
|
-
|
|
274
|
-
---
|
|
275
|
-
|
|
276
|
-
## Search, Sort, Filter, and Pagination
|
|
277
|
-
|
|
278
|
-
`useBDOTable` supports `initialState`, `onError`, and `onSuccess` options.
|
|
279
|
-
|
|
280
|
-
- **Search** — `table.search.set(field, query)`, `table.search.clear()`, 300ms debounce
|
|
281
|
-
- **Sort** — `table.sort.toggle(field)`, `table.sort.set(field, direction)`, `table.sort.clear()`
|
|
282
|
-
- **Filter** — `table.filter.addCondition(...)`, `table.filter.removeCondition(...)`, `table.filter.clearAllConditions()`
|
|
283
|
-
- **Pagination** — `table.pagination.goToNext()`, `table.pagination.goToPrevious()`, `table.pagination.goToPage(n)`, `table.pagination.setPageSize(n)`
|
|
284
|
-
|