@atlashub/smartstack-cli 4.32.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/.documentation/index.html +2 -2
- package/.documentation/init.html +358 -174
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +271 -44
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/mcp-scaffolding/controller.cs.hbs +54 -128
- package/templates/project/README.md +19 -0
- 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
- package/templates/skills/apex/SKILL.md +16 -10
- package/templates/skills/apex/_shared.md +1 -1
- package/templates/skills/apex/references/checks/architecture-checks.sh +154 -0
- package/templates/skills/apex/references/checks/backend-checks.sh +194 -0
- package/templates/skills/apex/references/checks/frontend-checks.sh +448 -0
- package/templates/skills/apex/references/checks/infrastructure-checks.sh +255 -0
- package/templates/skills/apex/references/checks/security-checks.sh +153 -0
- package/templates/skills/apex/references/checks/seed-checks.sh +536 -0
- package/templates/skills/apex/references/frontend-route-wiring-app-tsx.md +49 -192
- package/templates/skills/apex/references/post-checks.md +124 -2156
- package/templates/skills/apex/references/smartstack-api.md +160 -957
- package/templates/skills/apex/references/smartstack-frontend.md +134 -1022
- package/templates/skills/apex/references/smartstack-layers.md +12 -6
- package/templates/skills/apex/steps/step-00-init.md +81 -238
- package/templates/skills/apex/steps/step-03-execute.md +25 -752
- package/templates/skills/apex/steps/step-03a-layer0-domain.md +118 -0
- package/templates/skills/apex/steps/step-03b-layer1-seed.md +91 -0
- package/templates/skills/apex/steps/step-03c-layer2-backend.md +240 -0
- package/templates/skills/apex/steps/step-03d-layer3-frontend.md +300 -0
- package/templates/skills/apex/steps/step-03e-layer4-devdata.md +44 -0
- package/templates/skills/apex/steps/step-04-examine.md +70 -150
- package/templates/skills/application/references/frontend-i18n-and-output.md +2 -2
- package/templates/skills/application/references/frontend-route-naming.md +5 -1
- package/templates/skills/application/references/frontend-route-wiring-app-tsx.md +49 -198
- package/templates/skills/application/references/frontend-verification.md +11 -11
- package/templates/skills/application/steps/step-05-frontend.md +26 -15
- package/templates/skills/application/templates-frontend.md +4 -0
- package/templates/skills/cli-app-sync/SKILL.md +2 -2
- package/templates/skills/cli-app-sync/references/comparison-map.md +1 -1
- package/templates/skills/controller/references/controller-code-templates.md +70 -67
- package/templates/skills/controller/references/mcp-scaffold-workflow.md +5 -1
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
# {{ProjectName}} - Claude Instructions
|
|
2
|
+
|
|
3
|
+
> **{{ProjectName}}** is built on the SmartStack platform (NuGet backend + npm frontend).
|
|
4
|
+
> It extends SmartStack via the **Extensions pattern**: own DbContext, own schema, own modules.
|
|
5
|
+
>
|
|
6
|
+
> 1. SmartStack provides the core platform (auth, navigation, multi-tenant, licensing)
|
|
7
|
+
> 2. {{ProjectName}} adds business-specific modules via `ExtensionsDbContext`
|
|
8
|
+
> 3. Pages are registered via `PageRegistry.register()` — DynamicRouter resolves routes automatically
|
|
9
|
+
> 4. Navigation is managed in DB (admin UI or seed data)
|
|
10
|
+
|
|
11
|
+
> **Priority**: Read sections in order. Earlier sections override later ones.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. GOLDEN RULES (ALWAYS APPLY)
|
|
16
|
+
|
|
17
|
+
| # | Rule | Enforcement |
|
|
18
|
+
|---|------|-------------|
|
|
19
|
+
| 1 | **Domain layer has ZERO dependencies** | NEVER import from Application/Infrastructure/Api in Domain |
|
|
20
|
+
| 2 | **Route = Permission path** | `/module/section` → `module.section.*` |
|
|
21
|
+
| 3 | **All GUIDs MUST be random** | Generate via `[guid]::NewGuid()` - NEVER invent patterns |
|
|
22
|
+
| 4 | **SQL objects via SqlObjectHelper** | TVFs/views use `SqlObjectHelper` + embedded `.sql` files. No other raw SQL. |
|
|
23
|
+
| 5 | **Tables use domain prefix** | `ext_Orders`, `ext_Products` in `extensions` schema |
|
|
24
|
+
| 6 | **Auto BugFix Issue after /debug** | After completing a `/debug` skill, ALWAYS run `/bugfix-issue` |
|
|
25
|
+
| 7 | **One migration per feature branch** | Use `/efcore:migration` to manage |
|
|
26
|
+
| 8 | **All code MUST have unit tests** | Backend: xUnit + FluentAssertions. Frontend: Vitest. No PR without tests |
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 2. GIT RESTRICTIONS
|
|
31
|
+
|
|
32
|
+
### NEVER Execute Directly (propose to user)
|
|
33
|
+
```
|
|
34
|
+
git commit | git push | git merge | git rebase | git cherry-pick | git reset | git revert
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### EXCEPTION: GitFlow Skills
|
|
38
|
+
When user invokes `/gitflow:*` commands → **FOLLOW the skill instructions** and execute git commands as defined by that skill.
|
|
39
|
+
|
|
40
|
+
| Context | Action |
|
|
41
|
+
|---------|--------|
|
|
42
|
+
| Manual git commands | **PROPOSE** only |
|
|
43
|
+
| `/gitflow:*` skills | **FOLLOW skill** and execute |
|
|
44
|
+
|
|
45
|
+
**GitFlow Rule**: Skills define a workflow. Execute commands **as specified by the skill**, not as an automatic pipeline.
|
|
46
|
+
|
|
47
|
+
### Allowed (read-only)
|
|
48
|
+
`git status` | `git log` | `git diff` | `git branch` | `git fetch` | `git worktree list`
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 3. ARCHITECTURE
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
{{ProjectName}}.sln
|
|
56
|
+
├── src/
|
|
57
|
+
│ ├── {{ProjectName}}.Domain → Business logic (NO dependencies)
|
|
58
|
+
│ ├── {{ProjectName}}.Application → CQRS, use cases (→ Domain)
|
|
59
|
+
│ ├── {{ProjectName}}.Infrastructure → EF Core, repositories, external services (→ Domain, Application)
|
|
60
|
+
│ └── {{ProjectName}}.Api → REST entry point (→ Application, Infrastructure)
|
|
61
|
+
│ └── Controllers/ → Organized by Application/Module
|
|
62
|
+
├── tests/
|
|
63
|
+
│ └── {{ProjectName}}.Tests.Unit → xUnit + FluentAssertions + Moq
|
|
64
|
+
└── web/{{ProjectNameLower}}-web → React + TypeScript + Vite
|
|
65
|
+
├── tests/ → Vitest unit tests (NOT in src/)
|
|
66
|
+
└── src/
|
|
67
|
+
├── pages/ → Organized by Application/Module
|
|
68
|
+
└── components/
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Dependency flow**: `Domain ← Application ← Infrastructure ← Api`
|
|
72
|
+
|
|
73
|
+
> **Note**: Application references `Microsoft.EntityFrameworkCore` for `DbSet<T>` (used by `IExtensionsDbContext`),
|
|
74
|
+
> `IQueryable<T>` async extensions, and `QueryableExtensions`.
|
|
75
|
+
> This is an intentional architectural tradeoff — the `IExtensionsDbContext` interface remains the abstraction boundary.
|
|
76
|
+
|
|
77
|
+
### Tech Stack
|
|
78
|
+
| Layer | Tech | Version |
|
|
79
|
+
|-------|------|---------|
|
|
80
|
+
| Runtime | .NET | 10.0 |
|
|
81
|
+
| ORM | EF Core | 10.0.1 |
|
|
82
|
+
| Database | SQL Server | Latest |
|
|
83
|
+
| Frontend | React + Vite | 19.x |
|
|
84
|
+
| Language | TypeScript | 5.x |
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 4. NAVIGATION HIERARCHY
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
Application → Module → Section → Resource
|
|
92
|
+
↓ ↓
|
|
93
|
+
myapp orders
|
|
94
|
+
│ │
|
|
95
|
+
└───────────┴──→ Route: /myapp/orders
|
|
96
|
+
Permission: myapp.orders.{action}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
> **Note**: Applications have an `ApplicationZone` property (enum: `Platform`, `Personal`, `Business`) used for UI layout grouping only. It does NOT appear in routes or permissions.
|
|
100
|
+
|
|
101
|
+
### Permission Actions
|
|
102
|
+
`access` | `read` | `create` | `update` | `delete` | `export` | `import` | `approve` | `reject` | `assign` | `execute`
|
|
103
|
+
|
|
104
|
+
### Adding Navigation
|
|
105
|
+
1. Add HasData in `Navigation*Configuration.cs` (include `ComponentKey` for DynamicRouter)
|
|
106
|
+
2. Run `/efcore:migration AddXxx`
|
|
107
|
+
3. Register page in `componentRegistry.generated.ts` via `PageRegistry.register()`
|
|
108
|
+
4. Add i18n in `navigation.json` (fr/en/it/de — all 4 languages)
|
|
109
|
+
5. Create page component
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 5. DATABASE CONVENTIONS
|
|
114
|
+
|
|
115
|
+
### Table Naming
|
|
116
|
+
Format: `extensions.{prefix}_{TableName}`
|
|
117
|
+
|
|
118
|
+
> **Note**: {{ProjectName}} uses the `extensions` schema, NOT the `core` schema (which belongs to SmartStack platform).
|
|
119
|
+
|
|
120
|
+
### EF Core Config Pattern
|
|
121
|
+
```csharp
|
|
122
|
+
builder.ToTable("ext_Orders", SchemaConstants.Extensions); // ✅ CORRECT
|
|
123
|
+
builder.ToTable("Orders", "ext"); // ❌ WRONG
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Migration Naming
|
|
127
|
+
Format: `extensions_v{version}_{sequence}_{Description}`
|
|
128
|
+
|
|
129
|
+
| Example |
|
|
130
|
+
|---------|
|
|
131
|
+
| `extensions_v1.0.0_001_CreateOrders` |
|
|
132
|
+
| `extensions_v1.0.0_002_AddOrderItems` |
|
|
133
|
+
|
|
134
|
+
> **Note**: Use `/efcore:migration` which calls MCP `suggest_migration` for automatic naming.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 6. CODE CONVENTIONS
|
|
139
|
+
|
|
140
|
+
### Naming
|
|
141
|
+
| Type | Pattern | Example |
|
|
142
|
+
|------|---------|---------|
|
|
143
|
+
| Entity | Singular PascalCase | `Order`, `Product` |
|
|
144
|
+
| Command | `{Action}{Entity}Command` | `CreateOrderCommand` |
|
|
145
|
+
| Query | `Get{Entity}Query` | `GetOrderByIdQuery` |
|
|
146
|
+
| Handler | `{Command/Query}Handler` | `CreateOrderCommandHandler` |
|
|
147
|
+
| DTO | `{Entity}Dto` | `OrderDto`, `OrderResponseDto` |
|
|
148
|
+
|
|
149
|
+
### Backend Patterns
|
|
150
|
+
- **CQRS**: Commands/Queries in Application layer
|
|
151
|
+
- **Repository**: Interfaces in Application, impl in Infrastructure
|
|
152
|
+
- **DI**: Register in each layer's `DependencyInjection.cs`
|
|
153
|
+
- **Domain Events**: Raise in Domain, handle in Application
|
|
154
|
+
|
|
155
|
+
### Frontend Patterns
|
|
156
|
+
- **Feature-based structure**: Group by feature
|
|
157
|
+
- **Custom hooks**: `use*.ts` for logic
|
|
158
|
+
- **API layer**: Centralized in `services/api/`
|
|
159
|
+
- **Placeholder pages**: Use `UnderDevelopment` component for WIP sections
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 7. COMMANDS
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
# Build & Run
|
|
167
|
+
dotnet build
|
|
168
|
+
cd src/{{ProjectName}}.Api && dotnet run
|
|
169
|
+
cd web/{{ProjectNameLower}}-web && npm run dev
|
|
170
|
+
|
|
171
|
+
# Tests (MANDATORY before commit)
|
|
172
|
+
dotnet test tests/{{ProjectName}}.Tests.Unit
|
|
173
|
+
cd web/{{ProjectNameLower}}-web && npm test
|
|
174
|
+
|
|
175
|
+
# EF Core
|
|
176
|
+
/efcore:migration <Name> # Create migration (auto -o)
|
|
177
|
+
/efcore:db-deploy # Apply migrations
|
|
178
|
+
/efcore:db-status # Check status
|
|
179
|
+
|
|
180
|
+
# GitFlow
|
|
181
|
+
/gitflow commit # Commit with validation
|
|
182
|
+
/gitflow pr # Create PR
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## 8. API MANAGEMENT
|
|
188
|
+
|
|
189
|
+
Claude MAY start/stop API as needed.
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
# Start (background)
|
|
193
|
+
cd src/{{ProjectName}}.Api && dotnet run &
|
|
194
|
+
|
|
195
|
+
# Stop
|
|
196
|
+
taskkill /F /IM {{ProjectName}}.Api.exe 2>nul || pkill -f {{ProjectName}}.Api
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**Rules**: Stop API when done. Stop before rebuild if files locked. Restart after code changes.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## 9. SECURITY RULES
|
|
204
|
+
|
|
205
|
+
### GUID Generation (MANDATORY)
|
|
206
|
+
```powershell
|
|
207
|
+
# ALWAYS generate via PowerShell
|
|
208
|
+
[guid]::NewGuid().ToString()
|
|
209
|
+
1..10 | ForEach-Object { [guid]::NewGuid().ToString() } # Multiple
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**FORBIDDEN patterns**:
|
|
213
|
+
- Repeated: `a1a1a1a1-b1b1-...`
|
|
214
|
+
- Sequential: `00000001-0001-...`
|
|
215
|
+
- Predictable: `a1b2c3d4-1111-2222-...`
|
|
216
|
+
|
|
217
|
+
### Why
|
|
218
|
+
- Predictable GUIDs enable enumeration attacks
|
|
219
|
+
- OWASP requires cryptographically random identifiers
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## 10. ENTITY HOOKS
|
|
224
|
+
|
|
225
|
+
| Interface | When | Use Case |
|
|
226
|
+
|-----------|------|----------|
|
|
227
|
+
| `IBeforeCreate<T>` | Pre-create | Validation |
|
|
228
|
+
| `IAfterCreate<T>` | Post-commit | Notifications |
|
|
229
|
+
| `IBeforeUpdate<T>` | Pre-update | Validation |
|
|
230
|
+
| `IAfterUpdate<T>` | Post-commit | Notifications |
|
|
231
|
+
| `IBeforeDelete<T>` | Pre-delete | Can cancel |
|
|
232
|
+
| `IAfterDelete<T>` | Post-commit | Cleanup |
|
|
233
|
+
|
|
234
|
+
```csharp
|
|
235
|
+
// Register in DependencyInjection.cs
|
|
236
|
+
services.AddScoped<IAfterCreate<Order>, OrderCreatedNotificationHook>();
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## 11. LOGGING
|
|
242
|
+
|
|
243
|
+
### Backend (Serilog)
|
|
244
|
+
| Level | Usage |
|
|
245
|
+
|-------|-------|
|
|
246
|
+
| Critical | Security breaches, unauthorized access |
|
|
247
|
+
| Error | Unhandled exceptions |
|
|
248
|
+
| Warning | Validation failures |
|
|
249
|
+
| Information | HTTP requests, user actions |
|
|
250
|
+
| Debug | SQL queries (dev only) |
|
|
251
|
+
|
|
252
|
+
### Frontend
|
|
253
|
+
```typescript
|
|
254
|
+
import { logService } from '@/services/logging/logService';
|
|
255
|
+
logService.logError(error, 'ComponentName');
|
|
256
|
+
logService.logCritical(error, 'ComponentName'); // Immediate send
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## 12. TESTING (MANDATORY)
|
|
262
|
+
|
|
263
|
+
### Rule
|
|
264
|
+
**Every new feature, entity, service, or utility MUST have corresponding unit tests.** No pull request should be merged without tests.
|
|
265
|
+
|
|
266
|
+
### Backend Tests (xUnit v3 + FluentAssertions 8.x + Moq)
|
|
267
|
+
|
|
268
|
+
| Layer | What to Test | Location |
|
|
269
|
+
|-------|-------------|----------|
|
|
270
|
+
| Domain | Entity creation, validation, state transitions | `tests/{{ProjectName}}.Tests.Unit/Domain/` |
|
|
271
|
+
| Application | Behaviors, mappings, DTOs | `tests/{{ProjectName}}.Tests.Unit/Application/` |
|
|
272
|
+
| Infrastructure | Services (JWT, FileStorage, etc.) | `tests/{{ProjectName}}.Tests.Unit/Services/` |
|
|
273
|
+
| API | Controller responses, authorization | `tests/{{ProjectName}}.Tests.Unit/Controllers/` |
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
dotnet test tests/{{ProjectName}}.Tests.Unit
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**Conventions:**
|
|
280
|
+
- Test file naming: `{ClassName}Tests.cs`
|
|
281
|
+
- Use factory methods (private constructors) - NEVER `new Entity { ... }`
|
|
282
|
+
- Use `Guid.NewGuid()` for all Guid parameters - NEVER hardcode or use strings
|
|
283
|
+
|
|
284
|
+
**FluentAssertions 8.x Gotchas:**
|
|
285
|
+
- DateTime: `BeOnOrAfter()` / `BeBefore()` / `BeCloseTo(expected, TimeSpan.FromSeconds(5))` - NOT `BeGreaterThan()`
|
|
286
|
+
- Collections: `HaveCount(n)` - NOT `HaveLength(n)`
|
|
287
|
+
- String length: `content.Length.Should().BeGreaterThan(n)` - NOT `HaveLengthGreaterThan()`
|
|
288
|
+
- Nullable: use `.Value` explicitly
|
|
289
|
+
- `WithMessage("*keyword*")` wildcard OK - but do NOT pass `StringComparison` parameter
|
|
290
|
+
|
|
291
|
+
**Domain Entity Test Pattern:**
|
|
292
|
+
```csharp
|
|
293
|
+
[Fact]
|
|
294
|
+
public void Create_WithValidData_SetsAllProperties()
|
|
295
|
+
{
|
|
296
|
+
var before = DateTime.UtcNow;
|
|
297
|
+
var entity = Entity.Create("name", "value");
|
|
298
|
+
entity.Name.Should().Be("name");
|
|
299
|
+
entity.CreatedAt.Should().BeOnOrAfter(before);
|
|
300
|
+
entity.Id.Should().NotBeEmpty();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
[Fact]
|
|
304
|
+
public void Create_WithEmptyName_ThrowsDomainException()
|
|
305
|
+
{
|
|
306
|
+
var act = () => Entity.Create("", "value");
|
|
307
|
+
act.Should().Throw<DomainException>().WithMessage("*name*");
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Frontend Tests (Vitest + Testing Library)
|
|
312
|
+
|
|
313
|
+
| Category | What to Test | Location |
|
|
314
|
+
|----------|-------------|----------|
|
|
315
|
+
| Utils | Pure functions | `web/{{ProjectNameLower}}-web/tests/utils/` |
|
|
316
|
+
| Services | Business logic | `web/{{ProjectNameLower}}-web/tests/services/` |
|
|
317
|
+
| Hooks | Custom hooks | `web/{{ProjectNameLower}}-web/tests/hooks/` |
|
|
318
|
+
| Components | UI with interactions | `web/{{ProjectNameLower}}-web/tests/components/` |
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
cd web/{{ProjectNameLower}}-web && npm test
|
|
322
|
+
cd web/{{ProjectNameLower}}-web && npm run test:watch
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**Conventions:**
|
|
326
|
+
- Test file naming: `{fileName}.test.ts` or `{fileName}.test.tsx`
|
|
327
|
+
- Tests in `web/{{ProjectNameLower}}-web/tests/` (NOT in `src/`)
|
|
328
|
+
- Import with `@/` alias
|
|
329
|
+
- Use `vi.mock()` for external dependencies
|
|
330
|
+
|
|
331
|
+
### When to Write Tests
|
|
332
|
+
|
|
333
|
+
| Trigger | Action |
|
|
334
|
+
|---------|--------|
|
|
335
|
+
| New domain entity | Add `{Entity}Tests.cs` with factory, validation, state machine tests |
|
|
336
|
+
| New utility function | Add `{util}.test.ts` covering edge cases |
|
|
337
|
+
| New service | Add `{Service}Tests.cs` or `{service}.test.ts` with mocked dependencies |
|
|
338
|
+
| Bug fix | Add regression test that reproduces the bug BEFORE fixing |
|
|
339
|
+
| New API endpoint | Add controller test with auth and response validation |
|
|
@@ -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}/`
|