PR: #566: feat(matters): optimistic matter create on the register [Story-2267] by @pramaan-board-bot[bot]
View PR →

Files changed

9 file(s), +567 / -29 lines

Path Role + -
messages/en.jsonConfig+2-0
messages/hi.jsonConfig+2-0
src/components/matters/add-matter-launcher.tsxComponent+25-16
src/components/matters/live-matters-list-page.tsxComponent+10-7
src/components/matters/matters-live-register-row.stories.tsxComponent+78-0
src/components/matters/matters-live-register-row.tsxComponent+48-5
src/lib/matters/optimistic.test.tsLibrary+146-0
src/lib/matters/optimistic.tsLibrary+129-0
src/lib/query/invalidation.tsLibrary+127-1

Story-2267: optimistic matter create on the matters register

The matters list already read through the forum pattern (RSC prefetch → HydrationBoundary → useAppListMatters), but its create path used useAppCreateMatter with invalidate-on-success only. Per the AGENTS.md optimistic-writes doctrine that is not optimistic: the user waited the full create round-trip (~1.8 s) plus the list refetch (~1.5 s) before the new row appeared — measured at ~5 s live on trace 6a5218e9dd3b5080dc330ddea8028a31. This revision makes the row appear the instant the wizard is submitted, then reconciles with server truth.

What changed

Acceptance criteria map

CriterionWhere
Reusable optimistic insert helper in invalidation.ts, no per-component onMutate optimisticCollectionInsert() in src/lib/query/invalidation.ts
Submitting the wizard shows the row instantly; drawer closes immediately add-matter-launcher.tsx: onOpenChange(false) before the awaited create; onMutate prepends synchronously
Pending row non-clickable + visually distinct; swaps to the real clickable row on success MatterLiveRegisterRowView pending branch; helper onSuccess swap by temp id
On server error the temp row is removed and an error toast shows helper onError snapshot restore + onError: () => toast.error(...) in the launcher
Temp row only in matching cache entries; non-matching invalidated matterMatchesListParams() + onSettled invalidate
Storybook story demonstrates the pending-row state matters-live-register-row.stories.tsx (Pending, PendingAboveSettled)
i18n: new copy in both locales; tokens only Matters.pendingRef / Matters.pendingCreate in en.json + hi.json; pending style uses existing utilities/tokens
Verify gate green pnpm lint, pnpm test (1085 passing), pnpm build, Storybook build all pass

Rollback verification

The forced-failure path is covered by src/lib/matters/optimistic.test.ts — “rolls the pending row back and reports when the create fails”: after onMutate prepends the pending row, invoking onError restores the snapshot (the cache page returns to empty) and the reporter is called with the failure. The swap, filter-awareness, and prepend behaviours have their own cases in the same file.