@atlashub/smartstack-cli 2.5.3 → 2.6.1

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.
Files changed (44) hide show
  1. package/.documentation/business-analyse.html +4 -4
  2. package/.documentation/commands.html +2 -2
  3. package/.documentation/index.html +2 -2
  4. package/.documentation/js/app.js +2 -2
  5. package/dist/index.js +163 -56
  6. package/dist/index.js.map +1 -1
  7. package/package.json +1 -1
  8. package/templates/mcp-scaffolding/component.tsx.hbs +14 -14
  9. package/templates/mcp-scaffolding/controller.cs.hbs +6 -5
  10. package/templates/skills/_resources/docs-manifest-schema.md +3 -3
  11. package/templates/skills/_resources/mcp-validate-documentation-spec.md +6 -6
  12. package/templates/skills/apex/steps/step-04b-doc-sync.md +4 -4
  13. package/templates/skills/apex/steps/step-05-examine.md +1 -1
  14. package/templates/skills/apex/templates/04b-doc-sync.md +1 -1
  15. package/templates/skills/application/SKILL.md +33 -16
  16. package/templates/skills/application/steps/step-00-init.md +86 -3
  17. package/templates/skills/application/steps/step-01-navigation.md +34 -0
  18. package/templates/skills/application/steps/step-02-permissions.md +37 -0
  19. package/templates/skills/application/steps/step-03-roles.md +23 -2
  20. package/templates/skills/application/steps/step-03b-provider.md +251 -0
  21. package/templates/skills/application/steps/step-04-backend.md +75 -0
  22. package/templates/skills/application/steps/step-05-frontend.md +149 -10
  23. package/templates/skills/application/steps/step-06-migration.md +27 -15
  24. package/templates/skills/application/steps/step-07-tests.md +404 -0
  25. package/templates/skills/application/steps/step-08-documentation.md +137 -0
  26. package/templates/skills/application/templates-frontend.md +133 -26
  27. package/templates/skills/application/templates-seed.md +116 -0
  28. package/templates/skills/business-analyse/SKILL.md +1 -1
  29. package/templates/skills/business-analyse/questionnaire/07-ui.md +15 -0
  30. package/templates/skills/business-analyse/questionnaire/10-documentation.md +2 -2
  31. package/templates/skills/business-analyse/schemas/feature-schema.json +96 -7
  32. package/templates/skills/business-analyse/steps/step-03-specify.md +134 -5
  33. package/templates/skills/business-analyse/steps/step-05-handoff.md +61 -8
  34. package/templates/skills/business-analyse/templates/tpl-frd.md +1 -1
  35. package/templates/skills/business-analyse/templates-frd.md +8 -8
  36. package/templates/skills/business-analyse/templates-react.md +26 -26
  37. package/templates/skills/documentation/SKILL.md +6 -6
  38. package/templates/skills/documentation/data-schema.md +70 -44
  39. package/templates/skills/documentation/templates.md +6 -6
  40. package/templates/skills/ralph-loop/SKILL.md +1 -2
  41. package/templates/skills/ralph-loop/steps/step-01-task.md +1 -1
  42. package/templates/skills/ui-components/SKILL.md +33 -2
  43. package/templates/skills/ui-components/patterns/dashboard-chart.md +327 -0
  44. package/templates/skills/ui-components/style-guide.md +27 -0
@@ -2,68 +2,94 @@
2
2
 
3
3
  ## Purpose
4
4
 
5
- This file describes how to generate `frd-data.ts` files for the `FrdDocRenderer` shared component.
5
+ This file describes how to generate `doc-data.ts` files for the `DocRenderer` shared component.
6
6
  Instead of generating full TSX components (~250 lines) per module, generate only the data (~50 lines).
7
7
 
8
- ## FrdData Interface Reference
8
+ ## DocData Interface Reference
9
9
 
10
10
  The full TypeScript interface is in `web/smartstack-web/src/components/docs/types.ts`.
11
11
 
12
12
  ## Data Extraction Mapping
13
13
 
14
- ### From FRD (3-functional-specification.md)
14
+ ### From feature.json (single source of truth)
15
15
 
