Hotfix Rule: Force Fresh Data After CRUD Mutation

Task-h not implements
This commit is contained in:
phaichayon
2026-06-16 11:59:41 +07:00
parent 357414c247
commit 56683ee7b9
16 changed files with 763 additions and 186 deletions

View File

@@ -375,6 +375,31 @@ Preferred pattern for new pages:
3. hydrate with `HydrationBoundary`
4. read with `useSuspenseQuery()`
Required pattern for CRUD mutations:
1. every feature must define centralized query key factories in `queries.ts`
2. use grouped keys such as `all`, `lists()`, `list(filters)`, `details()`, `detail(id)`, plus child-resource keys like `contacts(id)` or `followups(id)` when needed
3. after create, explicitly invalidate the list-level key
4. after update, explicitly invalidate both list and detail keys
5. after delete, explicitly invalidate the list key and remove stale detail queries with `removeQueries`
6. invalidate related queries affected by the mutation, such as counts, child tabs, document previews, approval panels, and cross-feature related lists
7. do not rely on `staleTime` alone to refresh production CRUD data
8. do not rely on `router.refresh()` alone for CRUD freshness; React Query invalidation is required first, and `router.refresh()` is optional only as a supplement
Mutation callback safety rule:
- if a shared mutation object from `api/mutations.ts` is spread into `useMutation({ ...sharedMutation, ... })`, do not override `onSuccess` or `onSettled` in a way that drops cache invalidation
- prefer keeping cache invalidation inside the shared mutation definition
- if component-level success behavior is needed, either:
- put invalidation in shared `onSettled` and keep UI concerns like toast and closing dialogs in component `onSuccess`, or
- call the shared invalidate helper explicitly from the component before closing the sheet/dialog
UI completion rule after successful mutation:
- close sheet/dialog only after the mutation promise resolves successfully
- table/detail views must show fresh data immediately without a full browser refresh
- related tabs and counts must refresh in the same interaction when their backing data changed
### URL State
Use `nuqs` for table search params and list filters.
@@ -486,3 +511,8 @@ Ensure these are set:
12. Treat legacy mock-backed modules as migration seams.
13. Follow the migrated `products` and `users` feature patterns when building new CRUD modules.
14. Do not re-enable self-service sign-up unless the product requirement changes explicitly.
15. For every CRUD mutation, explicitly invalidate React Query caches for list/detail/related data; never rely on passive staleness recovery.
16. Centralize query keys in each feature's `api/queries.ts`; do not scatter manual string query keys in components.
17. When deleting entities, remove stale detail queries with `removeQueries` in addition to invalidating lists.
18. If a component overrides `useMutation` callbacks on top of a shared mutation config, preserve or explicitly call the shared invalidation behavior before closing UI.
19. After successful create/update/delete, sheets, dialogs, and destructive modals must close only after cache invalidation has been triggered and the UI can refresh from fresh data.