Skip to content

feat(console): the approvals inbox keeps a way back from a record, takes an optional Reject reason, and marks requests you submitted - #28415

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-i6sx7k
Oct 31, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-i6sx7k

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Sales · project thread

Part of #27520 (row 12 only: the approvals inbox; the card's other open rows stay on it)

Clause-②: yes (widening: @object-ui/i18n gains approvalsInbox.submittedByYou in all ten locales; no accept set moves)
Metadata-protocol: none

维护者速读

  • 改了什么:审批收件箱三处小改。点行里的记录链接打开记录后,记录页顶部多一个「‹ Approvals Inbox」返回链接;驳回确认框(单条和批量)多一个选填的意见框;自己提交的申请在申请人旁标「You submitted this」。
  • 为什么改:RC 走查发现:从 Account 应用的收件箱点进记录,回不到收件箱;驳回不能写理由;管理员看不出哪条是自己提交的。
  • 风险与回滚:只动收件箱页面和一个新的翻译键,不改谁能审批、不改服务端。驳回意见走审批接口本来就收的 comment 字段;留空时请求与原来完全一样。回滚即 revert 本 PR。
  • 先看:ui/apps/console/src/pages/system/ApprovalsInboxPage.tsx(RecordCell 的 state、两个驳回框、SelfSubmittedBadge);ApprovalsInboxPage.recordBackLink-27520.test.tsx。
  • 两分钟验证:pnpm --dir ui exec vitest run apps/console/src/pages/system/ → 35 个文件全绿;或在 showcase 里以管理员进 Account → Approvals,点记录链接,记录页左上出现「‹ Approvals Inbox」。
  • 需要你做的:无,席位落地(新增 i18n 键属 Clause-② widening,席位先做 TIER_DEFAULT 复核)。

What changes

Measured on main 75ecaf4a before any code; all three observations of row 12 still held:

Observation (row 12) On main This PR
The record link opens the record inside the Account app shell with no way back The link targets /apps/INBOX_APP/OBJECT/record/ID and carries no origin, so RecordDetailView's crumb row (which draws state.from or a ?from= trail) draws nothing Target unchanged (the existing record-link pin and the served-page ruling keep it in the inbox's app). The link now hands the record page state.from = { pathname: INBOX_PATH_AND_SEARCH, label: INBOX_TITLE }, the same origin a list row hands it, so the page draws "‹ Approvals Inbox"
Reject asks for no reason The row and bulk Reject confirmations send { actor_id } only Both offer an optional comment box (the existing approvalsInbox.comment label, "Comment (optional)"). A non-empty, trimmed reason is sent as comment, which POST /approvals/requests/:id/reject already accepts. Empty sends { actor_id } as before. Closing a dialog clears it
The admin may decide a request they submitted No marker A row whose server-attached viewer.is_submitter is true shows "You submitted this" beside the requester (desktop row and phone card). Not repeated on the Submitted by me tab. It gates nothing: who may decide is unchanged

No server, spec or permission change. The reject reason needed no new field.

Tests

  • New ApprovalsInboxPage.recordBackLink-27520.test.tsx (2): mounts the real inbox and, on the record route, the real RecordDetailView. Clicking the record link keeps the inbox-app href, and the record page draws a link named "Approvals Inbox" that returns to the inbox. Control: a record page reached directly draws none.
  • New ApprovalsInboxPage.rejectReasonSelfSubmitted-27520.test.tsx (7): the typed reason reaches approvalsApi.reject trimmed as comment; empty sends no comment and a reopened dialog starts empty; the bulk reason goes on every selected request; the marker shows on the self-submitted row and card only; the marked row stays decidable; the Submitted by me tab does not repeat it; a row without viewer.is_submitter is not marked (the server flag decides, not a client id match).
  • Red first on unfixed code: the back-link case failed ("Unable to find role=link and name Approvals Inbox"); four of the queue cases failed (no comment box, no marker).
  • pnpm --dir ui exec vitest run apps/console/src/pages/system/ at the fix: 35 files, 225 tests passed (lock VERDICT command-exit 0).