16
- | FRD Section | FrdData Field | Extraction |
17
- |-------------|---------------|------------|
18
- | Section 1: Overview | `overview.objective` | Functional objective text |
19
- | Section 1: Overview | `overview.problem` | Problem statement |
20
- | Section 1: Overview | `overview.solution` | Solution statement |
21
- | Section 2: Use Cases | `useCases[]` | UC-XXX entries (id, name, actor, description, permission, priority) |
22
- | Section 3: Functional Requirements | `features[]` | FR-XXX entries grouped as features |
23
- | Section 5: Permission Matrix | `permissions[]` | Role-permission entries |
24
- | Section 6: Gherkin Scenarios | (used for test data, not doc) | - |
25
- | Section 7: Validations | `businessRules[]` | BR-XXX from BRD cross-referenced |
16
+ The business analysis skill (`/business-analyse`) produces a `feature.json` file per module at:
17
+ `docs/business/{app}/{module}/business-analyse/v{X.Y}/feature.json`
26
18
 
27
- ### From BRD (2-business-requirements.md)
19
+ All documentation data is extracted from this file.
28
20
 
29
- | BRD Section | FrdData Field | Extraction |
30
- |-------------|---------------|------------|
31
- | Section 2: Business Objectives | `overview.stats` | Count objectives |
32
- | Section 3: Business Rules | `businessRules[]` | BR-XXX entries (id, name, category, statement) |
33
- | Section 6: Integrations | `technicalRef` | Integration notes |
21
+ #### Specification section DocData fields
34
22
 
35
- ### From Handoff (4-development-handoff.md)
23
+ | feature.json Path | DocData Field | Extraction |
24
+ |-------------------|---------------|------------|
25
+ | `specification.useCases[]` | `useCases[]` | UC-XXX entries (id, name, actor, description, permission, priority) |
26
+ | `specification.functionalRequirements[]` | `features[]` | FR-XXX entries grouped as features |
27
+ | `specification.permissionsMatrix` | `permissions[]` | Role-permission entries |
28
+ | `specification.apiEndpoints[]` | `apiEndpoints[]` | Endpoint entries (method, path, handler, permission) |
29
+ | `specification.wireframes[]` | `steps[]` | Inferred from wireframe descriptions |
30
+ | `specification.gherkinScenarios` | (used for test data, not doc) | - |
36
31
 
37
- | Handoff Section | FrdData Field | Extraction |
38
- |-----------------|---------------|------------|
39
- | Section 3: Files to Create | `technicalRef.entityNames` | Entity names |
40
- | Section 6: API Endpoints | `apiEndpoints[]` | Endpoint entries |
41
- | Section 1: Quick Context | `technicalRef.permissionBase` | Permission base path |
42
- | Section 1: Quick Context | `technicalRef.controllerRoute` | Controller route |
32
+ #### Analysis section DocData fields
33
+
34
+ | feature.json Path | DocData Field | Extraction |
35
+ |-------------------|---------------|------------|
36
+ | `analysis.businessRules[]` | `businessRules[]` | BR-XXX entries (id, name, category, statement) |
37
+ | `analysis.objectives[]` | `overview.stats` | Count objectives |
38
+ | `analysis.entities[]` | `technicalRef.entityNames` | Entity names from analysis |
39
+ | `analysis.integrations[]` | `technicalRef` | Integration notes |
40
+
41
+ #### Discovery section → DocData fields
42
+
43
+ | feature.json Path | DocData Field | Extraction |
44
+ |-------------------|---------------|------------|
45
+ | `discovery.problem` | `overview.problem` | Problem statement |
46
+ | `discovery.scope.inScope` | `overview.objective` | Functional objective text |
47
+ | `discovery.scope.outOfScope` | `overview.solution` | Solution statement (inferred) |
48
+ | `discovery.risks[]` | (optional) | Risk documentation |
49
+
50
+ #### Handoff section → DocData fields
51
+
52
+ | feature.json Path | DocData Field | Extraction |
53
+ |-------------------|---------------|------------|
54
+ | `handoff.filesToCreate.domain` | `technicalRef.entityNames` | Entity names |
55
+ | `handoff.apiEndpointSummary[]` | `apiEndpoints[]` | Endpoint entries (cross-validate) |
56
+ | `handoff.brToCodeMapping[]` | `technicalRef` | BR-to-code mapping |
43
57
 
44
58
  ### Generated/Inferred Fields
45
59
 
46
60
  | Field | Source |
47
61
  |-------|--------|
48
- | `featureId` | From 00-context.md state |
49
- | `moduleName` | From 00-context.md state |
50
- | `applicationName` | From 00-context.md state |
51
- | `version` | "1.0" (initial) |
52
- | `benefits[]` | Inferred from BRD objectives or i18n |
62
+ | `featureId` | `metadata.featureId` from feature.json |
63
+ | `moduleName` | `metadata.moduleName` from feature.json |
64
+ | `applicationName` | `metadata.applicationName` from feature.json |
65
+ | `version` | `metadata.version` from feature.json |
66
+ | `benefits[]` | Inferred from analysis objectives or i18n |
53
67
  | `beforeAfter` | Inferred from discovery problem/solution |
