Repository navigation
fix(console): the approvals drawer's record summary shows the record page's labels and value faces (objectui#11677) - #11702
Merged
objectstack-fleet[bot] merged 1 commit intoOct 6, 2026
Conversation
…d faces The request drawer's record summary read labels from the snapshot (`payload_labels` or a title-cased key) and printed values through a type-blind formatter, so option values, dates and currency showed as stored. It now reads the request object's field declarations and draws each declared field through the record page's own cell dispatch (`resolveCellRendererType` and `getCellRenderer`), under the declared label. Undeclared snapshot keys and unresolved references take no row. Without metadata the card falls back to the snapshot rendering. The injected `owner_id` column is now dropped as bookkeeping via `isSystemManagedField`, which replaces the page's private six-name list, so an object with its own `owner` field no longer shows two "Owner" rows. Rows are keyed by field key instead of label, which removes React's duplicate-key warning, and any label two rows still share is qualified with the field key. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FngvPpdrnhHMdHHq6vwwju
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
This was referenced Oct 6, 2026
objectstack-fleet
Bot
deleted the
claude/issue-11677-approvals-summary-faces
branch
October 6, 2026 07:54
This was referenced Oct 6, 2026
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.
Fixes #11677
Clause-②: no
What changed
The approvals inbox's request drawer now shows its record summary the way the record page shows the record. The only code that changed is the console page
apps/console/src/pages/system/ApprovalsInboxPage.tsx. No published package export, registry input or locale key changes.getObjectSchema(object_name)thatuseHiddenFieldsByObjectalready makes for this object. That read lands on the adapter'sMetadataCache, which de-duplicates in-flight reads, so no network request is added. The new hook isuseRequestFieldDefs.useSafeFieldLabel().fieldLabel, the translator the record page uses. The card no longer usespayload_labelsorprettifyKeyfor it.RecordFieldFace. It passes the declaration and the stored value toresolveCellRendererTypeand thengetCellRendererfrom@object-ui/fields, the chainDetailSectionrenders a field through, and translates the options asDetailSectiondoes. This is the existing helper, imported. ⛔ There is no second formatter. A server-resolvedpayload_displayvalue (a lookup's title) still wins.hiddentrim's.@object-ui/types'isSystemManagedField, the classifier the default list columns, record pickers and related lists already use. It also drops the injectedowner_idcolumn. A label two remaining rows still share is qualified with the field key (withDistinctLabels).The measurement, and three premises it corrected
The real stack was booted: a showcase backend from objectstack
01e0f71a(objectstack dev --seed-admin --fresh) and this branch's console on its own port, driven with Playwright. The same shapes were then rendered through the page in jsdom.Before (merge base
57d82cb), Invoice Dual Sign-off:Owner: Dev Admin,Invoice Number,Owner: linus@example.com,Region: amer,Status: sent,Issued On: 2026-04-19, leadSubtotal: 0. React logged "Encountered two children with the same key" twice per drawer open, live and in jsdom. In jsdom the key argument readsOwner. High-Value Expense:Approved: 0, leadTotal: 8,900.After (this branch, same live stack): Invoice:
Invoice Number: INV-1012,Owner: linus@example.com,Region: AMER,Status: Sent,Issued On: Apr 19,Tax Rate (%): 8, leadTotal: 0. These are the record page's faces for the same record: AMER and Sent badges,Apr 19,TOTAL 0. Expense:Status: Submitted,Submitted On: Yesterday,Approved: 0,Reimbursable: 8900, leadTotal: 8900. No duplicate-key warning on open, and none after approving the invoice from the drawer.Corrected premises:
Owner.owner_idis a declared field. The registry injects it on every business object as asystem: truelookup labelled "Owner". The live metadata read andpayload_labelsboth say so. The showcase invoice also declares its own textowner(its row-level-security anchor) with the same label, so the collision is between two declarations, not between a declaration and a stray key. Droppingowner_idas bookkeeping is what the platform's other default surfaces already do. The label qualifier is the backstop for any other pair of fields with one label.Approved 0is not a boolean.showcase_expense_reportdeclares no boolean field. "Approved" is the label ofapproved_amount, asummaryroll-up, and its value for the demo report is 0. Its record-page face is the summary cell, which prints the stored number. The invoice's "Subtotal" is asummarytoo.Tests
New pin file
ApprovalsInboxPage.summaryFaces-11677.test.tsx, 8 tests. Its fixtures follow the live shapes: the snapshot key order, the nulls, the hydratedaccount,payload_displaywithowner_idonly, bothOwnerlabels inpayload_labels, and the metadata read'sTotalagainst the snapshot'sSubtotal. Each value is compared with the record page's own dispatch rendered standalone, next to literal anchors (EMEA,Sent, raw strings absent). The file covers: both measured requests, oneOwner, no duplicate-key warning, the lead's label and face, undeclared keys, two declared fields sharing a label, and the fail-open path.Fixtures updated beside it:
hiddenFieldTrimdeclares every snapshot key it expects on the card. Under the new rule an undeclared key takes no row once metadata has answered. The trim and the promotion it pins are unchanged.characterizationPinsmoves its_id-trim fixture fromowner_idtovendor_id, becauseowner_idis now dropped as bookkeeping and never reachesprettifyKey.rawPayloadGate: a header comment now names the classifier instead of the removed private list.Reverse verification, committed fix first, then each leg restored and proven (blob equal to HEAD,
git diff HEADempty):isSystemManagedField: 3 red (the twoOwnertests and the fail-open test);withDistinctLabelsremoved: 1 red (two declared fields sharing a label);The duplicate-key assertions sit after the label assertions in each test, so on a defective tree the label assertion goes red first. Their red direction is the merge-base measurement above, not those assertion lines themselves.
Gates, local, at the head this body names:
All at head
e4b9a61. Exit codes were captured before any pipe.pnpm exec vitest run apps/console/(the console project)Test Files 151 passed (151),Tests 1783 passed (1783)pnpm exec vitest run apps/console/src/pages/system/ApprovalsInboxPage.summaryFaces-11677.test.tsxTests 8 passed (8)pnpm --filter @object-ui/console type-check(afterturbo run build --filter='@object-ui/console^...')tsc --noEmit && tsc -b tsconfig.node.json --forceclean;--listFilesincludes the new test filepnpm --filter @object-ui/console lint0 errors; this page has the same warnings as the merge base, none newpnpm check:new-line-citations0 new citation(s)pnpm check:control-bytesOKpnpm check:changeset-claimsNo pending changeset names a file this change touchespnpm check:pending-changeset-literalsNo test source names a pending changesetpnpm check:handler-key-readsOKnode scripts/check-changeset-no-major.mjsNo changeset declares a major bumpnode scripts/check-changeset-presence.mjs5 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)node scripts/check-changeset-fixed.mjsAll workspace packages are in the changeset fixed groupnode scripts/check-changeset-overwrite.mjsNo pre-existing changeset was modified or deletedpnpm check:vi-mock-override-shapeOKpnpm check:test-path-rootsOKpnpm check:phantom-depsEvery in-scope import is declared by the package that publishes itpnpm check:self-importNo package names itself inside its own src/pnpm check:i18n-keysNot run locally, declared to CI: the repository-wide
pnpm lint/pnpm test/pnpm type-checkfarm. The local runs are the console package's own lint, type-check and test project.turboaffected-set reasoning was not used.Acceptance notes
summaryfields. Through the record page's summary cell (FormulaCellRenderer) they print the stored number in mono, without digit grouping or currency:8900where the old card printed8,900. The record page draws them the same way. The gap belongs to that cell in@object-ui/fieldsand is outside this card's file surface. Carrier: none.showcase_invoice.total"Total" and the object declares "Subtotal". The metadata read serves the bundle's label, which the record page shows and this card now shows.payload_labelscarries the object's own label. The spec documentspayload_labelsas localized only for a single-locale project. Observation only.Generated by Claude Code