@atlashub/smartstack-cli 4.33.0 → 4.34.0
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 +45 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/project/claude-md/api.CLAUDE.md.template +315 -0
- package/templates/project/claude-md/application.CLAUDE.md.template +181 -0
- package/templates/project/claude-md/domain.CLAUDE.md.template +125 -0
- package/templates/project/claude-md/infrastructure.CLAUDE.md.template +168 -0
- package/templates/project/claude-md/root.CLAUDE.md.template +339 -0
- package/templates/project/claude-md/web.CLAUDE.md.template +339 -0
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
# {{ProjectName}} Web - React Frontend Memory
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
React SPA frontend. Communicates with {{ProjectName}}.Api via REST + SignalR.
|
|
6
|
+
|
|
7
|
+
## Tech Stack
|
|
8
|
+
|
|
9
|
+
| Technology | Version | Purpose |
|
|
10
|
+
|------------|---------|---------|
|
|
11
|
+
| React | ^18 / ^19 | UI library |
|
|
12
|
+
| TypeScript | ~5.9.3 | Type safety (strict mode) |
|
|
13
|
+
| Vite | ^7.2 | Build tool + dev server |
|
|
14
|
+
| React Router | ^6 / ^7 | Routing (lazy-loaded) |
|
|
15
|
+
| Context API | - | Client + server state |
|
|
16
|
+
| Axios | ^1.13 | HTTP client |
|
|
17
|
+
| Tailwind CSS | ^4.1 | Styling |
|
|
18
|
+
| i18next | ^25.7 | Internationalization |
|
|
19
|
+
| SignalR | ^10.0 | Real-time updates |
|
|
20
|
+
| Lucide React | ^0.562 | Icons |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## CRITICAL: Internationalization (i18n) - 4 Languages Required
|
|
25
|
+
|
|
26
|
+
### Rules
|
|
27
|
+
|
|
28
|
+
- **CRITICAL**: Every page MUST use translations via `useTranslation()` hook
|
|
29
|
+
- **CRITICAL**: All user-visible text MUST be in translation files (fr, en, it, de)
|
|
30
|
+
- **NEVER** hardcode text strings in components
|
|
31
|
+
- **ALWAYS** add translations to ALL 4 locales: `fr/`, `en/`, `it/`, `de/`
|
|
32
|
+
|
|
33
|
+
### Supported Languages
|
|
34
|
+
|
|
35
|
+
| Language | Code | Folder |
|
|
36
|
+
|----------|------|--------|
|
|
37
|
+
| French | `fr` | `locales/fr/` |
|
|
38
|
+
| English | `en` | `locales/en/` |
|
|
39
|
+
| Italian | `it` | `locales/it/` |
|
|
40
|
+
| German | `de` | `locales/de/` |
|
|
41
|
+
|
|
42
|
+
### Structure
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
src/i18n/
|
|
46
|
+
├── config.ts → i18next configuration
|
|
47
|
+
└── locales/
|
|
48
|
+
├── en/
|
|
49
|
+
│ ├── common.json → Common UI (buttons, labels, errors)
|
|
50
|
+
│ ├── navigation.json → Menu and navigation
|
|
51
|
+
│ └── {feature}.json → Feature-specific translations
|
|
52
|
+
├── fr/
|
|
53
|
+
│ └── ... → Same structure as en/
|
|
54
|
+
├── it/
|
|
55
|
+
│ └── ... → Same structure as en/
|
|
56
|
+
└── de/
|
|
57
|
+
└── ... → Same structure as en/
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Usage in Pages
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
import { useTranslation } from 'react-i18next';
|
|
64
|
+
|
|
65
|
+
export function ExamplePage() {
|
|
66
|
+
const { t } = useTranslation('feature'); // Specify namespace
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<div>
|
|
70
|
+
<h1>{t('pageTitle')}</h1>
|
|
71
|
+
<p>{t('description')}</p>
|
|
72
|
+
<button>{t('common:save')}</button> {/* Cross-namespace */}
|
|
73
|
+
</div>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Adding New Translations
|
|
79
|
+
|
|
80
|
+
**ALWAYS add to ALL 4 languages** (en, fr, it, de) with the same structure.
|
|
81
|
+
|
|
82
|
+
### Translation Keys Convention
|
|
83
|
+
|
|
84
|
+
- Use **dot notation** for nested keys: `orders.pageTitle`
|
|
85
|
+
- Use **camelCase** for key names
|
|
86
|
+
- Group by feature/section: `orders.table.headerName`
|
|
87
|
+
- Common actions in `common` namespace: `common:save`, `common:cancel`
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Structure
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
{{ProjectNameLower}}-web/
|
|
95
|
+
├── src/
|
|
96
|
+
│ ├── main.tsx → Entry point
|
|
97
|
+
│ ├── App.tsx → Root component + providers + DynamicRouter
|
|
98
|
+
│ ├── i18n/ → Internationalization
|
|
99
|
+
│ │ ├── config.ts → i18next setup
|
|
100
|
+
│ │ └── locales/ → Translation files (fr/en/it/de)
|
|
101
|
+
│ ├── services/ → API client layer
|
|
102
|
+
│ │ └── api/
|
|
103
|
+
│ │ ├── apiClient.ts → Axios instance + interceptors
|
|
104
|
+
│ │ └── {feature}Api.ts → Feature-specific API modules
|
|
105
|
+
│ ├── components/ → Shared & domain components
|
|
106
|
+
│ │ ├── ui/ → Base UI components (DataTable, Modal, etc.)
|
|
107
|
+
│ │ ├── layout/ → Layout components
|
|
108
|
+
│ │ ├── routing/ → Route guards + dynamic routing
|
|
109
|
+
│ │ └── {feature}/ → Feature-specific components
|
|
110
|
+
│ ├── pages/ → Page components
|
|
111
|
+
│ │ └── {application}/{module}/ → Organized by navigation hierarchy
|
|
112
|
+
│ ├── contexts/ → React Context providers
|
|
113
|
+
│ ├── hooks/ → Custom hooks
|
|
114
|
+
│ ├── layouts/ → Layout wrappers
|
|
115
|
+
│ ├── utils/ → Utility functions
|
|
116
|
+
│ ├── types/ → Global types
|
|
117
|
+
│ ├── routes/ → Route definitions
|
|
118
|
+
│ └── extensions/ → Extension system (PageRegistry)
|
|
119
|
+
├── tests/ → Vitest tests (NOT in src/)
|
|
120
|
+
│ ├── utils/ → Pure utility tests
|
|
121
|
+
│ ├── components/ → Component tests
|
|
122
|
+
│ ├── hooks/ → Hook tests
|
|
123
|
+
│ └── services/ → Service tests
|
|
124
|
+
├── vite.config.ts
|
|
125
|
+
├── vitest.config.ts
|
|
126
|
+
├── tsconfig.json
|
|
127
|
+
└── eslint.config.js
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Patterns
|
|
131
|
+
|
|
132
|
+
### Page Template with i18n
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
// src/pages/{module}/{PageName}Page.tsx
|
|
136
|
+
import { useTranslation } from 'react-i18next';
|
|
137
|
+
|
|
138
|
+
export function ExamplePage() {
|
|
139
|
+
const { t } = useTranslation('feature');
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<div className="container mx-auto p-4">
|
|
143
|
+
<h1 className="text-2xl font-bold">{t('example.pageTitle')}</h1>
|
|
144
|
+
<p className="text-muted-foreground">{t('example.description')}</p>
|
|
145
|
+
|
|
146
|
+
<div className="mt-4 flex gap-2">
|
|
147
|
+
<button className="btn-primary">{t('common:save')}</button>
|
|
148
|
+
<button className="btn-secondary">{t('common:cancel')}</button>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### API Client
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
// src/services/api/apiClient.ts — uses Axios instance
|
|
159
|
+
import { api } from '@/services/api/apiClient';
|
|
160
|
+
|
|
161
|
+
// Request interceptors automatically add:
|
|
162
|
+
// - Authorization: Bearer {token}
|
|
163
|
+
// - X-Tenant-Slug: {currentTenantSlug}
|
|
164
|
+
// - Accept-Language: {i18nextLng}
|
|
165
|
+
// - X-Correlation-ID: {UUID}
|
|
166
|
+
|
|
167
|
+
// Usage in service files:
|
|
168
|
+
const orders = await api.get<Order[]>('/orders');
|
|
169
|
+
const order = await api.post<Order>('/orders', { name, amount });
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### API Service File Pattern
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
// src/services/api/{feature}Api.ts
|
|
176
|
+
import { api } from './apiClient';
|
|
177
|
+
|
|
178
|
+
export interface OrderDto {
|
|
179
|
+
id: string;
|
|
180
|
+
name: string;
|
|
181
|
+
amount: number;
|
|
182
|
+
isActive: boolean;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export const orderApi = {
|
|
186
|
+
getAll: (search?: string) =>
|
|
187
|
+
api.get<OrderDto[]>('/orders', { params: { search } }),
|
|
188
|
+
|
|
189
|
+
getById: (id: string) =>
|
|
190
|
+
api.get<OrderDto>(`/orders/${id}`),
|
|
191
|
+
|
|
192
|
+
create: (data: Partial<OrderDto>) =>
|
|
193
|
+
api.post<OrderDto>('/orders', data),
|
|
194
|
+
|
|
195
|
+
update: (id: string, data: Partial<OrderDto>) =>
|
|
196
|
+
api.put<OrderDto>(`/orders/${id}`, data),
|
|
197
|
+
|
|
198
|
+
delete: (id: string) =>
|
|
199
|
+
api.delete(`/orders/${id}`),
|
|
200
|
+
};
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Custom Hook Pattern
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
// src/hooks/useOrderDetail.ts
|
|
207
|
+
import { useState, useCallback } from 'react';
|
|
208
|
+
import { orderApi, OrderDto } from '@/services/api/orderApi';
|
|
209
|
+
|
|
210
|
+
export function useOrderDetail(id: string) {
|
|
211
|
+
const [data, setData] = useState<OrderDto | null>(null);
|
|
212
|
+
const [loading, setLoading] = useState(false);
|
|
213
|
+
const [error, setError] = useState<string | null>(null);
|
|
214
|
+
|
|
215
|
+
const load = useCallback(async () => {
|
|
216
|
+
setLoading(true);
|
|
217
|
+
setError(null);
|
|
218
|
+
try {
|
|
219
|
+
const result = await orderApi.getById(id);
|
|
220
|
+
setData(result);
|
|
221
|
+
} catch (err: unknown) {
|
|
222
|
+
setError(err instanceof Error ? err.message : 'Unknown error');
|
|
223
|
+
} finally {
|
|
224
|
+
setLoading(false);
|
|
225
|
+
}
|
|
226
|
+
}, [id]);
|
|
227
|
+
|
|
228
|
+
return { data, loading, error, load };
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Context Provider Pattern
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
// src/contexts/ExampleContext.tsx
|
|
236
|
+
import { createContext, useContext, useState, type ReactNode } from 'react';
|
|
237
|
+
|
|
238
|
+
interface ExampleContextType {
|
|
239
|
+
value: string;
|
|
240
|
+
setValue: (v: string) => void;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const ExampleContext = createContext<ExampleContextType | null>(null);
|
|
244
|
+
|
|
245
|
+
export function ExampleProvider({ children }: { children: ReactNode }) {
|
|
246
|
+
const [value, setValue] = useState('');
|
|
247
|
+
return (
|
|
248
|
+
<ExampleContext.Provider value={{ value, setValue }}>
|
|
249
|
+
{children}
|
|
250
|
+
</ExampleContext.Provider>
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export function useExample() {
|
|
255
|
+
const ctx = useContext(ExampleContext);
|
|
256
|
+
if (!ctx) throw new Error('useExample must be used within ExampleProvider');
|
|
257
|
+
return ctx;
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## UI Styling Patterns
|
|
262
|
+
|
|
263
|
+
### Semantic Color Usage
|
|
264
|
+
|
|
265
|
+
| Color | CSS Variables | Usage |
|
|
266
|
+
|-------|---------------|-------|
|
|
267
|
+
| `blue` | `--info-*` | Totals, informational, in progress |
|
|
268
|
+
| `green` | `--success-*` | Completed, resolved, active |
|
|
269
|
+
| `yellow` | `--warning-*` | Pending, on hold, warnings |
|
|
270
|
+
| `red` | `--error-*` | Errors, critical, failed |
|
|
271
|
+
| `neutral` | `--bg-secondary`, `--text-primary` | Inactive, closed, disabled |
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## Commands
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
# Development
|
|
279
|
+
npm run dev # Start dev server (port 6173)
|
|
280
|
+
|
|
281
|
+
# Build
|
|
282
|
+
npm run build # Production build
|
|
283
|
+
|
|
284
|
+
# Tests (MANDATORY before commit)
|
|
285
|
+
npm test # Run all tests
|
|
286
|
+
npm run test:watch # Watch mode
|
|
287
|
+
npm run test:coverage # Coverage report
|
|
288
|
+
|
|
289
|
+
# Lint
|
|
290
|
+
npm run lint # ESLint check
|
|
291
|
+
npm run typecheck # TypeScript type check
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Routing
|
|
295
|
+
|
|
296
|
+
### Lazy Loading
|
|
297
|
+
|
|
298
|
+
All non-critical pages use `React.lazy()` for code splitting:
|
|
299
|
+
|
|
300
|
+
```tsx
|
|
301
|
+
const OrdersPage = lazy(() =>
|
|
302
|
+
import('@/pages/{module}/OrdersPage').then(m => ({ default: m.OrdersPage }))
|
|
303
|
+
);
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Route Guards (Layered)
|
|
307
|
+
|
|
308
|
+
```
|
|
309
|
+
Auth check → Tenant transition → Permission check → Render
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## Rules
|
|
315
|
+
|
|
316
|
+
1. **CRITICAL: i18n Required** - ALL pages must use `useTranslation()` with all 4 language files (fr/en/it/de)
|
|
317
|
+
2. **Pages in `pages/`**, components in `components/`** - organized by application/module
|
|
318
|
+
3. **Custom hooks for logic** - extract API calls and state management from components into `hooks/`
|
|
319
|
+
4. **Context API for global state** - use `contexts/` for auth, tenant, navigation, theme, etc.
|
|
320
|
+
5. **Axios API client** - all HTTP calls via `services/api/apiClient.ts`, never raw `fetch()`
|
|
321
|
+
6. **TypeScript strict mode** - no `any` types
|
|
322
|
+
7. **Tailwind for styling** - no CSS files, no inline styles (`style={{}}`)
|
|
323
|
+
8. **Functional components only** - no class components
|
|
324
|
+
9. **Composition over inheritance** - use props and children
|
|
325
|
+
10. **Tests in `tests/`** - NOT in `src/`, use Vitest + Testing Library
|
|
326
|
+
|
|
327
|
+
## CRITICAL: Tabs Must Persist in URL
|
|
328
|
+
|
|
329
|
+
When creating a page with tabs, **ALWAYS** persist the active tab in the URL query params (`?tab=xxx`).
|
|
330
|
+
Use the `useTabNavigation` hook.
|
|
331
|
+
|
|
332
|
+
## When Adding New Feature
|
|
333
|
+
|
|
334
|
+
1. Create page in `pages/{application}/{module}/`
|
|
335
|
+
2. Create components in `components/{feature}/`
|
|
336
|
+
3. Create API service in `services/api/{feature}Api.ts`
|
|
337
|
+
4. Create custom hooks in `hooks/use{Feature}.ts`
|
|
338
|
+
5. Register page via `PageRegistry.register()`
|
|
339
|
+
6. **CRITICAL**: Add translations to ALL 4 locales: `i18n/locales/{fr,en,it,de}/`
|