Repository navigation
feat(console): a save's toast shows the notice a hook set on the write, one toast per write - #28307
Conversation
…rning event Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibwGdCkvhAHHVnAyVG1
…ast per write Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibwGdCkvhAHHVnAyVG1
|
domain:ui#1 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz Contract-review-claim: |
|
domain:ui#1 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz Contract reviewServed-tier: Inputs: card #28252 (body and all five comments: triage 6532337619 with the one-toast rule, the unblock 6533319341, the seat's claim 6538004798, the dev report 6538812279, the claim amendment 6538826704 which flips Clause-② to widening, accepts ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Verdict reasoning. The adapter reads the key exactly as the spec declares it; the notice rides the claim #28216 built, so every save surface the card names stays at one toast and an unclaimed write gets one from the channel; the grid's new claims are opened before the write and released in Implemented-by: VERDICT: PASS |
|
domain:ui#1 · os-tesla · session_01L9qibzC6RxMfB59EsgAoNN 席位核对ACCEPT · seat 席位意见:服务器在单条记录写入的回答里带
|
Requested by Tesla · project thread
Fixes #28252
Refs #28000
Clause-②: yes (widening:
@object-ui/data-objectstack'sWriteWarningEventgains an optionalnotice;@object-ui/reactgains the typeRecordSaveFeedback,AnswerWarningsEventgains an optionalnotice, andAnswerWarningClaim.take/toastRecordSavemove to it. Those two were added by #28267 and have not been released, so no released signature changes)Metadata-protocol: none
维护者速读
notice,比如「已提交给经理审批」),控制台现在会显示它。记录弹窗(含子表单任务)、整页表单、object-form抽屉/弹窗、表格行内编辑(单行保存和「全部保存」)都在原来的保存提示里把这句话放在标题下面;仍然只弹一个提示。notice,但控制台没有任何地方读它,HotCRM「保存即送审」的提示被丢掉了(Save toast shows the notice a hook or flow attached to the save #28252)。适配器原本只把应答里的记录交出去,notice在那一层就被丢了。create/update返回值不变,只是写入提醒事件多带一个可选字段;只有提醒的保存仍是成功样式,同时有警告时才是警告样式。没有页面认领的写入(例如记录详情页行内编辑的保存条)由写入提醒通道弹一个 info 提示。回滚即 revert 本 PR。ui/packages/react/src/record-save-feedback.ts(take交回RecordSaveFeedback,toastRecordSave的样式选择);ui/packages/plugin-grid/src/ObjectGrid.tsx(行内保存新增认领,「全部保存」按行认领再合并成一个提示);ui/packages/app-shell/src/providers/writeWarningToast.ts(未被认领时通知的落点)。pnpm --dir ui exec vitest run packages/react/src/record-save-feedback.test.ts packages/plugin-grid/src/__tests__/inlineEditSaveNotice-28252.test.tsx,预期 2 个文件全绿。What changes
Which surfaces show the notice (the card's should-fix (b)), one toast per write in every row:
AppContentcrud_success) and its child-form taskRecordFormPage)object-formdrawer and modal (ObjectFormContainer)successMessage, the notice under itObjectGrid)A notice alone keeps the save toast on the success variant (it reports an outcome, never a problem); with warnings too, the toast is the warning variant and the notice leads its lines. The stripped-fields toast stays its own toast, as before.
The adapter (
ui/packages/data-objectstack/src/index.ts). Measured:create/updateresolvedresult.recordand read onlydroppedFieldsandwarningsoff the answer, sonoticewas dropped there.notifyDroppedFieldsnow also readsnotice(a non-empty string, else nothing) onto the write-warning event, and emits the event when the notice is the answer's only news. What the two methods resolve is unchanged. The batch path is not touched: the spec declaresnoticeon the single-record answers only.The claim (
ui/packages/react/src/record-save-feedback.ts).AnswerWarningsEventgainsnotice;take(saved)returnsRecordSaveFeedback(warnings, optionalnotice, the last one parked winning as on the server);toastRecordSave(sink, message, feedback)titles the toast with the surface's message and puts the notice, then the warnings, under it.The channel (
ui/packages/app-shell/src/providers/writeWarningToast.ts).emitWriteWarningparks when the event carries warnings or a notice, still synchronously before its firstawait. Unclaimed, the notice is one toast as the table says.WriteWarningSinkgainsinfo; sonner'stoast, whichAdapterProviderpasses, has it. The type is not exported from the package.The surfaces.
AppContent,RecordFormPageandObjectFormContainerhandtake's result totoastRecordSave(thecrud_successparams carrynoticebesidewarnings).ObjectGrid's default row save opens a claim on the row's update for the length of the save, and "Save All" one per row, read once into the single toast, released in afinally.Tests
New:
ui/packages/plugin-grid/src/__tests__/inlineEditSaveNotice-28252.test.tsx(3): per-row save shows one success toast with the notice, the channel nothing; "Save All" over two rows shows one warning toast, the shared notice once then the warning line; CONTROL: a write the grid did not make keeps the channel toast after the save.onWriteWarning.test.ts(3: create and update carry the notice and still resolve the record; empty or non-string notice emits nothing),record-save-feedback.test.ts(3),writeWarningToast.test.ts(5: claimed update and create take it; unclaimed notice alone is one info toast; beside warnings it leads the one warnings toast; a released claim shows it),RecordFormPage.saveToastCarriesWarnings-27999.test.tsx(3),AppContent.recordSaveOneToast-27999.test.tsx(4) andcontainerSaveToastCarriesWarnings-28216.test.tsx(4, drawer and modal).takeandtoastRecordSaveassertions read the new object shape; their meaning is unchanged.Ablation (through
scripts/ablation-replace.mjs, each mutation proved on disk and restored to the HEAD blob):writeWarningToast,RecordFormPageandAppContent(the notice-with-warning cells stay green, as predicted: the warnings still open the park);noticefrom the event → 7 red acrossonWriteWarningandwriteWarningToast;take→ 2 red in the new grid file (the per-row cell, and its CONTROL, whose count includes the notice the unread claim hands back).Acceptance notes
InlineEditSaveBar) raises no save toast at all today, so it is not a claiming surface; its notice reaches the user through the channel's info toast, which is one toast per write. Giving that bar a save toast of its own is outside this card.onNotice; the server half that lets sandboxed and flow hooks set it is Save notice from a sandboxed hook or a flow: hook context and flow node set the write answer's notice #28251. The chain is pinned through the real adapter (writeWarningToast.test.ts), the realRecordFormPage/AppContent/ObjectFormContainer/ObjectGridsave paths in jsdom.Verification
Local runs
Head
4056fd97, onmain6285203d.mainhas moved 7 commits since; none touches a file in this diff andgit merge-treewithorigin/main(53749673) is clean.pnpm turbo run build --filter='@object-ui/app-shell^...' --filter='@object-ui/plugin-grid^...' --concurrency=2(the dependency closure, under the verify lock) →Tasks: 64 successful, 64 total, VERDICT command-exit 0; then the four touched lower packages rebuilt → VERDICT command-exit 0.type-checkof@object-ui/data-objectstack,@object-ui/react,@object-ui/plugin-form,@object-ui/plugin-grid,@object-ui/app-shell(each package'stsc --noEmit, plustsc -p tsconfig.test.jsonwhere it declares one) → exit 0 each.inlineEditSaveFeedback-12303,onWriteWarning,record-save-feedback,writeWarningToast,RecordFormPage.saveToastCarriesWarnings-27999,AppContent.recordSaveOneToast-27999,containerSaveToastCarriesWarnings-28216) →Test Files 8 passed (8),Tests 100 passed (100).pnpm --dir ui exec vitest run packages/react/ packages/data-objectstack/ packages/plugin-form/ packages/plugin-grid/plus the 130 app-shell and console test files that namewriteWarningToast,RecordFormPage,AppContent,toastRecordSave,claimAnswerWarnings,ObjectFormContaineroronWriteWarning→Test Files 845 passed (845),Tests 8464 passed | 1 skipped (8465), VERDICT command-exit 0. The wholepackages/app-shell/suite was not run locally (CI's Test UI runs it).ts/tsxfiles → 0 errors.pnpm --dir ui run check:new-line-citations→ 0 new citations, exit 0.Derived gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon4056fd97→ 35 commands, each run with stdin from /dev/null and its exit code captured before any pipe;--ranreconcile:35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN, exit 0.check:type-check-debt's first run was a lock queue-timeout (exit 3, NOT MEASURED, behind this branch's own test run); the line below is its re-run under the lock.Generated by Claude Code