@ram_28/kf-ai-sdk 2.0.15 → 2.0.16

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 (83) hide show
  1. package/README.md +8 -8
  2. package/dist/bdo/core/BaseBdo.d.ts +1 -1
  3. package/dist/bdo.mjs +2 -2
  4. package/dist/components/hooks/useActivityForm/createActivityItemProxy.d.ts +1 -1
  5. package/dist/components/hooks/useActivityForm/createActivityItemProxy.d.ts.map +1 -1
  6. package/dist/components/hooks/useActivityForm/types.d.ts +2 -2
  7. package/dist/components/hooks/useActivityForm/types.d.ts.map +1 -1
  8. package/dist/components/hooks/useActivityForm/useActivityForm.d.ts.map +1 -1
  9. package/dist/components/hooks/useActivityTable/types.d.ts +4 -4
  10. package/dist/components/hooks/useActivityTable/types.d.ts.map +1 -1
  11. package/dist/components/hooks/useActivityTable/useActivityTable.d.ts +1 -1
  12. package/dist/components/hooks/useActivityTable/useActivityTable.d.ts.map +1 -1
  13. package/dist/components/hooks/useBDOForm/createItemProxy.d.ts.map +1 -0
  14. package/dist/components/hooks/useBDOForm/createResolver.d.ts.map +1 -0
  15. package/dist/components/hooks/useBDOForm/index.d.ts +6 -0
  16. package/dist/components/hooks/useBDOForm/index.d.ts.map +1 -0
  17. package/dist/components/hooks/useBDOForm/shared.d.ts +50 -0
  18. package/dist/components/hooks/useBDOForm/shared.d.ts.map +1 -0
  19. package/dist/components/hooks/{useForm → useBDOForm}/types.d.ts +6 -6
  20. package/dist/components/hooks/useBDOForm/types.d.ts.map +1 -0
  21. package/dist/components/hooks/{useForm/useForm.d.ts → useBDOForm/useBDOForm.d.ts} +4 -4
  22. package/dist/components/hooks/useBDOForm/useBDOForm.d.ts.map +1 -0
  23. package/dist/components/hooks/useBDOTable/types.d.ts +1 -3
  24. package/dist/components/hooks/useBDOTable/types.d.ts.map +1 -1
  25. package/dist/components/hooks/useBDOTable/useBDOTable.d.ts.map +1 -1
  26. package/dist/form.cjs +1 -1
  27. package/dist/form.d.ts +1 -1
  28. package/dist/form.d.ts.map +1 -1
  29. package/dist/form.mjs +250 -253
  30. package/dist/form.types.d.ts +1 -1
  31. package/dist/form.types.d.ts.map +1 -1
  32. package/dist/shared-5a7UkED1.js +1180 -0
  33. package/dist/shared-nnmlRVs7.cjs +1 -0
  34. package/dist/table.cjs +1 -1
  35. package/dist/table.mjs +12 -11
  36. package/dist/types/constants.d.ts +3 -3
  37. package/dist/workflow/Activity.d.ts +15 -3
  38. package/dist/workflow/Activity.d.ts.map +1 -1
  39. package/dist/workflow/client.d.ts +2 -2
  40. package/dist/workflow/client.d.ts.map +1 -1
  41. package/dist/workflow/types.d.ts +7 -3
  42. package/dist/workflow/types.d.ts.map +1 -1
  43. package/dist/workflow.cjs +1 -1
  44. package/dist/workflow.mjs +503 -546
  45. package/docs/bdo.md +1 -1
  46. package/docs/gaps.md +14 -64
  47. package/docs/useActivityForm.md +393 -0
  48. package/docs/useActivityTable.md +42 -105
  49. package/docs/{useForm.md → useBDOForm.md} +24 -24
  50. package/docs/useBDOTable.md +6 -39
  51. package/docs/workflow.md +43 -301
  52. package/package.json +2 -2
  53. package/sdk/bdo/core/BaseBdo.ts +2 -2
  54. package/sdk/components/hooks/useActivityForm/createActivityItemProxy.ts +1 -1
  55. package/sdk/components/hooks/useActivityForm/createActivityResolver.ts +1 -1
  56. package/sdk/components/hooks/useActivityForm/types.ts +4 -4
  57. package/sdk/components/hooks/useActivityForm/useActivityForm.ts +44 -194
  58. package/sdk/components/hooks/useActivityTable/types.ts +4 -2
  59. package/sdk/components/hooks/useActivityTable/useActivityTable.ts +8 -39
  60. package/sdk/components/hooks/{useForm → useBDOForm}/index.ts +4 -3
  61. package/sdk/components/hooks/useBDOForm/shared.ts +250 -0
  62. package/sdk/components/hooks/{useForm → useBDOForm}/types.ts +9 -9
  63. package/sdk/components/hooks/{useForm/useForm.ts → useBDOForm/useBDOForm.ts} +70 -96
  64. package/sdk/components/hooks/useBDOTable/types.ts +1 -3
  65. package/sdk/components/hooks/useBDOTable/useBDOTable.ts +3 -2
  66. package/sdk/form.ts +2 -2
  67. package/sdk/form.types.ts +4 -4
  68. package/sdk/types/constants.ts +3 -3
  69. package/sdk/workflow/Activity.ts +29 -6
  70. package/sdk/workflow/client.ts +65 -25
  71. package/sdk/workflow/types.ts +10 -2
  72. package/dist/components/hooks/useForm/createItemProxy.d.ts.map +0 -1
  73. package/dist/components/hooks/useForm/createResolver.d.ts.map +0 -1
  74. package/dist/components/hooks/useForm/index.d.ts +0 -5
  75. package/dist/components/hooks/useForm/index.d.ts.map +0 -1
  76. package/dist/components/hooks/useForm/types.d.ts.map +0 -1
  77. package/dist/components/hooks/useForm/useForm.d.ts.map +0 -1
  78. package/dist/createResolver-AIgUwoS6.cjs +0 -1
  79. package/dist/createResolver-ZHXQ7QMa.js +0 -1078
  80. /package/dist/components/hooks/{useForm → useBDOForm}/createItemProxy.d.ts +0 -0
  81. /package/dist/components/hooks/{useForm → useBDOForm}/createResolver.d.ts +0 -0
  82. /package/sdk/components/hooks/{useForm → useBDOForm}/createItemProxy.ts +0 -0
  83. /package/sdk/components/hooks/{useForm → useBDOForm}/createResolver.ts +0 -0