54
68
  | `steps[]` | Inferred from use cases main scenario |
55
69
  | `faq[]` | Inferred from discovery open questions |
56
70
  | `screenshots` | Convention: `/assets/docs/{module}/step-{N}.png` |
57
71
 
72
+ ## Fallback: No feature.json Available
73
+
74
+ If no feature.json exists (e.g., module was created without `/business-analyse`),
75
+ the `/documentation` skill extracts data directly from code:
76
+
77
+ | Source | DocData Field | Extraction |
78
+ |--------|---------------|------------|
79
+ | Controller `[Http*]` attributes | `apiEndpoints[]` | Method, path, permission |
80
+ | `Permissions.cs` constants | `permissions[]` | Permission paths |
81
+ | `Domain/Entities/*.cs` | `technicalRef.entityNames` | Entity class names |
82
+ | i18n JSON files | `overview`, `features` | Existing translations |
83
+
58
84
  ## Generated File Structure
59
85
 
60
- ### frd-data.ts (per module, ~50-80 lines)
86
+ ### doc-data.ts (per module, ~50-80 lines)
61
87
 
62
88
  ```typescript
63
- // web/smartstack-web/src/pages/docs/business/{app}/{module}/frd-data.ts
64
- import type { FrdData } from '@/components/docs';
89
+ // web/smartstack-web/src/pages/docs/business/{app}/{module}/doc-data.ts
90
+ import type { DocData } from '@/components/docs';
65
91
 
66
- export const frdData: FrdData = {
92
+ export const docData: DocData = {
67
93
  featureId: 'FEAT-001',
68
94
  moduleName: 'Sla',
69
95
  applicationName: 'Support',
@@ -142,13 +168,13 @@ export const frdData: FrdData = {
142
168
 
143
169
  ```typescript
144
170
  // web/smartstack-web/src/pages/docs/business/{app}/{module}/index.tsx
145
- import { FrdDocRenderer } from '@/components/docs';
146
- import { frdData } from './frd-data';
171
+ import { DocRenderer } from '@/components/docs';
172
+ import { docData } from './doc-data';
147
173
 
