@ram_28/kf-ai-sdk 2.0.4 → 2.0.5
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/dist/api.mjs +1 -1
- package/dist/auth.mjs +1 -1
- package/dist/bdo/core/Item.d.ts.map +1 -1
- package/dist/bdo/core/types.d.ts +1 -0
- package/dist/bdo/core/types.d.ts.map +1 -1
- package/dist/bdo.cjs +1 -1
- package/dist/bdo.mjs +91 -86
- package/dist/components/hooks/useForm/createItemProxy.d.ts.map +1 -1
- package/dist/components/hooks/useForm/index.d.ts +1 -1
- package/dist/components/hooks/useForm/index.d.ts.map +1 -1
- package/dist/components/hooks/useForm/types.d.ts +6 -16
- package/dist/components/hooks/useForm/types.d.ts.map +1 -1
- package/dist/components/hooks/useForm/useForm.d.ts +0 -1
- package/dist/components/hooks/useForm/useForm.d.ts.map +1 -1
- package/dist/components/hooks/useTable/types.d.ts +2 -2
- package/dist/components/hooks/useTable/useTable.d.ts.map +1 -1
- package/dist/{constants-BQrBcCON.js → constants-CYJih7y4.js} +2 -2
- package/dist/filter.mjs +1 -1
- package/dist/form.cjs +1 -1
- package/dist/form.mjs +158 -283
- package/dist/form.types.d.ts +1 -1
- package/dist/form.types.d.ts.map +1 -1
- package/dist/table.cjs +1 -1
- package/dist/table.mjs +65 -65
- package/dist/types/constants.d.ts +11 -11
- package/dist/types/constants.d.ts.map +1 -1
- package/dist/utils/api/buildListOptions.d.ts +1 -1
- package/dist/utils/api/buildListOptions.d.ts.map +1 -1
- package/dist/workflow/Activity.d.ts +14 -5
- package/dist/workflow/Activity.d.ts.map +1 -1
- package/dist/workflow/ActivityInstance.d.ts +1 -1
- package/dist/workflow/ActivityInstance.d.ts.map +1 -1
- package/dist/workflow/client.d.ts +14 -4
- package/dist/workflow/client.d.ts.map +1 -1
- package/dist/workflow/components/useActivityForm/createActivityItemProxy.d.ts.map +1 -1
- package/dist/workflow/types.d.ts +26 -9
- package/dist/workflow/types.d.ts.map +1 -1
- package/dist/workflow.cjs +1 -1
- package/dist/workflow.mjs +296 -240
- package/docs/bdo.md +63 -0
- package/docs/useAuth.md +27 -0
- package/docs/useFilter.md +67 -0
- package/docs/useForm.md +59 -0
- package/docs/useTable.md +106 -13
- package/docs/workflow.md +93 -49
- package/package.json +2 -2
- package/sdk/bdo/core/Item.ts +8 -0
- package/sdk/bdo/core/types.ts +1 -0
- package/sdk/components/hooks/useForm/createItemProxy.ts +8 -0
- package/sdk/components/hooks/useForm/index.ts +0 -1
- package/sdk/components/hooks/useForm/types.ts +7 -18
- package/sdk/components/hooks/useForm/useForm.ts +23 -109
- package/sdk/components/hooks/useTable/types.ts +2 -2
- package/sdk/components/hooks/useTable/useTable.llm.txt +7 -7
- package/sdk/components/hooks/useTable/useTable.ts +9 -8
- package/sdk/form.types.ts +0 -1
- package/sdk/types/constants.ts +11 -11
- package/sdk/utils/api/buildListOptions.ts +2 -3
- package/sdk/workflow/Activity.ts +31 -10
- package/sdk/workflow/ActivityInstance.ts +1 -1
- package/sdk/workflow/client.ts +73 -10
- package/sdk/workflow/components/useActivityForm/createActivityItemProxy.ts +4 -0
- package/sdk/workflow/types.ts +22 -9
- package/dist/components/hooks/useForm/useDraftInteraction.d.ts +0 -26
- package/dist/components/hooks/useForm/useDraftInteraction.d.ts.map +0 -1
- package/sdk/components/hooks/useForm/useDraftInteraction.ts +0 -251
package/sdk/workflow/client.ts
CHANGED
|
@@ -41,9 +41,14 @@ import type {
|
|
|
41
41
|
* await act.draftEnd("inst_123", data);
|
|
42
42
|
* await act.complete("inst_123");
|
|
43
43
|
*
|
|
44
|
-
* // List operations
|
|
45
|
-
* await act.
|
|
46
|
-
* await act.
|
|
44
|
+
* // List operations (by status)
|
|
45
|
+
* await act.inProgressList({ Page: 1, PageSize: 10 });
|
|
46
|
+
* await act.completedList({ Page: 1, PageSize: 10 });
|
|
47
|
+
* await act.inProgressMetric({ Metric: [...] });
|
|
48
|
+
* await act.completedMetric({ Metric: [...] });
|
|
49
|
+
*
|
|
50
|
+
* // Global process progress
|
|
51
|
+
* const progress = await wf.progress();
|
|
47
52
|
* ```
|
|
48
53
|
*/
|
|
49
54
|
export class Workflow<T = any> {
|
|
@@ -72,6 +77,26 @@ export class Workflow<T = any> {
|
|
|
72
77
|
return response.json();
|
|
73
78
|
}
|
|
74
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Get global progress across the entire business process.
|
|
82
|
+
* Returns a list of progress entries for each stage/activity.
|
|
83
|
+
*/
|
|
84
|
+
async progress(): Promise<ActivityProgressType[]> {
|
|
85
|
+
const response = await fetch(
|
|
86
|
+
`${getApiBaseUrl()}/api/app/process/${this.bp_id}/progress`,
|
|
87
|
+
{
|
|
88
|
+
method: "GET",
|
|
89
|
+
headers: getDefaultHeaders(),
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
if (!response.ok) {
|
|
94
|
+
throw new Error(`Failed to get process progress: ${response.statusText}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return response.json();
|
|
98
|
+
}
|
|
99
|
+
|
|
75
100
|
/**
|
|
76
101
|
* Get all operations for a specific activity
|
|
77
102
|
* @param activity_id - Activity identifier
|
|
@@ -82,39 +107,77 @@ export class Workflow<T = any> {
|
|
|
82
107
|
return {
|
|
83
108
|
// ── List-level ────────────────────────────────────────────
|
|
84
109
|
|
|
85
|
-
async
|
|
110
|
+
async inProgressList(options?: ListOptionsType): Promise<ListResponseType<ActivityInstanceFieldsType & T>> {
|
|
86
111
|
const requestBody: ListOptionsType = {
|
|
87
112
|
Type: "List",
|
|
88
113
|
...options,
|
|
89
114
|
};
|
|
90
115
|
|
|
91
|
-
const response = await fetch(`${getApiBaseUrl()}${base}/list`, {
|
|
116
|
+
const response = await fetch(`${getApiBaseUrl()}${base}/inprogress/list`, {
|
|
117
|
+
method: "POST",
|
|
118
|
+
headers: getDefaultHeaders(),
|
|
119
|
+
body: JSON.stringify(requestBody),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
if (!response.ok) {
|
|
123
|
+
throw new Error(`Failed to list in-progress activities: ${response.statusText}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return response.json();
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
async completedList(options?: ListOptionsType): Promise<ListResponseType<ActivityInstanceFieldsType & T>> {
|
|
130
|
+
const requestBody: ListOptionsType = {
|
|
131
|
+
Type: "List",
|
|
132
|
+
...options,
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const response = await fetch(`${getApiBaseUrl()}${base}/completed/list`, {
|
|
136
|
+
method: "POST",
|
|
137
|
+
headers: getDefaultHeaders(),
|
|
138
|
+
body: JSON.stringify(requestBody),
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
if (!response.ok) {
|
|
142
|
+
throw new Error(`Failed to list completed activities: ${response.statusText}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return response.json();
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
async inProgressMetric(options: Omit<MetricOptionsType, "Type">): Promise<MetricResponseType> {
|
|
149
|
+
const requestBody: MetricOptionsType = {
|
|
150
|
+
Type: "Metric",
|
|
151
|
+
...options,
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const response = await fetch(`${getApiBaseUrl()}${base}/inprogress/metric`, {
|
|
92
155
|
method: "POST",
|
|
93
156
|
headers: getDefaultHeaders(),
|
|
94
157
|
body: JSON.stringify(requestBody),
|
|
95
158
|
});
|
|
96
159
|
|
|
97
160
|
if (!response.ok) {
|
|
98
|
-
throw new Error(`Failed to
|
|
161
|
+
throw new Error(`Failed to get in-progress activity metrics: ${response.statusText}`);
|
|
99
162
|
}
|
|
100
163
|
|
|
101
164
|
return response.json();
|
|
102
165
|
},
|
|
103
166
|
|
|
104
|
-
async
|
|
167
|
+
async completedMetric(options: Omit<MetricOptionsType, "Type">): Promise<MetricResponseType> {
|
|
105
168
|
const requestBody: MetricOptionsType = {
|
|
106
169
|
Type: "Metric",
|
|
107
170
|
...options,
|
|
108
171
|
};
|
|
109
172
|
|
|
110
|
-
const response = await fetch(`${getApiBaseUrl()}${base}/metric`, {
|
|
173
|
+
const response = await fetch(`${getApiBaseUrl()}${base}/completed/metric`, {
|
|
111
174
|
method: "POST",
|
|
112
175
|
headers: getDefaultHeaders(),
|
|
113
176
|
body: JSON.stringify(requestBody),
|
|
114
177
|
});
|
|
115
178
|
|
|
116
179
|
if (!response.ok) {
|
|
117
|
-
throw new Error(`Failed to get activity metrics: ${response.statusText}`);
|
|
180
|
+
throw new Error(`Failed to get completed activity metrics: ${response.statusText}`);
|
|
118
181
|
}
|
|
119
182
|
|
|
120
183
|
return response.json();
|
|
@@ -191,7 +254,7 @@ export class Workflow<T = any> {
|
|
|
191
254
|
return response.json();
|
|
192
255
|
},
|
|
193
256
|
|
|
194
|
-
async progress(instanceId: string): Promise<ActivityProgressType> {
|
|
257
|
+
async progress(instanceId: string): Promise<ActivityProgressType[]> {
|
|
195
258
|
const response = await fetch(`${getApiBaseUrl()}${base}/${instanceId}/progress`, {
|
|
196
259
|
method: "GET",
|
|
197
260
|
headers: getDefaultHeaders(),
|
|
@@ -80,6 +80,8 @@ export function createActivityItemProxy<A extends Activity<any, any, any>>(
|
|
|
80
80
|
defaultValue: bdoField?.defaultValue,
|
|
81
81
|
meta: fieldMeta,
|
|
82
82
|
get: () => form.getValues(prop as Path<FieldValues>),
|
|
83
|
+
getOrDefault: (fallback: unknown) =>
|
|
84
|
+
form.getValues(prop as Path<FieldValues>) ?? fallback,
|
|
83
85
|
set: (value: unknown) => {
|
|
84
86
|
form.setValue(prop as Path<FieldValues>, value as any, {
|
|
85
87
|
shouldDirty: true,
|
|
@@ -99,6 +101,8 @@ export function createActivityItemProxy<A extends Activity<any, any, any>>(
|
|
|
99
101
|
defaultValue: bdoField?.defaultValue,
|
|
100
102
|
meta: fieldMeta,
|
|
101
103
|
get: () => form.getValues(prop as Path<FieldValues>),
|
|
104
|
+
getOrDefault: (fallback: unknown) =>
|
|
105
|
+
form.getValues(prop as Path<FieldValues>) ?? fallback,
|
|
102
106
|
validate,
|
|
103
107
|
};
|
|
104
108
|
return accessor;
|
package/sdk/workflow/types.ts
CHANGED
|
@@ -28,12 +28,19 @@ export interface WorkflowStartResponseType {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
|
-
* Response from the activity progress endpoint
|
|
31
|
+
* Response from the activity progress endpoint.
|
|
32
|
+
* Both global (Workflow.progress()) and instance-level (ActivityInstance.progress())
|
|
33
|
+
* return ActivityProgressType[].
|
|
32
34
|
*/
|
|
33
35
|
export interface ActivityProgressType {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
ActivityId: string;
|
|
37
|
+
ActivityInstanceId: string;
|
|
38
|
+
ActivityType: string;
|
|
39
|
+
AssignedTo: { Type: string; _id: string }[];
|
|
40
|
+
CompletedAt: string | null;
|
|
41
|
+
CompletedBy: { _id: string; _name: string } | null;
|
|
42
|
+
Status: "COMPLETED" | "IN_PROGRESS";
|
|
43
|
+
_name: string;
|
|
37
44
|
}
|
|
38
45
|
|
|
39
46
|
/**
|
|
@@ -56,11 +63,17 @@ export type ActivityInstanceFieldsType = {
|
|
|
56
63
|
export interface ActivityOperations<T> {
|
|
57
64
|
// ── List-level ──────────────────────────────────────────────
|
|
58
65
|
|
|
59
|
-
/** List activity instances (POST .../list) */
|
|
60
|
-
|
|
66
|
+
/** List in-progress activity instances (POST .../inprogress/list) */
|
|
67
|
+
inProgressList(options?: ListOptionsType): Promise<ListResponseType<ActivityInstanceFieldsType & T>>;
|
|
61
68
|
|
|
62
|
-
/**
|
|
63
|
-
|
|
69
|
+
/** List completed activity instances (POST .../completed/list) */
|
|
70
|
+
completedList(options?: ListOptionsType): Promise<ListResponseType<ActivityInstanceFieldsType & T>>;
|
|
71
|
+
|
|
72
|
+
/** Get in-progress activity metrics (POST .../inprogress/metric) */
|
|
73
|
+
inProgressMetric(options: Omit<MetricOptionsType, "Type">): Promise<MetricResponseType>;
|
|
74
|
+
|
|
75
|
+
/** Get completed activity metrics (POST .../completed/metric) */
|
|
76
|
+
completedMetric(options: Omit<MetricOptionsType, "Type">): Promise<MetricResponseType>;
|
|
64
77
|
|
|
65
78
|
// ── Instance-level ──────────────────────────────────────────
|
|
66
79
|
|
|
@@ -80,5 +93,5 @@ export interface ActivityOperations<T> {
|
|
|
80
93
|
complete(instanceId: string): Promise<CreateUpdateResponseType>;
|
|
81
94
|
|
|
82
95
|
/** Get activity progress (GET .../progress) */
|
|
83
|
-
progress(instanceId: string): Promise<ActivityProgressType>;
|
|
96
|
+
progress(instanceId: string): Promise<ActivityProgressType[]>;
|
|
84
97
|
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { UseFormReturn, FieldValues } from "react-hook-form";
|
|
2
|
-
import type { BaseField } from "../../../bdo/fields/BaseField";
|
|
3
|
-
import type { InteractiveCreatableBdo } from "./types";
|
|
4
|
-
interface UseDraftInteractionOptions {
|
|
5
|
-
/** The BDO instance — must expose interactive draft methods */
|
|
6
|
-
bdo: InteractiveCreatableBdo;
|
|
7
|
-
/** RHF form instance */
|
|
8
|
-
form: UseFormReturn<FieldValues>;
|
|
9
|
-
/** RHF validation mode */
|
|
10
|
-
mode: "onBlur" | "onChange" | "onSubmit" | "onTouched" | "all";
|
|
11
|
-
/** BDO field definitions (for determining dirty fields) */
|
|
12
|
-
fields: Record<string, BaseField<unknown>>;
|
|
13
|
-
/** Whether interactive mode is enabled */
|
|
14
|
-
enabled: boolean;
|
|
15
|
-
}
|
|
16
|
-
interface UseDraftInteractionReturn {
|
|
17
|
-
draftId: string | undefined;
|
|
18
|
-
isInitializingDraft: boolean;
|
|
19
|
-
isInteracting: boolean;
|
|
20
|
-
interactionError: Error | null;
|
|
21
|
-
triggerInteraction: () => void;
|
|
22
|
-
commitDraft: (dirtyData: Record<string, unknown>) => Promise<unknown>;
|
|
23
|
-
}
|
|
24
|
-
export declare function useDraftInteraction(options: UseDraftInteractionOptions): UseDraftInteractionReturn;
|
|
25
|
-
export {};
|
|
26
|
-
//# sourceMappingURL=useDraftInteraction.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useDraftInteraction.d.ts","sourceRoot":"","sources":["../../../../sdk/components/hooks/useForm/useDraftInteraction.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAMvD,UAAU,0BAA0B;IAClC,+DAA+D;IAC/D,GAAG,EAAE,uBAAuB,CAAC;IAC7B,wBAAwB;IACxB,IAAI,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IACjC,0BAA0B;IAC1B,IAAI,EAAE,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,WAAW,GAAG,KAAK,CAAC;IAC/D,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IAC3C,0CAA0C;IAC1C,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,UAAU,yBAAyB;IACjC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,aAAa,EAAE,OAAO,CAAC;IACvB,gBAAgB,EAAE,KAAK,GAAG,IAAI,CAAC;IAC/B,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACvE;AAYD,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,0BAA0B,GAClC,yBAAyB,CA+M3B"}
|
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect, useCallback, useRef } from "react";
|
|
2
|
-
import type { UseFormReturn, FieldValues } from "react-hook-form";
|
|
3
|
-
import type { BaseField } from "../../../bdo/fields/BaseField";
|
|
4
|
-
import type { InteractiveCreatableBdo } from "./types";
|
|
5
|
-
|
|
6
|
-
// ============================================================
|
|
7
|
-
// TYPES
|
|
8
|
-
// ============================================================
|
|
9
|
-
|
|
10
|
-
interface UseDraftInteractionOptions {
|
|
11
|
-
/** The BDO instance — must expose interactive draft methods */
|
|
12
|
-
bdo: InteractiveCreatableBdo;
|
|
13
|
-
/** RHF form instance */
|
|
14
|
-
form: UseFormReturn<FieldValues>;
|
|
15
|
-
/** RHF validation mode */
|
|
16
|
-
mode: "onBlur" | "onChange" | "onSubmit" | "onTouched" | "all";
|
|
17
|
-
/** BDO field definitions (for determining dirty fields) */
|
|
18
|
-
fields: Record<string, BaseField<unknown>>;
|
|
19
|
-
/** Whether interactive mode is enabled */
|
|
20
|
-
enabled: boolean;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface UseDraftInteractionReturn {
|
|
24
|
-
draftId: string | undefined;
|
|
25
|
-
isInitializingDraft: boolean;
|
|
26
|
-
isInteracting: boolean;
|
|
27
|
-
interactionError: Error | null;
|
|
28
|
-
triggerInteraction: () => void;
|
|
29
|
-
commitDraft: (dirtyData: Record<string, unknown>) => Promise<unknown>;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// ============================================================
|
|
33
|
-
// DEBOUNCE DELAY
|
|
34
|
-
// ============================================================
|
|
35
|
-
|
|
36
|
-
const DEBOUNCE_MS = 300;
|
|
37
|
-
|
|
38
|
-
// ============================================================
|
|
39
|
-
// HOOK
|
|
40
|
-
// ============================================================
|
|
41
|
-
|
|
42
|
-
export function useDraftInteraction(
|
|
43
|
-
options: UseDraftInteractionOptions,
|
|
44
|
-
): UseDraftInteractionReturn {
|
|
45
|
-
const { bdo, form, mode, fields, enabled } = options;
|
|
46
|
-
|
|
47
|
-
// ============================================================
|
|
48
|
-
// STATE
|
|
49
|
-
// ============================================================
|
|
50
|
-
|
|
51
|
-
const [draftId, setDraftId] = useState<string | undefined>(undefined);
|
|
52
|
-
const [isInitializingDraft, setIsInitializingDraft] = useState(false);
|
|
53
|
-
const [isInteracting, setIsInteracting] = useState(false);
|
|
54
|
-
const [interactionError, setInteractionError] = useState<Error | null>(null);
|
|
55
|
-
|
|
56
|
-
// ============================================================
|
|
57
|
-
// REFS (for concurrency control & loop prevention)
|
|
58
|
-
// ============================================================
|
|
59
|
-
|
|
60
|
-
/** Tracks the latest interaction call — stale responses are discarded */
|
|
61
|
-
const interactionCounterRef = useRef(0);
|
|
62
|
-
|
|
63
|
-
/** Prevents re-trigger when applying computed values via setValue */
|
|
64
|
-
const isApplyingComputedRef = useRef(false);
|
|
65
|
-
|
|
66
|
-
/** Debounce timer for onChange mode */
|
|
67
|
-
const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
68
|
-
|
|
69
|
-
/** AbortController for cleanup on unmount */
|
|
70
|
-
const abortControllerRef = useRef<AbortController | null>(null);
|
|
71
|
-
|
|
72
|
-
// ============================================================
|
|
73
|
-
// DRAFT INITIALIZATION (Create mode only)
|
|
74
|
-
// ============================================================
|
|
75
|
-
|
|
76
|
-
useEffect(() => {
|
|
77
|
-
if (!enabled) return;
|
|
78
|
-
|
|
79
|
-
let cancelled = false;
|
|
80
|
-
setIsInitializingDraft(true);
|
|
81
|
-
|
|
82
|
-
const controller = new AbortController();
|
|
83
|
-
abortControllerRef.current = controller;
|
|
84
|
-
|
|
85
|
-
bdo
|
|
86
|
-
.draftInteraction({})
|
|
87
|
-
.then((response) => {
|
|
88
|
-
if (cancelled || controller.signal.aborted) return;
|
|
89
|
-
const id = response._id;
|
|
90
|
-
setDraftId(id);
|
|
91
|
-
// Set _id into form without marking dirty
|
|
92
|
-
form.setValue("_id" as any, id, { shouldDirty: false });
|
|
93
|
-
setInteractionError(null);
|
|
94
|
-
})
|
|
95
|
-
.catch((error) => {
|
|
96
|
-
if (cancelled || controller.signal.aborted) return;
|
|
97
|
-
setInteractionError(error instanceof Error ? error : new Error(String(error)));
|
|
98
|
-
})
|
|
99
|
-
.finally(() => {
|
|
100
|
-
if (!cancelled) setIsInitializingDraft(false);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
return () => {
|
|
104
|
-
cancelled = true;
|
|
105
|
-
controller.abort();
|
|
106
|
-
};
|
|
107
|
-
}, [enabled, bdo, form]);
|
|
108
|
-
|
|
109
|
-
// ============================================================
|
|
110
|
-
// CORE INTERACTION LOGIC
|
|
111
|
-
// ============================================================
|
|
112
|
-
|
|
113
|
-
const executeInteraction = useCallback(async () => {
|
|
114
|
-
if (!enabled || !draftId) return;
|
|
115
|
-
if (isApplyingComputedRef.current) return;
|
|
116
|
-
|
|
117
|
-
// Build payload from dirty fields
|
|
118
|
-
const dirtyFields = form.formState.dirtyFields;
|
|
119
|
-
const allValues = form.getValues();
|
|
120
|
-
const payload: Record<string, unknown> = {};
|
|
121
|
-
|
|
122
|
-
for (const [key, isDirty] of Object.entries(dirtyFields)) {
|
|
123
|
-
if (isDirty && fields[key] && !fields[key].readOnly) {
|
|
124
|
-
payload[key] = allValues[key];
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Skip if nothing changed
|
|
129
|
-
if (Object.keys(payload).length === 0) return;
|
|
130
|
-
|
|
131
|
-
// Increment counter for concurrency control
|
|
132
|
-
const currentCounter = ++interactionCounterRef.current;
|
|
133
|
-
|
|
134
|
-
setIsInteracting(true);
|
|
135
|
-
|
|
136
|
-
try {
|
|
137
|
-
const response = await bdo.draftInteraction({
|
|
138
|
-
_id: draftId,
|
|
139
|
-
...payload,
|
|
140
|
-
} as any);
|
|
141
|
-
|
|
142
|
-
// Only apply if this is still the latest interaction
|
|
143
|
-
if (currentCounter !== interactionCounterRef.current) return;
|
|
144
|
-
|
|
145
|
-
// Apply computed values back to form
|
|
146
|
-
isApplyingComputedRef.current = true;
|
|
147
|
-
try {
|
|
148
|
-
for (const [key, value] of Object.entries(response)) {
|
|
149
|
-
// Skip _id and fields the user has dirty (don't overwrite user input)
|
|
150
|
-
if (key === "_id") continue;
|
|
151
|
-
if (dirtyFields[key]) continue;
|
|
152
|
-
|
|
153
|
-
form.setValue(key as any, value, {
|
|
154
|
-
shouldDirty: false,
|
|
155
|
-
shouldValidate: false,
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
} finally {
|
|
159
|
-
// Reset flag after React settles
|
|
160
|
-
// Using setTimeout(0) ensures setValue batch is complete
|
|
161
|
-
setTimeout(() => {
|
|
162
|
-
isApplyingComputedRef.current = false;
|
|
163
|
-
}, 0);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
setInteractionError(null);
|
|
167
|
-
} catch (error) {
|
|
168
|
-
// Only set error if this is still the latest interaction
|
|
169
|
-
if (currentCounter !== interactionCounterRef.current) return;
|
|
170
|
-
setInteractionError(error instanceof Error ? error : new Error(String(error)));
|
|
171
|
-
} finally {
|
|
172
|
-
if (currentCounter === interactionCounterRef.current) {
|
|
173
|
-
setIsInteracting(false);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}, [enabled, draftId, form, fields, bdo]);
|
|
177
|
-
|
|
178
|
-
// ============================================================
|
|
179
|
-
// TRIGGER INTERACTION (with optional debounce)
|
|
180
|
-
// ============================================================
|
|
181
|
-
|
|
182
|
-
const triggerInteraction = useCallback(() => {
|
|
183
|
-
if (!enabled) return;
|
|
184
|
-
|
|
185
|
-
// For onChange/all modes, debounce the interaction
|
|
186
|
-
if (mode === "onChange" || mode === "all") {
|
|
187
|
-
if (debounceTimerRef.current) {
|
|
188
|
-
clearTimeout(debounceTimerRef.current);
|
|
189
|
-
}
|
|
190
|
-
debounceTimerRef.current = setTimeout(() => {
|
|
191
|
-
executeInteraction();
|
|
192
|
-
}, DEBOUNCE_MS);
|
|
193
|
-
} else {
|
|
194
|
-
// For onBlur/onTouched/onSubmit, trigger immediately
|
|
195
|
-
executeInteraction();
|
|
196
|
-
}
|
|
197
|
-
}, [enabled, mode, executeInteraction]);
|
|
198
|
-
|
|
199
|
-
// ============================================================
|
|
200
|
-
// COMMIT DRAFT (for handleSubmit)
|
|
201
|
-
// ============================================================
|
|
202
|
-
|
|
203
|
-
const commitDraft = useCallback(
|
|
204
|
-
async (dirtyData: Record<string, unknown>): Promise<unknown> => {
|
|
205
|
-
return bdo.draft({
|
|
206
|
-
_id: draftId,
|
|
207
|
-
...dirtyData,
|
|
208
|
-
} as any);
|
|
209
|
-
},
|
|
210
|
-
[bdo, draftId],
|
|
211
|
-
);
|
|
212
|
-
|
|
213
|
-
// ============================================================
|
|
214
|
-
// CLEANUP
|
|
215
|
-
// ============================================================
|
|
216
|
-
|
|
217
|
-
useEffect(() => {
|
|
218
|
-
return () => {
|
|
219
|
-
if (debounceTimerRef.current) {
|
|
220
|
-
clearTimeout(debounceTimerRef.current);
|
|
221
|
-
}
|
|
222
|
-
abortControllerRef.current?.abort();
|
|
223
|
-
};
|
|
224
|
-
}, []);
|
|
225
|
-
|
|
226
|
-
// ============================================================
|
|
227
|
-
// DISABLED MODE (return no-ops)
|
|
228
|
-
// ============================================================
|
|
229
|
-
|
|
230
|
-
if (!enabled) {
|
|
231
|
-
return {
|
|
232
|
-
draftId: undefined,
|
|
233
|
-
isInitializingDraft: false,
|
|
234
|
-
isInteracting: false,
|
|
235
|
-
interactionError: null,
|
|
236
|
-
triggerInteraction: () => {},
|
|
237
|
-
commitDraft: async () => {
|
|
238
|
-
throw new Error("Draft interaction is not enabled");
|
|
239
|
-
},
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
return {
|
|
244
|
-
draftId,
|
|
245
|
-
isInitializingDraft,
|
|
246
|
-
isInteracting,
|
|
247
|
-
interactionError,
|
|
248
|
-
triggerInteraction,
|
|
249
|
-
commitDraft,
|
|
250
|
-
};
|
|
251
|
-
}
|