package/docs/bdo.md CHANGED
@@ -125,7 +125,7 @@ product.product_name.defaultValue // unknown
125
125
 
126
126
  | Class | Extra Getters | Notes |
127
127
  |-------|--------------|-------|
128
- | `StringField` | `length` | May have `Constraint.Enum` — see useForm Mistake #3 |
128
+ | `StringField` | `length` | May have `Constraint.Enum` — see useBDOForm Mistake #3 |
129
129
  | `NumberField` | `integerPart`, `fractionPart` | |
130
130
  | `BooleanField` | — | |
131
131
  | `DateField` | — | Format: YYYY-MM-DD |
package/docs/gaps.md CHANGED
@@ -91,9 +91,9 @@ When an Item field is Image or File type, the accessor gets enriched with attach
91
91
 
92
92
  ---
93
93
 
94
- ## docs/useForm.md
94
+ ## docs/useBDOForm.md
95
95
 
96
- ### 6. `UseFormOptionsType` missing 3 feature flags
96
+ ### 6. `UseBDOFormOptionsType` missing 3 feature flags
97
97
 
98
98
  All three variants of the options type are missing these properties:
99
99
 
@@ -103,9 +103,9 @@ enableConstraintValidation?: boolean; // default: true (required, length, etc.
103
103
  enableExpressionValidation?: boolean; // default: true (backend expression rules)
104
104
  ```
105
105
 
106
- **Source**: `sdk/components/hooks/useForm/types.ts` lines 90-123
106
+ **Source**: `sdk/components/hooks/useBDOForm/types.ts` lines 90-123
107
107
 
108
- ### 7. `UseFormReturnType` missing ~half its properties
108
+ ### 7. `UseBDOFormReturnType` missing ~half its properties
109
109
 
110
110
  These properties are returned by the hook but not in the doc:
111
111
 
@@ -123,7 +123,7 @@ These properties are returned by the hook but not in the doc:
123
123
  | `draftId` | `string \| undefined` |
124
124
  | `isCreatingDraft` | `boolean` |
125
125
 
126
- **Source**: `sdk/components/hooks/useForm/types.ts` lines 190-232, `sdk/components/hooks/useForm/useForm.ts` lines 320-352
126
+ **Source**: `sdk/components/hooks/useBDOForm/types.ts` lines 190-232, `sdk/components/hooks/useBDOForm/useBDOForm.ts` lines 320-352
127
127
 
128
128
  ### 8. `handleSubmit` behavior is wrong — doc says `bdo.create()`/`bdo.update()`, actual uses `api()` directly
129
129
 
@@ -138,7 +138,7 @@ result = await api(bdo.meta._id).draft(filteredData);
138
138
  result = await api(bdo.meta._id).update(recordId!, filteredData);
139
139
  ```