148
174
  export default function {Module}DocPage() {
149
175
  return (
150
- <FrdDocRenderer
151
- data={frdData}
176
+ <DocRenderer
177
+ data={docData}
152
178
  backPath="/docs/business/{app}"
153
179
  backLabel="nav.backToApp"
154
180
  />
@@ -169,14 +195,14 @@ export default function {Module}DocPage() {
169
195
  "solution": "Monitoring automatique des SLA avec alertes et escalades configurables"
170
196
  },
171
197
  "benefits": {
172
- "time": { "title": "Temps économisé", "description": "Réduction de 70% du temps de suivi manuel" },
173
- "quality": { "title": "Fiabilité", "description": "95% des SLA respectés grâce au monitoring" },
198
+ "time": { "title": "Temps economise", "description": "Reduction de 70% du temps de suivi manuel" },
199
+ "quality": { "title": "Fiabilite", "description": "95% des SLA respectes grace au monitoring" },
174
200
  "roi": { "title": "ROI", "description": "Retour sur investissement en 3 mois" }
175
201
  },
176
202
  "faq": {
177
203
  "1": {
178
204
  "question": "Dois-je configurer chaque SLA manuellement ?",
179
- "answer": "Non, des templates prédéfinis sont disponibles pour les cas courants."
205
+ "answer": "Non, des templates predefinis sont disponibles pour les cas courants."
180
206
  }
181
207
  }
182
208
  }
@@ -184,7 +210,7 @@ export default function {Module}DocPage() {
184
210
  }
185
211
  ```
186
212
 
187
- > **Note:** Seul FR est généré. EN/IT/DE sont marqués comme "deferred" et générés par un pipeline de traduction séparé.
213
+ > **Note:** Seul FR est genere. EN/IT/DE sont marques comme "deferred" et generes par un pipeline de traduction separe.
188
214
 
189
215
  ## Screenshot Convention
190
216
 
@@ -195,4 +221,4 @@ export default function {Module}DocPage() {
195
221
  | `dashboard` | `/assets/docs/{module}/dashboard.png` | Dashboard view |
196
222
 
197
223
  Screenshots are added manually or via Playwright E2E tests.
198
- The `FrdDocRenderer` shows a "Screenshot à venir" placeholder when the image file is not found.
224
+ The `DocRenderer` shows a "Screenshot a venir" placeholder when the image file is not found.
@@ -25,20 +25,20 @@ Chaque documentation de module DOIT inclure ces sections dans cet ordre:
25
25
  ## Data-Driven Approach (Recommended)
26
26
 
27
27
  > **Principe:** Au lieu de générer un composant TSX complet (~250 lignes) par module,
28
- > générer uniquement un fichier de DONNÉES (`frd-data.ts`, ~50 lignes) + un wrapper minimal.
29
- > Le rendu est assuré par le composant partagé `FrdDocRenderer` dans `web/.../components/docs/`.
28
+ > générer uniquement un fichier de DONNÉES (`doc-data.ts`, ~50 lignes) + un wrapper minimal.
29
+ > Le rendu est assuré par le composant partagé `DocRenderer` dans `web/.../components/docs/`.
30
30
 
31
31
  ### Fichiers à générer par module
32
32
 
33
- **1. Data file** (`frd-data.ts`) : Voir [data-schema.md](data-schema.md) pour le mapping complet.
33
+ **1. Data file** (`doc-data.ts`) : Voir [data-schema.md](data-schema.md) pour le mapping complet.
34
34
 
35
35
  **2. Page wrapper** (`index.tsx`, ~10 lignes) :
36
36
  ```tsx
37
- import { FrdDocRenderer } from '@/components/docs';
38
- import { frdData } from './frd-data';
37
+ import { DocRenderer } from '@/components/docs';
38
+ import { docData } from './doc-data';
39
39
 
40
40
  export default function {ModuleName}DocPage() {
41
- return <FrdDocRenderer data={frdData} backPath="/docs/business/{app}" />;
41
+ return <DocRenderer data={docData} backPath="/docs/business/{app}" />;
42
42
  }
43
43
  ```
44
44
 
@@ -214,8 +214,7 @@ Before ANY work, verify MCP servers:
214
214
  "source": {
215
215
  "type": "ba-handoff",
216
216
  "handoff_path": "path/to/4-development-handoff.md",
217
- "frd_path": null,
218
- "brd_path": null
217
+ "feature_json_path": null
219
218
  },
220
219
  "tasks": [
221
220
  {
@@ -51,7 +51,7 @@ SOURCE_PATH=${HANDOFF:-$BA_OUTPUT}
51
51
  - Read the handoff document
52
52
  - Derive tasks from its specifications (per-layer breakdown)
53
53
  - Set `source.type = "ba-handoff"` and `source.handoff_path`
54
- - Look for FRD and BRD in the same directory
54
+ - Look for feature.json in the same directory
55
55
 
56
56
  **If no handoff:**
57
57
  - Generate task breakdown from `{task_description}`
@@ -11,7 +11,9 @@ description: |
11
11
  - User asks for tooltips or infobubbles
12
12
  - Creating a page with entity display
13
13
  - Managing disabled states with explanatory messages
14
- Scope: Pages, Components, Cards, Tables, Grids, Kanban boards, Tooltips
14
+ - User mentions "dashboard", "chart", "kpi", "analytics", "graph"
15
+ - Creating a dashboard with KPI cards or charts
16
+ Scope: Pages, Components, Cards, Tables, Grids, Kanban boards, Tooltips, Dashboards, Charts
15
17
  ---
16
18
 
17
19
  # Skill UI Components SmartStack
@@ -26,6 +28,7 @@ description: |
26
28
  | List creation | "Display products in cards" |
27
29
  | Keywords | "card", "grid", "table", "kanban", "tooltip" |
28
30
  | Disabled states | "Disable button with explanatory message" |
31
+ | Dashboard/Chart creation | "dashboard", "chart", "kpi", "analytics", "graph", "metrics" |
29
32
 
30
33
  ## Detailed patterns
31
34
 
@@ -33,6 +36,7 @@ description: |
33
36
  - For DataTable: [patterns/data-table.md](patterns/data-table.md)
34
37
  - For Grid layouts: [patterns/grid-layout.md](patterns/grid-layout.md)
35
38
  - For Kanban boards: [patterns/kanban.md](patterns/kanban.md)
39
+ - For Dashboard & Charts: [patterns/dashboard-chart.md](patterns/dashboard-chart.md)
36
40
  - For styling rules: [style-guide.md](style-guide.md)
37
41
  - For accessibility: [accessibility.md](accessibility.md)
38
42
 
@@ -91,9 +95,36 @@ import { EntityCard, ProviderCard, TemplateCard } from '@/components/ui/EntityCa
91
95
  | EntityCard for entities | Custom cards with manual divs |
92
96
  | Responsive grid 1->2->3->4 | Fixed non-responsive grid |
93
97
  | h-full + flex-1 + mt-auto | Unaligned buttons in grid |
94
- | Empty and loading states | Native HTML tooltip |
98
+ | Empty, loading, and error states | Native HTML tooltip |
95
99
  | Explicit TypeScript interfaces | `any` or implicit types |
96
100
  | `<button>` for click actions | `<div role="button">` |
97
101
  | `memo()` for list item components | Unmemoized components in loops |
98
102
  | `useCallback` for event handlers | Inline arrow functions in JSX |
99
103
  | Unique `id` for list keys | Array index as key |
104
+
105
+ ### Error States with Retry
106
+
107
+ **ALWAYS provide user-friendly error states with retry capability.**
108
+
109
+ ```tsx
110
+ {error && (
111
+ <div className="p-4 bg-[var(--error-bg)] border border-[var(--error-border)] rounded-[var(--radius-card)]">
112
+ <div className="flex items-center">
113
+ <AlertCircle className="h-5 w-5 text-[var(--error-text)]" />
114
+ <span className="ml-2 text-[var(--error-text)]">{error}</span>
115
+ </div>
116
+ <button
117
+ onClick={() => refetch()}
118
+ className="mt-2 text-sm text-[var(--error-text)] hover:opacity-80 underline"
119
+ >
120
+ {t('common:actions.retry')}
121
+ </button>
122
+ </div>
123
+ )}
124
+ ```
125
+
126
+ **Rules:**
127
+ - Use semantic error CSS variables (`--error-bg`, `--error-border`, `--error-text`)
128
+ - Always provide retry button for recoverable errors
129
+ - Use Lucide `AlertCircle` icon (not inline SVG)
130
+ - Keep error messages user-friendly (not technical stack traces)
@@ -0,0 +1,327 @@
1
+ # Dashboard & Chart Patterns
2
+
3
+ > **Library:** Recharts (standardized for SmartStack)
4
+ > **Install:** `npm install recharts` (add to package.json if absent)
5
+ > **Reference:** https://recharts.org/
6
+
7
+ ## CRITICAL RULES
8
+
9
+ 1. **NEVER hardcode chart colors** - Use CSS variables (`--chart-1` to `--chart-5`)
10
+ 2. **NEVER hardcode text** - Use `useTranslation()` for all labels, tooltips, legends
11
+ 3. **ALWAYS use ResponsiveContainer** - Charts must be responsive
12
+ 4. **ALWAYS provide loading/error states** - Skeleton loaders for charts
13
+ 5. **ALWAYS use CSS variables** for backgrounds, borders, text (see style-guide.md)
14
+
15
+ ---
16
+
17
+ ## KPI CARD (StatCard)
18
+
19
+ Use for single-value metrics with trend indicator.
20
+
21
+ ```tsx
22
+ import { LucideIcon } from 'lucide-react';
23
+
24
+ interface StatCardProps {
25
+ label: string;
26
+ value: string | number;
27
+ icon: LucideIcon;
28
+ trend?: { value: number; direction: 'up' | 'down' | 'flat' };
29
+ format?: 'number' | 'currency' | 'percent' | 'duration';
30
+ }
31
+
32
+ function StatCard({ label, value, icon: Icon, trend, format = 'number' }: StatCardProps) {
33
+ const formattedValue = formatValue(value, format);
34
+
35
+ return (
36
+ <div className="rounded-[var(--radius-card)] bg-[var(--bg-secondary)] border border-[var(--border-color)] p-6">
37
+ <div className="flex items-center justify-between">
38
+ <span className="text-sm font-medium text-[var(--text-muted)]">{label}</span>
39
+ <Icon className="h-5 w-5 text-[var(--color-primary-500)]" />
40
+ </div>
41
+ <div className="mt-2 text-2xl font-bold text-[var(--text-primary)]">
42
+ {formattedValue}
43
+ </div>
44
+ {trend && (
45
+ <div className={`mt-1 flex items-center text-sm ${
46
+ trend.direction === 'up' ? 'text-[var(--success-text)]' :
47
+ trend.direction === 'down' ? 'text-[var(--error-text)]' :
48
+ 'text-[var(--text-muted)]'
49
+ }`}>
50
+ {trend.direction === 'up' ? '↑' : trend.direction === 'down' ? '↓' : '→'}
51
+ <span className="ml-1">{Math.abs(trend.value)}%</span>
52
+ </div>
53
+ )}
54
+ </div>
55
+ );
56
+ }
57
+ ```
58
+
59
+ ### KPI Card with Thresholds
60
+
61
+ ```tsx
62
+ function getThresholdColor(value: number, thresholds?: { warning: number; critical: number }) {
63
+ if (!thresholds) return 'text-[var(--text-primary)]';
64
+ if (value >= thresholds.critical) return 'text-[var(--error-text)]';
65
+ if (value >= thresholds.warning) return 'text-[var(--warning-text)]';
66
+ return 'text-[var(--success-text)]';
67
+ }
68
+ ```
69
+
70
+ ---
71
+
72
+ ## CHART WRAPPER (ChartCard)
73
+
74
+ Wraps any Recharts chart in a consistent card layout.
75
+
76
+ ```tsx
77
+ interface ChartCardProps {
78
+ title: string;
79
+ subtitle?: string;
80
+ children: React.ReactNode;
81
+ height?: number;
82
+ loading?: boolean;
83
+ }
84
+
85
+ function ChartCard({ title, subtitle, children, height = 300, loading }: ChartCardProps) {
86
+ if (loading) {
87
+ return (
88
+ <div className="rounded-[var(--radius-card)] bg-[var(--bg-secondary)] border border-[var(--border-color)] p-6">
89
+ <div className="h-4 w-1/3 bg-[var(--bg-tertiary)] rounded animate-pulse mb-4" />
90
+ <div className="bg-[var(--bg-tertiary)] rounded animate-pulse" style={{ height }} />
91
+ </div>
92
+ );
93
+ }
94
+
95
+ return (
96
+ <div className="rounded-[var(--radius-card)] bg-[var(--bg-secondary)] border border-[var(--border-color)] p-6">
97
+ <h3 className="text-base font-semibold text-[var(--text-primary)]">{title}</h3>
98
+ {subtitle && (
99
+ <p className="text-sm text-[var(--text-muted)] mt-1">{subtitle}</p>
100
+ )}
101
+ <div className="mt-4" style={{ height }}>
102
+ {children}
103
+ </div>
104
+ </div>
105
+ );
106
+ }
107
+ ```
108
+
109
+ ---
110
+
111
+ ## CHART TYPES
112
+
113
+ ### Shared Tooltip Style
114
+
115
+ ```tsx
116
+ const TOOLTIP_STYLE = {
117
+ contentStyle: {
118
+ backgroundColor: 'var(--bg-secondary)',
119
+ border: '1px solid var(--border-color)',
120
+ borderRadius: 'var(--radius-card)',
121
+ color: 'var(--text-primary)',
122
+ fontSize: '0.875rem',
123
+ },
124
+ labelStyle: {
125
+ color: 'var(--text-primary)',
126
+ fontWeight: 600,
127
+ },
128
+ };
129
+ ```
130
+
131
+ ### Chart Color Palette
132
+
133
+ ```tsx
134
+ const CHART_COLORS = [
135
+ 'var(--chart-1)',
136
+ 'var(--chart-2)',
137
+ 'var(--chart-3)',
138
+ 'var(--chart-4)',
139
+ 'var(--chart-5)',
140
+ ];
141
+ ```
142
+
143
+ ### Bar Chart
144
+
145
+ ```tsx
146
+ import { ResponsiveContainer, BarChart, Bar, XAxis, YAxis, Tooltip, CartesianGrid } from 'recharts';
147
+
148
+ <ChartCard title={t('charts.itemsByMonth')}>
149
+ <ResponsiveContainer width="100%" height="100%">
150
+ <BarChart data={data}>
151
+ <CartesianGrid strokeDasharray="3 3" stroke="var(--border-color)" />
152
+ <XAxis dataKey="name" stroke="var(--text-muted)" fontSize={12} />
153
+ <YAxis stroke="var(--text-muted)" fontSize={12} />
154
+ <Tooltip {...TOOLTIP_STYLE} />
155
+ <Bar dataKey="value" fill="var(--chart-1)" radius={[4, 4, 0, 0]} />
156
+ </BarChart>
157
+ </ResponsiveContainer>
158
+ </ChartCard>
159
+ ```
160
+
161
+ ### Stacked Bar Chart
162
+
163
+ ```tsx
164
+ <ResponsiveContainer width="100%" height="100%">
165
+ <BarChart data={data}>
166
+ <CartesianGrid strokeDasharray="3 3" stroke="var(--border-color)" />
167
+ <XAxis dataKey="month" stroke="var(--text-muted)" fontSize={12} />
168
+ <YAxis stroke="var(--text-muted)" fontSize={12} />
169
+ <Tooltip {...TOOLTIP_STYLE} />
170
+ <Bar dataKey="active" stackId="a" fill="var(--chart-1)" />
171
+ <Bar dataKey="pending" stackId="a" fill="var(--chart-2)" />
172
+ <Bar dataKey="closed" stackId="a" fill="var(--chart-3)" radius={[4, 4, 0, 0]} />
173
+ </BarChart>
174
+ </ResponsiveContainer>
175
+ ```
176
+
177
+ ### Line Chart
178
+
179
+ ```tsx
180
+ import { ResponsiveContainer, LineChart, Line, XAxis, YAxis, Tooltip, CartesianGrid } from 'recharts';
181
+
182
+ <ChartCard title={t('charts.trendOverTime')}>
183
+ <ResponsiveContainer width="100%" height="100%">
184
+ <LineChart data={data}>
185
+ <CartesianGrid strokeDasharray="3 3" stroke="var(--border-color)" />
186
+ <XAxis dataKey="date" stroke="var(--text-muted)" fontSize={12} />
187
+ <YAxis stroke="var(--text-muted)" fontSize={12} />
188
+ <Tooltip {...TOOLTIP_STYLE} />
189
+ <Line type="monotone" dataKey="value" stroke="var(--chart-1)" strokeWidth={2} dot={false} />
190
+ <Line type="monotone" dataKey="target" stroke="var(--chart-2)" strokeWidth={2} strokeDasharray="5 5" dot={false} />
191
+ </LineChart>
192
+ </ResponsiveContainer>
193
+ </ChartCard>
194
+ ```
195
+
196
+ ### Pie / Donut Chart
197
+
198
+ ```tsx
199
+ import { ResponsiveContainer, PieChart, Pie, Cell, Tooltip, Legend } from 'recharts';
200
+
201
+ <ChartCard title={t('charts.distributionByStatus')}>
202
+ <ResponsiveContainer width="100%" height="100%">
203
+ <PieChart>
204
+ <Pie
205
+ data={data}
206
+ cx="50%"
207
+ cy="50%"
208
+ innerRadius={60} // 0 for pie, >0 for donut
209
+ outerRadius={100}
210
+ dataKey="value"
211
+ nameKey="name"
212
+ label={({ name, percent }) => `${name} ${(percent * 100).toFixed(0)}%`}
213
+ >
214
+ {data.map((_, index) => (
215
+ <Cell key={index} fill={CHART_COLORS[index % CHART_COLORS.length]} />
216
+ ))}
217
+ </Pie>
218
+ <Tooltip {...TOOLTIP_STYLE} />
219
+ <Legend />
220
+ </PieChart>
221
+ </ResponsiveContainer>
222
+ </ChartCard>
223
+ ```
224
+
225
+ ### Area Chart
226
+
227
+ ```tsx
228
+ import { ResponsiveContainer, AreaChart, Area, XAxis, YAxis, Tooltip, CartesianGrid } from 'recharts';
229
+
230
+ <ChartCard title={t('charts.volumeOverTime')}>
231
+ <ResponsiveContainer width="100%" height="100%">
232
+ <AreaChart data={data}>
233
+ <CartesianGrid strokeDasharray="3 3" stroke="var(--border-color)" />
234
+ <XAxis dataKey="date" stroke="var(--text-muted)" fontSize={12} />
235
+ <YAxis stroke="var(--text-muted)" fontSize={12} />
236
+ <Tooltip {...TOOLTIP_STYLE} />
237
+ <Area type="monotone" dataKey="value" stroke="var(--chart-1)" fill="var(--chart-1)" fillOpacity={0.2} />
238
+ </AreaChart>
239
+ </ResponsiveContainer>
240
+ </ChartCard>
241
+ ```
242
+
243
+ ---
244
+
245
+ ## DASHBOARD LAYOUT
246
+
247
+ ### Standard Dashboard Grid
248
+
249
+ ```tsx
250
+ interface DashboardLayoutProps {
251
+ kpis: StatCardProps[];
252
+ charts: ChartCardProps[];
253
+ filters?: React.ReactNode;
254
+ loading?: boolean;
255
+ }
256
+
257
+ function DashboardLayout({ kpis, charts, filters, loading }: DashboardLayoutProps) {
258
+ return (
259
+ <div className="space-y-6">
260
+ {/* Filters */}
261
+ {filters && (
262
+ <div className="flex flex-wrap items-center gap-4">
263
+ {filters}
264
+ </div>
265
+ )}
266
+
267
+ {/* KPI Row - 1 col mobile, 2 col tablet, 4 col desktop */}
268
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
269
+ {kpis.map((kpi, i) => (
270
+ <StatCard key={i} {...kpi} />
271
+ ))}
272
+ </div>
273
+
274
+ {/* Charts Row - 1 col mobile, 2 col desktop */}
275
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
276
+ {charts.map((chart, i) => (
277
+ <ChartCard key={i} {...chart} loading={loading} />
278
+ ))}
279
+ </div>
280
+ </div>
281
+ );
282
+ }
283
+ ```
284
+
285
+ ### Period Filter
286
+
287
+ ```tsx
288
+ import { useTranslation } from 'react-i18next';
289
+
290
+ function PeriodFilter({ value, onChange }: { value: string; onChange: (v: string) => void }) {
291
+ const { t } = useTranslation();
292
+ const periods = [
293
+ { value: 'day', label: t('common:periods.day') },
294
+ { value: 'week', label: t('common:periods.week') },
295
+ { value: 'month', label: t('common:periods.month') },
296
+ { value: 'quarter', label: t('common:periods.quarter') },
297
+ { value: 'year', label: t('common:periods.year') },
298
+ ];
299
+
300
+ return (
301
+ <select
302
+ value={value}
303
+ onChange={(e) => onChange(e.target.value)}
304
+ className="rounded-[var(--radius-button)] bg-[var(--bg-secondary)] border border-[var(--border-color)] text-[var(--text-primary)] px-3 py-1.5 text-sm"
305
+ >
306
+ {periods.map(p => (
307
+ <option key={p.value} value={p.value}>{p.label}</option>
308
+ ))}
309
+ </select>
310
+ );
311
+ }
312
+ ```
313
+
314
+ ---
315
+
316
+ ## ABSOLUTE RULES
317
+
318
+ | DO | DON'T |
319
+ |----|-------|
320
+ | CSS variables for ALL chart colors (`--chart-1` to `--chart-5`) | Hardcoded hex colors (`fill="#8884d8"`) |
321
+ | CSS variables for backgrounds/borders/text | Hardcoded Tailwind (`bg-blue-500`) |
322
+ | `ResponsiveContainer width="100%" height="100%"` | Fixed pixel dimensions |
323
+ | `useTranslation()` for labels and tooltips | Hardcoded text strings |
324
+ | Skeleton loading states for charts | Blank space during loading |
325
+ | Separate `StatCard` for KPI values | Inline divs with numbers |
326
+ | `ChartCard` wrapper for consistent styling | Naked charts without card wrapper |
327
+ | `memo()` for chart components in lists | Re-rendering charts on every state change |
@@ -40,6 +40,33 @@ const STATUS_CONFIG = {
40
40
  };
41
41
  ```
42
42
 
43
+ ### Chart Colors
44
+
45
+ For data visualizations (Recharts), use dedicated chart CSS variables:
46
+
47
+ | Variable | Usage | Example |
48
+ |----------|-------|---------|
49
+ | `--chart-1` | Primary data series | Main bar/line/area |
50
+ | `--chart-2` | Secondary data series | Comparison line |
51
+ | `--chart-3` | Tertiary data series | Third category |
52
+ | `--chart-4` | Quaternary data series | Fourth category |
53
+ | `--chart-5` | Quinary data series | Fifth category |
54
+
55
+ These variables adapt automatically to light/dark theme.
56
+
57
+ ```tsx
58
+ // CORRECT - CSS variable
59
+ <Bar dataKey="value" fill="var(--chart-1)" />
60
+ <Line stroke="var(--chart-2)" />
61
+ <Cell fill={CHART_COLORS[index % CHART_COLORS.length]} />
62
+
63
+ // WRONG - hardcoded colors
64
+ <Bar dataKey="value" fill="#8884d8" />
65
+ <Line stroke="blue" />
66
+ ```
67
+
68
+ **See [patterns/dashboard-chart.md](patterns/dashboard-chart.md) for complete chart patterns.**
69
+
43
70
  ## BUTTON VARIANTS
44
71
 
45
72
  ### Action Buttons (Status changes)