Repository navigation
feat(core,app-shell,plugin-view,i18n): the delete confirmation names the record and confirms with a destructive Delete (objectui#11695) - #11744
Conversation
…ecord and confirms with a destructive Delete (objectui#11695) recordDelete.confirmCopy builds the whole delete confirmation once - a title naming the record by the ADR-0079 resolver plus the object label (or counting a batch), the existing question as the body, and a Delete confirm label - and every host's delete dialog shows it: the console list's row and bulk Delete (useObjectActions), the record page's Delete (RecordDetailView) and the registered object-view's grid Delete (plugin-view). ConfirmationHandler's options gain `destructive`, which ActionConfirmDialog paints with the destructive button variant; plugin-view paints its own dialog the same way. New keys in all ten packs. Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
…mation in en and zh (objectui#11695) The console list (real useObjectActions + ActionConfirmDialog under a real I18nProvider), the record page and the registered object-view each show a title naming the record (or counting the batch) and a destructive Delete confirm, in en and zh; a confirmation without `destructive` keeps the primary confirm as the control. Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
…n (objectui#11695) Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
…e's named copy (objectui#11695) Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
… pair (objectui#11695) Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
…`any` (objectui#11695) Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
…ndler (objectui#11695) `ReturnType<typeof vi.fn>` includes a constructable mock and is refused by the test config's tsc against `ConfirmationHandler`. Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
✅ 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
|
Contract reviewServed-tier: Inputs read: card objectui#11695 (body and all 4 comments: triage ① Derived judgmentsPublic-surface and accept-set changes the diff implies, each judged:
Card Done-when and pin, judged against the diff: the confirmation names the record by its title field (ADR-0079 ② Semver levelChangeset Clause-②: ③ Boundary flagsDev deviations (os-dev-report
Open question (plural label; objectui#11728 merged after the build): the seat answered A in Out-of-scope findings (carrier: none, noted, not filed) — each answered:
No Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #11695
Clause-②: yes
Session:
https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8(os-dev seatdomain:ui#3, PM loop round 1).What changes
A record delete confirmation now names what it deletes and confirms with a destructive "Delete" button:
Delete Product "QA Widget 0"?. zh:删除Product“QA Widget 0”?.Delete 3 Product records?(zh删除 3 条 Product 记录?).buttonVariants({ variant: 'destructive' }).Every delete dialog takes this copy from one builder,
recordDelete.confirmCopy(deps, target)in@object-ui/core. It sits beside the existingconfirmText, which is now its one-record body. The name comes fromgetRecordDisplayName, the ADR-0079 resolver the record page already uses for its title. Each host passes its object definition, so a declarednameFieldwins.@object-ui/app-shelluseObjectActions, called by app-shellObjectViewdeleteRecordand the newdeleteRecordsaskonConfirmwith the copy anddestructive: true, then run the delete through the runner with noconfirmText@object-ui/app-shellRecordDetailView, thesys_deleteheader actionobject-viewgrid Delete@object-ui/plugin-viewObjectView,deleteConfirmDialog@object-ui/app-shellActionConfirmDialogoptions.destructiveis set. Its options type is now the publishedConfirmationHandleroptions, not a copyThe
ConfirmationHandleroptions in@object-ui/coregaindestructive?: boolean. Every other confirmation keeps "Confirm Action" / "Continue" in the primary style. TheactionConfirm.*text is unchanged.packages/components/src/ui/alert-dialog.tsxis not touched (No-Touch zone, AGENTS.md #7). The destructive style is a className override at each call site. objectui#8978'sactionVariantuses the same approach in thealert-dialogrenderer.Premise check on origin/main
0cfe772, hypothesis by hypothesisdeleteConfirmDialogusedactionConfirm.title/actionConfirm.confirm, withrecordDelete.confirmTextas the body. Held.actionConfirm.title/.confirmare shared. Held.ActionConfirmDialogfalls back to them for every action-runner confirmation. Their text is unchanged; the delete has its own keys.RecordDetailViewinpackages/app-shell, notpackages/plugin-detail. Itssys_deleteaction askeddetail.deleteConfirmationthrough the page's confirm handler with one argument.onDeletecallsuseObjectActions.deleteRecord, which handed the action runner aconfirmText.useObjectActions.ts,ActionConfirmDialog.tsxandObjectView.tsx.getRecordDisplayName(@object-ui/core). Reused, no new resolver.objectPluralLabel) was still open when this was built (REST read before implementation:merged: false). So the batch title uses the singular label with a count family that mirrorsobjectActions.bulkDeleteSuccess(Delete 3 Product records?). That is the same shape as the toast that follows. It has merged since (main6be0f7a). The Acceptance notes explain why this PR still keeps the count-agreeing noun.objectui#8978: the card body says it "added the destructive confirm button to
alert-dialog", and the triage says there is "no destructive variant". Both are partly right:actionVariantto thealert-dialogrenderer (packages/components/src/renderers/overlay/alert-dialog.tsx), as a className override.ui/alert-dialog.tsxprimitive still bakes inbuttonVariants()with no variant.Keys
objectActions:deleteConfirmTitle({{label}},{{name}});bulkDeleteConfirmTitlecount family ({{count}},{{label}}). Each pack's slots mirror its ownbulkDeleteSuccessfamily: es/fr/pt_many, ru_few/_many, ar all six;deleteConfirmButton.actionConfirm.title/.confirm, which that host no longer asks for.de-quote-pairing-3876.test.tsmoves from 70 to 71 for the title's one matched „…“ pair.The console list now passes its translated object label to
useObjectActions(objectLabel(objectDef), the label its header shows), so the delete toasts use that label too. With no translation bundle it isobjectDef.label, as before.Tests and gates
The package suites ran at
c8f5c14. The final HEADc3110bediffers from it only by a type annotation in one test file. The changed test files and the app-shell type-check were re-run atc3110be.pnpm exec vitest run packages/app-shell/ --shard=N/4, N = 1..4c8f5c14pnpm exec vitest run packages/core/ packages/i18n/ packages/plugin-view/c8f5c14c3110bepnpm --filter @object-ui/core type-check/@object-ui/i18nc8f5c14pnpm --filter @object-ui/plugin-view type-checkafterpnpm --workspace-concurrency=2 --filter '@object-ui/plugin-view^...' buildc8f5c14pnpm --filter @object-ui/app-shell type-checkafter building its closurec3110betsc --noEmitandtsc -p tsconfig.test.json. Its--listFilesincludes the three changed app-shell testspnpm exec eslintover the 23 changed TS filesc3110becheck:control-bytes,check:test-path-roots,check:changeset-claims,check:pending-changeset-literals,check:i18n-keys,check:i18n-drift,check:i18n-dead-keys,check:i18n-designer-parity,check:new-line-citations,scripts/check-changeset-presence.mjsc3110becheck:readme-exportsc3110beConsole compile: narrowed, not run.
apps/consoleresolves@object-ui/*through builtdist; its tsconfig has nopaths.useObjectActions|ActionConfirmDialog|ConfirmDialogState|ConfirmationHandler|recordDeleteinapps/console/srcfinds one hit, in a comment.from '@object-ui/app-shell'does find the console's real imports.Reverse verification (ablation)
Every leg ran through objectstack's
scripts/ablation-replace.mjsin WRAP mode, which checks three things:git diff HEAD.The expected direction for every leg was red.
ActionConfirmDialog: theoptions.destructiveclassName replaced byclassName={undefined}bg-destructive. The primary-style controls stayed greenrecordDelete.confirmCopy:deleteConfirmButtonreplaced byactionConfirm.confirmuseObjectActions.deleteRecord:objectDefdropped from the copy's depsDelete Product "SKU-1"?(a record key) instead of the declarednameFieldvalueRecordDetailView:destructive: truemisspelleddestructivtsc --noEmitreports TS2561 on that key against the rebuiltConfirmationHandleroptionsAcceptance notes
console.objectView.bulkDeleteConfirm("Delete N selected records? This cannot be undone."), so the title and the body both ask. The claim limited locale edits to new delete keys, so no existing value was changed.execute.useObjectActionsnow asks beforeexecute, so the hook'sloadingis false while the dialog is open. No code in the repo reads that hook'sloadingorerror. A cancel still returns the runner's former{ success: false, error: 'Action cancelled by user' }.Record #idfallback, the same one every other surface shows.plugin-detailDetailViewstill deletes through the nativewindow.confirm, with the authorabledeleteConfirmationor the genericdetail.deleteConfirmation. That is the drawer and panel detail used by gantt rows and similar.recordDelete.runtoasts the reset. Not measured through a public entry point.objectSchema.label, the one its form titles and toasts already use.ded4494) is textually clean (git merge-tree). main's de pack quote count is unchanged since the base, so the census pin does not collide.Generated by Claude Code