Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/25501-approval-timeline-step-label.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@objectstack/plugin-approvals': patch
---

fix: the approval request page's Timeline shows each action's step by its label, not the node's machine id

Clause-②: no
Metadata-protocol: none

The Timeline tab of the `sys_approval_request` record page lists the request's `sys_approval_action` rows. Its step column asked for `step_name`, which holds the Approval node's id (for example `dual_signoff`), so every row showed that id. The column is now `step_label`, the step's label that the service writes on every row and the boot backfill fills on older rows. The strip's step chip (`current_step_label`) and the object's own lists already show this label.
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,23 @@ describe('sys_approval_request detail page — the slot map', () => {
expect((SysApprovalAction.fields as AnyRec).request_id).toMatchObject({ type: 'lookup', reference: 'sys_approval_request' });
});

it('names each Timeline step by its label, step_label, never by the node\'s machine id, step_name', () => {
// Both are stored on every `sys_approval_action` row: `step_name` is the
// Approval node's id (`dual_signoff`), `step_label` the step's label the
// service copies from the request (`actionRow`). The existence check below
// cannot tell them apart, since both are declared fields, so the choice is
// pinned here, the way the strip's `current_step_label` is.
const [timeline] = TAB_ITEMS[1].children as AnyRec[];
const columns = timeline.properties.columns as string[];
const fields = SysApprovalAction.fields as AnyRec;
expect({ label: columns.includes('step_label'), machine: columns.includes('step_name') })
.toEqual({ label: true, machine: false });
expect(fields.step_label).toMatchObject({ type: 'text', label: 'Approval Step' });
// Control: the machine id stays a declared field. It is dropped from the
// Timeline, not from the object.
expect(fields.step_name).toMatchObject({ type: 'text' });
});

it('every field the page names exists on its object', () => {
const requestFields = Object.keys(SysApprovalRequest.fields);
const actionFields = Object.keys(SysApprovalAction.fields);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ import type { Page } from '@objectstack/spec/ui';
* above, so the Details tab does not draw it as a row.
* - `tabs` is authored: Details (`record:details`, a bare node: see the
* strip above) and Timeline — `sys_approval_action` as a
* related list keyed on `request_id`, newest first. The `tabs` slot is
* related list keyed on `request_id`, newest first. Its step column is
* `step_label` (the step's label, stored on every row), not `step_name`
* (the node's machine id, such as `dual_signoff`), as the strip's step
* chip is `current_step_label`. The `tabs` slot is
* authored rather than `details` because the console builds the tab strip
* from `tabs` when both are present; the details body therefore lives in
* the first tab here, and the page authors no `details` slot.
Expand Down Expand Up @@ -120,7 +123,7 @@ export const SysApprovalRequestDetailPage: Page = {
properties: {
objectName: 'sys_approval_action',
relationshipField: 'request_id',
columns: ['created_at', 'action', 'actor_id', 'step_name', 'comment', 'attachments'],
columns: ['created_at', 'action', 'actor_id', 'step_label', 'comment', 'attachments'],
sort: [{ field: 'created_at', order: 'desc' }],
limit: 50,
showViewAll: true,
Expand Down
Loading