feat(approvals-inbox): retire the approve/reject composer for declared actions with file attachments (#2698)#2710
Merged
Conversation
…d actions with file attachments Completes the #2678 P2-4 endgame: the approvals inbox drawer now renders every decision — approve, reject, reassign, send-back, request-info, remind, recall, resubmit — purely through DeclaredActionsBar, with zero hand-wired action UI. Two parts: 1. Generic upload-param support (useful for any declared action, not just approvals): - `FileField` now surfaces the storage id it already receives from the upload adapter (`meta.fileId`) as `file_id` on each emitted file object. - `ActionParamDialog` maps `file`/`image` param values to their `file_id`(s) at submit (`serializeParamValues`, pure + unit-tested) — single → string, multiple → string[] — so an action POSTs `attachments: string[]`, the portable API contract, instead of the widget's rich object. 2. Retire the composer (`ApprovalsInboxPage`): remove the hand-written comment box + quick-phrase chips, the CommentAttachment staging UI, the Approve button + Reject dialog, `doAction`, the upload handlers, and — with the maintainer's sign-off — the admin "act as another identity" (actorOverride) escape hatch, whose proper successor is enterprise act-as (cloud#861). The two DeclaredActionsBar mounts drop their `exclude` so the bar renders approve/reject too (with the declared `attachments` file param). The participant-gated reply box, the "why disabled" hint, and the timeline's attachment chips (name resolution + signed-URL open) are kept. A generic `onDecisionDone` preserves the Fiori "advance to the next waiting item" queue flow off the refreshed row, rather than a per-action handler. Net negative diff. Verified in-browser against a live backend: the drawer has no composer (`textarea#comment` gone), approve/reject render in the bar, the declared Approve dialog shows a file-upload widget, and staging a file + confirming POSTs `{comment, attachments:["<fileId>"]}` — the upload id serialized correctly end-to-end. app-shell + console typecheck clean; app-shell suite green. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ypkQikZ55oWUHUnMebwXA
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
…omposer retirement Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ypkQikZ55oWUHUnMebwXA
Contributor
❌ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
os-zhuang
marked this pull request as ready for review
July 20, 2026 02:38
This was referenced Jul 20, 2026
os-zhuang
added a commit
that referenced
this pull request
Jul 20, 2026
) Since ADR-0059 ActionParamDialog routes every param through the shared form field-widget renderer, so each param emits its widget's own value shape on confirm and those shapes differ per type. Nothing pinned that contract, so a future widget swap could silently change a param's emitted shape and break an endpoint's input contract with no failing test. Add the value-shape parity net: - `paramValueShape.ts` — the emitted-shape contract (`PARAM_VALUE_SHAPES`, one entry per FORM_FIELD_TYPES widget key) plus `expectedParamShape()`, which reuses the SAME `paramToField()` the dialog renders with (so it can't drift from the dialog's own type resolution) and folds in `multiple` and the file/image fileId serialization. `classifyValueShape()` backs the render proofs. - `paramValueShape.test.ts` — the drift guard (mirrors paramToField.test.ts's style): coverage of every FORM_FIELD_TYPES key, and the endpoint contracts pinned by value (number→number, boolean→boolean, date→string, select→string / select+multiple→string[], lookup→id(s), file→fileId(s)). - ActionParamDialog.test.tsx — contract-tied render proofs that drive the REAL widget (number/datetime/time/select+multiple/file) and assert the value the dialog resolves classifies to the declared contract, so a silent onChange swap fails at the render layer too, not just in the table. - Document the emitted-shape table in ADR-0059 and the app-shell README. The file/image → fileId serialization itself is owned by #2698/#2710 (`serializeParamValues`); this net asserts the post-serialize endpoint shape and the general contract for the non-upload types. Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Completes the #2678 P2-4 endgame (#2698). Pairs with framework#3332 (declared
attachmentsparam). The approvals inbox drawer now renders every decision — approve, reject, reassign, send-back, request-info, remind, recall, resubmit — purely throughDeclaredActionsBar, with zero hand-wired action UI.What
1. Generic upload-param support (reusable — any declared action, not just approvals)
FileFieldnow surfaces the storage id it already receives from the upload adapter (meta.fileId) asfile_idon each emitted file object (additive; record file-field value shape unchanged).ActionParamDialogmapsfile/imageparam values to theirfile_id(s) at submit —serializeParamValues(pure + unit-tested): single → string,multiple→string[]. So an action POSTsattachments: string[](the portable API contract) instead of the widget's rich{name,url,…}object. The api handler already passes values through untouched, so the body is exactly{comment, attachments:[…]}.2. Retire the composer (
ApprovalsInboxPage)Removed: the hand-written comment box + quick-phrase chips, the
CommentAttachmentstaging UI, the Approve button + Reject dialog,doAction, the upload handlers, and — with maintainer sign-off — the admin "act as another identity" (actorOverride) escape hatch (successor: enterprise act-as, cloud#861). BothDeclaredActionsBarmounts drop theirexclude, so the bar renders approve/reject too (with the declaredattachmentsfile param).Kept: the participant-gated reply box, the "why disabled" hint, and the timeline's attachment chips (name resolution + signed-URL open). A generic
onDecisionDonepreserves the Fiori "advance to the next waiting item" queue flow — driven off the refreshed row rather than a per-action handler (advances when the item is no longer yours: finalized, approved-away in a quorum, reassigned).Net negative diff (~240 lines removed from the inbox).
Verification
Browser, against a live backend (framework main + framework#3332):
textarea#commentgone); approve/reject render in the bar.{comment, attachments:["<fileId>"]}— the upload id serialized correctly end-to-end (the headline of this change). (The observed request was a 403 only because the test admin was the request's submitter, not an approver — an authz fact of the seed, not the change; persistence ofattachmentson a real approver's decision is the pre-existing, testeddecidepath.)serializeParamValuestests); console typecheck clean; lint delta zero (pre-existing React-Compiler advisories unchanged).Refs
framework#3332 (declared
attachmentsparam) · #2700 / #2707 (shared-widget param rendering + upload guard) · #2697 (secondary-button retirement,excludeprop) · #2678 (P2-4) · cloud#861 (enterprise act-as, successor to the removed admin actor-override)🤖 Generated with Claude Code
Generated by Claude Code