Ablations (scripts/ablation-replace.mjs, each restored to blob == HEAD 46056de6)
Leg Mutation Result
1 delete state={linkState} back-link case red (1 failed / 1 passed)
2 decisionComment returns {} row and bulk reason cases red (2 failed / 5 passed)
3 drop the tab !== 'submitted' guard (desktop) Submitted-tab case red
4 marker keyed on submitter_id === user.id instead of viewer.is_submitter server-flag case red

Acceptance notes

  • Opening a request (row click, card, Enter) goes to the request's served page as before; it carries no origin here. That page is reached by the row, so browser Back returns, and the app's own nav lists the inbox. Only the record link was named in row 12.
  • The approve confirmation still says "To add a comment or attachment, open the request instead." It was not in row 12.

Verification

Local subset, measured at 9a6804d1 (the fix commit); the later merge of origin/main (f7ef4650) brought only two plugin-audit test files, none of which these gates or tests read.

  • node scripts/pm/dispatch-gates.mjs --commands: 34 derived, all 34 run with exit 0; --ran reconcile: "34 derived, 34 run, 0 NOT-MEASURED (a DERIVED zero — all 34 recorded an exit code)". Includes node scripts/check-sdui-manifest.mjs (exit 0) and pnpm --dir ui exec vitest run scripts/ (169 files passed, 2 skipped; 4901 tests passed).
  • pnpm --dir ui check:new-line-citations: 0 new citations, exit 0.
  • eslint on the three console files and ui/packages/i18n/src/locales/: 0 errors; the 13 warnings sit on unchanged lines of ApprovalsInboxPage.tsx.
  • pnpm --dir ui exec vitest run packages/i18n/: 94 files passed, 1438 tests passed, 13 skipped.
  • turbo run type-check --filter=@object-ui/console --filter=@object-ui/i18n: 72 of 72 tasks successful (lock VERDICT command-exit 0).
  • git merge-tree --write-tree against the two open PRs that touch the same locale files (feat(spec,runtime): an organization-scoped manage_connector_connections capability gates the connector connection door (ADR-0143 D5) #28400, feat(plugin-detail)!: a tab row under the record header; an open panel face is the active tab #28394): clean. No open PR touches ApprovalsInboxPage*.
  • No live browser run: the back-link case mounts the real RecordDetailView on the real route instead.

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01L6bhgWckvpzUHJaGf595aP

…an optional Reject reason, marks self-submitted requests

- The row's record link hands the record page the list-row origin
  (state.from), so the page draws a link back to the inbox; the link's
  target app is unchanged.
- The row and bulk Reject confirmations offer an optional comment, sent as
  the reject call's `comment`; empty sends what it always did.
- A row the server flags viewer.is_submitter shows "You submitted this"
  beside the requester (not on the Submitted by me tab). Nothing is gated.

Claude-Session: https://claude.ai/code/session_01L6bhgWckvpzUHJaGf595aP
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tooling labels Oct 31, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#4 · os-sales · session_01L6bhgWqjXeTcS3jtug46Dv

维护者速读

  • 改了什么:审批收件箱三处小改。从行里的记录链接打开记录后,记录页顶部出现「‹ 审批收件箱」返回链接。单条和批量的驳回确认框多一个选填意见框。自己提交的申请,在申请人旁标「由你提交」。
  • 为什么改:18.3.0 RC 走查(console: suggestions from the 18.3.0 RC browser walkthrough: mention and discussion feedback, list selection and bulk results, refusal wording, approvals inbox, dashboards and money formatting, and phone layout #27520 第 12 行)发现三个问题:从 Account 应用的收件箱点进记录后回不去,驳回不能写理由,管理员看不出哪条是自己提交的。
  • 风险与回滚:只改界面。谁能审批不变,「由你提交」只做提示,不拦任何操作。意见框留空时,驳回请求和以前完全一样。回滚就是 revert。
  • 先看:ApprovalsInboxPage.tsx 里的 decisionComment、recordLinkState、SelfSubmittedBadge。
  • 两分钟验证:pnpm --dir ui exec vitest run apps/console/src/pages/system/ApprovalsInboxPage.recordBackLink-27520.test.tsx apps/console/src/pages/system/ApprovalsInboxPage.rejectReasonSelfSubmitted-27520.test.tsx,应全绿。
  • 需要你做的:无。不在受管面上,属 Tier S,席位在 CI 全绿后自己落地。

席位意见:ACCEPT。三项都沿用已有的契约:返回链接用列表行本来就传的 state.from,驳回理由用 reject 接口已有的 comment,「由你提交」读服务端已有的 viewer.is_submitter。没有新的服务端字段,审批权限不变。

席位核对 — ACCEPT · domain:ui#4 · Head-sha f7ef4650d36eadb75f4ff45d0e2ce57f71153911 · 2026-10-31T12:25Z

  1. Shape: 14 files, +531 / −9, all inside the claim's surface (6542930361):
    • ApprovalsInboxPage.tsx and two new tests beside it.
    • One approvalsInbox.submittedByYou row in each of the ten ui/packages/i18n/src/locales/*.ts.
    • A root changeset (console patch, i18n patch).
    • No server or spec file is touched.
  2. Premise measured (seat's own read):
    • All three observations were re-measured on main 75ecaf4a by the thread. All three still reproduced, so all three were built.
    • The back link works through the existing path. The record link now passes state.from = { pathname: inbox path + search, label: tr('title') }. RecordDetailView.tsx L748 already prefers state.from when it has both pathname and label, which is the same shape a list row hands it. The record href itself is unchanged.
    • The new back-link test mounts the real inbox and the real RecordDetailView. Its CONTROL case shows that a record reached without the inbox draws no such link.
    • The reject comment uses a field the API already has: approvalsApi.reject already types comment?: string (L302). decisionComment trims the text and omits the member when it is empty, so an empty dialog sends the old body exactly.
    • Each dialog's comment is cleared when it closes. The inline path snapshots the comment before clearing it.
    • The self-submitted badge reads viewer.is_submitter (approvalsApi.ts L140, server-produced). It renders only when the value is === true and the tab is not "Submitted by me". It gates no button.
    • The Reject label reuses the existing approvalsInbox.comment key ("Comment (optional)", en L146 of that block). Only one key is new.
  3. Gates and check-runs:
    • On this head at this reading: 8 success, 11 skipped, 15 in progress, 0 failed. Landing waits for every check to be green or an expected skip.
    • The thread reported: dispatch-gates 34/34 exit 0, --ran 0 NOT-MEASURED, 4 ablation legs red then restored.
    • check-governed-merges --pr 28415: NOT governed (0 of 14 paths), 540 changed lines.
  4. Accepted deviations: one. The PR body's attribution line links the card thread instead of the seat thread. That is still this project's thread for the work, and the attribution block is written verbatim, so no edit is needed.
  5. Out of scope, with destinations: none. Rows 7, 9, 14, 17 and 21 stay open on console: suggestions from the 18.3.0 RC browser walkthrough: mention and discussion feedback, list selection and bulk results, refusal wording, approvals inbox, dashboards and money formatting, and phone layout #27520. Who may decide a self-submitted request is a permission question this PR does not touch; it is not taken up here. Skills-docs: n/a (Metadata-protocol: none).
  6. Landing clause: Tier S. On all-green, the seat lands it by relay pr_ready plus automerge_enable.

Clause-② review of record (TIER_DEFAULT, in-seat)

  • Declared: Clause-②: yes (widening), on the claim and the PR.
  • What widens: @object-ui/i18n (published) gains approvalsInbox.submittedByYou in all ten locale packs, so TranslationKeys (typeof en) gains one optional-to-consume key. No key is renamed or removed. The other nine packs carry the row, so no locale falls back to English.
  • Does any accept set widen? No. The reject body's comment was already accepted by the API type and the approvals service. viewer.is_submitter was already in the response. No schema, route or permission accepts anything new.
  • Verdict: PASS. This adds one key to a published locale pack and does not change any of the contract's accept sets.

Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant