@ram_28/kf-ai-sdk 2.0.20-beta.2 → 2.0.21
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/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.map +1 -1
- package/dist/bdo.cjs +1 -1
- package/dist/bdo.mjs +1 -1
- 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 -2
- 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 +1 -1
- 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 +15 -14
- package/sdk/auth/authConfig.ts +1 -1
- package/sdk/auth/types.ts +1 -1
- package/sdk/bdo/core/Item.ts +2 -1
- package/sdk/bdo/expressions/evaluator.ts +8 -4
- 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 +9 -5
- 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 -498
- 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
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# useActivityTable API Reference
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
import { useActivityTable, ActivityTableStatus } from "@ram_28/kf-ai-sdk/workflow";
|
|
5
|
+
import type {
|
|
6
|
+
UseActivityTableOptionsType,
|
|
7
|
+
UseActivityTableReturnType,
|
|
8
|
+
ActivityRowType,
|
|
9
|
+
ActivityTableStatusType,
|
|
10
|
+
} from "@ram_28/kf-ai-sdk/workflow";
|
|
11
|
+
import type { ActivityInstanceFieldsType } from "@ram_28/kf-ai-sdk/workflow";
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Signature
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
const table: UseActivityTableReturnType<A> = useActivityTable<A>(options: UseActivityTableOptionsType<A>);
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Options
|
|
21
|
+
|
|
22
|
+
`UseActivityTableOptionsType<A>`
|
|
23
|
+
|
|
24
|
+
- `activity: A`
|
|
25
|
+
- **Required**
|
|
26
|
+
- Activity instance with `getInProgressList()`, `getCompletedList()`, `inProgressCount()`, and `completedCount()` methods. Must be memoized with `useMemo()`.
|
|
27
|
+
- `status: ActivityTableStatusType`
|
|
28
|
+
- **Required**
|
|
29
|
+
- `ActivityTableStatus.InProgress` or `ActivityTableStatus.Completed`. Determines whether in-progress or completed items are fetched.
|
|
30
|
+
- `initialState`
|
|
31
|
+
- **Optional**
|
|
32
|
+
- Object with:
|
|
33
|
+
- `sort?: SortType` — Initial sort configuration. Format: `[{ "fieldName": "ASC" }]`
|
|
34
|
+
- `pagination?: PaginationStateType` — Initial page and page size. Defaults to `{ pageNo: 1, pageSize: 10 }`.
|
|
35
|
+
- `filter?: UseFilterOptionsType<ActivityRowType<A>>` — Initial filter conditions and operator.
|
|
36
|
+
- `onError?: (error: Error) => void`
|
|
37
|
+
- **Optional**
|
|
38
|
+
- Called when fetching items or count fails.
|
|
39
|
+
- `onSuccess?: (data: ActivityRowType<A>[]) => void`
|
|
40
|
+
- **Optional**
|
|
41
|
+
- Called with the current page rows after a successful list fetch.
|
|
42
|
+
|
|
43
|
+
## Return Value
|
|
44
|
+
|
|
45
|
+
`UseActivityTableReturnType<A>`
|
|
46
|
+
|
|
47
|
+
Alias for `UseTableReturnType<ActivityRowType<A>>`. `ActivityRowType<A>` resolves to `ActivityInstanceType<TEntity & ActivityInstanceFieldsType, TEditable, TReadonly & ActivitySystemReadonlyType>` — inferred from the Activity's `getInProgressList()` return type. Rows are proxy objects with `.get()` accessors, persistence methods, and activity system fields.
|
|
48
|
+
|
|
49
|
+
### Data
|
|
50
|
+
|
|
51
|
+
- `rows: ActivityInstanceType<...>[]`
|
|
52
|
+
- Array of `ActivityInstanceType` proxy instances for the current page. Access field values via `.get()` (e.g. `row.LeaveType.get()`), not direct property access. Each row also has persistence methods (`update()`, `save()`, `complete()`, `progress()`) and activity system fields (`BPInstanceId`, `Status`, `AssignedTo`, `CompletedAt`). Empty array during initial loading.
|
|
53
|
+
- `totalItems: number`
|
|
54
|
+
- Total number of records matching the current filters and search. `0` while count is loading.
|
|
55
|
+
|
|
56
|
+
### Loading & Error
|
|
57
|
+
|
|
58
|
+
- `isLoading: boolean`
|
|
59
|
+
- `true` during the initial load of both items and count.
|
|
60
|
+
- `isFetching: boolean`
|
|
61
|
+
- `true` during any fetch including background refetches for items or count.
|
|
62
|
+
- `error: Error | null`
|
|
63
|
+
- Most recent fetch error from either items or count. `null` when no error.
|
|
64
|
+
|
|
65
|
+
### Search
|
|
66
|
+
|
|
67
|
+
- `search.query: string`
|
|
68
|
+
- Current search input value (updated immediately on `set()`).
|
|
69
|
+
- `search.field: keyof T | null`
|
|
70
|
+
- The field currently being searched. `null` when no search is active.
|
|
71
|
+
- `search.set(field: keyof T, query: string): void`
|
|
72
|
+
- Set the search field and query. The input value updates immediately; the data fetch is debounced by 300 ms. Pagination resets to page 1 after the debounce. Query is capped at 255 characters.
|
|
73
|
+
- `search.clear(): void`
|
|
74
|
+
- Clear the search field and query. Pagination resets to page 1.
|
|
75
|
+
|
|
76
|
+
### Sort
|
|
77
|
+
|
|
78
|
+
- `sort.field: keyof T | null`
|
|
79
|
+
- Currently sorted field. `null` when no sort is active.
|
|
80
|
+
- `sort.direction: "ASC" | "DESC" | null`
|
|
81
|
+
- Current sort direction. `null` when no sort is active.
|
|
82
|
+
- `sort.toggle(field: keyof T): void`
|
|
83
|
+
- Cycle the sort for a field: ASC -> DESC -> cleared. If a different field was sorted, starts at ASC.
|
|
84
|
+
- `sort.clear(): void`
|
|
85
|
+
- Remove all sorting.
|
|
86
|
+
- `sort.set(field: keyof T | null, direction: "ASC" | "DESC" | null): void`
|
|
87
|
+
- Explicitly set the sort field and direction.
|
|
88
|
+
|
|
89
|
+
### Filter
|
|
90
|
+
|
|
91
|
+
- `filter: UseFilterReturnType<T>`
|
|
92
|
+
- Full filter state and operations. See [useFilter reference](../useFilter/api_reference.md) for the complete API.
|
|
93
|
+
|
|
94
|
+
### Pagination
|
|
95
|
+
|
|
96
|
+
- `pagination.pageNo: number`
|
|
97
|
+
- Current page number (1-indexed).
|
|
98
|
+
- `pagination.pageSize: number`
|
|
99
|
+
- Current number of items per page.
|
|
100
|
+
- `pagination.totalPages: number`
|
|
101
|
+
- Total number of pages based on `totalItems` and `pageSize`.
|
|
102
|
+
- `pagination.totalItems: number`
|
|
103
|
+
- Same as the top-level `totalItems`.
|
|
104
|
+
- `pagination.canGoNext: boolean`
|
|
105
|
+
- `true` when a next page exists.
|
|
106
|
+
- `pagination.canGoPrevious: boolean`
|
|
107
|
+
- `true` when a previous page exists (i.e. `pageNo > 1`).
|
|
108
|
+
- `pagination.goToNext(): void`
|
|
109
|
+
- Navigate to the next page. No-op if already on the last page.
|
|
110
|
+
- `pagination.goToPrevious(): void`
|
|
111
|
+
- Navigate to the previous page. No-op if already on page 1.
|
|
112
|
+
- `pagination.goToPage(page: number): void`
|
|
113
|
+
- Navigate to a specific page. Clamped to `[1, totalPages]`.
|
|
114
|
+
- `pagination.setPageSize(size: number): void`
|
|
115
|
+
- Change the page size. Resets to page 1.
|
|
116
|
+
|
|
117
|
+
### Operations
|
|
118
|
+
|
|
119
|
+
- `refetch(): Promise<ListResponseType<T>>`
|
|
120
|
+
- Manually refetch both the items and the count. Returns the list response.
|
|
121
|
+
|
|
122
|
+
## Exported Constants
|
|
123
|
+
|
|
124
|
+
Available from `@ram_28/kf-ai-sdk/workflow`:
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
import { ActivityTableStatus } from "@ram_28/kf-ai-sdk/workflow";
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**ActivityTableStatus**
|
|
131
|
+
|
|
132
|
+
| Key | Value | Description |
|
|
133
|
+
|-----|-------|-------------|
|
|
134
|
+
| `InProgress` | `"inprogress"` | Fetches the in-progress items of the current activity |
|
|
135
|
+
| `Completed` | `"completed"` | Fetches the completed items of the current activity |
|
|
136
|
+
|
|
137
|
+
**SortDirection** (from `@ram_28/kf-ai-sdk/table`)
|
|
138
|
+
|
|
139
|
+
| Key | Value |
|
|
140
|
+
|-----|-------|
|
|
141
|
+
| `ASC` | `"ASC"` |
|
|
142
|
+
| `DESC` | `"DESC"` |
|
|
143
|
+
|
|
144
|
+
**TableDefaults** (from `@ram_28/kf-ai-sdk/table`)
|
|
145
|
+
|
|
146
|
+
| Key | Value | Description |
|
|
147
|
+
|-----|-------|-------------|
|
|
148
|
+
| `SEARCH_DEBOUNCE_MS` | `300` | Debounce delay for search (ms) |
|
|
149
|
+
| `PAGE_SIZE` | `10` | Default items per page |
|
|
150
|
+
| `PAGE` | `1` | Default page number (1-indexed) |
|
|
151
|
+
| `SEARCH_MAX_LENGTH` | `255` | Maximum search query length |
|
|
152
|
+
|
|
153
|
+
For filter constants (`ConditionOperator`, `GroupOperator`, `RHSType`), see [useFilter reference](../useFilter/api_reference.md).
|
|
154
|
+
|
|
155
|
+
## Types
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
import type { ActivityInstanceType } from "@ram_28/kf-ai-sdk/workflow";
|
|
159
|
+
|
|
160
|
+
// ============================================================
|
|
161
|
+
// Options
|
|
162
|
+
// ============================================================
|
|
163
|
+
|
|
164
|
+
interface UseActivityTableOptionsType<A extends Activity<any, any, any>> {
|
|
165
|
+
activity: A;
|
|
166
|
+
status: ActivityTableStatusType;
|
|
167
|
+
initialState?: {
|
|
168
|
+
sort?: SortType;
|
|
169
|
+
pagination?: PaginationStateType;
|
|
170
|
+
filter?: UseFilterOptionsType<ActivityRowType<A>>;
|
|
171
|
+
};
|
|
172
|
+
onError?: (error: Error) => void;
|
|
173
|
+
onSuccess?: (data: ActivityRowType<A>[]) => void;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ============================================================
|
|
177
|
+
// Return type
|
|
178
|
+
// ============================================================
|
|
179
|
+
|
|
180
|
+
type UseActivityTableReturnType<A extends Activity<any, any, any>> =
|
|
181
|
+
UseTableReturnType<ActivityRowType<A>>;
|
|
182
|
+
|
|
183
|
+
// ActivityRowType resolves to ActivityInstanceType<...>
|
|
184
|
+
// ActivityInstanceType<TEntity, TEditable, TReadonly> provides:
|
|
185
|
+
// row._id: string (direct access, no .get())
|
|
186
|
+
// row.Field.get(): value (read field value)
|
|
187
|
+
// row.Field.set(value): void (editable fields only)
|
|
188
|
+
// row.Field.validate(): ValidationResultType (single field)
|
|
189
|
+
// row.Field.label / .required / .readOnly / .defaultValue / .meta
|
|
190
|
+
// row.toJSON(): plain object
|
|
191
|
+
// row.validate(): ValidationResultType (all fields)
|
|
192
|
+
// row.update(data): Promise (persist changes)
|
|
193
|
+
// row.save(data): Promise (commit draft)
|
|
194
|
+
// row.complete(): Promise (complete activity)
|
|
195
|
+
// row.progress(): Promise (get workflow progress)
|
|
196
|
+
|
|
197
|
+
interface UseTableReturnType<T> {
|
|
198
|
+
// Data
|
|
199
|
+
rows: T[];
|
|
200
|
+
totalItems: number;
|
|
201
|
+
|
|
202
|
+
// Loading States
|
|
203
|
+
isLoading: boolean;
|
|
204
|
+
isFetching: boolean;
|
|
205
|
+
|
|
206
|
+
// Error Handling
|
|
207
|
+
error: Error | null;
|
|
208
|
+
|
|
209
|
+
// Search
|
|
210
|
+
search: {
|
|
211
|
+
query: string;
|
|
212
|
+
field: keyof T | null;
|
|
213
|
+
set: (field: keyof T, query: string) => void;
|
|
214
|
+
clear: () => void;
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// Sorting
|
|
218
|
+
sort: {
|
|
219
|
+
field: keyof T | null;
|
|
220
|
+
direction: "ASC" | "DESC" | null;
|
|
221
|
+
toggle: (field: keyof T) => void;
|
|
222
|
+
clear: () => void;
|
|
223
|
+
set: (field: keyof T | null, direction: "ASC" | "DESC" | null) => void;
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
// Filter
|
|
227
|
+
filter: UseFilterReturnType<T>;
|
|
228
|
+
|
|
229
|
+
// Pagination
|
|
230
|
+
pagination: {
|
|
231
|
+
pageNo: number;
|
|
232
|
+
pageSize: number;
|
|
233
|
+
totalPages: number;
|
|
234
|
+
totalItems: number;
|
|
235
|
+
canGoNext: boolean;
|
|
236
|
+
canGoPrevious: boolean;
|
|
237
|
+
goToNext: () => void;
|
|
238
|
+
goToPrevious: () => void;
|
|
239
|
+
goToPage: (page: number) => void;
|
|
240
|
+
setPageSize: (size: number) => void;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// Operations
|
|
244
|
+
refetch: () => Promise<ListResponseType<T>>;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ============================================================
|
|
248
|
+
// Status
|
|
249
|
+
// ============================================================
|
|
250
|
+
|
|
251
|
+
const ActivityTableStatus = {
|
|
252
|
+
InProgress: "inprogress",
|
|
253
|
+
Completed: "completed",
|
|
254
|
+
} as const;
|
|
255
|
+
|
|
256
|
+
type ActivityTableStatusType =
|
|
257
|
+
(typeof ActivityTableStatus)[keyof typeof ActivityTableStatus];
|
|
258
|
+
// resolves to: "inprogress" | "completed"
|
|
259
|
+
|
|
260
|
+
// ============================================================
|
|
261
|
+
// Row type inference
|
|
262
|
+
// ============================================================
|
|
263
|
+
|
|
264
|
+
type ActivityRowType<A extends Activity<any, any, any>> =
|
|
265
|
+
A extends { getInProgressList(opts?: any): Promise<(infer R)[]> }
|
|
266
|
+
? R
|
|
267
|
+
: never;
|
|
268
|
+
|
|
269
|
+
// ============================================================
|
|
270
|
+
// Activity system fields
|
|
271
|
+
// ============================================================
|
|
272
|
+
|
|
273
|
+
type ActivityInstanceFieldsType = {
|
|
274
|
+
_id: string;
|
|
275
|
+
BPInstanceId: string;
|
|
276
|
+
Status: "InProgress" | "Completed";
|
|
277
|
+
AssignedTo: Array<{ _id: string; _name: string }>;
|
|
278
|
+
CompletedAt: string; // ISO 8601 datetime
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// ============================================================
|
|
282
|
+
// State types
|
|
283
|
+
// ============================================================
|
|
284
|
+
|
|
285
|
+
interface PaginationStateType {
|
|
286
|
+
pageNo: number;
|
|
287
|
+
pageSize: number;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
type SortOptionType = Record<string, "ASC" | "DESC">;
|
|
291
|
+
type SortType = SortOptionType[];
|
|
292
|
+
|
|
293
|
+
// Filter types — see useFilter reference (../useFilter/api_reference.md)
|
|
294
|
+
```
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# useBDOForm
|
|
2
|
+
|
|
3
|
+
BDO-integrated form hook that wraps React Hook Form with automatic schema fetching, validation, per-field sync, and API submission.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
|
|
7
|
+
**Use `useBDOForm` when:**
|
|
8
|
+
|
|
9
|
+
- Building create or edit forms backed by a BDO (Business Data Object)
|
|
10
|
+
- You want automatic validation without manual rules
|
|
11
|
+
- You want `handleSubmit` to call the API automatically (no manual `bdo.create()` / `bdo.update()`)
|
|
12
|
+
|
|
13
|
+
**Use something else when:**
|
|
14
|
+
|
|
15
|
+
- Building a workflow/activity form — use [`useActivityForm`](../useActivityForm/README.md) instead
|
|
16
|
+
- Building a filter or search form — use [`useFilter`](../useFilter/README.md) instead
|
|
17
|
+
|
|
18
|
+
## Imports
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import { useBDOForm } from "@ram_28/kf-ai-sdk/form";
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import { useMemo } from "react";
|
|
28
|
+
import { useBDOForm } from "@ram_28/kf-ai-sdk/form";
|
|
29
|
+
import { SellerProduct } from "@/bdo/seller/Product";
|
|
30
|
+
|
|
31
|
+
function CreateProductForm({ id }: { id?: string }) {
|
|
32
|
+
const product = useMemo(() => new SellerProduct(), []);
|
|
33
|
+
|
|
34
|
+
const { register, handleSubmit, errors, isLoading, isSubmitting } =
|
|
35
|
+
useBDOForm({ bdo: product, recordId: id });
|
|
36
|
+
|
|
37
|
+
if (isLoading) return <p>Loading...</p>;
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<form onSubmit={handleSubmit((data) => console.log("Saved", data._id))}>
|
|
41
|
+
<label>{product.Title.label}</label>
|
|
42
|
+
<input {...register(product.Title.id)} />
|
|
43
|
+
{errors.Title && <p>{errors.Title.message}</p>}
|
|
44
|
+
|
|
45
|
+
<label>{product.Price.label}</label>
|
|
46
|
+
<input type="number" step="0.01" {...register(product.Price.id)} />
|
|
47
|
+
{errors.Price && <p>{errors.Price.message}</p>}
|
|
48
|
+
|
|
49
|
+
<button type="submit" disabled={isSubmitting}>
|
|
50
|
+
{isSubmitting ? "Saving..." : "Save"}
|
|
51
|
+
</button>
|
|
52
|
+
</form>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Usage Guide
|
|
58
|
+
|
|
59
|
+
### Create Mode
|
|
60
|
+
|
|
61
|
+
When no `recordId` is passed (or it's `undefined`), the hook enters create mode. A draft is allocated on mount to get an `_id`.
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
useBDOForm({ bdo: product });
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Update Mode
|
|
68
|
+
|
|
69
|
+
When `recordId` is a string, the hook enters update mode. The record is fetched and the form is populated.
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
useBDOForm({ bdo: product, recordId: id });
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Registering Fields
|
|
76
|
+
|
|
77
|
+
Use the BDO instance's field to get the field ID, label, and metadata — never hardcode field names as strings.
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
<label>{product.Title.label} {product.Title.required && <span>*</span>}</label>
|
|
81
|
+
<input {...register(product.Title.id)} />
|
|
82
|
+
{errors.Title && <p>{errors.Title.message}</p>}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
For **select, reference, boolean, and other custom components** that don't fire native change events, use `watch()` + `setValue()` instead of `register()` (see [Fields — Selection & Reference](../fields/README.md#selection--reference-fields) for full patterns):
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
<Select
|
|
89
|
+
value={watch(product.Category.id) ?? ""}
|
|
90
|
+
onValueChange={(v) => setValue(product.Category.id, v)}
|
|
91
|
+
>
|
|
92
|
+
...
|
|
93
|
+
</Select>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Readonly fields are auto-disabled by `register()` — no manual disable needed.
|
|
97
|
+
|
|
98
|
+
### Validation
|
|
99
|
+
|
|
100
|
+
Validation happens automatically. The hook validates field types, constraints (required, length, integerPart/fractionPart), and backend expression rules — all without any manual configuration.
|
|
101
|
+
|
|
102
|
+
Errors appear in `errors.FieldName.message`:
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
{errors.Title && <p>{errors.Title.message}</p>}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Handling Submit
|
|
109
|
+
|
|
110
|
+
Pass `handleSubmit` to your form's `onSubmit`. It validates, filters the payload, calls the API, and invokes your callbacks:
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
<form onSubmit={handleSubmit(onSuccess, onError)}>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
- **`onSuccess(data)`** — Called with `{ _id: string }` after a successful create or update.
|
|
117
|
+
- **`onError(error)`** — Called with `FieldErrors` (validation failure) or `Error` (API failure).
|
|
118
|
+
|
|
119
|
+
> **Don't** call `bdo.create()` or `bdo.update()` manually — `handleSubmit` does it for you.
|
|
120
|
+
|
|
121
|
+
### Working with `item`
|
|
122
|
+
|
|
123
|
+
`item` represents the current record instance. Each field on `item` is an accessor with methods to read, write, and validate that field's value.
|
|
124
|
+
|
|
125
|
+
**Instance-level members:**
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
const { item } = useBDOForm({ bdo: product });
|
|
129
|
+
|
|
130
|
+
item._id; // current record ID
|
|
131
|
+
item.toJSON(); // all form values as plain object
|
|
132
|
+
await item.validate(); // trigger validation for all fields
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Field-level accessors** (`item.FieldName`):
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
// Read/write
|
|
139
|
+
item.Title.get(); // current value
|
|
140
|
+
item.Price.getOrDefault(0); // value or fallback
|
|
141
|
+
item.Title.set("New Title"); // update value
|
|
142
|
+
|
|
143
|
+
// Validate
|
|
144
|
+
item.Title.validate(); // { valid: boolean, errors: string[] }
|
|
145
|
+
|
|
146
|
+
// Metadata
|
|
147
|
+
item.Title.label; // display label
|
|
148
|
+
item.Title.required; // is required?
|
|
149
|
+
item.Title.readOnly; // is readonly?
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**When to use `item` vs `register`/`watch`/`setValue`:**
|
|
153
|
+
|
|
154
|
+
- Use `register()` for standard HTML inputs (text, number, date)
|
|
155
|
+
- Use `watch()` + `setValue()` for custom components (select, checkbox, reference)
|
|
156
|
+
- Use `item.Field.get()` / `item.Field.set()` for programmatic read/write (event handlers, computed logic)
|
|
157
|
+
|
|
158
|
+
## Further Reading
|
|
159
|
+
|
|
160
|
+
- [API Reference](./api_reference.md) — All options, return values, and type definitions
|
|
161
|
+
- [Fields](../fields/README.md) — All 13 field classes, constraint getters, and attachment methods
|
|
162
|
+
- [Create Product](../examples/bdo/create-product.md) — Create form with validation and select fields
|
|
163
|
+
- [Edit Product Dialog](../examples/bdo/edit-product-dialog.md) — Table + edit dialog + refetch
|
|
164
|
+
- [Supplier Dropdown](../examples/bdo/supplier-dropdown.md) — Lazy-loaded reference field options
|
|
165
|
+
- [Primitive Fields](../examples/fields/primitive-fields.md) — Form with String, Number, Boolean, Date, DateTime, Text
|
|
166
|
+
- [Complex Fields](../examples/fields/complex-fields.md) — Form with Select, Reference, User, File, Image
|
|
167
|
+
|
|
168
|
+
## Common Mistakes
|
|
169
|
+
|
|
170
|
+
- **Don't forget `useMemo` on the BDO.** `new BdoClass()` must be wrapped in `useMemo(() => ..., [])`. Re-creating the instance on every render breaks the hook.
|
|
171
|
+
- **Don't set date `defaultValues` to empty string.** Use `undefined` instead. Empty strings cause type validation errors for Date and DateTime fields.
|
|
172
|
+
- **Don't mix `register()` and `watch()`+`setValue()` for the same field.** Pick one approach per field. `register()` for native inputs, `watch()`+`setValue()` for custom components.
|
|
173
|
+
- **Don't use `register()` for select, checkbox, or reference components.** They don't fire native change events. Use `watch()` + `setValue()`.
|
|
174
|
+
- **Don't call `bdo.create()` or `bdo.update()` manually.** `handleSubmit` handles the API call. Calling them yourself will double-submit.
|
|
175
|
+
- **Don't render the form before `isLoading` is false.** In create mode, the draft is being allocated. In update mode, the record is being fetched. Guard with `if (isLoading) return <Loading />`.
|