@ram_28/kf-ai-sdk 2.0.6 → 2.0.9
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/client.d.ts +21 -1
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/index.d.ts +1 -1
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api.cjs +1 -1
- package/dist/api.mjs +2 -2
- package/dist/api.types.d.ts +1 -1
- package/dist/api.types.d.ts.map +1 -1
- package/dist/auth.cjs +1 -1
- package/dist/auth.mjs +1 -1
- package/dist/bdo/core/BaseBdo.d.ts +21 -1
- package/dist/bdo/core/BaseBdo.d.ts.map +1 -1
- package/dist/bdo/core/Item.d.ts +22 -3
- package/dist/bdo/core/Item.d.ts.map +1 -1
- package/dist/bdo/core/types.d.ts +28 -0
- package/dist/bdo/core/types.d.ts.map +1 -1
- package/dist/bdo/fields/FileField.d.ts +2 -2
- package/dist/bdo/fields/FileField.d.ts.map +1 -1
- package/dist/bdo/fields/ImageField.d.ts +18 -0
- package/dist/bdo/fields/ImageField.d.ts.map +1 -0
- package/dist/bdo/fields/attachment-constants.d.ts +9 -0
- package/dist/bdo/fields/attachment-constants.d.ts.map +1 -0
- package/dist/bdo/fields/index.d.ts +1 -0
- package/dist/bdo/fields/index.d.ts.map +1 -1
- package/dist/bdo/index.d.ts +2 -2
- package/dist/bdo/index.d.ts.map +1 -1
- package/dist/bdo.cjs +1 -1
- package/dist/bdo.d.ts +1 -1
- package/dist/bdo.d.ts.map +1 -1
- package/dist/bdo.mjs +381 -157
- package/dist/bdo.types.d.ts +2 -2
- package/dist/bdo.types.d.ts.map +1 -1
- package/dist/client-BnVxSHAm.cjs +1 -0
- package/dist/client-CMERmrC-.js +279 -0
- package/dist/form.cjs +1 -1
- package/dist/form.mjs +14 -14
- package/dist/{metadata-BJWukIqS.cjs → metadata-BfJtHz84.cjs} +1 -1
- package/dist/{metadata-CJuFxytC.js → metadata-CwAo6a8e.js} +1 -1
- package/dist/table.cjs +1 -1
- package/dist/table.mjs +1 -1
- package/dist/types/base-fields.d.ts +19 -3
- package/dist/types/base-fields.d.ts.map +1 -1
- package/dist/types/common.d.ts +40 -0
- package/dist/types/common.d.ts.map +1 -1
- package/dist/types/constants.d.ts +8 -0
- package/dist/types/constants.d.ts.map +1 -1
- package/dist/workflow/Activity.d.ts +11 -9
- package/dist/workflow/Activity.d.ts.map +1 -1
- package/dist/workflow/client.d.ts +12 -10
- package/dist/workflow/client.d.ts.map +1 -1
- package/dist/workflow/types.d.ts +12 -11
- package/dist/workflow/types.d.ts.map +1 -1
- package/dist/workflow.cjs +1 -1
- package/dist/workflow.mjs +201 -213
- package/docs/useForm.md +174 -0
- package/docs/workflow.md +38 -76
- package/package.json +1 -1
- package/sdk/api/client.ts +145 -0
- package/sdk/api/index.ts +4 -0
- package/sdk/api.types.ts +5 -0
- package/sdk/bdo/core/BaseBdo.ts +60 -0
- package/sdk/bdo/core/Item.ts +231 -3
- package/sdk/bdo/core/types.ts +63 -0
- package/sdk/bdo/fields/FileField.ts +14 -5
- package/sdk/bdo/fields/ImageField.ts +46 -0
- package/sdk/bdo/fields/attachment-constants.ts +72 -0
- package/sdk/bdo/fields/index.ts +1 -0
- package/sdk/bdo/index.ts +6 -0
- package/sdk/bdo.ts +1 -0
- package/sdk/bdo.types.ts +7 -0
- package/sdk/components/hooks/useForm/createResolver.ts +1 -1
- package/sdk/types/base-fields.ts +21 -3
- package/sdk/types/common.ts +45 -0
- package/sdk/types/constants.ts +8 -0
- package/sdk/workflow/Activity.ts +18 -26
- package/sdk/workflow/client.ts +25 -47
- package/sdk/workflow/types.ts +11 -12
- package/dist/client-BULEEaCP.js +0 -222
- package/dist/client-DtPpfJc1.cjs +0 -1
package/docs/useForm.md
CHANGED
|
@@ -174,6 +174,63 @@ interface ReadonlyFormFieldAccessorType<T> {
|
|
|
174
174
|
}
|
|
175
175
|
```
|
|
176
176
|
|
|
177
|
+
### File & Image Value Types
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
// Metadata for an uploaded file or image
|
|
181
|
+
interface FileType {
|
|
182
|
+
_id: string;
|
|
183
|
+
_name: string;
|
|
184
|
+
FileName: string;
|
|
185
|
+
FileExtension: string;
|
|
186
|
+
Size: number;
|
|
187
|
+
ContentType: string;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
type ImageFieldType = FileType | null; // Single image, nullable
|
|
191
|
+
type FileFieldType = FileType[]; // Array of files
|
|
192
|
+
|
|
193
|
+
// Request a thumbnail or preview variant from the backend
|
|
194
|
+
type AttachmentViewType = "thumbnail" | "preview";
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### File & Image Accessor Types
|
|
198
|
+
|
|
199
|
+
Image and File fields extend the base accessor with attachment operations.
|
|
200
|
+
Editable accessors get `upload` and `deleteAttachment`; readonly accessors only get download.
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
// Editable Image — single file
|
|
204
|
+
interface EditableImageFieldAccessorType
|
|
205
|
+
extends EditableFormFieldAccessorType<ImageFieldType> {
|
|
206
|
+
upload(file: File): Promise<FileType>;
|
|
207
|
+
getDownloadUrl(viewType?: AttachmentViewType): Promise<FileDownloadResponseType>;
|
|
208
|
+
deleteAttachment(): Promise<void>;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Readonly Image — download only
|
|
212
|
+
interface ReadonlyImageFieldAccessorType
|
|
213
|
+
extends ReadonlyFormFieldAccessorType<ImageFieldType> {
|
|
214
|
+
getDownloadUrl(viewType?: AttachmentViewType): Promise<FileDownloadResponseType>;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Editable File — multi-file
|
|
218
|
+
interface EditableFileFieldAccessorType
|
|
219
|
+
extends EditableFormFieldAccessorType<FileFieldType> {
|
|
220
|
+
upload(files: File[]): Promise<FileType[]>;
|
|
221
|
+
getDownloadUrl(attachmentId: string, viewType?: AttachmentViewType): Promise<FileDownloadResponseType>;
|
|
222
|
+
getDownloadUrls(viewType?: AttachmentViewType): Promise<FileDownloadResponseType[]>;
|
|
223
|
+
deleteAttachment(attachmentId: string): Promise<void>;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Readonly File — download only
|
|
227
|
+
interface ReadonlyFileFieldAccessorType
|
|
228
|
+
extends ReadonlyFormFieldAccessorType<FileFieldType> {
|
|
229
|
+
getDownloadUrl(attachmentId: string, viewType?: AttachmentViewType): Promise<FileDownloadResponseType>;
|
|
230
|
+
getDownloadUrls(viewType?: AttachmentViewType): Promise<FileDownloadResponseType[]>;
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
177
234
|
---
|
|
178
235
|
|
|
179
236
|
## Basic Example
|
|
@@ -720,6 +777,123 @@ function ProductForm({ productId, onSuccess }: ProductFormProps) {
|
|
|
720
777
|
|
|
721
778
|
---
|
|
722
779
|
|
|
780
|
+
## File & Image Attachments in Forms
|
|
781
|
+
|
|
782
|
+
Image and File fields get attachment methods on the `item` accessor automatically.
|
|
783
|
+
`upload()` updates local field state only — the form's `handleSubmit` persists everything to the backend.
|
|
784
|
+
`deleteAttachment()` is atomic — it removes the file from storage and the backend record immediately.
|
|
785
|
+
|
|
786
|
+
```tsx
|
|
787
|
+
// Assuming SellerProduct BDO has:
|
|
788
|
+
// readonly ProductImage = new ImageField({ _id: "ProductImage", Name: "Product Image", Type: "Image" });
|
|
789
|
+
// readonly Attachments = new FileField({ _id: "Attachments", Name: "Attachments", Type: "File" });
|
|
790
|
+
|
|
791
|
+
function ProductFormWithAttachments({ productId }: { productId?: string }) {
|
|
792
|
+
const product = useMemo(() => new SellerProduct(), []);
|
|
793
|
+
|
|
794
|
+
const { register, handleSubmit, item, errors, isSubmitting, watch } = useForm({
|
|
795
|
+
bdo: product,
|
|
796
|
+
recordId: productId,
|
|
797
|
+
mode: ValidationMode.OnBlur,
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
// --- Image field (single file) ---
|
|
801
|
+
|
|
802
|
+
const handleImageUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
803
|
+
const file = e.target.files?.[0];
|
|
804
|
+
if (!file) return;
|
|
805
|
+
|
|
806
|
+
// Uploads to storage, sets local value (FileType)
|
|
807
|
+
// Validates extension client-side before uploading
|
|
808
|
+
const metadata = await item.ProductImage.upload(file);
|
|
809
|
+
console.log("Uploaded image:", metadata.FileName);
|
|
810
|
+
};
|
|
811
|
+
|
|
812
|
+
const handleImageDelete = async () => {
|
|
813
|
+
// Atomic — deletes from storage + backend immediately
|
|
814
|
+
await item.ProductImage.deleteAttachment();
|
|
815
|
+
};
|
|
816
|
+
|
|
817
|
+
const handleImagePreview = async () => {
|
|
818
|
+
// Get a thumbnail URL (backend-generated variant)
|
|
819
|
+
const { DownloadUrl } = await item.ProductImage.getDownloadUrl("thumbnail");
|
|
820
|
+
window.open(DownloadUrl);
|
|
821
|
+
};
|
|
822
|
+
|
|
823
|
+
// --- File field (multi-file) ---
|
|
824
|
+
|
|
825
|
+
const handleFilesUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
826
|
+
const files = Array.from(e.target.files ?? []);
|
|
827
|
+
if (!files.length) return;
|
|
828
|
+
|
|
829
|
+
// Uploads all files in parallel, appends to existing array
|
|
830
|
+
const uploaded = await item.Attachments.upload(files);
|
|
831
|
+
console.log(`Uploaded ${uploaded.length} files`);
|
|
832
|
+
};
|
|
833
|
+
|
|
834
|
+
const handleFileDelete = async (attachmentId: string) => {
|
|
835
|
+
// Removes single file from storage + backend + local array
|
|
836
|
+
await item.Attachments.deleteAttachment(attachmentId);
|
|
837
|
+
};
|
|
838
|
+
|
|
839
|
+
const handleFileDownload = async (attachmentId: string) => {
|
|
840
|
+
const { DownloadUrl } = await item.Attachments.getDownloadUrl(attachmentId);
|
|
841
|
+
window.open(DownloadUrl);
|
|
842
|
+
};
|
|
843
|
+
|
|
844
|
+
// Current field values
|
|
845
|
+
const currentImage = item.ProductImage.get(); // FileType | null
|
|
846
|
+
const currentFiles = item.Attachments.get() ?? []; // FileType[]
|
|
847
|
+
|
|
848
|
+
return (
|
|
849
|
+
<form onSubmit={handleSubmit((data) => console.log("Saved:", data._id))}>
|
|
850
|
+
{/* ... other fields with register() ... */}
|
|
851
|
+
|
|
852
|
+
{/* Image field */}
|
|
853
|
+
<div className="field">
|
|
854
|
+
<label>{product.ProductImage.label}</label>
|
|
855
|
+
{currentImage ? (
|
|
856
|
+
<div>
|
|
857
|
+
<span>{currentImage.FileName}</span>
|
|
858
|
+
<button type="button" onClick={handleImagePreview}>Preview</button>
|
|
859
|
+
<button type="button" onClick={handleImageDelete}>Remove</button>
|
|
860
|
+
</div>
|
|
861
|
+
) : (
|
|
862
|
+
<input type="file" accept="image/*" onChange={handleImageUpload} />
|
|
863
|
+
)}
|
|
864
|
+
</div>
|
|
865
|
+
|
|
866
|
+
{/* File field */}
|
|
867
|
+
<div className="field">
|
|
868
|
+
<label>{product.Attachments.label}</label>
|
|
869
|
+
<input type="file" multiple onChange={handleFilesUpload} />
|
|
870
|
+
<ul>
|
|
871
|
+
{currentFiles.map((file) => (
|
|
872
|
+
<li key={file._id}>
|
|
873
|
+
{file.FileName} ({file.Size} bytes)
|
|
874
|
+
<button type="button" onClick={() => handleFileDownload(file._id)}>Download</button>
|
|
875
|
+
<button type="button" onClick={() => handleFileDelete(file._id)}>Delete</button>
|
|
876
|
+
</li>
|
|
877
|
+
))}
|
|
878
|
+
</ul>
|
|
879
|
+
</div>
|
|
880
|
+
|
|
881
|
+
<button type="submit" disabled={isSubmitting}>
|
|
882
|
+
{isSubmitting ? "Saving..." : "Save"}
|
|
883
|
+
</button>
|
|
884
|
+
</form>
|
|
885
|
+
);
|
|
886
|
+
}
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
**Key points:**
|
|
890
|
+
- `upload()` validates file extensions client-side (throws on unsupported types)
|
|
891
|
+
- Image upload replaces the current value; File upload appends to the array
|
|
892
|
+
- `getDownloadUrl("thumbnail")` / `getDownloadUrl("preview")` request backend-generated variants
|
|
893
|
+
- Readonly Image/File fields only have `getDownloadUrl` — no `upload` or `deleteAttachment`
|
|
894
|
+
|
|
895
|
+
---
|
|
896
|
+
|
|
723
897
|
## Constants Reference
|
|
724
898
|
|
|
725
899
|
```typescript
|
package/docs/workflow.md
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
StringField,
|
|
28
28
|
NumberField,
|
|
29
29
|
BooleanField,
|
|
30
|
+
DateField,
|
|
30
31
|
DateTimeField,
|
|
31
32
|
SelectField,
|
|
32
33
|
ReferenceField,
|
|
@@ -52,8 +53,9 @@ import type {
|
|
|
52
53
|
|
|
53
54
|
```typescript
|
|
54
55
|
interface WorkflowStartResponseType {
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
BPInstanceId: string; // Business process instance ID
|
|
57
|
+
ActivityId: string; // First activity ID in the workflow
|
|
58
|
+
_id: string; // Activity instance ID
|
|
57
59
|
}
|
|
58
60
|
```
|
|
59
61
|
|
|
@@ -170,17 +172,13 @@ export class EmployeeInputActivity extends Activity<
|
|
|
170
172
|
activityId: "EMPLOYEE_INPUT",
|
|
171
173
|
};
|
|
172
174
|
|
|
173
|
-
readonly StartDate = new
|
|
174
|
-
readonly EndDate = new
|
|
175
|
+
readonly StartDate = new DateField({ "_id": "StartDate", "Name": "Start Date", "Type": "Date" });
|
|
176
|
+
readonly EndDate = new DateField({ "_id": "EndDate", "Name": "End Date", "Type": "Date" });
|
|
175
177
|
readonly LeaveType = new SelectField<"PTO" | "Sick" | "Parental">({
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
{ value: "PTO", label: "PTO" },
|
|
179
|
-
{ value: "Sick", label: "Sick" },
|
|
180
|
-
{ value: "Parental", label: "Parental" },
|
|
181
|
-
],
|
|
178
|
+
"_id": "LeaveType", "Name": "Leave Type", "Type": "String",
|
|
179
|
+
"Constraint": { "Enum": ["PTO", "Sick", "Parental"] },
|
|
182
180
|
});
|
|
183
|
-
readonly LeaveDays = new NumberField({
|
|
181
|
+
readonly LeaveDays = new NumberField({ "_id": "LeaveDays", "Name": "Leave Days", "Type": "Number", "ReadOnly": true });
|
|
184
182
|
}
|
|
185
183
|
|
|
186
184
|
export type ManagerApprovalEntityType = {
|
|
@@ -198,14 +196,15 @@ export class ManagerApprovalActivity extends Activity<
|
|
|
198
196
|
activityId: "MANAGER_APPROVAL",
|
|
199
197
|
};
|
|
200
198
|
|
|
201
|
-
readonly ManagerApproved = new BooleanField({
|
|
202
|
-
readonly ManagerReason = new StringField({
|
|
199
|
+
readonly ManagerApproved = new BooleanField({ "_id": "ManagerApproved", "Name": "Manager Approved", "Type": "Boolean" });
|
|
200
|
+
readonly ManagerReason = new StringField({ "_id": "ManagerReason", "Name": "Manager's Reason", "Type": "String" });
|
|
203
201
|
}
|
|
204
202
|
|
|
205
203
|
// --- Workflow class ---
|
|
206
204
|
|
|
207
205
|
export class SimpleLeaveProcess {
|
|
208
206
|
async start(): Promise<WorkflowStartResponseType>;
|
|
207
|
+
async progress(instance_id: string): Promise<ActivityProgressType[]>;
|
|
209
208
|
|
|
210
209
|
employeeInputActivity(): EmployeeInputActivity;
|
|
211
210
|
managerApprovalActivity(): ManagerApprovalActivity;
|
|
@@ -222,23 +221,24 @@ Start a new workflow instance.
|
|
|
222
221
|
|
|
223
222
|
```typescript
|
|
224
223
|
const wf = new SimpleLeaveProcess();
|
|
225
|
-
const {
|
|
224
|
+
const { BPInstanceId, ActivityId, _id } = await wf.start();
|
|
226
225
|
```
|
|
227
226
|
|
|
228
|
-
### progress()
|
|
227
|
+
### progress(instance_id)
|
|
229
228
|
|
|
230
|
-
Get
|
|
229
|
+
Get progress for a specific process instance. Returns a list of progress entries for each stage/activity.
|
|
231
230
|
|
|
232
231
|
```typescript
|
|
233
232
|
const wf = new SimpleLeaveProcess();
|
|
234
|
-
const
|
|
233
|
+
const { BPInstanceId } = await wf.start();
|
|
234
|
+
const progressList = await wf.progress(BPInstanceId);
|
|
235
235
|
// progressList: ActivityProgressType[]
|
|
236
236
|
for (const entry of progressList) {
|
|
237
237
|
console.log(entry._name, entry.Status, entry.CompletedAt);
|
|
238
238
|
}
|
|
239
239
|
```
|
|
240
240
|
|
|
241
|
-
**Endpoint:** `GET /api/app/process/{bp_id}/progress`
|
|
241
|
+
**Endpoint:** `GET /api/app/process/{bp_id}/{instance_id}/progress`
|
|
242
242
|
|
|
243
243
|
---
|
|
244
244
|
|
|
@@ -250,54 +250,44 @@ Each Activity class provides methods to query and access activity instances. Lis
|
|
|
250
250
|
const activity = wf.employeeInputActivity();
|
|
251
251
|
```
|
|
252
252
|
|
|
253
|
-
### getInProgressList(
|
|
253
|
+
### getInProgressList()
|
|
254
254
|
|
|
255
|
-
List in-progress activity instances
|
|
255
|
+
List in-progress activity instances. Filtering and pagination are handled server-side automatically.
|
|
256
256
|
|
|
257
257
|
```typescript
|
|
258
|
-
const result = await activity.getInProgressList(
|
|
259
|
-
Page: 1,
|
|
260
|
-
PageSize: 20,
|
|
261
|
-
});
|
|
258
|
+
const result = await activity.getInProgressList();
|
|
262
259
|
|
|
263
260
|
for (const item of result.Data) {
|
|
264
261
|
console.log(item._id, item.Status, item.StartDate);
|
|
265
262
|
}
|
|
266
263
|
```
|
|
267
264
|
|
|
268
|
-
### getCompletedList(
|
|
265
|
+
### getCompletedList()
|
|
269
266
|
|
|
270
|
-
List completed activity instances
|
|
267
|
+
List completed activity instances. Filtering and pagination are handled server-side automatically.
|
|
271
268
|
|
|
272
269
|
```typescript
|
|
273
|
-
const result = await activity.getCompletedList(
|
|
274
|
-
Page: 1,
|
|
275
|
-
PageSize: 20,
|
|
276
|
-
});
|
|
270
|
+
const result = await activity.getCompletedList();
|
|
277
271
|
|
|
278
272
|
for (const item of result.Data) {
|
|
279
273
|
console.log(item._id, item.CompletedAt, item.StartDate);
|
|
280
274
|
}
|
|
281
275
|
```
|
|
282
276
|
|
|
283
|
-
### inProgressMetrics(
|
|
277
|
+
### inProgressMetrics()
|
|
284
278
|
|
|
285
279
|
Get aggregated metrics for in-progress activity instances.
|
|
286
280
|
|
|
287
281
|
```typescript
|
|
288
|
-
const metrics = await activity.inProgressMetrics(
|
|
289
|
-
Metric: [{ Field: "Status", Function: "Count" }],
|
|
290
|
-
});
|
|
282
|
+
const metrics = await activity.inProgressMetrics();
|
|
291
283
|
```
|
|
292
284
|
|
|
293
|
-
### completedMetrics(
|
|
285
|
+
### completedMetrics()
|
|
294
286
|
|
|
295
287
|
Get aggregated metrics for completed activity instances.
|
|
296
288
|
|
|
297
289
|
```typescript
|
|
298
|
-
const metrics = await activity.completedMetrics(
|
|
299
|
-
Metric: [{ Field: "Status", Function: "Count" }],
|
|
300
|
-
});
|
|
290
|
+
const metrics = await activity.completedMetrics();
|
|
301
291
|
```
|
|
302
292
|
|
|
303
293
|
### getInstance(instanceId)
|
|
@@ -311,7 +301,7 @@ const instance = await activity.getInstance("inst_abc123");
|
|
|
311
301
|
instance._id; // "inst_abc123"
|
|
312
302
|
instance.StartDate.get(); // read value (typed)
|
|
313
303
|
instance.StartDate.set("2026-03-01"); // set value (editable fields only)
|
|
314
|
-
instance.StartDate.meta; // {
|
|
304
|
+
instance.StartDate.meta; // { _id: "StartDate", Name: "Start Date", Type: "Date" }
|
|
315
305
|
instance.LeaveDays.get(); // read computed/readonly field
|
|
316
306
|
// instance.LeaveDays.set(...) // NOT available — readonly, no set()
|
|
317
307
|
|
|
@@ -401,10 +391,10 @@ import { SimpleLeaveProcess } from "@/bdo/workflows/SimpleLeaveProcess";
|
|
|
401
391
|
import type { WorkflowStartResponseType } from "@ram_28/kf-ai-sdk/workflow";
|
|
402
392
|
|
|
403
393
|
const wf = new SimpleLeaveProcess();
|
|
404
|
-
const {
|
|
394
|
+
const { BPInstanceId, ActivityId, _id }: WorkflowStartResponseType = await wf.start();
|
|
405
395
|
|
|
406
|
-
// Check
|
|
407
|
-
const progress = await wf.progress();
|
|
396
|
+
// Check progress at any time
|
|
397
|
+
const progress = await wf.progress(BPInstanceId);
|
|
408
398
|
```
|
|
409
399
|
|
|
410
400
|
### Step 2 — React component with useActivityForm
|
|
@@ -532,7 +522,7 @@ function LeaveRequestPage() {
|
|
|
532
522
|
const startLeaveRequest = async () => {
|
|
533
523
|
const wf = new SimpleLeaveProcess();
|
|
534
524
|
const result: WorkflowStartResponseType = await wf.start();
|
|
535
|
-
setInstanceId(result.
|
|
525
|
+
setInstanceId(result._id);
|
|
536
526
|
};
|
|
537
527
|
|
|
538
528
|
if (!instanceId) {
|
|
@@ -560,10 +550,7 @@ import { SimpleLeaveProcess } from "@/bdo/workflows/SimpleLeaveProcess";
|
|
|
560
550
|
const wf = new SimpleLeaveProcess();
|
|
561
551
|
const activity = wf.managerApprovalActivity();
|
|
562
552
|
|
|
563
|
-
const result = await activity.getInProgressList(
|
|
564
|
-
Page: 1,
|
|
565
|
-
PageSize: 20,
|
|
566
|
-
});
|
|
553
|
+
const result = await activity.getInProgressList();
|
|
567
554
|
|
|
568
555
|
for (const item of result.Data) {
|
|
569
556
|
console.log(item._id, item.Status, item.AssignedTo.username);
|
|
@@ -685,7 +672,7 @@ const progress = await instance.progress();
|
|
|
685
672
|
|
|
686
673
|
## Filtering Reference
|
|
687
674
|
|
|
688
|
-
Status filtering is built into the method names (`getInProgressList` / `getCompletedList`),
|
|
675
|
+
Status filtering is built into the method names (`getInProgressList` / `getCompletedList`). All filtering (by current user, activity status, and activity ID) and pagination are handled server-side automatically — no client-side options are needed.
|
|
689
676
|
|
|
690
677
|
### In-progress items
|
|
691
678
|
|
|
@@ -699,37 +686,12 @@ const result = await activity.getInProgressList();
|
|
|
699
686
|
const result = await activity.getCompletedList();
|
|
700
687
|
```
|
|
701
688
|
|
|
702
|
-
###
|
|
703
|
-
|
|
704
|
-
```typescript
|
|
705
|
-
const result = await activity.getInProgressList({
|
|
706
|
-
Filter: {
|
|
707
|
-
Operator: "And",
|
|
708
|
-
Condition: [
|
|
709
|
-
{ LHSField: "AssignedTo._id", Operator: "EQ", RHSValue: userId, RHSType: "Constant" },
|
|
710
|
-
],
|
|
711
|
-
},
|
|
712
|
-
});
|
|
713
|
-
```
|
|
714
|
-
|
|
715
|
-
### Assigned to a specific user (completed)
|
|
716
|
-
|
|
717
|
-
```typescript
|
|
718
|
-
const result = await activity.getCompletedList({
|
|
719
|
-
Filter: {
|
|
720
|
-
Operator: "And",
|
|
721
|
-
Condition: [
|
|
722
|
-
{ LHSField: "AssignedTo._id", Operator: "EQ", RHSValue: userId, RHSType: "Constant" },
|
|
723
|
-
],
|
|
724
|
-
},
|
|
725
|
-
});
|
|
726
|
-
```
|
|
727
|
-
|
|
728
|
-
### Global process progress
|
|
689
|
+
### Process progress
|
|
729
690
|
|
|
730
691
|
```typescript
|
|
731
692
|
const wf = new SimpleLeaveProcess();
|
|
732
|
-
const
|
|
693
|
+
const { BPInstanceId } = await wf.start();
|
|
694
|
+
const progressList = await wf.progress(BPInstanceId);
|
|
733
695
|
// Returns ActivityProgressType[] — one entry per activity in the process
|
|
734
696
|
```
|
|
735
697
|
|
package/package.json
CHANGED
package/sdk/api/client.ts
CHANGED
|
@@ -16,6 +16,10 @@ import type {
|
|
|
16
16
|
DraftResponseType,
|
|
17
17
|
FieldsResponseType,
|
|
18
18
|
FetchFieldOptionType,
|
|
19
|
+
FileUploadRequestType,
|
|
20
|
+
FileUploadResponseType,
|
|
21
|
+
FileDownloadResponseType,
|
|
22
|
+
AttachmentViewType,
|
|
19
23
|
} from "../types/common";
|
|
20
24
|
|
|
21
25
|
/**
|
|
@@ -115,6 +119,51 @@ export interface ResourceClientType<T = any> {
|
|
|
115
119
|
instanceId: string,
|
|
116
120
|
fieldId: string,
|
|
117
121
|
): Promise<TResult[]>;
|
|
122
|
+
|
|
123
|
+
// ============================================================
|
|
124
|
+
// ATTACHMENT OPERATIONS
|
|
125
|
+
// ============================================================
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get signed upload URLs for file attachments
|
|
129
|
+
* POST /{bo_id}/{instance_id}/field/{field_id}/attachment/upload
|
|
130
|
+
*/
|
|
131
|
+
getUploadUrl(
|
|
132
|
+
instanceId: string,
|
|
133
|
+
fieldId: string,
|
|
134
|
+
files: FileUploadRequestType[],
|
|
135
|
+
): Promise<FileUploadResponseType[]>;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get signed download URL for a single attachment
|
|
139
|
+
* GET /{bo_id}/{instance_id}/field/{field_id}/attachment/{attachment_id}/read
|
|
140
|
+
*/
|
|
141
|
+
getDownloadUrl(
|
|
142
|
+
instanceId: string,
|
|
143
|
+
fieldId: string,
|
|
144
|
+
attachmentId: string,
|
|
145
|
+
viewType?: AttachmentViewType,
|
|
146
|
+
): Promise<FileDownloadResponseType>;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get signed download URLs for all attachments on a field
|
|
150
|
+
* GET /{bo_id}/{instance_id}/field/{field_id}/attachment/read
|
|
151
|
+
*/
|
|
152
|
+
getDownloadUrls(
|
|
153
|
+
instanceId: string,
|
|
154
|
+
fieldId: string,
|
|
155
|
+
viewType?: AttachmentViewType,
|
|
156
|
+
): Promise<FileDownloadResponseType[]>;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Delete an attachment
|
|
160
|
+
* DELETE /{bo_id}/{instance_id}/field/{field_id}/attachment/{attachment_id}/delete
|
|
161
|
+
*/
|
|
162
|
+
deleteAttachment(
|
|
163
|
+
instanceId: string,
|
|
164
|
+
fieldId: string,
|
|
165
|
+
attachmentId: string,
|
|
166
|
+
): Promise<void>;
|
|
118
167
|
}
|
|
119
168
|
|
|
120
169
|
/**
|
|
@@ -455,6 +504,102 @@ export function createResourceClient<T = any>(
|
|
|
455
504
|
const responseData: { Data: TResult[] } = await response.json();
|
|
456
505
|
return responseData.Data;
|
|
457
506
|
},
|
|
507
|
+
|
|
508
|
+
// ============================================================
|
|
509
|
+
// ATTACHMENT OPERATIONS
|
|
510
|
+
// ============================================================
|
|
511
|
+
|
|
512
|
+
async getUploadUrl(
|
|
513
|
+
instanceId: string,
|
|
514
|
+
fieldId: string,
|
|
515
|
+
files: FileUploadRequestType[],
|
|
516
|
+
): Promise<FileUploadResponseType[]> {
|
|
517
|
+
const response = await fetch(
|
|
518
|
+
`${baseUrl}${basePath}/${instanceId}/field/${fieldId}/attachment/upload`,
|
|
519
|
+
{
|
|
520
|
+
method: "POST",
|
|
521
|
+
headers: getDefaultHeaders(),
|
|
522
|
+
body: JSON.stringify(files),
|
|
523
|
+
},
|
|
524
|
+
);
|
|
525
|
+
|
|
526
|
+
if (!response.ok) {
|
|
527
|
+
throw new Error(
|
|
528
|
+
`Failed to get upload URL for ${basePath}/${fieldId}: ${response.statusText}`,
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const responseData: { Data: FileUploadResponseType[] } =
|
|
533
|
+
await response.json();
|
|
534
|
+
return responseData.Data;
|
|
535
|
+
},
|
|
536
|
+
|
|
537
|
+
async getDownloadUrl(
|
|
538
|
+
instanceId: string,
|
|
539
|
+
fieldId: string,
|
|
540
|
+
attachmentId: string,
|
|
541
|
+
viewType?: AttachmentViewType,
|
|
542
|
+
): Promise<FileDownloadResponseType> {
|
|
543
|
+
let url = `${baseUrl}${basePath}/${instanceId}/field/${fieldId}/attachment/${attachmentId}/read`;
|
|
544
|
+
if (viewType) url += `?view_type=${viewType}`;
|
|
545
|
+
const response = await fetch(url, {
|
|
546
|
+
method: "GET",
|
|
547
|
+
headers: getDefaultHeaders(),
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
if (!response.ok) {
|
|
551
|
+
throw new Error(
|
|
552
|
+
`Failed to get download URL for ${basePath}/${fieldId}/${attachmentId}: ${response.statusText}`,
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const responseData: { Data: FileDownloadResponseType } =
|
|
557
|
+
await response.json();
|
|
558
|
+
return responseData.Data;
|
|
559
|
+
},
|
|
560
|
+
|
|
561
|
+
async getDownloadUrls(
|
|
562
|
+
instanceId: string,
|
|
563
|
+
fieldId: string,
|
|
564
|
+
viewType?: AttachmentViewType,
|
|
565
|
+
): Promise<FileDownloadResponseType[]> {
|
|
566
|
+
let url = `${baseUrl}${basePath}/${instanceId}/field/${fieldId}/attachment/read`;
|
|
567
|
+
if (viewType) url += `?view_type=${viewType}`;
|
|
568
|
+
const response = await fetch(url, {
|
|
569
|
+
method: "GET",
|
|
570
|
+
headers: getDefaultHeaders(),
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
if (!response.ok) {
|
|
574
|
+
throw new Error(
|
|
575
|
+
`Failed to get download URLs for ${basePath}/${fieldId}: ${response.statusText}`,
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
const responseData: { Data: FileDownloadResponseType[] } =
|
|
580
|
+
await response.json();
|
|
581
|
+
return responseData.Data;
|
|
582
|
+
},
|
|
583
|
+
|
|
584
|
+
async deleteAttachment(
|
|
585
|
+
instanceId: string,
|
|
586
|
+
fieldId: string,
|
|
587
|
+
attachmentId: string,
|
|
588
|
+
): Promise<void> {
|
|
589
|
+
const response = await fetch(
|
|
590
|
+
`${baseUrl}${basePath}/${instanceId}/field/${fieldId}/attachment/${attachmentId}/delete`,
|
|
591
|
+
{
|
|
592
|
+
method: "DELETE",
|
|
593
|
+
headers: getDefaultHeaders(),
|
|
594
|
+
},
|
|
595
|
+
);
|
|
596
|
+
|
|
597
|
+
if (!response.ok) {
|
|
598
|
+
throw new Error(
|
|
599
|
+
`Failed to delete attachment ${attachmentId} for ${basePath}/${fieldId}: ${response.statusText}`,
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
},
|
|
458
603
|
};
|
|
459
604
|
}
|
|
460
605
|
|
package/sdk/api/index.ts
CHANGED
|
@@ -58,6 +58,10 @@ export type {
|
|
|
58
58
|
DraftResponseType,
|
|
59
59
|
// Fields types
|
|
60
60
|
FieldsResponseType,
|
|
61
|
+
// Attachment types
|
|
62
|
+
FileUploadRequestType,
|
|
63
|
+
FileUploadResponseType,
|
|
64
|
+
FileDownloadResponseType,
|
|
61
65
|
} from "../types/common";
|
|
62
66
|
|
|
63
67
|
// Re-export date types from base-fields
|