140
140
 
141
- **Source**: `sdk/components/hooks/useForm/useForm.ts` lines 296-301
141
+ **Source**: `sdk/components/hooks/useBDOForm/useBDOForm.ts` lines 296-301
142
142
 
143
143
  ### 9. Draft mode is completely undocumented
144
144
 
@@ -151,7 +151,7 @@ The entire draft-based creation flow is absent from the doc:
151
151
  5. `isLoading` reflects `isCreatingDraft` state
152
152
  6. `draftId` and `isCreatingDraft` are returned
153
153
 
154
- **Source**: `sdk/components/hooks/useForm/useForm.ts` lines 125-138, 186-190, 222-261, 296-298
154
+ **Source**: `sdk/components/hooks/useBDOForm/useBDOForm.ts` lines 125-138, 186-190, 222-261, 296-298
155
155
 
156
156
  ---
157
157
 
@@ -185,41 +185,7 @@ The complete example at line 121 has the same broken import, so the entire examp
185
185
 
186
186
  ## docs/useActivityTable.md
187
187
 
188
- ### 11. `ActivityRowType` has `{ ADO: E }` but API returns flat data
189
-
190
- The type definition wraps entity fields under `ADO`:
191
- ```typescript
192
- // sdk/components/hooks/useActivityTable/types.ts
193
- type ActivityRowType<A> = ActivityInstanceFieldsType & { ADO: E }
194
- ```
195
-
196
- But the API returns flat data (no `ADO` wrapper):
197
- ```typescript
198
- // sdk/workflow/Activity.ts
199
- async getInProgressList(): Promise<ListResponseType<ActivityInstanceFieldsType & TEntity>>
200
- ```
201
-
202
- `useActivityTable` passes raw API data to `useTable` with **no transformation** — so runtime data is flat. All doc examples using `row.ADO.StartDate` may fail at runtime.
203
-
204
- **Source**: `sdk/components/hooks/useActivityTable/types.ts` lines 22-25, `sdk/workflow/Activity.ts` line 130, `sdk/components/hooks/useActivityTable/index.ts` lines 19-40
205
-
206
- ### 12. Search, sort, filter, and pagination are non-functional
207
-
208
- The backend currently only supports GET for activity list/metric endpoints. The `_options` parameter (underscore prefix = unused) is ignored in all four methods:
209
-
210
- ```typescript
211
- // sdk/workflow/client.ts
212
- // TODO: Backend currently only supports GET for list/metric endpoints.
213
- async inProgressList(_options?: ListOptionsType): Promise<...> {
214
- const response = await fetch(`${url}/inprogress/list`, {
215
- method: "GET", // NOT POST — options are ignored
216
- headers: getDefaultHeaders(),
217
- });
218
- ```
219
-
220
- The doc presents search, sort, filter, and pagination as working features with full examples, but none of these have any effect on the data returned.
221
-
222
- **Source**: `sdk/workflow/client.ts` lines 112-164
188
+ *No remaining critical gaps — #11 (GET endpoints ignoring options) was resolved when list/metric endpoints were switched to POST.*
223
189
 
224
190
  ---
225
191
 
@@ -349,25 +315,9 @@ Also missing from the "ActivityInstance System Fields Reference" table at the bo
349
315
 
350
316
  **Source**: `sdk/workflow/types.ts` lines 49-55
351
317
 
