diff --git a/.changeset/approvals-decision-attachments.md b/.changeset/approvals-decision-attachments.md new file mode 100644 index 0000000000..39ee142bc6 --- /dev/null +++ b/.changeset/approvals-decision-attachments.md @@ -0,0 +1,18 @@ +--- +"@objectstack/plugin-approvals": minor +--- + +feat(approvals): declare file attachments on approve/reject decisions + +The declared `approval_approve` / `approval_reject` actions on +`sys_approval_request` gain an optional multi-file `attachments` param +(`type: 'file'`, `multiple`). The console renders `type:'file'` action params +through the shared upload widget (objectui ADR-0059) and POSTs the resolved +`attachments: string[]`, so a reviewer can attach supporting files to a +decision through the generic declared-action dialog — letting the approvals +inbox retire its hand-wired attachment composer (objectui#2698). + +Purely additive metadata: the decision route already forwards +`body.attachments` to `ApprovalService.decide`, and the +`sys_approval_action.attachments` column (file, multiple) already persists them +(#3266/#3274). No service or route change. diff --git a/packages/plugins/plugin-approvals/src/sys-approval-request.object.test.ts b/packages/plugins/plugin-approvals/src/sys-approval-request.object.test.ts index 4dbb5c52f4..e293ea90d9 100644 --- a/packages/plugins/plugin-approvals/src/sys-approval-request.object.test.ts +++ b/packages/plugins/plugin-approvals/src/sys-approval-request.object.test.ts @@ -91,4 +91,12 @@ describe('sys_approval_request declared actions', () => { expect(c.required ?? false).toBe(false); } }); + + it('approve/reject collect optional multi-file decision attachments', () => { + for (const name of ['approval_approve', 'approval_reject']) { + const att = byName(name).params.find((p: any) => p.name === 'attachments'); + expect(att, `${name}.attachments`).toMatchObject({ type: 'file', multiple: true }); + expect(att.required ?? false).toBe(false); + } + }); }); diff --git a/packages/plugins/plugin-approvals/src/sys-approval-request.object.ts b/packages/plugins/plugin-approvals/src/sys-approval-request.object.ts index 617c49ff68..2b0d55e03e 100644 --- a/packages/plugins/plugin-approvals/src/sys-approval-request.object.ts +++ b/packages/plugins/plugin-approvals/src/sys-approval-request.object.ts @@ -248,6 +248,10 @@ export const SysApprovalRequest = ObjectSchema.create({ target: '/api/v1/approvals/requests/{id}/approve', params: [ { name: 'comment', label: 'Comment', type: 'textarea', required: false }, + // Decision attachments (#3266). The console renders `type:'file'` params + // through the shared upload widget and POSTs the resolved `attachments: + // string[]`; the decision route persists them on `sys_approval_action`. + { name: 'attachments', label: 'Attachments', type: 'file', multiple: true, required: false }, ], visible: 'record.status == "pending"', locations: ['record_section', 'list_item'], @@ -263,6 +267,7 @@ export const SysApprovalRequest = ObjectSchema.create({ target: '/api/v1/approvals/requests/{id}/reject', params: [ { name: 'comment', label: 'Comment', type: 'textarea', required: false }, + { name: 'attachments', label: 'Attachments', type: 'file', multiple: true, required: false }, ], visible: 'record.status == "pending"', confirmText: 'Reject this request? A rejection is final for every approver.',