Skip to content

Commit 8a7ce3d

Browse files
committed
feat(approvals): declare decision actions on sys_approval_request (objectui#2678 P2-4)
Server-declared approve / reject / reassign actions on the request object, so the console's generic action runtime renders and executes them wherever the object is surfaced — the approvals inbox included. Each is a `type:'api'` action targeting the existing REST route ({id} resolves from the row; actorId defaults to the caller server-side), with typed params (comment; reassign's `to`), a pending-only `visible` gate (display-only — the service's pending-approver check stays authoritative), and a confirm on reject. This is the framework half of the P2-4 SDUI migration: future decision capabilities (enterprise act-as included) ship as metadata on this object, not as hand-written inbox buttons. 137 tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ypkQikZ55oWUHUnMebwXA
1 parent d6c3f06 commit 8a7ce3d

1 file changed

Lines changed: 58 additions & 0 deletions

File tree

packages/plugins/plugin-approvals/src/sys-approval-request.object.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,4 +229,62 @@ export const SysApprovalRequest = ObjectSchema.create({
229229
{ fields: ['status', 'updated_at'] },
230230
{ fields: ['submitter_id', 'status'] },
231231
],
232+
233+
// Server-declared decision actions (objectui#2678 P2-4). The console's
234+
// generic action runtime renders and executes these wherever this object is
235+
// surfaced — the approvals inbox included — so new decision capabilities
236+
// (and their params) ship as metadata, not as hand-written buttons. Each
237+
// targets the existing approvals REST route; `{id}` resolves from the row
238+
// and `actorId` defaults to the caller server-side. The service remains the
239+
// authority on who may act (pending-approver check) — `visible` only trims
240+
// the obvious non-pending case.
241+
actions: [
242+
{
243+
name: 'approval_approve',
244+
label: 'Approve',
245+
icon: 'check-circle',
246+
type: 'api',
247+
method: 'POST',
248+
target: '/api/v1/approvals/requests/{id}/approve',
249+
params: [
250+
{ name: 'comment', label: 'Comment', type: 'textarea', required: false },
251+
],
252+
visible: 'record.status == "pending"',
253+
locations: ['record_section', 'list_item'],
254+
successMessage: 'Approved.',
255+
refreshAfter: true,
256+
},
257+
{
258+
name: 'approval_reject',
259+
label: 'Reject',
260+
icon: 'x-circle',
261+
type: 'api',
262+
method: 'POST',
263+
target: '/api/v1/approvals/requests/{id}/reject',
264+
params: [
265+
{ name: 'comment', label: 'Comment', type: 'textarea', required: false },
266+
],
267+
visible: 'record.status == "pending"',
268+
confirmText: 'Reject this request? A rejection is final for every approver.',
269+
locations: ['record_section', 'list_item'],
270+
successMessage: 'Rejected.',
271+
refreshAfter: true,
272+
},
273+
{
274+
name: 'approval_reassign',
275+
label: 'Reassign',
276+
icon: 'arrow-right-left',
277+
type: 'api',
278+
method: 'POST',
279+
target: '/api/v1/approvals/requests/{id}/reassign',
280+
params: [
281+
{ name: 'to', label: 'New approver (user id)', type: 'text', required: true },
282+
{ name: 'comment', label: 'Comment', type: 'textarea', required: false },
283+
],
284+
visible: 'record.status == "pending"',
285+
locations: ['record_section'],
286+
successMessage: 'Reassigned.',
287+
refreshAfter: true,
288+
},
289+
],
232290
});

0 commit comments

Comments
 (0)