@rashidee/co2 1.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +25449 -0
- package/drizzle/0000_remarkable_charles_xavier.sql +210 -0
- package/drizzle/0001_wise_wolf_cub.sql +13 -0
- package/drizzle/0002_secret_spyke.sql +16 -0
- package/drizzle/0003_workspace_config_singleton.sql +16 -0
- package/drizzle/0004_terminal_claim_v120.sql +11 -0
- package/drizzle/0005_brainstorm_v124.sql +10 -0
- package/drizzle/0006_project_v140.sql +6 -0
- package/drizzle/0007_prd_wizard_v124.sql +14 -0
- package/drizzle/0008_workflow_artifact_v126.sql +24 -0
- package/drizzle/0009_project_import_v150.sql +7 -0
- package/drizzle/0010_quality_v151.sql +3 -0
- package/drizzle/meta/0000_snapshot.json +1335 -0
- package/drizzle/meta/0001_snapshot.json +1416 -0
- package/drizzle/meta/0002_snapshot.json +1510 -0
- package/drizzle/meta/_journal.json +83 -0
- package/package.json +41 -0
- package/plugin/.claude-plugin/marketplace.json +36 -0
- package/plugin/.claude-plugin/plugin.json +22 -0
- package/plugin/README.md +625 -0
- package/plugin/SKILLS.md +350 -0
- package/plugin/co2.svg +5 -0
- package/plugin/commands/brainstorm-loop.md +129 -0
- package/plugin/skills/conductor-defect/SKILL.md +898 -0
- package/plugin/skills/conductor-feature-develop/SKILL.md +1383 -0
- package/plugin/skills/conductor-feature-develop/references/implementation-domain-template.md +131 -0
- package/plugin/skills/conductor-feature-develop/references/implementation-master-template.md +89 -0
- package/plugin/skills/conductor-feature-develop/references/playwright-setup.md +224 -0
- package/plugin/skills/conductor-feature-prepare/SKILL.md +604 -0
- package/plugin/skills/depgen-k8s/SKILL.md +468 -0
- package/plugin/skills/depgen-k8s/references/dockerfile-laravel.md +197 -0
- package/plugin/skills/depgen-k8s/references/dockerfile-nodejs.md +163 -0
- package/plugin/skills/depgen-k8s/references/dockerfile-spring-boot.md +174 -0
- package/plugin/skills/depgen-k8s/references/k8s-patterns.md +869 -0
- package/plugin/skills/mockgen-shadcn/SKILL.md +1067 -0
- package/plugin/skills/mockgen-shadcn/references/admin-layout-template.md +1467 -0
- package/plugin/skills/mockgen-shadcn/references/mockup-hub-template.md +498 -0
- package/plugin/skills/mockgen-shadcn/references/mockup-index-template.md +216 -0
- package/plugin/skills/mockgen-tailwind/SKILL.md +904 -0
- package/plugin/skills/mockgen-tailwind/references/admin-layout-template.md +720 -0
- package/plugin/skills/mockgen-tailwind/references/mockup-hub-template.md +498 -0
- package/plugin/skills/mockgen-tailwind/references/mockup-index-template.md +190 -0
- package/plugin/skills/modelgen-nosql/SKILL.md +617 -0
- package/plugin/skills/modelgen-nosql/references/model-extraction-methodology.md +497 -0
- package/plugin/skills/modelgen-nosql/references/nosql-design-guide.md +258 -0
- package/plugin/skills/modelgen-relational/SKILL.md +707 -0
- package/plugin/skills/modelgen-relational/references/model-extraction-methodology.md +499 -0
- package/plugin/skills/specgen-flutter-riverpod/SKILL.md +898 -0
- package/plugin/skills/specgen-flutter-riverpod/references/component-patterns.md +971 -0
- package/plugin/skills/specgen-flutter-riverpod/references/notification-patterns.md +457 -0
- package/plugin/skills/specgen-flutter-riverpod/references/routing-patterns.md +362 -0
- package/plugin/skills/specgen-flutter-riverpod/references/security-patterns.md +709 -0
- package/plugin/skills/specgen-flutter-riverpod/references/spec-template.md +1566 -0
- package/plugin/skills/specgen-flutter-riverpod/references/state-patterns.md +518 -0
- package/plugin/skills/specgen-flutter-riverpod/references/storage-patterns.md +370 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/SKILL.md +984 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/batch-patterns.md +359 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/messaging-patterns.md +468 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/modulith-patterns.md +461 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/reporting-patterns.md +803 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/security-patterns.md +501 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/spec-template.md +1268 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/web-patterns.md +301 -0
- package/plugin/skills/specgen-node-cli-web/SKILL.md +561 -0
- package/plugin/skills/specgen-node-cli-web/references/auth-patterns.md +293 -0
- package/plugin/skills/specgen-node-cli-web/references/cli-packaging-patterns.md +250 -0
- package/plugin/skills/specgen-node-cli-web/references/server-patterns.md +247 -0
- package/plugin/skills/specgen-node-cli-web/references/spec-template.md +401 -0
- package/plugin/skills/specgen-node-cli-web/references/testing-patterns.md +174 -0
- package/plugin/skills/specgen-react-mui/SKILL.md +789 -0
- package/plugin/skills/specgen-react-mui/references/component-patterns.md +1077 -0
- package/plugin/skills/specgen-react-mui/references/reporting-patterns.md +1998 -0
- package/plugin/skills/specgen-react-mui/references/routing-patterns.md +461 -0
- package/plugin/skills/specgen-react-mui/references/security-patterns.md +599 -0
- package/plugin/skills/specgen-react-mui/references/spec-template.md +1030 -0
- package/plugin/skills/specgen-react-mui/references/state-patterns.md +327 -0
- package/plugin/skills/specgen-react-tailwind/SKILL.md +859 -0
- package/plugin/skills/specgen-react-tailwind/references/component-patterns.md +1212 -0
- package/plugin/skills/specgen-react-tailwind/references/reporting-patterns.md +1529 -0
- package/plugin/skills/specgen-react-tailwind/references/routing-patterns.md +669 -0
- package/plugin/skills/specgen-react-tailwind/references/security-patterns.md +656 -0
- package/plugin/skills/specgen-react-tailwind/references/spec-template.md +1237 -0
- package/plugin/skills/specgen-react-tailwind/references/state-patterns.md +327 -0
- package/plugin/skills/specgen-sdk-java/SKILL.md +933 -0
- package/plugin/skills/specgen-sdk-java/references/api-extraction-patterns.md +247 -0
- package/plugin/skills/specgen-sdk-java/references/http-client-patterns.md +495 -0
- package/plugin/skills/specgen-sdk-java/references/packaging-patterns.md +366 -0
- package/plugin/skills/specgen-sdk-java/references/spec-template.md +956 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/SKILL.md +1284 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/batch-patterns.md +1224 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/jasper-patterns.md +1638 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/messaging-patterns.md +512 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/modulith-patterns.md +354 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/security-patterns.md +551 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/spec-template.md +2502 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/web-patterns.md +503 -0
- package/plugin/skills/specgen-spring-jpa-restapi/SKILL.md +990 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/batch-patterns.md +1224 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/messaging-patterns.md +512 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/modulith-patterns.md +279 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/reporting-patterns.md +1536 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/restapi-patterns.md +722 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/security-patterns.md +351 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/spec-template.md +553 -0
- package/plugin/skills/specgen-ts-cli/SKILL.md +861 -0
- package/plugin/skills/specgen-ts-cli/references/async-patterns.md +1101 -0
- package/plugin/skills/specgen-ts-cli/references/command-patterns.md +383 -0
- package/plugin/skills/specgen-ts-cli/references/config-patterns.md +430 -0
- package/plugin/skills/specgen-ts-cli/references/database-patterns.md +750 -0
- package/plugin/skills/specgen-ts-cli/references/packaging-patterns.md +426 -0
- package/plugin/skills/specgen-ts-cli/references/spec-template.md +1391 -0
- package/plugin/skills/specgen-ts-cli/references/testing-patterns.md +909 -0
- package/plugin/skills/testgen-functional/SKILL.md +1381 -0
- package/plugin/skills/tracegen-matrix/SKILL.md +243 -0
- package/plugin/skills/util-preparek8senv/SKILL.md +422 -0
- package/plugin/skills/util-projectinit/SKILL.md +307 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-loop.md +129 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-protocol.md +144 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-setup-guide.md +153 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-template-appdev.md +87 -0
- package/plugin/skills/util-projectinit/reference/co2-context-generation-guide.md +761 -0
- package/plugin/skills/util-projectsync/SKILL.md +501 -0
- package/plugin/skills/util-updprd/SKILL.md +324 -0
- package/plugin/skills/util-usanalyzer/SKILL.md +307 -0
- package/plugin/skills/util-ustagger/SKILL.md +154 -0
- package/static/assets/abnfDiagram-VRR7QNED-DvPuwyCV.js +1 -0
- package/static/assets/arc-DRKw7sjs.js +1 -0
- package/static/assets/architectureDiagram-ZJ3FMSHR-CaNcxUbC.js +36 -0
- package/static/assets/blockDiagram-677ZJIJ3-Demd4AOK.js +132 -0
- package/static/assets/c4Diagram-LMCZKHZV-PVKUfc2x.js +10 -0
- package/static/assets/channel-C2nVbuwv.js +1 -0
- package/static/assets/chunk-2Q5K7J3B-CExf8ww_.js +1 -0
- package/static/assets/chunk-32BRIVSS-CFPaoONF.js +1 -0
- package/static/assets/chunk-5VM5RSS4-DE2esM67.js +15 -0
- package/static/assets/chunk-EX3LRPZG-SON_YdDG.js +231 -0
- package/static/assets/chunk-JWPE2WC7-Qxah8LNr.js +1 -0
- package/static/assets/chunk-MOJQB5TN-BcWGQV3j.js +88 -0
- package/static/assets/chunk-RYQCIY6F-DkDj-OR5.js +1 -0
- package/static/assets/chunk-V7JOEXUC-BlI1Mrah.js +206 -0
- package/static/assets/chunk-VR4S4FIN-Agf__duj.js +1 -0
- package/static/assets/chunk-XXDRQBXY-B5CfAh85.js +1 -0
- package/static/assets/classDiagram-OUVF2IWQ-DXJIljsR.js +1 -0
- package/static/assets/classDiagram-v2-EOCWNBFH-DXJIljsR.js +1 -0
- package/static/assets/cose-bilkent-JH36ORCC-DO0cQkcy.js +1 -0
- package/static/assets/cynefin-VYW2F7L2-CfOnsnXc.js +178 -0
- package/static/assets/cynefinDiagram-TSTJHNR4-BJ8tULAv.js +62 -0
- package/static/assets/cytoscape.esm-DTSO7Bv0.js +331 -0
- package/static/assets/dagre-VKFMJZFB-DfjwzHqP.js +4 -0
- package/static/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/static/assets/diagram-FQU43EPY-CndSOIIM.js +3 -0
- package/static/assets/diagram-G47NLZAW-Czf5hN0Z.js +24 -0
- package/static/assets/diagram-NH7WQ7WH-COAyojGh.js +24 -0
- package/static/assets/diagram-OA4YK3LP-H8PoiN2m.js +30 -0
- package/static/assets/diagram-WEI45ONY-DzcaVzjp.js +41 -0
- package/static/assets/ebnfDiagram-CCIWWBDH-DPazGYLJ.js +1 -0
- package/static/assets/erDiagram-Q63AITRT-Bo-fubgF.js +85 -0
- package/static/assets/flowDiagram-23GEKE2U-CSvxUDwh.js +156 -0
- package/static/assets/ganttDiagram-NO4QXBWP-DnBveWX6.js +292 -0
- package/static/assets/gitGraphDiagram-IHSO6WYX-B3rVL8c-.js +106 -0
- package/static/assets/graph-C9eacEi8.js +1 -0
- package/static/assets/index-LZSQkLE2.css +41 -0
- package/static/assets/index-y7XDSgMG.js +495 -0
- package/static/assets/infoDiagram-FWYZ7A6U-CvWV9yy2.js +2 -0
- package/static/assets/init-Gi6I4Gst.js +1 -0
- package/static/assets/ishikawaDiagram-FXEZZL3T-DF28a1N6.js +70 -0
- package/static/assets/journeyDiagram-5HDEW3XC-CB6CugYk.js +139 -0
- package/static/assets/kanban-definition-HUTT4EX6-ResSLF6d.js +89 -0
- package/static/assets/katex-C5jXJg4s.js +257 -0
- package/static/assets/layout-DEXfKzaS.js +1 -0
- package/static/assets/linear-CLO--ryk.js +1 -0
- package/static/assets/map-Czzmt4hB.js +1 -0
- package/static/assets/mindmap-definition-LN4V7U3C-BvW3EbxX.js +96 -0
- package/static/assets/ordinal-Cboi1Yqb.js +1 -0
- package/static/assets/pegDiagram-2B236MQR-BHCgqrrM.js +1 -0
- package/static/assets/pieDiagram-ENE6RG2P-DVmCQ5FA.js +39 -0
- package/static/assets/quadrantDiagram-ABIIQ3AL-B3e5KR52.js +7 -0
- package/static/assets/railroadDiagram-RFXS5EU6-Csv5ysLc.js +1 -0
- package/static/assets/requirementDiagram-TGXJPOKE-DOMVzvnB.js +84 -0
- package/static/assets/sankeyDiagram-HTMAVEWB-B1dcTOvy.js +40 -0
- package/static/assets/sequenceDiagram-DBY2YBRQ-BY414yeO.js +162 -0
- package/static/assets/sizeCapture-X5ZJPWSS-CuJfrfBN.js +1 -0
- package/static/assets/stateDiagram-2N3HPSRC-FX4rJkzV.js +1 -0
- package/static/assets/stateDiagram-v2-6OUMAXLB-BVufbO_H.js +1 -0
- package/static/assets/swimlanes-5IMT3BWC-Sfy58mmv.js +2 -0
- package/static/assets/swimlanesDiagram-G3AALYLV-wDCDw08H.js +8 -0
- package/static/assets/timeline-definition-FHXFAJF6-CmV7by8p.js +120 -0
- package/static/assets/vennDiagram-L72KCM5P-FqIyFZFt.js +34 -0
- package/static/assets/wardleyDiagram-EHGQE667-BZ06O45i.js +78 -0
- package/static/assets/xychartDiagram-FW5EYKEG-BOkffR06.js +7 -0
- package/static/index.html +24 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
# State Management Patterns — Zustand Feature Stores & TanStack Query
|
|
2
|
+
|
|
3
|
+
This reference describes when and how to use feature-level Zustand stores alongside
|
|
4
|
+
TanStack Query. Include this content in Section 9 of the generated specification and
|
|
5
|
+
in any module SPEC.md that requires client-side state beyond server cache.
|
|
6
|
+
|
|
7
|
+
The state model is identical to the MUI variant — TanStack Query owns server state, Zustand
|
|
8
|
+
owns UI state, React Hook Form owns form state. Only the rendered UI in the examples is
|
|
9
|
+
Tailwind/Headless UI instead of MUI.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## State Separation Principle
|
|
14
|
+
|
|
15
|
+
The golden rule for React SPA state management:
|
|
16
|
+
|
|
17
|
+
| State Type | Tool | Examples |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| **Server state** (data from API) | TanStack Query | Hero section list, user profile, blog posts |
|
|
20
|
+
| **UI state** (ephemeral, per-session) | Zustand | Selected rows, filter panel open/closed, active tab |
|
|
21
|
+
| **Global UI state** | Zustand (persisted) | Theme mode, sidebar open, user preferences |
|
|
22
|
+
| **Auth state** | Zustand (memory-only, no persist) | JWT token, logged-in user info |
|
|
23
|
+
| **Form state** | React Hook Form | All form field values and validation errors |
|
|
24
|
+
|
|
25
|
+
**Do NOT use Zustand to cache API responses.** If data comes from the server, TanStack
|
|
26
|
+
Query owns it. Zustand holds only the filters/pagination params that TanStack Query uses
|
|
27
|
+
as its query key.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Feature Store Pattern
|
|
32
|
+
|
|
33
|
+
Create a feature store only when the module has complex UI state that needs to be shared
|
|
34
|
+
between sibling components (e.g., a list page and a detail panel rendered side-by-side).
|
|
35
|
+
|
|
36
|
+
### When to create a feature store
|
|
37
|
+
|
|
38
|
+
| Scenario | Use feature store? |
|
|
39
|
+
|---|---|
|
|
40
|
+
| List page with filter bar → filter values | **Yes** — filter state drives TanStack Query params |
|
|
41
|
+
| Multi-step form with shared wizard state | **Yes** — wizard step and accumulated values |
|
|
42
|
+
| List page + slide-over detail panel (side-by-side) | **Yes** — selected item ID |
|
|
43
|
+
| Simple list page with local filter `useState` | **No** — keep state local |
|
|
44
|
+
| Simple create/edit form | **No** — React Hook Form handles form state |
|
|
45
|
+
|
|
46
|
+
### Feature Store Template
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
// Example: src/features/heroSection/store/heroSection.store.ts
|
|
50
|
+
import { create } from 'zustand';
|
|
51
|
+
import type { HeroSectionListParams } from '../heroSection.types';
|
|
52
|
+
|
|
53
|
+
interface HeroSectionState {
|
|
54
|
+
// Filter and pagination params — these drive the TanStack Query key
|
|
55
|
+
filters: HeroSectionListParams;
|
|
56
|
+
|
|
57
|
+
// UI state
|
|
58
|
+
selectedIds: Set<string>;
|
|
59
|
+
isDetailPanelOpen: boolean;
|
|
60
|
+
selectedId: string | null;
|
|
61
|
+
|
|
62
|
+
// Actions
|
|
63
|
+
setFilter: (key: keyof HeroSectionListParams, value: unknown) => void;
|
|
64
|
+
resetFilters: () => void;
|
|
65
|
+
setPage: (page: number) => void;
|
|
66
|
+
toggleSelectId: (id: string) => void;
|
|
67
|
+
clearSelection: () => void;
|
|
68
|
+
openDetail: (id: string) => void;
|
|
69
|
+
closeDetail: () => void;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const DEFAULT_FILTERS: HeroSectionListParams = { page: 0, size: 10, status: '' };
|
|
73
|
+
|
|
74
|
+
export const useHeroSectionStore = create<HeroSectionState>((set) => ({
|
|
75
|
+
filters: { ...DEFAULT_FILTERS },
|
|
76
|
+
selectedIds: new Set(),
|
|
77
|
+
isDetailPanelOpen: false,
|
|
78
|
+
selectedId: null,
|
|
79
|
+
|
|
80
|
+
setFilter: (key, value) =>
|
|
81
|
+
set((state) => ({ filters: { ...state.filters, [key]: value, page: 0 } })),
|
|
82
|
+
|
|
83
|
+
resetFilters: () => set({ filters: { ...DEFAULT_FILTERS } }),
|
|
84
|
+
|
|
85
|
+
setPage: (page) => set((state) => ({ filters: { ...state.filters, page } })),
|
|
86
|
+
|
|
87
|
+
toggleSelectId: (id) =>
|
|
88
|
+
set((state) => {
|
|
89
|
+
const next = new Set(state.selectedIds);
|
|
90
|
+
next.has(id) ? next.delete(id) : next.add(id);
|
|
91
|
+
return { selectedIds: next };
|
|
92
|
+
}),
|
|
93
|
+
|
|
94
|
+
clearSelection: () => set({ selectedIds: new Set() }),
|
|
95
|
+
|
|
96
|
+
openDetail: (id) => set({ selectedId: id, isDetailPanelOpen: true }),
|
|
97
|
+
|
|
98
|
+
closeDetail: () => set({ isDetailPanelOpen: false, selectedId: null }),
|
|
99
|
+
}));
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Integration with TanStack Query
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
// src/features/heroSection/pages/HeroSectionListPage.tsx
|
|
106
|
+
import { useHeroSectionStore } from '../store/heroSection.store';
|
|
107
|
+
import { useHeroSections } from '../hooks/useHeroSections';
|
|
108
|
+
import { SearchInput } from '../../../shared/components/SearchInput';
|
|
109
|
+
import { DataTable } from '../../../shared/components/DataTable';
|
|
110
|
+
import { Pagination } from '../../../shared/components/Pagination';
|
|
111
|
+
|
|
112
|
+
export function HeroSectionListPage() {
|
|
113
|
+
// Filter state lives in Zustand — changes trigger React re-render
|
|
114
|
+
const { filters, setFilter, setPage } = useHeroSectionStore();
|
|
115
|
+
|
|
116
|
+
// TanStack Query uses filters as the query key — auto-refetches when filters change
|
|
117
|
+
const { data, isLoading } = useHeroSections(filters);
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<div className="space-y-4">
|
|
121
|
+
<div className="flex flex-wrap items-center gap-3">
|
|
122
|
+
<SearchInput
|
|
123
|
+
placeholder="Search…"
|
|
124
|
+
onChange={(value) => setFilter('search', value)}
|
|
125
|
+
/>
|
|
126
|
+
<select
|
|
127
|
+
value={filters.status ?? ''}
|
|
128
|
+
onChange={(e) => setFilter('status', e.target.value)}
|
|
129
|
+
className="rounded border border-slate-300 bg-surface px-3 py-2 text-sm focus:border-primary focus:ring-1 focus:ring-primary dark:border-slate-600"
|
|
130
|
+
>
|
|
131
|
+
<option value="">All Statuses</option>
|
|
132
|
+
<option value="ACTIVE">Active</option>
|
|
133
|
+
<option value="DRAFT">Draft</option>
|
|
134
|
+
</select>
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
{/* DataTable + Pagination — see routing-patterns.md for implementations */}
|
|
138
|
+
<DataTable columns={columns} data={data?.content ?? []} emptyMessage={isLoading ? 'Loading…' : undefined} />
|
|
139
|
+
<Pagination
|
|
140
|
+
page={filters.page ?? 0}
|
|
141
|
+
size={filters.size ?? 10}
|
|
142
|
+
totalElements={data?.totalElements ?? 0}
|
|
143
|
+
onPageChange={(page) => setPage(page)}
|
|
144
|
+
onSizeChange={(size) => setFilter('size', size)}
|
|
145
|
+
/>
|
|
146
|
+
</div>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Optimistic Updates Pattern
|
|
154
|
+
|
|
155
|
+
For fast UI feedback on mutations (useful for toggle operations like status changes):
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
// src/features/heroSection/hooks/useHeroSectionMutations.ts
|
|
159
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
160
|
+
import { heroSectionApi } from '../api/heroSectionApi';
|
|
161
|
+
import { heroSectionKeys } from './useHeroSections';
|
|
162
|
+
import type { HeroSection } from '../heroSection.types';
|
|
163
|
+
|
|
164
|
+
export function useToggleHeroSectionStatus() {
|
|
165
|
+
const queryClient = useQueryClient();
|
|
166
|
+
|
|
167
|
+
return useMutation({
|
|
168
|
+
mutationFn: ({ id, status }: { id: string; status: HeroSection['status'] }) =>
|
|
169
|
+
heroSectionApi.update(id, { status }),
|
|
170
|
+
|
|
171
|
+
// Optimistic update — immediately update the cache before the API responds
|
|
172
|
+
onMutate: async ({ id, status }) => {
|
|
173
|
+
await queryClient.cancelQueries({ queryKey: heroSectionKeys.all });
|
|
174
|
+
const previousData = queryClient.getQueryData<HeroSection>(heroSectionKeys.detail(id));
|
|
175
|
+
queryClient.setQueryData<HeroSection>(heroSectionKeys.detail(id), (old) =>
|
|
176
|
+
old ? { ...old, status } : old,
|
|
177
|
+
);
|
|
178
|
+
return { previousData };
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
// On error, roll back to the previous value
|
|
182
|
+
onError: (_error, { id }, context) => {
|
|
183
|
+
if (context?.previousData) {
|
|
184
|
+
queryClient.setQueryData(heroSectionKeys.detail(id), context.previousData);
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
// Always refetch after error or success to sync with server
|
|
189
|
+
onSettled: (_, __, { id }) => {
|
|
190
|
+
queryClient.invalidateQueries({ queryKey: heroSectionKeys.detail(id) });
|
|
191
|
+
queryClient.invalidateQueries({ queryKey: heroSectionKeys.lists() });
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Multi-Step Form Pattern (Wizard)
|
|
200
|
+
|
|
201
|
+
For complex forms spanning multiple steps (e.g., blog post creation with metadata, content, and preview steps):
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
// src/features/blog/store/blogCreate.store.ts
|
|
205
|
+
import { create } from 'zustand';
|
|
206
|
+
import type { BlogCreateStep1Values, BlogCreateStep2Values } from '../blog.schema';
|
|
207
|
+
|
|
208
|
+
type WizardStep = 'metadata' | 'content' | 'preview';
|
|
209
|
+
|
|
210
|
+
interface BlogCreateState {
|
|
211
|
+
currentStep: WizardStep;
|
|
212
|
+
step1Data: BlogCreateStep1Values | null;
|
|
213
|
+
step2Data: BlogCreateStep2Values | null;
|
|
214
|
+
|
|
215
|
+
goToStep: (step: WizardStep) => void;
|
|
216
|
+
saveStep1: (data: BlogCreateStep1Values) => void;
|
|
217
|
+
saveStep2: (data: BlogCreateStep2Values) => void;
|
|
218
|
+
reset: () => void;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export const useBlogCreateStore = create<BlogCreateState>((set) => ({
|
|
222
|
+
currentStep: 'metadata',
|
|
223
|
+
step1Data: null,
|
|
224
|
+
step2Data: null,
|
|
225
|
+
|
|
226
|
+
goToStep: (step) => set({ currentStep: step }),
|
|
227
|
+
saveStep1: (data) => set({ step1Data: data, currentStep: 'content' }),
|
|
228
|
+
saveStep2: (data) => set({ step2Data: data, currentStep: 'preview' }),
|
|
229
|
+
reset: () => set({ currentStep: 'metadata', step1Data: null, step2Data: null }),
|
|
230
|
+
}));
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
> Render wizard steps with the Headless UI `Tab` group, or with conditional rendering keyed
|
|
234
|
+
> on `currentStep`. A step indicator can be a simple Tailwind flex row of numbered badges.
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Cache Invalidation Strategy
|
|
239
|
+
|
|
240
|
+
Follow this decision tree when invalidating TanStack Query cache after mutations:
|
|
241
|
+
|
|
242
|
+
| Mutation | Invalidation Target |
|
|
243
|
+
|---|---|
|
|
244
|
+
| Create item | `queryKey: entityKeys.lists()` — all list queries |
|
|
245
|
+
| Update item | `queryKey: entityKeys.detail(id)` + `entityKeys.lists()` |
|
|
246
|
+
| Delete item | `queryKey: entityKeys.lists()` |
|
|
247
|
+
| Bulk action (delete multiple) | `queryKey: entityKeys.all` |
|
|
248
|
+
| Status toggle | `queryKey: entityKeys.detail(id)` + `entityKeys.lists()` |
|
|
249
|
+
|
|
250
|
+
Always use `invalidateQueries` (not `removeQueries`) so the UI shows stale data with
|
|
251
|
+
a loading indicator rather than an empty flash.
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Prefetching for Navigation
|
|
256
|
+
|
|
257
|
+
Prefetch list data when hovering a navigation link to make page transitions feel instant:
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
// In AppSidebar or a NavLink wrapper component
|
|
261
|
+
import { useQueryClient } from '@tanstack/react-query';
|
|
262
|
+
import { heroSectionKeys } from '../../features/heroSection/hooks/useHeroSections';
|
|
263
|
+
import { heroSectionApi } from '../../features/heroSection/api/heroSectionApi';
|
|
264
|
+
|
|
265
|
+
function useHeroSectionPrefetch() {
|
|
266
|
+
const queryClient = useQueryClient();
|
|
267
|
+
|
|
268
|
+
return () =>
|
|
269
|
+
queryClient.prefetchQuery({
|
|
270
|
+
queryKey: heroSectionKeys.list({ page: 0, size: 10 }),
|
|
271
|
+
queryFn: () => heroSectionApi.list({ page: 0, size: 10 }),
|
|
272
|
+
staleTime: 30_000,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Usage: <NavLink to="/hero-section" onMouseEnter={prefetch}>…</NavLink>
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Error State Handling in Pages
|
|
282
|
+
|
|
283
|
+
```tsx
|
|
284
|
+
// Pattern for handling query errors in page components (Tailwind alert)
|
|
285
|
+
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline';
|
|
286
|
+
import { Button } from '../../../shared/components/Button';
|
|
287
|
+
|
|
288
|
+
export function HeroSectionListPage() {
|
|
289
|
+
const { data, isLoading, isError, error, refetch } = useHeroSections(filters);
|
|
290
|
+
|
|
291
|
+
if (isError) {
|
|
292
|
+
return (
|
|
293
|
+
<div className="mt-6 flex items-start gap-3 rounded border border-danger/40 bg-danger/5 p-4">
|
|
294
|
+
<ExclamationTriangleIcon className="mt-0.5 h-5 w-5 shrink-0 text-danger" />
|
|
295
|
+
<div className="flex-1">
|
|
296
|
+
<p className="text-sm text-danger">
|
|
297
|
+
{error instanceof Error ? error.message : 'Failed to load data. Please try again.'}
|
|
298
|
+
</p>
|
|
299
|
+
</div>
|
|
300
|
+
<Button variant="outline" size="sm" onClick={() => refetch()}>
|
|
301
|
+
Retry
|
|
302
|
+
</Button>
|
|
303
|
+
</div>
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Normal render...
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## Scroll Position Restoration
|
|
314
|
+
|
|
315
|
+
React Router v7 handles scroll restoration automatically when navigating between routes.
|
|
316
|
+
For paginated lists, use TanStack Query's `placeholderData` to keep showing previous data
|
|
317
|
+
while new data loads (avoids an empty flash on page change):
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
export function useHeroSections(params: HeroSectionListParams) {
|
|
321
|
+
return useQuery({
|
|
322
|
+
queryKey: heroSectionKeys.list(params),
|
|
323
|
+
queryFn: () => heroSectionApi.list(params),
|
|
324
|
+
placeholderData: (previousData) => previousData,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
```
|