@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,1237 @@
|
|
|
1
|
+
# Specification Template — React SPA with Tailwind CSS
|
|
2
|
+
|
|
3
|
+
This is the authoritative template for the generated specification. The specification is
|
|
4
|
+
split into **two types of files**:
|
|
5
|
+
|
|
6
|
+
1. **`SPECIFICATION.md`** (root) — Table of Contents, shared infrastructure, and all
|
|
7
|
+
application-level sections. Generated once per application.
|
|
8
|
+
2. **`<module>/SPEC.md`** (per-module) — Self-contained module blueprint.
|
|
9
|
+
Generated once per module from PRD.md.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
spec/
|
|
13
|
+
├── SPECIFICATION.md
|
|
14
|
+
├── hero-section/
|
|
15
|
+
│ └── SPEC.md
|
|
16
|
+
├── product-service/
|
|
17
|
+
│ └── SPEC.md
|
|
18
|
+
└── ...
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Placeholders use `{{VARIABLE}}` syntax and must be replaced with actual values gathered
|
|
22
|
+
from context files.
|
|
23
|
+
|
|
24
|
+
Sections marked **[CONDITIONAL]** should only be included when the corresponding
|
|
25
|
+
integration is selected.
|
|
26
|
+
|
|
27
|
+
**CRITICAL: Sample code requirement.** Every component in the spec MUST include a
|
|
28
|
+
complete, continuous code sample in a fenced code block (TypeScript, TSX, or JSON as
|
|
29
|
+
appropriate). The code must be self-explanatory and directly usable as a reference for
|
|
30
|
+
a coding agent. Do not describe components with bullet points alone — always accompany
|
|
31
|
+
descriptions with full code.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
# Part A: Root SPECIFICATION.md
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Table of Contents
|
|
40
|
+
|
|
41
|
+
Generate a TOC with clickable Markdown anchor links to every H2 and H3 section.
|
|
42
|
+
Additionally, include a **Modules** section in the TOC that links to each module's
|
|
43
|
+
`SPEC.md` file:
|
|
44
|
+
|
|
45
|
+
```markdown
|
|
46
|
+
## Table of Contents
|
|
47
|
+
|
|
48
|
+
### Shared Infrastructure
|
|
49
|
+
- [1. Project Overview](#1-project-overview)
|
|
50
|
+
- [2. Package Configuration](#2-package-configuration)
|
|
51
|
+
- ...all shared sections...
|
|
52
|
+
|
|
53
|
+
### Modules
|
|
54
|
+
- [Hero Section](hero-section/SPEC.md)
|
|
55
|
+
- [Product and Service](product-service/SPEC.md)
|
|
56
|
+
- [Blog](blog/SPEC.md)
|
|
57
|
+
- ...(one link per module)...
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Only include conditional sections that apply based on determined selections.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Section 1: Project Overview
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
# {{APPLICATION_NAME}} — Technical Specification
|
|
68
|
+
|
|
69
|
+
## 1. Project Overview
|
|
70
|
+
|
|
71
|
+
**Application Name**: {{APPLICATION_NAME}}
|
|
72
|
+
**Project Slug**: {{PROJECT_SLUG}}
|
|
73
|
+
**App Title**: {{APP_TITLE}}
|
|
74
|
+
**Framework**: React SPA (Tailwind CSS)
|
|
75
|
+
**Description**: {{APP_DESCRIPTION}}
|
|
76
|
+
**Versions Covered**: v1.0.0 — v{{LATEST_VERSION}}
|
|
77
|
+
|
|
78
|
+
### Optional Components (Auto-Determined)
|
|
79
|
+
**Backend API Base URL**: {{API_BASE_URL}}
|
|
80
|
+
**Authentication**: {{AUTH_TYPE}}
|
|
81
|
+
**DataGrid**: {{DATAGRID}} (TanStack Table)
|
|
82
|
+
**Charts**: {{CHARTS}} (Recharts)
|
|
83
|
+
**DatePickers**: {{DATE_PICKERS}} (react-day-picker)
|
|
84
|
+
**WebSocket**: {{WEBSOCKET}}
|
|
85
|
+
**i18n**: {{I18N}}
|
|
86
|
+
**RichText**: {{RICH_TEXT}} (Tiptap)
|
|
87
|
+
|
|
88
|
+
### Technology Stack
|
|
89
|
+
(Render the core stack version table from SKILL.md, plus selected optional integration versions)
|
|
90
|
+
|
|
91
|
+
### User Roles
|
|
92
|
+
(List each user role extracted from mockup role folders, with role constant and access scope.)
|
|
93
|
+
|
|
94
|
+
| Role | Description | Constant | Accessible Modules |
|
|
95
|
+
|------|-------------|----------|-------------------|
|
|
96
|
+
| Administrator | Full system access | `ADMIN` | All modules |
|
|
97
|
+
| Editor | Content management | `EDITOR` | Hero Section, Blog, ... |
|
|
98
|
+
|
|
99
|
+
### Modules
|
|
100
|
+
(List each module from PRD.md, with type and link to its SPEC.md.)
|
|
101
|
+
|
|
102
|
+
| Module | Type | Stories | Versions | Spec |
|
|
103
|
+
|--------|------|---------|----------|------|
|
|
104
|
+
| Hero Section | Business | 3 | 1.0.0, 1.0.4 | [SPEC](hero-section/SPEC.md) |
|
|
105
|
+
| Blog | Business | 5 | 1.0.0 | [SPEC](blog/SPEC.md) |
|
|
106
|
+
|
|
107
|
+
### Input Sources
|
|
108
|
+
- CLAUDE.md: {{path}}
|
|
109
|
+
- PRD.md: {{path}}
|
|
110
|
+
- Module Model: {{path to MODEL.md}}
|
|
111
|
+
- Mockup Screens: {{path to MOCKUP.html}}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Section 2: Package Configuration
|
|
117
|
+
|
|
118
|
+
### 2.1 package.json
|
|
119
|
+
|
|
120
|
+
Generate a complete `package.json` inside a code block. Include all core and
|
|
121
|
+
conditional dependencies based on the determined selections.
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"name": "{{PROJECT_SLUG}}",
|
|
126
|
+
"version": "1.0.0",
|
|
127
|
+
"private": true,
|
|
128
|
+
"type": "module",
|
|
129
|
+
"scripts": {
|
|
130
|
+
"dev": "vite",
|
|
131
|
+
"build": "tsc -b && vite build",
|
|
132
|
+
"preview": "vite preview",
|
|
133
|
+
"lint": "eslint .",
|
|
134
|
+
"type-check": "tsc --noEmit"
|
|
135
|
+
},
|
|
136
|
+
"dependencies": {
|
|
137
|
+
"react": "^19.0.0",
|
|
138
|
+
"react-dom": "^19.0.0",
|
|
139
|
+
"react-router-dom": "^7.0.0",
|
|
140
|
+
"@tanstack/react-query": "^5.0.0",
|
|
141
|
+
"zustand": "^5.0.0",
|
|
142
|
+
"react-hook-form": "^7.0.0",
|
|
143
|
+
"@hookform/resolvers": "^3.0.0",
|
|
144
|
+
"zod": "^3.0.0",
|
|
145
|
+
"axios": "^1.0.0",
|
|
146
|
+
"@headlessui/react": "^2.0.0",
|
|
147
|
+
"@heroicons/react": "^2.0.0",
|
|
148
|
+
"clsx": "^2.0.0",
|
|
149
|
+
"tailwind-merge": "^2.0.0",
|
|
150
|
+
"class-variance-authority": "^0.7.0"
|
|
151
|
+
},
|
|
152
|
+
"devDependencies": {
|
|
153
|
+
"typescript": "^5.0.0",
|
|
154
|
+
"@types/react": "^19.0.0",
|
|
155
|
+
"@types/react-dom": "^19.0.0",
|
|
156
|
+
"vite": "^6.0.0",
|
|
157
|
+
"@vitejs/plugin-react": "^4.0.0",
|
|
158
|
+
"tailwindcss": "^3.4.0",
|
|
159
|
+
"postcss": "^8.4.0",
|
|
160
|
+
"autoprefixer": "^10.4.0",
|
|
161
|
+
"@tailwindcss/forms": "^0.5.0",
|
|
162
|
+
"@tanstack/react-query-devtools": "^5.0.0",
|
|
163
|
+
"eslint": "^9.0.0",
|
|
164
|
+
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
165
|
+
"@typescript-eslint/parser": "^8.0.0"
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Include conditional dependencies as annotated comments in the final output:
|
|
171
|
+
|
|
172
|
+
```jsonc
|
|
173
|
+
// [If Auth = Keycloak or OIDC]
|
|
174
|
+
// "oidc-client-ts": "^3.1.0",
|
|
175
|
+
// "react-oidc-context": "^3.2.0"
|
|
176
|
+
// [If DataGrid = yes]
|
|
177
|
+
// "@tanstack/react-table": "^8.0.0"
|
|
178
|
+
// [If Charts = yes]
|
|
179
|
+
// "recharts": "^2.0.0"
|
|
180
|
+
// [If DatePickers = yes]
|
|
181
|
+
// "react-day-picker": "^9.0.0",
|
|
182
|
+
// "date-fns": "^4.0.0"
|
|
183
|
+
// [If WebSocket = yes]
|
|
184
|
+
// "socket.io-client": "^4.0.0"
|
|
185
|
+
// [If i18n = yes]
|
|
186
|
+
// "react-i18next": "^15.0.0", "i18next": "^24.0.0",
|
|
187
|
+
// "i18next-browser-languagedetector": "^8.0.0", "i18next-http-backend": "^3.0.0"
|
|
188
|
+
// [If RichText = yes]
|
|
189
|
+
// "@tiptap/react": "^2.0.0", "@tiptap/starter-kit": "^2.0.0",
|
|
190
|
+
// "@tiptap/extension-link": "^2.0.0", "@tiptap/extension-placeholder": "^2.0.0",
|
|
191
|
+
// "dompurify": "^3.0.0" (+ devDep "@types/dompurify", "@tailwindcss/typography")
|
|
192
|
+
// [If Reporting = yes]
|
|
193
|
+
// "xlsx": "^0.18.5", "papaparse": "^5.4.1" (+ devDep "@types/papaparse")
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### 2.2 vite.config.ts
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
import { defineConfig } from 'vite';
|
|
200
|
+
import react from '@vitejs/plugin-react';
|
|
201
|
+
import path from 'path';
|
|
202
|
+
|
|
203
|
+
export default defineConfig({
|
|
204
|
+
plugins: [react()],
|
|
205
|
+
resolve: {
|
|
206
|
+
alias: {
|
|
207
|
+
'@': path.resolve(__dirname, './src'),
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
server: {
|
|
211
|
+
port: {{DEV_PORT}},
|
|
212
|
+
proxy: {
|
|
213
|
+
'/api': {
|
|
214
|
+
target: '{{API_BASE_URL_DEV}}',
|
|
215
|
+
changeOrigin: true,
|
|
216
|
+
rewrite: (path) => path.replace(/^\/api/, ''),
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
> Note: Tailwind v3 is wired through PostCSS (see `postcss.config.js` in Section 3), so no
|
|
224
|
+
> Vite plugin is required for Tailwind itself.
|
|
225
|
+
|
|
226
|
+
### 2.3 tsconfig.json
|
|
227
|
+
|
|
228
|
+
```json
|
|
229
|
+
{
|
|
230
|
+
"compilerOptions": {
|
|
231
|
+
"target": "ES2020",
|
|
232
|
+
"useDefineForClassFields": true,
|
|
233
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
234
|
+
"module": "ESNext",
|
|
235
|
+
"skipLibCheck": true,
|
|
236
|
+
"moduleResolution": "bundler",
|
|
237
|
+
"allowImportingTsExtensions": true,
|
|
238
|
+
"isolatedModules": true,
|
|
239
|
+
"moduleDetection": "force",
|
|
240
|
+
"noEmit": true,
|
|
241
|
+
"jsx": "react-jsx",
|
|
242
|
+
"strict": true,
|
|
243
|
+
"noUnusedLocals": true,
|
|
244
|
+
"noUnusedParameters": true,
|
|
245
|
+
"noFallthroughCasesInSwitch": true,
|
|
246
|
+
"baseUrl": ".",
|
|
247
|
+
"paths": {
|
|
248
|
+
"@/*": ["./src/*"]
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
"include": ["src"],
|
|
252
|
+
"references": [{ "path": "./tsconfig.node.json" }]
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### 2.4 Environment Variables
|
|
257
|
+
|
|
258
|
+
```env
|
|
259
|
+
# .env.development
|
|
260
|
+
VITE_APP_TITLE={{APP_TITLE}}
|
|
261
|
+
VITE_APP_VERSION={{LATEST_VERSION}}
|
|
262
|
+
VITE_API_BASE_URL={{API_BASE_URL_DEV}}
|
|
263
|
+
|
|
264
|
+
# [If Auth = Keycloak]
|
|
265
|
+
VITE_KEYCLOAK_AUTHORITY=http://localhost:8180/realms/{{KEYCLOAK_REALM}}
|
|
266
|
+
VITE_KEYCLOAK_CLIENT_ID={{KEYCLOAK_CLIENT_ID}}
|
|
267
|
+
VITE_KEYCLOAK_REDIRECT_URI=http://localhost:{{DEV_PORT}}/auth/callback
|
|
268
|
+
VITE_KEYCLOAK_POST_LOGOUT_REDIRECT_URI=http://localhost:{{DEV_PORT}}
|
|
269
|
+
VITE_KEYCLOAK_SCOPE=openid profile email
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
```env
|
|
273
|
+
# .env.production
|
|
274
|
+
VITE_APP_TITLE={{APP_TITLE}}
|
|
275
|
+
VITE_APP_VERSION={{LATEST_VERSION}}
|
|
276
|
+
VITE_API_BASE_URL=${API_BASE_URL}
|
|
277
|
+
|
|
278
|
+
# [If Auth = Keycloak]
|
|
279
|
+
VITE_KEYCLOAK_AUTHORITY=${KEYCLOAK_AUTHORITY}
|
|
280
|
+
VITE_KEYCLOAK_CLIENT_ID=${KEYCLOAK_CLIENT_ID}
|
|
281
|
+
VITE_KEYCLOAK_REDIRECT_URI=${APP_URL}/auth/callback
|
|
282
|
+
VITE_KEYCLOAK_POST_LOGOUT_REDIRECT_URI=${APP_URL}
|
|
283
|
+
VITE_KEYCLOAK_SCOPE=openid profile email
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Section 3: Application Configuration
|
|
289
|
+
|
|
290
|
+
### 3.1 .gitignore
|
|
291
|
+
|
|
292
|
+
```gitignore
|
|
293
|
+
# Build output
|
|
294
|
+
dist/
|
|
295
|
+
dist-ssr/
|
|
296
|
+
|
|
297
|
+
# Dependencies
|
|
298
|
+
node_modules/
|
|
299
|
+
|
|
300
|
+
# IDE
|
|
301
|
+
.idea/
|
|
302
|
+
*.iml
|
|
303
|
+
.vscode/
|
|
304
|
+
*.swp
|
|
305
|
+
*~
|
|
306
|
+
|
|
307
|
+
# OS
|
|
308
|
+
.DS_Store
|
|
309
|
+
Thumbs.db
|
|
310
|
+
|
|
311
|
+
# Environment (never commit secrets)
|
|
312
|
+
.env
|
|
313
|
+
.env.local
|
|
314
|
+
.env.production
|
|
315
|
+
.env.*.local
|
|
316
|
+
|
|
317
|
+
# TypeScript
|
|
318
|
+
*.tsbuildinfo
|
|
319
|
+
|
|
320
|
+
# Logs
|
|
321
|
+
*.log
|
|
322
|
+
npm-debug.log*
|
|
323
|
+
yarn-debug.log*
|
|
324
|
+
pnpm-debug.log*
|
|
325
|
+
|
|
326
|
+
# Testing
|
|
327
|
+
coverage/
|
|
328
|
+
playwright-report/
|
|
329
|
+
test-results/
|
|
330
|
+
e2e/node_modules/
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### 3.2 Environment Type Declarations
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
// src/types/global.d.ts
|
|
337
|
+
|
|
338
|
+
/// <reference types="vite/client" />
|
|
339
|
+
|
|
340
|
+
interface ImportMetaEnv {
|
|
341
|
+
readonly VITE_APP_TITLE: string;
|
|
342
|
+
readonly VITE_APP_VERSION: string;
|
|
343
|
+
readonly VITE_API_BASE_URL: string;
|
|
344
|
+
// [If Auth = Keycloak]
|
|
345
|
+
readonly VITE_KEYCLOAK_AUTHORITY: string;
|
|
346
|
+
readonly VITE_KEYCLOAK_CLIENT_ID: string;
|
|
347
|
+
readonly VITE_KEYCLOAK_REDIRECT_URI: string;
|
|
348
|
+
readonly VITE_KEYCLOAK_POST_LOGOUT_REDIRECT_URI: string;
|
|
349
|
+
readonly VITE_KEYCLOAK_SCOPE: string;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
interface ImportMeta {
|
|
353
|
+
readonly env: ImportMetaEnv;
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### 3.3 postcss.config.js
|
|
358
|
+
|
|
359
|
+
```javascript
|
|
360
|
+
export default {
|
|
361
|
+
plugins: {
|
|
362
|
+
tailwindcss: {},
|
|
363
|
+
autoprefixer: {},
|
|
364
|
+
},
|
|
365
|
+
};
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## Section 4: Directory Structure
|
|
371
|
+
|
|
372
|
+
Generate the full `src/` directory tree. Use actual module names from PRD.md and MODEL.md.
|
|
373
|
+
Follow the feature-based structure from `references/routing-patterns.md`.
|
|
374
|
+
|
|
375
|
+
```
|
|
376
|
+
src/
|
|
377
|
+
├── main.tsx
|
|
378
|
+
├── App.tsx
|
|
379
|
+
├── index.css ← Tailwind directives + design-token CSS variables
|
|
380
|
+
├── router/
|
|
381
|
+
│ └── index.tsx
|
|
382
|
+
├── lib/
|
|
383
|
+
│ ├── api/
|
|
384
|
+
│ │ ├── axiosInstance.ts
|
|
385
|
+
│ │ └── [If Auth = Keycloak] authBridge.ts
|
|
386
|
+
│ ├── auth/
|
|
387
|
+
│ │ ├── [If Auth = Keycloak] oidcConfig.ts
|
|
388
|
+
│ │ ├── [If Auth = Keycloak] keycloakRoles.ts
|
|
389
|
+
│ │ ├── [If Auth = Keycloak] useAuthUser.ts
|
|
390
|
+
│ │ └── roles.ts
|
|
391
|
+
│ ├── utils/
|
|
392
|
+
│ │ ├── cn.ts ← twMerge(clsx(...)) helper
|
|
393
|
+
│ │ └── zodErrors.ts
|
|
394
|
+
│ └── queryClient.ts
|
|
395
|
+
├── store/
|
|
396
|
+
│ ├── [If Auth = Local] auth.store.ts
|
|
397
|
+
│ ├── notification.store.ts
|
|
398
|
+
│ └── ui.store.ts
|
|
399
|
+
├── shared/
|
|
400
|
+
│ ├── layouts/
|
|
401
|
+
│ │ ├── DashboardLayout.tsx
|
|
402
|
+
│ │ ├── PublicLayout.tsx
|
|
403
|
+
│ │ ├── AuthLayout.tsx
|
|
404
|
+
│ │ └── Footer.tsx
|
|
405
|
+
│ ├── components/
|
|
406
|
+
│ │ ├── ProtectedRoute.tsx
|
|
407
|
+
│ │ ├── Button.tsx
|
|
408
|
+
│ │ ├── Card.tsx
|
|
409
|
+
│ │ ├── AppTopBar.tsx
|
|
410
|
+
│ │ ├── AppSidebar.tsx
|
|
411
|
+
│ │ ├── navigationConfig.ts
|
|
412
|
+
│ │ ├── PageHeader.tsx
|
|
413
|
+
│ │ ├── DataTable.tsx
|
|
414
|
+
│ │ ├── Pagination.tsx
|
|
415
|
+
│ │ ├── ConfirmDialog.tsx
|
|
416
|
+
│ │ ├── StatusBadge.tsx
|
|
417
|
+
│ │ ├── Modal.tsx
|
|
418
|
+
│ │ ├── ImageUpload.tsx
|
|
419
|
+
│ │ ├── LoadingOverlay.tsx
|
|
420
|
+
│ │ ├── Spinner.tsx
|
|
421
|
+
│ │ ├── EmptyState.tsx
|
|
422
|
+
│ │ ├── ErrorBoundary.tsx
|
|
423
|
+
│ │ ├── NotificationProvider.tsx
|
|
424
|
+
│ │ └── SearchInput.tsx
|
|
425
|
+
│ ├── hooks/
|
|
426
|
+
│ │ ├── useDebounce.ts
|
|
427
|
+
│ │ ├── useNotification.ts
|
|
428
|
+
│ │ ├── useColorMode.ts
|
|
429
|
+
│ │ └── useConfirm.ts
|
|
430
|
+
│ └── form/
|
|
431
|
+
│ ├── TextFieldController.tsx
|
|
432
|
+
│ ├── TextAreaController.tsx
|
|
433
|
+
│ ├── SelectController.tsx ← Headless UI Listbox
|
|
434
|
+
│ ├── DatePickerController.tsx ← react-day-picker in a Popover
|
|
435
|
+
│ ├── SwitchController.tsx ← Headless UI Switch
|
|
436
|
+
│ ├── ComboboxController.tsx ← Headless UI Combobox
|
|
437
|
+
│ └── [If RichText = yes] RichTextController.tsx ← Tiptap
|
|
438
|
+
├── features/
|
|
439
|
+
│ ├── auth/
|
|
440
|
+
│ │ ├── pages/
|
|
441
|
+
│ │ │ ├── [If Auth = Keycloak] AuthCallbackPage.tsx
|
|
442
|
+
│ │ │ └── [If Auth = Local] LoginPage.tsx
|
|
443
|
+
│ │ └── index.ts
|
|
444
|
+
│ ├── dashboard/
|
|
445
|
+
│ │ ├── pages/
|
|
446
|
+
│ │ │ └── DashboardPage.tsx
|
|
447
|
+
│ │ └── index.ts
|
|
448
|
+
│ ├── {{module1}}/ ← Replace with actual module names
|
|
449
|
+
│ │ ├── api/
|
|
450
|
+
│ │ │ └── {{module1}}Api.ts
|
|
451
|
+
│ │ ├── components/
|
|
452
|
+
│ │ ├── hooks/
|
|
453
|
+
│ │ ├── pages/
|
|
454
|
+
│ │ ├── {{module1}}.types.ts
|
|
455
|
+
│ │ ├── {{module1}}.schema.ts
|
|
456
|
+
│ │ └── index.ts
|
|
457
|
+
│ └── {{module2}}/
|
|
458
|
+
│ └── (same structure)
|
|
459
|
+
└── types/
|
|
460
|
+
└── global.d.ts
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
> **Note:** `tailwind.config.js` and `postcss.config.js` live at the project root (not under
|
|
464
|
+
> `src/`), alongside `package.json` and `vite.config.ts`.
|
|
465
|
+
|
|
466
|
+
---
|
|
467
|
+
|
|
468
|
+
## Section 5: Tailwind Configuration & Design Tokens
|
|
469
|
+
|
|
470
|
+
Generate the complete Tailwind configuration using design tokens extracted from MOCKUP.html.
|
|
471
|
+
Reference `references/component-patterns.md` for the token setup pattern.
|
|
472
|
+
|
|
473
|
+
Include:
|
|
474
|
+
- `tailwind.config.js` — `darkMode: 'class'`, content globs, `theme.extend` with actual
|
|
475
|
+
color/font/radius/shadow tokens from the mockup, and `plugins`
|
|
476
|
+
- `src/index.css` — `@tailwind` directives + `:root` / `.dark` CSS custom properties
|
|
477
|
+
- `src/lib/utils/cn.ts` — the `cn()` helper
|
|
478
|
+
- Design token mapping table showing mockup CSS property → Tailwind token
|
|
479
|
+
|
|
480
|
+
### 5.1 tailwind.config.js
|
|
481
|
+
|
|
482
|
+
```javascript
|
|
483
|
+
/** @type {import('tailwindcss').Config} */
|
|
484
|
+
export default {
|
|
485
|
+
darkMode: 'class',
|
|
486
|
+
content: ['./index.html', './src/**/*.{ts,tsx}'],
|
|
487
|
+
theme: {
|
|
488
|
+
extend: {
|
|
489
|
+
colors: {
|
|
490
|
+
// Map to CSS variables so light/dark switch automatically (see index.css)
|
|
491
|
+
primary: {
|
|
492
|
+
DEFAULT: 'rgb(var(--color-primary) / <alpha-value>)',
|
|
493
|
+
dark: 'rgb(var(--color-primary-dark) / <alpha-value>)',
|
|
494
|
+
},
|
|
495
|
+
secondary: 'rgb(var(--color-secondary) / <alpha-value>)',
|
|
496
|
+
danger: 'rgb(var(--color-danger) / <alpha-value>)',
|
|
497
|
+
warning: 'rgb(var(--color-warning) / <alpha-value>)',
|
|
498
|
+
success: 'rgb(var(--color-success) / <alpha-value>)',
|
|
499
|
+
surface: 'rgb(var(--color-surface) / <alpha-value>)',
|
|
500
|
+
canvas: 'rgb(var(--color-canvas) / <alpha-value>)',
|
|
501
|
+
},
|
|
502
|
+
fontFamily: {
|
|
503
|
+
// Extract from mockup CSS font-family declaration
|
|
504
|
+
sans: ['{{FONT_FAMILY}}', 'system-ui', 'sans-serif'],
|
|
505
|
+
},
|
|
506
|
+
borderRadius: {
|
|
507
|
+
// Extract base radius from mockup (e.g., '0.5rem')
|
|
508
|
+
DEFAULT: '{{BORDER_RADIUS_BASE}}',
|
|
509
|
+
},
|
|
510
|
+
},
|
|
511
|
+
},
|
|
512
|
+
plugins: [
|
|
513
|
+
require('@tailwindcss/forms'),
|
|
514
|
+
// [If RichText = yes] require('@tailwindcss/typography'),
|
|
515
|
+
],
|
|
516
|
+
};
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
### 5.2 src/index.css
|
|
520
|
+
|
|
521
|
+
```css
|
|
522
|
+
@tailwind base;
|
|
523
|
+
@tailwind components;
|
|
524
|
+
@tailwind utilities;
|
|
525
|
+
|
|
526
|
+
/* Semantic design tokens — values extracted from MOCKUP.html.
|
|
527
|
+
Stored as space-separated RGB channels so Tailwind's <alpha-value> works. */
|
|
528
|
+
:root {
|
|
529
|
+
--color-primary: 34 113 177; /* e.g., #2271b1 */
|
|
530
|
+
--color-primary-dark: 19 94 150; /* e.g., #135e96 */
|
|
531
|
+
--color-secondary: 100 105 112; /* e.g., #646970 */
|
|
532
|
+
--color-danger: 214 54 56; /* e.g., #d63638 */
|
|
533
|
+
--color-warning: 219 166 23; /* e.g., #dba617 */
|
|
534
|
+
--color-success: 0 163 42; /* e.g., #00a32a */
|
|
535
|
+
--color-canvas: 240 240 241; /* page background — e.g., #f0f0f1 */
|
|
536
|
+
--color-surface: 255 255 255; /* card/surface background — #ffffff */
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
.dark {
|
|
540
|
+
--color-primary: 96 165 250;
|
|
541
|
+
--color-primary-dark: 59 130 246;
|
|
542
|
+
--color-secondary: 148 163 184;
|
|
543
|
+
--color-danger: 248 113 113;
|
|
544
|
+
--color-warning: 250 204 21;
|
|
545
|
+
--color-success: 74 222 128;
|
|
546
|
+
--color-canvas: 15 23 42; /* slate-900 */
|
|
547
|
+
--color-surface: 30 41 59; /* slate-800 */
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
body {
|
|
551
|
+
@apply bg-canvas text-slate-900 antialiased dark:text-slate-100;
|
|
552
|
+
}
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
### 5.3 cn() helper
|
|
556
|
+
|
|
557
|
+
```typescript
|
|
558
|
+
// src/lib/utils/cn.ts
|
|
559
|
+
import { clsx, type ClassValue } from 'clsx';
|
|
560
|
+
import { twMerge } from 'tailwind-merge';
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Merge Tailwind class names, resolving conflicts (last-wins) via tailwind-merge.
|
|
564
|
+
* Use this for every conditional/composed className in the application.
|
|
565
|
+
*/
|
|
566
|
+
export function cn(...inputs: ClassValue[]): string {
|
|
567
|
+
return twMerge(clsx(inputs));
|
|
568
|
+
}
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
### 5.4 main.tsx integration
|
|
572
|
+
|
|
573
|
+
```tsx
|
|
574
|
+
// src/main.tsx
|
|
575
|
+
import React from 'react';
|
|
576
|
+
import ReactDOM from 'react-dom/client';
|
|
577
|
+
import { QueryClientProvider } from '@tanstack/react-query';
|
|
578
|
+
import { queryClient } from './lib/queryClient';
|
|
579
|
+
import { App } from './App';
|
|
580
|
+
import './index.css';
|
|
581
|
+
// [If Auth = Keycloak]
|
|
582
|
+
// import { AuthProvider } from 'react-oidc-context';
|
|
583
|
+
// import { oidcConfig } from './lib/auth/oidcConfig';
|
|
584
|
+
|
|
585
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
586
|
+
<React.StrictMode>
|
|
587
|
+
{/* [If Auth = Keycloak] <AuthProvider {...oidcConfig}> */}
|
|
588
|
+
<QueryClientProvider client={queryClient}>
|
|
589
|
+
<App />
|
|
590
|
+
</QueryClientProvider>
|
|
591
|
+
{/* [If Auth = Keycloak] </AuthProvider> */}
|
|
592
|
+
</React.StrictMode>,
|
|
593
|
+
);
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
### 5.5 App.tsx integration
|
|
597
|
+
|
|
598
|
+
```tsx
|
|
599
|
+
// src/App.tsx
|
|
600
|
+
import { useEffect } from 'react';
|
|
601
|
+
import { RouterProvider } from 'react-router-dom';
|
|
602
|
+
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
|
|
603
|
+
import { useUiStore } from './store/ui.store';
|
|
604
|
+
import { router } from './router';
|
|
605
|
+
import { NotificationProvider } from './shared/components/NotificationProvider';
|
|
606
|
+
import { ConfirmDialog } from './shared/components/ConfirmDialog';
|
|
607
|
+
// [If Auth = Keycloak]
|
|
608
|
+
// import { useAuth } from 'react-oidc-context';
|
|
609
|
+
// import { setAuthUser } from './lib/api/authBridge';
|
|
610
|
+
|
|
611
|
+
export function App() {
|
|
612
|
+
const themeMode = useUiStore((s) => s.themeMode);
|
|
613
|
+
|
|
614
|
+
// Sync the Tailwind `dark` class on the document root
|
|
615
|
+
useEffect(() => {
|
|
616
|
+
document.documentElement.classList.toggle('dark', themeMode === 'dark');
|
|
617
|
+
}, [themeMode]);
|
|
618
|
+
|
|
619
|
+
// [If Auth = Keycloak] Keep the auth bridge in sync
|
|
620
|
+
// const auth = useAuth();
|
|
621
|
+
// useEffect(() => { setAuthUser(auth.user ?? null); }, [auth.user]);
|
|
622
|
+
|
|
623
|
+
return (
|
|
624
|
+
<>
|
|
625
|
+
<RouterProvider router={router} />
|
|
626
|
+
<NotificationProvider />
|
|
627
|
+
<ConfirmDialog />
|
|
628
|
+
<ReactQueryDevtools initialIsOpen={false} />
|
|
629
|
+
</>
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
---
|
|
635
|
+
|
|
636
|
+
## Section 6: Authentication Configuration [CONDITIONAL — Include only if Auth != none]
|
|
637
|
+
|
|
638
|
+
**[If Auth = Keycloak]:**
|
|
639
|
+
Refer to `references/security-patterns.md` for the complete content.
|
|
640
|
+
Must cover:
|
|
641
|
+
- `src/lib/auth/oidcConfig.ts` — OIDC configuration object
|
|
642
|
+
- `src/lib/auth/keycloakRoles.ts` — role extraction utilities
|
|
643
|
+
- `src/lib/auth/useAuthUser.ts` — typed auth hook
|
|
644
|
+
- `src/lib/auth/roles.ts` — role constants
|
|
645
|
+
- `src/lib/api/authBridge.ts` — bridge for Axios interceptor
|
|
646
|
+
- `src/shared/components/ProtectedRoute.tsx` — route guard component
|
|
647
|
+
- `src/features/auth/pages/AuthCallbackPage.tsx` — OIDC callback handler
|
|
648
|
+
- main.tsx integration with `AuthProvider`
|
|
649
|
+
- Axios interceptor that injects Bearer token
|
|
650
|
+
|
|
651
|
+
**[If Auth = Local]:**
|
|
652
|
+
Refer to `references/security-patterns.md` (Local JWT section) for the complete content.
|
|
653
|
+
Must cover:
|
|
654
|
+
- `src/store/auth.store.ts` — Zustand auth store with login/logout/token management
|
|
655
|
+
- `src/lib/api/axiosInstance.ts` — interceptor reads token from Zustand store
|
|
656
|
+
- Token refresh interceptor for 401 handling
|
|
657
|
+
- `src/shared/components/ProtectedRoute.tsx` — redirects to /login
|
|
658
|
+
- `src/features/auth/pages/LoginPage.tsx` — email/password form with RHF + Zod + Tailwind
|
|
659
|
+
- `src/lib/auth/roles.ts` — role constants
|
|
660
|
+
|
|
661
|
+
---
|
|
662
|
+
|
|
663
|
+
## Section 7: Router Configuration
|
|
664
|
+
|
|
665
|
+
Refer to `references/routing-patterns.md` for the complete route tree.
|
|
666
|
+
Must cover:
|
|
667
|
+
- `src/router/index.tsx` — complete `createBrowserRouter()` with ALL routes from PRD.md
|
|
668
|
+
- Lazy loading via `React.lazy()` for every page component
|
|
669
|
+
- `<Suspense>` with `<LoadingOverlay />` fallback
|
|
670
|
+
- `<ProtectedRoute>` wrappers with `requiredRoles` matching the mockup role folders
|
|
671
|
+
- Public routes (login, callback, 404)
|
|
672
|
+
- The exact module-based URL paths (e.g., `/hero-section`, NOT `/admin/hero-section`)
|
|
673
|
+
|
|
674
|
+
---
|
|
675
|
+
|
|
676
|
+
## Section 8: API Client Configuration
|
|
677
|
+
|
|
678
|
+
```typescript
|
|
679
|
+
// src/lib/api/axiosInstance.ts
|
|
680
|
+
import axios from 'axios';
|
|
681
|
+
|
|
682
|
+
export const axiosInstance = axios.create({
|
|
683
|
+
baseURL: import.meta.env.VITE_API_BASE_URL,
|
|
684
|
+
timeout: 30_000,
|
|
685
|
+
headers: { 'Content-Type': 'application/json' },
|
|
686
|
+
// [If Auth = Local] withCredentials: true, // for httpOnly refresh token cookie
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
// Auth interceptor — see Section 6 for implementation
|
|
690
|
+
// Error interceptor — see below
|
|
691
|
+
axiosInstance.interceptors.response.use(
|
|
692
|
+
(response) => response,
|
|
693
|
+
(error) => {
|
|
694
|
+
if (axios.isAxiosError(error)) {
|
|
695
|
+
const status = error.response?.status;
|
|
696
|
+
const message =
|
|
697
|
+
(error.response?.data as { message?: string })?.message ??
|
|
698
|
+
error.message ??
|
|
699
|
+
'An unexpected error occurred';
|
|
700
|
+
|
|
701
|
+
// Global error notifications for non-401 errors
|
|
702
|
+
// (401 handled by the auth interceptor)
|
|
703
|
+
if (status && status !== 401) {
|
|
704
|
+
// Imported lazily to avoid circular dependency with Zustand store
|
|
705
|
+
import('../../store/notification.store').then(({ useNotificationStore }) => {
|
|
706
|
+
useNotificationStore.getState().push('error', message);
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
return Promise.reject(error);
|
|
711
|
+
},
|
|
712
|
+
);
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
---
|
|
716
|
+
|
|
717
|
+
## Section 9: Global State (Zustand)
|
|
718
|
+
|
|
719
|
+
Describe and provide code for:
|
|
720
|
+
- `src/store/notification.store.ts` — toast notification queue
|
|
721
|
+
- `src/store/ui.store.ts` — sidebar open/closed, theme mode (persisted)
|
|
722
|
+
- [If Auth = Local] `src/store/auth.store.ts` — auth state and actions
|
|
723
|
+
|
|
724
|
+
Reference `references/component-patterns.md` for the full store implementations.
|
|
725
|
+
|
|
726
|
+
---
|
|
727
|
+
|
|
728
|
+
## Section 10: TanStack Query Setup
|
|
729
|
+
|
|
730
|
+
```typescript
|
|
731
|
+
// src/lib/queryClient.ts
|
|
732
|
+
// Full implementation from references/component-patterns.md
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
Describe:
|
|
736
|
+
- `QueryClient` configuration (staleTime, gcTime, retry policy)
|
|
737
|
+
- `QueryClientProvider` in `main.tsx`
|
|
738
|
+
- `ReactQueryDevtools` in `App.tsx` (dev mode only)
|
|
739
|
+
- Query key factory pattern (object with `all`, `lists`, `list(params)`, `detail(id)`)
|
|
740
|
+
- Naming convention: `use{Entity}s` for list queries, `use{Entity}` for single queries,
|
|
741
|
+
`use{Entity}Mutations` for all CUD mutations of a feature
|
|
742
|
+
|
|
743
|
+
---
|
|
744
|
+
|
|
745
|
+
## Section 11: Shared Layouts
|
|
746
|
+
|
|
747
|
+
For each layout, provide:
|
|
748
|
+
1. The TypeScript props interface
|
|
749
|
+
2. The complete TSX component
|
|
750
|
+
|
|
751
|
+
- **DashboardLayout**: Tailwind flex layout — fixed sidebar (`AppSidebar`) + topbar
|
|
752
|
+
(`AppTopBar`) + scrollable `<main>` with `<Outlet>` and `<Footer>`
|
|
753
|
+
from `references/routing-patterns.md`
|
|
754
|
+
- **PublicLayout**: Minimal layout for landing/public pages
|
|
755
|
+
- **AuthLayout**: Centered card (`max-w-md` panel) for login/callback pages
|
|
756
|
+
|
|
757
|
+
---
|
|
758
|
+
|
|
759
|
+
## Section 12: Shared Components
|
|
760
|
+
|
|
761
|
+
For each shared component, provide the TypeScript props interface and complete TSX.
|
|
762
|
+
Reference `references/component-patterns.md` for implementations.
|
|
763
|
+
|
|
764
|
+
| Component | Purpose |
|
|
765
|
+
|---|---|
|
|
766
|
+
| `Button` | cva-based button with `variant`/`size` props (primary, outline, ghost, danger) |
|
|
767
|
+
| `Card` | Surface container (`bg-surface rounded shadow-sm border`) |
|
|
768
|
+
| `ProtectedRoute` | Auth + role guard |
|
|
769
|
+
| `AppTopBar` | Sticky topbar with sidebar toggle, user menu (Headless UI `Menu`), theme toggle |
|
|
770
|
+
| `AppSidebar` | Off-canvas/persistent sidebar with role-filtered navigation items |
|
|
771
|
+
| `navigationConfig.ts` | Navigation item definitions with role requirements |
|
|
772
|
+
| `PageHeader` | Page title + breadcrumbs + action buttons area |
|
|
773
|
+
| `StatusBadge` | cva-based colored badge for enum status values |
|
|
774
|
+
| `ConfirmDialog` + `useConfirm` | Imperative confirmation dialog (Headless UI `Dialog`) |
|
|
775
|
+
| `Modal` / `FormDialog` | Headless UI `Dialog` wrapper for forms |
|
|
776
|
+
| `LoadingOverlay` / `Spinner` | Full-page spinner overlay |
|
|
777
|
+
| `EmptyState` | Empty list placeholder with icon and message |
|
|
778
|
+
| `ErrorBoundary` | React ErrorBoundary with Tailwind fallback UI |
|
|
779
|
+
| `NotificationProvider` | Headless UI `Transition` toast stack renderer |
|
|
780
|
+
| `DataTable` | TanStack Table wrapper (or simple Tailwind table) |
|
|
781
|
+
| `Pagination` | Page navigation control |
|
|
782
|
+
| `ImageUpload` | Drag-and-drop file input with image preview |
|
|
783
|
+
| `SearchInput` | Debounced search text field |
|
|
784
|
+
|
|
785
|
+
---
|
|
786
|
+
|
|
787
|
+
## Section 13: Navigation Configuration
|
|
788
|
+
|
|
789
|
+
Generate `src/shared/components/navigationConfig.ts` with the actual navigation items
|
|
790
|
+
from the mockup sidebar files. For each role:
|
|
791
|
+
- List all nav items that role can see
|
|
792
|
+
- Include Heroicons imports (use appropriate icons from `@heroicons/react/24/outline`)
|
|
793
|
+
- Sidebar hrefs use module-based paths only (e.g., `/hero-section`, `/users`)
|
|
794
|
+
|
|
795
|
+
---
|
|
796
|
+
|
|
797
|
+
## Section 14: Form Infrastructure
|
|
798
|
+
|
|
799
|
+
Reference `references/component-patterns.md` for the complete RHF controller components.
|
|
800
|
+
Describe:
|
|
801
|
+
- `useForm()` + `zodResolver()` usage pattern
|
|
802
|
+
- All `*Controller` components with full TypeScript generics
|
|
803
|
+
- Form reset pattern for edit modes (`reset(data)` in `useEffect`)
|
|
804
|
+
- Loading state on submit button (`isSubmitting` → disabled + spinner inside `<Button>`)
|
|
805
|
+
|
|
806
|
+
---
|
|
807
|
+
|
|
808
|
+
## Section 15: Error Handling Strategy
|
|
809
|
+
|
|
810
|
+
Describe:
|
|
811
|
+
- `ErrorBoundary` component wrapping the router outlet
|
|
812
|
+
- Axios response interceptor for API errors (already in Section 8)
|
|
813
|
+
- Zod error message extraction utility
|
|
814
|
+
- TanStack Query `mutation.error` → displayed in form via `useEffect`
|
|
815
|
+
|
|
816
|
+
```typescript
|
|
817
|
+
// src/lib/utils/zodErrors.ts
|
|
818
|
+
|
|
819
|
+
import type { ZodError } from 'zod';
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Extract a flat error message from a Zod error or generic Error.
|
|
823
|
+
*/
|
|
824
|
+
export function extractErrorMessage(error: unknown): string {
|
|
825
|
+
if (error instanceof Error) return error.message;
|
|
826
|
+
if (typeof error === 'string') return error;
|
|
827
|
+
return 'An unexpected error occurred';
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
/**
|
|
831
|
+
* Map Zod field errors to React Hook Form setError calls.
|
|
832
|
+
*/
|
|
833
|
+
export function mapZodErrorsToForm<T extends Record<string, unknown>>(
|
|
834
|
+
error: ZodError,
|
|
835
|
+
setError: (field: keyof T, error: { message: string }) => void,
|
|
836
|
+
): void {
|
|
837
|
+
for (const issue of error.issues) {
|
|
838
|
+
const field = issue.path[0] as keyof T;
|
|
839
|
+
if (field) {
|
|
840
|
+
setError(field, { message: issue.message });
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
```
|
|
845
|
+
|
|
846
|
+
---
|
|
847
|
+
|
|
848
|
+
## Section 16: Notification System
|
|
849
|
+
|
|
850
|
+
Reference `references/component-patterns.md` for:
|
|
851
|
+
- `src/store/notification.store.ts` — full Zustand store
|
|
852
|
+
- `src/shared/components/NotificationProvider.tsx` — Headless UI `Transition` toast stack
|
|
853
|
+
- Usage: `useNotificationStore().push('success', 'Item saved.')` from any component or
|
|
854
|
+
mutation `onSuccess`/`onError` callback
|
|
855
|
+
|
|
856
|
+
---
|
|
857
|
+
|
|
858
|
+
## Section 17: Theming & Dark Mode
|
|
859
|
+
|
|
860
|
+
```tsx
|
|
861
|
+
// src/shared/hooks/useColorMode.ts
|
|
862
|
+
import { useUiStore } from '../../store/ui.store';
|
|
863
|
+
|
|
864
|
+
/** Returns the current theme mode and a toggle action. */
|
|
865
|
+
export function useColorMode() {
|
|
866
|
+
const themeMode = useUiStore((s) => s.themeMode);
|
|
867
|
+
const toggleThemeMode = useUiStore((s) => s.toggleThemeMode);
|
|
868
|
+
return { themeMode, toggleThemeMode };
|
|
869
|
+
}
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
```tsx
|
|
873
|
+
// src/shared/components/ThemeToggle.tsx (theme toggle example)
|
|
874
|
+
import { MoonIcon, SunIcon } from '@heroicons/react/24/outline';
|
|
875
|
+
import { useColorMode } from '../hooks/useColorMode';
|
|
876
|
+
|
|
877
|
+
export function ThemeToggle() {
|
|
878
|
+
const { themeMode, toggleThemeMode } = useColorMode();
|
|
879
|
+
|
|
880
|
+
return (
|
|
881
|
+
<button
|
|
882
|
+
type="button"
|
|
883
|
+
onClick={toggleThemeMode}
|
|
884
|
+
aria-label="Toggle theme"
|
|
885
|
+
className="rounded p-2 text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-700"
|
|
886
|
+
>
|
|
887
|
+
{themeMode === 'dark' ? (
|
|
888
|
+
<SunIcon className="h-5 w-5" />
|
|
889
|
+
) : (
|
|
890
|
+
<MoonIcon className="h-5 w-5" />
|
|
891
|
+
)}
|
|
892
|
+
</button>
|
|
893
|
+
);
|
|
894
|
+
}
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
The `dark` class is toggled on `document.documentElement` in `App.tsx` (see Section 5.5),
|
|
898
|
+
driven by the persisted `themeMode` value in the Zustand UI store.
|
|
899
|
+
|
|
900
|
+
---
|
|
901
|
+
|
|
902
|
+
## Section 18: Testing Strategy
|
|
903
|
+
|
|
904
|
+
Overview only — detailed per-feature test patterns in each module's SPEC.md.
|
|
905
|
+
|
|
906
|
+
- **Vitest + React Testing Library**: Unit tests for hooks and components
|
|
907
|
+
- **MSW (Mock Service Worker)**: API mocking for integration tests
|
|
908
|
+
- **Playwright**: E2E test scripts per CO2 workflow
|
|
909
|
+
|
|
910
|
+
```bash
|
|
911
|
+
# Add to package.json devDependencies:
|
|
912
|
+
# "vitest": "^2.0.0"
|
|
913
|
+
# "@testing-library/react": "^16.0.0"
|
|
914
|
+
# "@testing-library/user-event": "^14.0.0"
|
|
915
|
+
# "msw": "^2.0.0"
|
|
916
|
+
# "jsdom": "^25.0.0"
|
|
917
|
+
```
|
|
918
|
+
|
|
919
|
+
---
|
|
920
|
+
|
|
921
|
+
## Section 19: Build & Deployment
|
|
922
|
+
|
|
923
|
+
### Vite Build
|
|
924
|
+
|
|
925
|
+
```bash
|
|
926
|
+
npm run build
|
|
927
|
+
# Output: dist/ — static files ready for deployment
|
|
928
|
+
```
|
|
929
|
+
|
|
930
|
+
### nginx Configuration (SPA Routing)
|
|
931
|
+
|
|
932
|
+
```nginx
|
|
933
|
+
server {
|
|
934
|
+
listen 80;
|
|
935
|
+
root /usr/share/nginx/html;
|
|
936
|
+
index index.html;
|
|
937
|
+
|
|
938
|
+
# All routes fall back to index.html for client-side routing
|
|
939
|
+
location / {
|
|
940
|
+
try_files $uri $uri/ /index.html;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
# Static assets — cache aggressively (Vite hashes filenames)
|
|
944
|
+
location /assets/ {
|
|
945
|
+
expires 1y;
|
|
946
|
+
add_header Cache-Control "public, immutable";
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
```
|
|
950
|
+
|
|
951
|
+
### Chunk Splitting (vite.config.ts)
|
|
952
|
+
|
|
953
|
+
```typescript
|
|
954
|
+
// Optimised chunk splitting — add to build.rollupOptions
|
|
955
|
+
rollupOptions: {
|
|
956
|
+
output: {
|
|
957
|
+
manualChunks: {
|
|
958
|
+
vendor: ['react', 'react-dom', 'react-router-dom'],
|
|
959
|
+
ui: ['@headlessui/react', '@heroicons/react'],
|
|
960
|
+
query: ['@tanstack/react-query', 'axios'],
|
|
961
|
+
forms: ['react-hook-form', '@hookform/resolvers', 'zod'],
|
|
962
|
+
zustand: ['zustand'],
|
|
963
|
+
// [If DataGrid = yes] table: ['@tanstack/react-table'],
|
|
964
|
+
// [If Charts = yes] charts: ['recharts'],
|
|
965
|
+
},
|
|
966
|
+
},
|
|
967
|
+
},
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
> Tailwind ships no runtime JS — utility classes are compiled to a single CSS file at build
|
|
971
|
+
> time and tree-shaken against the `content` globs, so there is no "ui CSS" vendor chunk to split.
|
|
972
|
+
|
|
973
|
+
---
|
|
974
|
+
|
|
975
|
+
## Section 20: Internationalisation [CONDITIONAL — Include only if i18n = yes]
|
|
976
|
+
|
|
977
|
+
```typescript
|
|
978
|
+
// src/lib/i18n.ts
|
|
979
|
+
import i18n from 'i18next';
|
|
980
|
+
import { initReactI18next } from 'react-i18next';
|
|
981
|
+
import LanguageDetector from 'i18next-browser-languagedetector';
|
|
982
|
+
import HttpBackend from 'i18next-http-backend';
|
|
983
|
+
|
|
984
|
+
i18n
|
|
985
|
+
.use(HttpBackend)
|
|
986
|
+
.use(LanguageDetector)
|
|
987
|
+
.use(initReactI18next)
|
|
988
|
+
.init({
|
|
989
|
+
fallbackLng: 'en',
|
|
990
|
+
ns: ['common'],
|
|
991
|
+
defaultNS: 'common',
|
|
992
|
+
backend: {
|
|
993
|
+
loadPath: '/locales/{{lng}}/{{ns}}.json',
|
|
994
|
+
},
|
|
995
|
+
interpolation: { escapeValue: false },
|
|
996
|
+
});
|
|
997
|
+
|
|
998
|
+
export default i18n;
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
---
|
|
1002
|
+
|
|
1003
|
+
# Part B: Per-Module SPEC.md Template
|
|
1004
|
+
|
|
1005
|
+
Each module from PRD.md and MODEL.md gets its own `<module-name>/SPEC.md` file.
|
|
1006
|
+
|
|
1007
|
+
---
|
|
1008
|
+
|
|
1009
|
+
## Module SPEC.md Structure
|
|
1010
|
+
|
|
1011
|
+
```markdown
|
|
1012
|
+
# {{ModuleName}} — Module Specification
|
|
1013
|
+
|
|
1014
|
+
> Part of [{{APPLICATION_NAME}} Technical Specification](../SPECIFICATION.md)
|
|
1015
|
+
|
|
1016
|
+
## Overview
|
|
1017
|
+
|
|
1018
|
+
**Module:** {{ModuleName}}
|
|
1019
|
+
**Feature Path:** `src/features/{{moduleName}}/`
|
|
1020
|
+
**Type:** {{System Module | Business Module}}
|
|
1021
|
+
**Backend Endpoint Base:** `/{{api-path}}` (inferred from model/API context)
|
|
1022
|
+
```
|
|
1023
|
+
|
|
1024
|
+
---
|
|
1025
|
+
|
|
1026
|
+
### Traceability
|
|
1027
|
+
|
|
1028
|
+
```markdown
|
|
1029
|
+
## Traceability
|
|
1030
|
+
|
|
1031
|
+
### User Stories
|
|
1032
|
+
| ID | Version | Description |
|
|
1033
|
+
|---|---|---|
|
|
1034
|
+
| USA000030 | v1.0.0 | Create/update hero section content |
|
|
1035
|
+
| USA000033 | v1.0.0 | View list of hero section content |
|
|
1036
|
+
|
|
1037
|
+
### Non-Functional Requirements
|
|
1038
|
+
| ID | Version | Description |
|
|
1039
|
+
|---|---|---|
|
|
1040
|
+
| NFRA00021 | v1.0.0 | Image must be exactly 1600x500 pixels |
|
|
1041
|
+
| NFRA00024 | v1.0.0 | Headline max 100 characters |
|
|
1042
|
+
|
|
1043
|
+
### Constraints
|
|
1044
|
+
| ID | Version | Description |
|
|
1045
|
+
|---|---|---|
|
|
1046
|
+
| CONSA0012 | v1.0.0 | Status values: DRAFT, ACTIVE, EXPIRED |
|
|
1047
|
+
|
|
1048
|
+
### Removed / Replaced
|
|
1049
|
+
| ID | Removed In | Replaced By | Reason |
|
|
1050
|
+
|---|---|---|---|
|
|
1051
|
+
| CONSA0012 (READY status) | v1.0.4 | — | Auto-computed status; READY removed |
|
|
1052
|
+
_or "None." if no removals_
|
|
1053
|
+
|
|
1054
|
+
### Data Sources
|
|
1055
|
+
| Artifact | Reference |
|
|
1056
|
+
|---|---|
|
|
1057
|
+
| DB Table | `hero_section` |
|
|
1058
|
+
| Mockup Screen | `admin/content/hero_section.html` |
|
|
1059
|
+
```
|
|
1060
|
+
|
|
1061
|
+
---
|
|
1062
|
+
|
|
1063
|
+
### TypeScript Types
|
|
1064
|
+
|
|
1065
|
+
```markdown
|
|
1066
|
+
## TypeScript Types
|
|
1067
|
+
|
|
1068
|
+
File: `src/features/heroSection/heroSection.types.ts`
|
|
1069
|
+
|
|
1070
|
+
(Complete TypeScript interfaces and enums — field-for-field from MODEL.md)
|
|
1071
|
+
```
|
|
1072
|
+
|
|
1073
|
+
All enum values must match PRD.md constraints exactly (e.g., status DRAFT/ACTIVE/EXPIRED).
|
|
1074
|
+
Include the generic `PaginatedResponse<T>` type in the shared types file, not per-module.
|
|
1075
|
+
|
|
1076
|
+
---
|
|
1077
|
+
|
|
1078
|
+
### Zod Schema
|
|
1079
|
+
|
|
1080
|
+
```markdown
|
|
1081
|
+
## Zod Schema
|
|
1082
|
+
|
|
1083
|
+
File: `src/features/heroSection/heroSection.schema.ts`
|
|
1084
|
+
|
|
1085
|
+
(Complete Zod schema with all validation rules from PRD.md NFRs and Constraints)
|
|
1086
|
+
```
|
|
1087
|
+
|
|
1088
|
+
Rules must be explicitly derived from tagged NFR IDs:
|
|
1089
|
+
- Character limits → `.max(N, '...')`
|
|
1090
|
+
- URL format → `.url('...')`
|
|
1091
|
+
- Required fields → `.min(1, '...')`
|
|
1092
|
+
- Cross-field rules → `.refine()`
|
|
1093
|
+
|
|
1094
|
+
---
|
|
1095
|
+
|
|
1096
|
+
### API Functions
|
|
1097
|
+
|
|
1098
|
+
```markdown
|
|
1099
|
+
## API Functions
|
|
1100
|
+
|
|
1101
|
+
File: `src/features/heroSection/api/heroSectionApi.ts`
|
|
1102
|
+
|
|
1103
|
+
(Complete API client object with all methods matching user stories)
|
|
1104
|
+
```
|
|
1105
|
+
|
|
1106
|
+
- `list(params)` — for list/search user stories
|
|
1107
|
+
- `getById(id)` — for view detail user stories
|
|
1108
|
+
- `create(payload, imageFile?)` — for create user stories
|
|
1109
|
+
- `update(id, payload, imageFile?)` — for update user stories
|
|
1110
|
+
- `delete(id)` — for delete user stories
|
|
1111
|
+
- `imageUrl(id)` — if the module has image fields stored as blobs/binary
|
|
1112
|
+
|
|
1113
|
+
---
|
|
1114
|
+
|
|
1115
|
+
### TanStack Query Hooks
|
|
1116
|
+
|
|
1117
|
+
```markdown
|
|
1118
|
+
## TanStack Query Hooks
|
|
1119
|
+
|
|
1120
|
+
Files:
|
|
1121
|
+
- `src/features/heroSection/hooks/useHeroSections.ts`
|
|
1122
|
+
- `src/features/heroSection/hooks/useHeroSectionMutations.ts`
|
|
1123
|
+
|
|
1124
|
+
(Complete query key factory and hook implementations)
|
|
1125
|
+
```
|
|
1126
|
+
|
|
1127
|
+
- Query key factory object with `all`, `lists()`, `list(params)`, `detail(id)` entries
|
|
1128
|
+
- `use{Entity}s(params)` — paginated list query
|
|
1129
|
+
- `use{Entity}(id)` — single item query (disabled when id is empty)
|
|
1130
|
+
- `useCreate{Entity}()` — create mutation with cache invalidation
|
|
1131
|
+
- `useUpdate{Entity}()` — update mutation with cache invalidation
|
|
1132
|
+
- `useDelete{Entity}()` — delete mutation with cache invalidation
|
|
1133
|
+
|
|
1134
|
+
---
|
|
1135
|
+
|
|
1136
|
+
### Page Components
|
|
1137
|
+
|
|
1138
|
+
```markdown
|
|
1139
|
+
## Page Components
|
|
1140
|
+
|
|
1141
|
+
Files:
|
|
1142
|
+
- `src/features/heroSection/pages/HeroSectionListPage.tsx`
|
|
1143
|
+
- `src/features/heroSection/pages/HeroSectionFormPage.tsx`
|
|
1144
|
+
|
|
1145
|
+
(Complete page component implementations matching the mockup screens)
|
|
1146
|
+
```
|
|
1147
|
+
|
|
1148
|
+
**CRITICAL — page component requirements:**
|
|
1149
|
+
1. Use `PageHeader` with breadcrumbs matching `Dashboard → Module Name`
|
|
1150
|
+
2. Use `useParams()` in form page to distinguish create vs edit mode
|
|
1151
|
+
3. Reset form when edit data loads: `useEffect(() => { if (data) reset(mapToFormValues(data)); }, [data, reset])`
|
|
1152
|
+
4. Navigate back after successful submit: `navigate('/hero-section')`
|
|
1153
|
+
5. Use `LoadingOverlay` while edit data is loading
|
|
1154
|
+
6. Use `ConfirmDialog` for delete actions via `useConfirm()`
|
|
1155
|
+
7. Style all layout with Tailwind utilities; use Headless UI for any modal/menu/dropdown
|
|
1156
|
+
|
|
1157
|
+
Form page pattern for create/edit:
|
|
1158
|
+
```tsx
|
|
1159
|
+
// The same page handles both create and edit based on the presence of :id param
|
|
1160
|
+
export function HeroSectionFormPage() {
|
|
1161
|
+
const { id } = useParams<{ id: string }>();
|
|
1162
|
+
const isEditMode = !!id;
|
|
1163
|
+
// ...fetch data if isEditMode, reset form when data loads
|
|
1164
|
+
}
|
|
1165
|
+
```
|
|
1166
|
+
|
|
1167
|
+
---
|
|
1168
|
+
|
|
1169
|
+
### Form Component
|
|
1170
|
+
|
|
1171
|
+
```markdown
|
|
1172
|
+
## Form Component
|
|
1173
|
+
|
|
1174
|
+
File: `src/features/heroSection/components/HeroSectionForm.tsx`
|
|
1175
|
+
|
|
1176
|
+
(Complete form component using React Hook Form + Zod + Tailwind/Headless UI controllers)
|
|
1177
|
+
```
|
|
1178
|
+
|
|
1179
|
+
---
|
|
1180
|
+
|
|
1181
|
+
### Route Definitions
|
|
1182
|
+
|
|
1183
|
+
```markdown
|
|
1184
|
+
## Route Definitions
|
|
1185
|
+
|
|
1186
|
+
Add to `src/router/index.tsx`:
|
|
1187
|
+
|
|
1188
|
+
(Complete route definitions for this module — lazy-loaded, with ProtectedRoute wrapping
|
|
1189
|
+
and requiredRoles matching the mockup role folder access control)
|
|
1190
|
+
```
|
|
1191
|
+
|
|
1192
|
+
---
|
|
1193
|
+
|
|
1194
|
+
### Navigation Items
|
|
1195
|
+
|
|
1196
|
+
```markdown
|
|
1197
|
+
## Navigation Items
|
|
1198
|
+
|
|
1199
|
+
Add to `src/shared/components/navigationConfig.ts`:
|
|
1200
|
+
|
|
1201
|
+
(NavItem objects for this module, per role)
|
|
1202
|
+
```
|
|
1203
|
+
|
|
1204
|
+
---
|
|
1205
|
+
|
|
1206
|
+
### Status Badge Map (if module has status enum)
|
|
1207
|
+
|
|
1208
|
+
```markdown
|
|
1209
|
+
## Status Configuration
|
|
1210
|
+
|
|
1211
|
+
```typescript
|
|
1212
|
+
// Status display map for use with <StatusBadge />
|
|
1213
|
+
const HERO_SECTION_STATUS_MAP = {
|
|
1214
|
+
DRAFT: { label: 'Draft', variant: 'neutral' as const },
|
|
1215
|
+
ACTIVE: { label: 'Active', variant: 'success' as const },
|
|
1216
|
+
EXPIRED: { label: 'Expired', variant: 'danger' as const },
|
|
1217
|
+
};
|
|
1218
|
+
```
|
|
1219
|
+
|
|
1220
|
+
---
|
|
1221
|
+
|
|
1222
|
+
### CRITICAL Rules for Module SPEC.md
|
|
1223
|
+
|
|
1224
|
+
1. **Use real field names** from MODEL.md — not `fieldOne`/`fieldTwo`
|
|
1225
|
+
2. **Use real table/collection names** from MODEL.md
|
|
1226
|
+
3. **Map every user story** to a specific API function or hook
|
|
1227
|
+
4. **Map every NFR constraint** to a Zod rule or component behavior
|
|
1228
|
+
5. **Use module-based URLs** — `/hero-section`, NOT `/admin/hero-section`
|
|
1229
|
+
6. **Role guards** come from mockup role folder structure → `requiredRoles` on routes
|
|
1230
|
+
7. **Version-tag every ID** in traceability tables
|
|
1231
|
+
8. **Never use `any` type** — use proper TypeScript types or `unknown`
|
|
1232
|
+
9. **All code samples must be complete** — no `// ...` gaps
|
|
1233
|
+
10. **Form page handles both create and edit** via `useParams()` unless the mockup
|
|
1234
|
+
has clearly separate screens
|
|
1235
|
+
11. **Tailwind + Headless UI only** — no MUI/Chakra/Ant; compose classes with `cn()`,
|
|
1236
|
+
variants with `cva`, dark mode via `dark:` variants
|
|
1237
|
+
```
|