352
- ### 21. `getInProgressList()` / `getCompletedList()` return flat data, not ADO-nested
353
-
354
- Doc examples use `item.ADO.StartDate` — this would fail at runtime. The API returns `ActivityInstanceFieldsType & TEntity` (flat):
355
-
356
- ```typescript
357
- // Correct access
358
- item.StartDate // not item.ADO.StartDate
359
-
360
- // The ADO nesting only exists in ActivityRowType (useActivityTable hook type)
361
- // NOT in the raw getInProgressList() / getCompletedList() responses
362
- ```
363
-
364
- **Source**: `sdk/workflow/Activity.ts` line 130
365
-
366
- ### 22. List/metric endpoints use GET, not POST — options are ignored
367
-
368
- Same issue as #12. The doc's "Filtering Reference" section claims POST with `ListOptionsType` body works. It does not — all four methods use GET and ignore options.
318
+ ### ~~21. List/metric endpoints use GET, not POST — options are ignored~~
369
319
 
370
- **Source**: `sdk/workflow/client.ts` lines 112-164
320
+ **RESOLVED**: All four list/metric endpoints now use POST with proper request bodies. Count methods construct `{ Type: "Metric", Metric: [{Field: "_id", Type: "Count"}] }` and transform the response correctly.
371
321
 
372
322
  ### 23. `activity.Field.meta.label` and `activity.Field.meta.id` don't exist
373
323
 
@@ -401,10 +351,10 @@ This affects **every** `register()` call and `<label>` in the doc's examples —
401
351
  |-----|---------------|
402
352
  | api.md | 2 |
403
353
  | bdo.md | 3 |
404
- | useForm.md | 4 |
354
+ | useBDOForm.md | 4 |
405
355
  | useBDOTable.md | 1 |
406
- | useActivityTable.md | 2 |
356
+ | useActivityTable.md | 0 (resolved) |
407
357
  | useFilter.md | 2 |
408
358
  | useAuth.md | 5 |
409
- | workflow.md | 4 |
410
- | **Total** | **23** |
359
+ | workflow.md | 2 |
360
+ | **Total** | **19** |
@@ -0,0 +1,393 @@
1
+ # useActivityForm
2
+
3
+ React hook for building forms bound to workflow activity input fields. Integrates with `react-hook-form`.
4
+
5
+ ## Imports
6
+
7
+ ```typescript
8
+ import { useActivityForm } from "@ram_28/kf-ai-sdk/workflow";
9
+ import type {
10
+ UseActivityFormOptions,
11
+ UseActivityFormReturn,
12
+ } from "@ram_28/kf-ai-sdk/workflow";
13
+ import type { FieldErrors } from "react-hook-form";
14
+ ```
15
+
16
+ ---
17
+
18
+ ## Common Mistakes (READ FIRST)
19
+
20
+ ### 1. Passing a Workflow instead of an Activity
21
+
22
+ `useActivityForm` takes an **Activity** instance, not a Workflow.
23
+
24
+ ```typescript
25
+ // ❌ WRONG — passing a workflow
26
+ const wf = new SimpleLeaveProcess();
27
+ useActivityForm(wf, options);
28
+
29
+ // ✅ CORRECT — get the typed activity from the workflow
30
+ const activity = useMemo(() => new SimpleLeaveProcess().employeeInputActivity(), []);
31
+ useActivityForm(activity, options);
32
+ ```
33
+
34
+ ### 2. Forgetting to memoize the activity instance
35
+
36
+ Activity instances must be stable across renders, or the hook will re-fetch metadata on every render.
37
+
38
+ ```typescript
39
+ // ❌ WRONG — new instance on every render
40
+ const activity = new SimpleLeaveProcess().employeeInputActivity();
41
+ useActivityForm(activity, options);
42
+
43
+ // ✅ CORRECT — memoize with useMemo
44
+ const activity = useMemo(() => new SimpleLeaveProcess().employeeInputActivity(), []);
45
+ useActivityForm(activity, options);
46
+ ```
47
+
48
+ ### 3. Using handleSubmit instead of handleComplete to finish an activity
49
+
50
+ `handleSubmit` saves the form data but does NOT complete the activity. Use `handleComplete` to save AND complete.
51
+
52
+ ```typescript
53
+ // ❌ WRONG — only saves, does not complete
54
+ <button onClick={handleSubmit(onSuccess, onError)}>Submit Leave Request</button>
55
+
56
+ // ✅ CORRECT — saves AND completes the activity
57
+ <button onClick={handleComplete(onSuccess, onError)}>Submit Leave Request</button>
58
+
59
+ // ✅ ALSO CORRECT — save as draft (intentionally not completing)
60
+ <button onClick={handleSubmit(onSaveSuccess, onError)}>Save Draft</button>
61
+ ```
62
+
63
+ ### 4. Calling `.get()` on form item fields
64
+
65
+ Form item fields from `useActivityForm` follow the same accessor pattern as `useBDOForm`. Use `.get()` to read values programmatically, but prefer `watch()` for rendering.
66
+
67
+ ```tsx
68
+ // ❌ WRONG — renders [object Object]
69
+ <span>{item.StartDate}</span>
70
+
71
+ // ✅ CORRECT — use .get() for programmatic access
72
+ const startDate = item.StartDate.get();
73
+
74
+ // ✅ CORRECT — use watch() for reactive rendering in JSX
75
+ <span>{watch(activity.StartDate.id)}</span>
76
+ ```
77
+
78
+ ---
79
+
80
+ ## Type Definitions
81
+
82
+ ### UseActivityFormOptions\<A\>
83
+
84
+ ```typescript
85
+ interface UseActivityFormOptions<A extends Activity<any, any, any>> {
86
+ /** Activity instance identifier (from wf.start() or getInProgressList()) */
87
+ activity_instance_id: string;
88
+
89
+ /** Default form values */
90
+ defaultValues?: Partial<ExtractActivityEditable<A>>;
91
+
92
+ /** Validation mode (default: "onBlur") */
93
+ mode?: "onBlur" | "onChange" | "onSubmit" | "onTouched" | "all";
94
+
95
+ /** Whether to load activity data on mount (default: true) */
96
+ enabled?: boolean;
97
+ }
98
+ ```
99
+
100
+ ### UseActivityFormReturn\<A\>
101
+
102
+ ```typescript
103
+ interface UseActivityFormReturn<A extends Activity<any, any, any>> {
104
+ // Core
105
+ item: FormItemType<EditableFields, ReadonlyFields>;
106
+ activity: A;
107
+ register: FormRegisterType<EditableFields, ReadonlyFields>;
108
+ handleSubmit: HandleSubmitType; // Save the activity form
109
+ handleComplete: HandleSubmitType; // Complete the activity
110
+
111
+ // RHF methods
112
+ watch: UseFormWatch;
113
+ setValue: UseFormSetValue;
114
+ getValues: UseFormGetValues;
115
+ reset: UseFormReset;
116
+ trigger: UseFormTrigger;
117
+ control: Control;
118
+
119
+ // Form state
120
+ errors: FieldErrors;
121
+ isValid: boolean;
122
+ isDirty: boolean;
123
+ isSubmitting: boolean;
124
+ isSubmitSuccessful: boolean;
125
+
126
+ // Loading
127
+ isLoading: boolean;
128
+ loadError: Error | null;
129
+ hasError: boolean;
130
+
131
+ // Operations
132
+ clearErrors: () => void;
133
+ }
134
+ ```
135
+
136
+ ---
137
+
138
+ ## handleSubmit vs handleComplete
139
+
140
+ | | `handleSubmit` | `handleComplete` |
141
+ |---|---|---|
142
+ | **Saves dirty fields** | Yes | Yes |
143
+ | **Completes the activity** | No | Yes |
144
+ | **Use case** | Save draft / intermediate save | Final submission |
145
+
146
+ Both validate the form before executing. Both only send dirty (changed) fields to the API.
147
+
148
+ ---
149
+
150
+ ## Options
151
+
152
+ | Property | Type | Default | Description |
153
+ |----------|------|---------|-------------|
154
+ | `activity_instance_id` | `string` | *required* | Activity instance ID (from `wf.start()` or `getInProgressList()`) |
155
+ | `defaultValues` | `Partial<Editable>` | `{}` | Initial form values |
156
+ | `mode` | `"onBlur" \| "onChange" \| "onSubmit" \| "onTouched" \| "all"` | `"onBlur"` | Validation timing |
157
+ | `enabled` | `boolean` | `true` | Whether to load activity data on mount |
158
+
159
+ ## Lifecycle
160
+
161
+ ```
162
+ Mount
163
+ |
164
+ |-> Load activity instance data -> populate form
165
+ |
166
+ v
167
+ User edits field -> blurs
168
+ |
169
+ |-> Field validation (BaseField.validate)
170
+ |-> If valid + readonly fields exist -> update computed fields
171
+ |
172
+ v
173
+ User clicks Save
174
+ |
175
+ |-> handleSubmit -> save activity data
176
+ |
177
+ v
178
+ User clicks Complete
179
+ |
180
+ |-> handleComplete -> complete activity
181
+ ```
182
+
183
+ ## Return Value
184
+
185
+ | Property | Type | Description |
186
+ |----------|------|-------------|
187
+ | `item` | `FormItemType` | Proxy with typed field accessors (`.get()`, `.set()`, `.meta`) |
188
+ | `activity` | `A` | The Activity instance |
189
+ | `register` | `FormRegisterType` | Smart register (auto-disables readonly fields) |
190
+ | `handleSubmit` | `HandleSubmitType` | Save activity data |
191
+ | `handleComplete` | `HandleSubmitType` | Complete the activity |
192
+ | `watch` | `UseFormWatch` | Watch field values |
193
+ | `setValue` | `UseFormSetValue` | Set field value programmatically |
194
+ | `getValues` | `UseFormGetValues` | Get current field values |
195
+ | `reset` | `UseFormReset` | Reset form to default values |
196
+ | `trigger` | `UseFormTrigger` | Trigger validation |
197
+ | `control` | `Control` | RHF control (for Controller components) |
198
+ | `errors` | `FieldErrors` | Validation errors |
199
+ | `isValid` | `boolean` | Form is valid |
200
+ | `isDirty` | `boolean` | Form has been modified |
201
+ | `isSubmitting` | `boolean` | Currently submitting |
202
+ | `isSubmitSuccessful` | `boolean` | Last submission succeeded |
203
+ | `isLoading` | `boolean` | Loading activity data |
204
+ | `loadError` | `Error \| null` | Load error |
205
+ | `hasError` | `boolean` | Any error active |
206
+ | `clearErrors` | `() => void` | Clear all form errors |
207
+
208
+ ---
209
+
210
+ ## Example: Employee Leave Request Form
211
+
212
+ ```tsx
213
+ import { useMemo } from "react";
214
+ import { useActivityForm } from "@ram_28/kf-ai-sdk/workflow";
215
+ import type { UseActivityFormOptions } from "@ram_28/kf-ai-sdk/workflow";
216
+ import type { FieldErrors } from "react-hook-form";
217
+ import { SimpleLeaveProcess, EmployeeInputActivity } from "@/bdo/workflows/SimpleLeaveProcess";
218
+
219
+ interface LeaveRequestFormProps {
220
+ activityInstanceId: string;
221
+ onComplete: () => void;
222
+ }
223
+
224
+ function LeaveRequestForm({ activityInstanceId, onComplete }: LeaveRequestFormProps) {
225
+ // 1. Get the typed activity from the workflow
226
+ const activity = useMemo(() => new SimpleLeaveProcess().employeeInputActivity(), []);
227
+
228
+ // 2. Hook options
229
+ const options: UseActivityFormOptions<EmployeeInputActivity> = {
230
+ activity_instance_id: activityInstanceId,
231
+ defaultValues: {
232
+ StartDate: undefined,
233
+ EndDate: undefined,
234
+ LeaveType: undefined,
235
+ },
236
+ mode: "onBlur",
237
+ };
238
+
239
+ // 3. Initialize hook
240
+ const {
241
+ register,
242
+ handleSubmit,
243
+ handleComplete,
244
+ item,
245
+ errors,
246
+ isLoading,
247
+ isSubmitting,
248
+ loadError,
249
+ } = useActivityForm(activity, options);
250
+
251
+ if (isLoading) return <div>Loading...</div>;
252
+ if (loadError) return <div>Error: {loadError.message}</div>;
253
+
254
+ // 4. Handlers
255
+ const onSaveSuccess = (): void => {
256
+ console.log("Saved!");
257
+ };
258
+
259
+ const onCompleteSuccess = (): void => {
260
+ console.log("Leave request submitted!");
261
+ onComplete();
262
+ };
263
+
264
+ const onError = (error: FieldErrors | Error): void => {
265
+ if (error instanceof Error) {
266
+ console.error("API Error:", error.message);
267
+ } else {
268
+ console.error("Validation errors:", error);
269
+ }
270
+ };
271
+
272
+ return (
273
+ <form>
274
+ <h2>Leave Request</h2>
275
+
276
+ {/* Start Date */}
277
+ <div>
278
+ <label>{activity.StartDate.label}</label>
279
+ <input type="date" {...register(activity.StartDate.id)} />
280
+ {errors.StartDate && <span>{errors.StartDate.message}</span>}
281
+ </div>
282
+
283
+ {/* End Date */}
284
+ <div>
285
+ <label>{activity.EndDate.label}</label>
286
+ <input type="date" {...register(activity.EndDate.id)} />
287
+ {errors.EndDate && <span>{errors.EndDate.message}</span>}
288
+ </div>
289
+
290
+ {/* Leave Type */}
291
+ <div>
292
+ <label>{activity.LeaveType.label}</label>
293
+ <select {...register(activity.LeaveType.id)}>
294
+ <option value="">Select type</option>
295
+ <option value="PTO">PTO</option>
296
+ <option value="Sick">Sick</option>
297
+ <option value="Parental">Parental</option>
298
+ </select>
299
+ {errors.LeaveType && <span>{errors.LeaveType.message}</span>}
300
+ </div>
301
+
302
+ {/* Leave Days (readonly — auto-disabled, computed by server) */}
303
+ <div>
304
+ <label>{activity.LeaveDays.label}</label>
305
+ <input type="number" {...register(activity.LeaveDays.id)} />
306
+ </div>
307
+
308
+ {/* Actions */}
309
+ <div>
310
+ <button type="button" onClick={handleSubmit(onSaveSuccess, onError)}>
311
+ {isSubmitting ? "Saving..." : "Save Draft"}
312
+ </button>
313
+ <button type="button" onClick={handleComplete(onCompleteSuccess, onError)}>
314
+ {isSubmitting ? "Submitting..." : "Submit Leave Request"}
315
+ </button>
316
+ </div>
317
+ </form>
318
+ );
319
+ }
320
+ ```
321
+
322
+ ---
323
+
324
+ ## Example: Manager Approval Form
325
+
326
+ ```tsx
327
+ import { useMemo } from "react";
328
+ import { useActivityForm } from "@ram_28/kf-ai-sdk/workflow";
329
+ import type { UseActivityFormOptions } from "@ram_28/kf-ai-sdk/workflow";
330
+ import type { FieldErrors } from "react-hook-form";
331
+ import { SimpleLeaveProcess, ManagerApprovalActivity } from "@/bdo/workflows/SimpleLeaveProcess";
332
+
333
+ interface ApprovalFormProps {
334
+ activityInstanceId: string;
335
+ onComplete: () => void;
336
+ }
337
+
338
+ function ApprovalForm({ activityInstanceId, onComplete }: ApprovalFormProps) {
339
+ const activity = useMemo(() => new SimpleLeaveProcess().managerApprovalActivity(), []);
340
+
341
+ const options: UseActivityFormOptions<ManagerApprovalActivity> = {
342
+ activity_instance_id: activityInstanceId,
343
+ defaultValues: {
344
+ ManagerApproved: false,
345
+ ManagerReason: "",
346
+ },
347
+ mode: "onBlur",
348
+ };
349
+
350
+ const {
351
+ register,
352
+ handleComplete,
353
+ errors,
354
+ isLoading,
355
+ isSubmitting,
356
+ loadError,
357
+ } = useActivityForm(activity, options);
358
+
359
+ if (isLoading) return <div>Loading...</div>;
360
+ if (loadError) return <div>Error: {loadError.message}</div>;
361
+
362
+ const onSuccess = (): void => {
363
+ console.log("Approval submitted!");
364
+ onComplete();
365
+ };
366
+
367
+ const onError = (error: FieldErrors | Error): void => {
368
+ if (error instanceof Error) console.error("API Error:", error.message);
369
+ };
370
+
371
+ return (
372
+ <form>
373
+ <h2>Leave Approval</h2>
374
+
375
+ <div>
376
+ <label>
377
+ <input type="checkbox" {...register(activity.ManagerApproved.id)} />
378
+ {activity.ManagerApproved.label}
379
+ </label>
380
+ </div>
381
+
382
+ <div>
383
+ <label>{activity.ManagerReason.label}</label>
384
+ <textarea {...register(activity.ManagerReason.id)} rows={4} />
385
+ </div>
386
+
387
+ <button type="button" onClick={handleComplete(onSuccess, onError)}>
388
+ {isSubmitting ? "Submitting..." : "Submit Decision"}
389
+ </button>
390
+ </form>
391
+ );
392
+ }
393
+ ```