@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.
- package/.documentation/business-analyse.html +4 -4
- package/.documentation/commands.html +2 -2
- package/.documentation/index.html +2 -2
- package/.documentation/js/app.js +2 -2
- package/dist/index.js +163 -56
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/mcp-scaffolding/component.tsx.hbs +14 -14
- package/templates/mcp-scaffolding/controller.cs.hbs +6 -5
- package/templates/skills/_resources/docs-manifest-schema.md +3 -3
- package/templates/skills/_resources/mcp-validate-documentation-spec.md +6 -6
- package/templates/skills/apex/steps/step-04b-doc-sync.md +4 -4
- package/templates/skills/apex/steps/step-05-examine.md +1 -1
- package/templates/skills/apex/templates/04b-doc-sync.md +1 -1
- package/templates/skills/application/SKILL.md +33 -16
- package/templates/skills/application/steps/step-00-init.md +86 -3
- package/templates/skills/application/steps/step-01-navigation.md +34 -0
- package/templates/skills/application/steps/step-02-permissions.md +37 -0
- package/templates/skills/application/steps/step-03-roles.md +23 -2
- package/templates/skills/application/steps/step-03b-provider.md +251 -0
- package/templates/skills/application/steps/step-04-backend.md +75 -0
- package/templates/skills/application/steps/step-05-frontend.md +149 -10
- package/templates/skills/application/steps/step-06-migration.md +27 -15
- package/templates/skills/application/steps/step-07-tests.md +404 -0
- package/templates/skills/application/steps/step-08-documentation.md +137 -0
- package/templates/skills/application/templates-frontend.md +133 -26
- package/templates/skills/application/templates-seed.md +116 -0
- package/templates/skills/business-analyse/SKILL.md +1 -1
- package/templates/skills/business-analyse/questionnaire/07-ui.md +15 -0
- package/templates/skills/business-analyse/questionnaire/10-documentation.md +2 -2
- package/templates/skills/business-analyse/schemas/feature-schema.json +96 -7
- package/templates/skills/business-analyse/steps/step-03-specify.md +134 -5
- package/templates/skills/business-analyse/steps/step-05-handoff.md +61 -8
- package/templates/skills/business-analyse/templates/tpl-frd.md +1 -1
- package/templates/skills/business-analyse/templates-frd.md +8 -8
- package/templates/skills/business-analyse/templates-react.md +26 -26
- package/templates/skills/documentation/SKILL.md +6 -6
- package/templates/skills/documentation/data-schema.md +70 -44
- package/templates/skills/documentation/templates.md +6 -6
- package/templates/skills/ralph-loop/SKILL.md +1 -2
- package/templates/skills/ralph-loop/steps/step-01-task.md +1 -1
- package/templates/skills/ui-components/SKILL.md +33 -2
- package/templates/skills/ui-components/patterns/dashboard-chart.md +327 -0
- package/templates/skills/ui-components/style-guide.md +27 -0
|
@@ -64,12 +64,12 @@ export function $MODULE_PASCALPage() {
|
|
|
64
64
|
import { useState, useEffect } from 'react';
|
|
65
65
|
import { useNavigate } from 'react-router-dom';
|
|
66
66
|
import { useTranslation } from 'react-i18next';
|
|
67
|
-
import { Plus, Search, MoreVertical, Pencil, Trash2, Check } from 'lucide-react';
|
|
67
|
+
import { Plus, Search, MoreVertical, Pencil, Trash2, Check, AlertCircle } from 'lucide-react';
|
|
68
68
|
import { api } from '@/services/api/apiClient';
|
|
69
69
|
import { use$MODULE_PASCALPreferences } from '@/hooks/use$MODULE_PASCALPreferences';
|
|
70
70
|
import { Pagination } from '@/components/common/Pagination';
|
|
71
71
|
import { ColumnSelector } from '@/components/common/ColumnSelector';
|
|
72
|
-
import {
|
|
72
|
+
import { ViewToggle } from '@/components/ui/DataView';
|
|
73
73
|
import { EntityCard } from '@/components/ui/EntityCard'; // ⚠️ MANDATORY for Grid view
|
|
74
74
|
|
|
75
75
|
interface $ENTITY_PASCALDto {
|
|
@@ -217,7 +217,7 @@ export function $MODULE_PASCALListView({
|
|
|
217
217
|
visibleColumns={visibleColumns}
|
|
218
218
|
onChange={setVisibleColumns}
|
|
219
219
|
/>
|
|
220
|
-
<
|
|
220
|
+
<ViewToggle value={viewMode} onChange={setViewMode} />
|
|
221
221
|
</div>
|
|
222
222
|
</div>
|
|
223
223
|
|
|
@@ -227,8 +227,19 @@ export function $MODULE_PASCALListView({
|
|
|
227
227
|
<div className="animate-spin h-8 w-8 border-4 border-[var(--color-accent-500)] border-t-transparent rounded-full" />
|
|
228
228
|
</div>
|
|
229
229
|
) : error ? (
|
|
230
|
-
<div className="p-4 bg-[var(--error-bg)] border border-[var(--error-border)]
|
|
231
|
-
|
|
230
|
+
<div className="p-4 bg-[var(--error-bg)] border border-[var(--error-border)] rounded-[var(--radius-card)] flex items-center gap-3">
|
|
231
|
+
<AlertCircle className="w-5 h-5 text-[var(--error-dot)] flex-shrink-0" />
|
|
232
|
+
<span className="text-[var(--error-text)]">{error}</span>
|
|
233
|
+
<button onClick={() => fetchData()} className="ml-auto text-sm text-[var(--error-text)] hover:underline">
|
|
234
|
+
{t('common:actions.retry')}
|
|
235
|
+
</button>
|
|
236
|
+
</div>
|
|
237
|
+
) : data?.items.length === 0 ? (
|
|
238
|
+
<div className="text-center py-16">
|
|
239
|
+
<div className="w-16 h-16 mx-auto rounded-full bg-[var(--bg-secondary)] flex items-center justify-center mb-4">
|
|
240
|
+
<Search className="w-8 h-8 text-[var(--text-tertiary)]" />
|
|
241
|
+
</div>
|
|
242
|
+
<p className="text-[var(--text-secondary)]">{t('$module:list.empty')}</p>
|
|
232
243
|
</div>
|
|
233
244
|
) : viewMode === 'list' ? (
|
|
234
245
|
<div className="bg-[var(--bg-card)] border border-[var(--item-color-border)] overflow-hidden" style={{ borderRadius: 'var(--radius-card)' }}>
|
|
@@ -276,8 +287,8 @@ export function $MODULE_PASCALListView({
|
|
|
276
287
|
<span
|
|
277
288
|
className={`inline-flex items-center px-2 py-1 text-xs font-medium ${
|
|
278
289
|
item.isActive
|
|
279
|
-
? 'bg-
|
|
280
|
-
: 'bg-
|
|
290
|
+
? 'bg-[var(--success-bg)] text-[var(--success-text)]'
|
|
291
|
+
: 'bg-[var(--bg-tertiary)] text-[var(--text-secondary)]'
|
|
281
292
|
}`}
|
|
282
293
|
style={{ borderRadius: 'var(--radius-badge)' }}
|
|
283
294
|
>
|
|
@@ -477,17 +488,29 @@ export const $moduleApi = {
|
|
|
477
488
|
|
|
478
489
|
## TEMPLATE: ROUTES (App.tsx)
|
|
479
490
|
|
|
480
|
-
### ⚠️ CRITICAL RULE:
|
|
491
|
+
### ⚠️ CRITICAL RULE 1: ROUTES MUST BE INSIDE LAYOUT WRAPPER
|
|
481
492
|
|
|
482
|
-
|
|
493
|
+
SmartStack layouts (`AdminLayout`, `BusinessLayout`, `UserLayout`) provide the application shell: **header with AvatarMenu**, sidebar, navigation. They render child pages via React Router's `<Outlet />`.
|
|
483
494
|
|
|
484
|
-
|
|
495
|
+
**If routes are placed OUTSIDE the layout wrapper, the shell (header, sidebar, AvatarMenu) will NOT render. The page appears "naked" without any navigation.**
|
|
496
|
+
|
|
497
|
+
**Step-by-step insertion:**
|
|
498
|
+
|
|
499
|
+
1. Open `App.tsx`
|
|
500
|
+
2. Find the existing layout route for the target context:
|
|
501
|
+
- `platform` → `<Route path="/platform" element={<AdminLayout />}>`
|
|
502
|
+
- `business` → `<Route path="/business" element={<BusinessLayout />}>`
|
|
503
|
+
- `personal` → `<Route path="/personal/myspace" element={<UserLayout />}>`
|
|
504
|
+
3. Add the new routes **INSIDE** that `<Route>` block
|
|
505
|
+
4. If a tenant-prefixed block exists (`/t/:slug/...`), add the routes there too
|
|
506
|
+
|
|
507
|
+
**❌ FORBIDDEN - Flat routes OUTSIDE layout (AvatarMenu disappears!)**
|
|
485
508
|
```tsx
|
|
486
|
-
|
|
487
|
-
<Route path="
|
|
509
|
+
// These routes bypass the layout system entirely = NO header, NO sidebar, NO AvatarMenu
|
|
510
|
+
<Route path="/$CONTEXT/$APPLICATION/$MODULE" element={<$MODULE_PASCALPage />} />
|
|
488
511
|
```
|
|
489
512
|
|
|
490
|
-
**✅ MANDATORY -
|
|
513
|
+
**✅ MANDATORY - Routes INSIDE the existing layout wrapper**
|
|
491
514
|
```tsx
|
|
492
515
|
// Add to App.tsx
|
|
493
516
|
|
|
@@ -495,13 +518,44 @@ import { $MODULE_PASCALPage } from '@/pages/$CONTEXT/$APPLICATION/$MODULE/$MODUL
|
|
|
495
518
|
import { $MODULE_PASCALDetailPage } from '@/pages/$CONTEXT/$APPLICATION/$MODULE/$MODULE_PASCALDetailPage';
|
|
496
519
|
import { Create$MODULE_PASCALPage } from '@/pages/$CONTEXT/$APPLICATION/$MODULE/Create$MODULE_PASCALPage';
|
|
497
520
|
|
|
498
|
-
//
|
|
521
|
+
// Find the EXISTING layout route and add routes INSIDE it:
|
|
522
|
+
<Route path="/$CONTEXT" element={<$CONTEXT_Layout />}>
|
|
523
|
+
{/* ... existing routes stay here ... */}
|
|
524
|
+
|
|
525
|
+
{/* NEW: $APPLICATION routes - added as children of the layout */}
|
|
526
|
+
<Route path="$APPLICATION">
|
|
527
|
+
<Route index element={<Navigate to="$DEFAULT_MODULE" replace />} />
|
|
528
|
+
<Route path="$MODULE" element={<$MODULE_PASCALPage />} />
|
|
529
|
+
<Route path="$MODULE/new" element={<Create$MODULE_PASCALPage />} />
|
|
530
|
+
<Route path="$MODULE/:id" element={<$MODULE_PASCALDetailPage />} />
|
|
531
|
+
<Route path="$MODULE/:id/edit" element={<Create$MODULE_PASCALPage />} />
|
|
532
|
+
</Route>
|
|
533
|
+
</Route>
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### Context-to-Layout mapping
|
|
537
|
+
|
|
538
|
+
| Context | Layout Component | Layout Route Path |
|
|
539
|
+
|---------|------------------|-------------------|
|
|
540
|
+
| `platform` | `AdminLayout` | `/platform` |
|
|
541
|
+
| `business` | `BusinessLayout` | `/business` |
|
|
542
|
+
| `personal` | `UserLayout` | `/personal/myspace` |
|
|
543
|
+
|
|
544
|
+
### ⚠️ CRITICAL RULE 2: NESTED ROUTES MANDATORY
|
|
545
|
+
|
|
546
|
+
React Router v7 **requires** nested routes for multi-module applications.
|
|
547
|
+
|
|
548
|
+
**❌ FORBIDDEN - Flat routes (cause redirects to Home)**
|
|
549
|
+
```tsx
|
|
550
|
+
<Route path="$APPLICATION" element={<Navigate to="/$CONTEXT/$APPLICATION/$DEFAULT_MODULE" replace />} />
|
|
551
|
+
<Route path="$APPLICATION/$MODULE" element={<$MODULE_PASCALPage />} />
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
**✅ MANDATORY - Nested routes with index**
|
|
555
|
+
```tsx
|
|
499
556
|
<Route path="$APPLICATION">
|
|
500
557
|
<Route index element={<Navigate to="$DEFAULT_MODULE" replace />} />
|
|
501
558
|
<Route path="$MODULE" element={<$MODULE_PASCALPage />} />
|
|
502
|
-
<Route path="$MODULE/new" element={<Create$MODULE_PASCALPage />} />
|
|
503
|
-
<Route path="$MODULE/:id" element={<$MODULE_PASCALDetailPage />} />
|
|
504
|
-
<Route path="$MODULE/:id/edit" element={<Create$MODULE_PASCALPage />} />
|
|
505
559
|
</Route>
|
|
506
560
|
```
|
|
507
561
|
|
|
@@ -509,6 +563,8 @@ import { Create$MODULE_PASCALPage } from '@/pages/$CONTEXT/$APPLICATION/$MODULE/
|
|
|
509
563
|
|
|
510
564
|
| Aspect | Flat routes | Nested routes |
|
|
511
565
|
|--------|------------|----------------|
|
|
566
|
+
| Shell rendered | ❌ No (bypasses layout) | ✅ Yes (Outlet pattern) |
|
|
567
|
+
| AvatarMenu visible | ❌ No | ✅ Yes |
|
|
512
568
|
| Matching | Ambiguous between siblings | Hierarchical clear |
|
|
513
569
|
| Navigate | Must be absolute | Can be relative |
|
|
514
570
|
| Outlet | Not supported | Supported |
|
|
@@ -516,16 +572,67 @@ import { Create$MODULE_PASCALPage } from '@/pages/$CONTEXT/$APPLICATION/$MODULE/
|
|
|
516
572
|
|
|
517
573
|
---
|
|
518
574
|
|
|
575
|
+
## ⛔ FORBIDDEN PATTERNS
|
|
576
|
+
|
|
577
|
+
These patterns are **strictly prohibited** in generated frontend code:
|
|
578
|
+
|
|
579
|
+
| FORBIDDEN | REQUIRED |
|
|
580
|
+
|-----------|----------|
|
|
581
|
+
| `import axios from 'axios'` | `import { api } from '@/services/api/apiClient'` |
|
|
582
|
+
| `bg-blue-600`, `text-gray-900`, `bg-green-100` | `bg-[var(--color-accent-600)]`, `text-[var(--text-primary)]`, `bg-[var(--success-bg)]` |
|
|
583
|
+
| Any hardcoded Tailwind color (`bg-{color}-{shade}`) | CSS variables from style-guide.md |
|
|
584
|
+
| `rounded-lg`, `rounded-md` | `rounded-[var(--radius-card)]`, `rounded-[var(--radius-button)]` |
|
|
585
|
+
| Custom `<div>` cards for entity lists | `<EntityCard>` component (MANDATORY) |
|
|
586
|
+
| Flat routes outside Layout wrapper | Nested routes inside `<BusinessLayout>` / `<AdminLayout>` |
|
|
587
|
+
| Only 2 languages (fr/en) | All 4 languages (fr/en/it/de) |
|
|
588
|
+
| `[Authorize]` without permissions | `[RequirePermission("navRoute.action")]` per endpoint |
|
|
589
|
+
| Missing error state | Error state with retry button (AlertCircle + underline) |
|
|
590
|
+
| Missing empty state | Empty state with icon + message |
|
|
591
|
+
| `new Date().toLocaleDateString()` without locale | Date formatting via i18n or Intl |
|
|
592
|
+
|
|
593
|
+
---
|
|
594
|
+
|
|
519
595
|
## FRONTEND CHECKLIST
|
|
520
596
|
|
|
597
|
+
### Structure
|
|
598
|
+
| Check | Status |
|
|
599
|
+
|-------|--------|
|
|
600
|
+
| ☐ Main page created (`$MODULE_PASCALPage.tsx`) | |
|
|
601
|
+
| ☐ ListView component created (`$MODULE_PASCALListView.tsx`) | |
|
|
602
|
+
| ☐ Preferences hook created (`use$MODULE_PASCALPreferences.ts`) | |
|
|
603
|
+
| ☐ API service created (uses `apiClient`, NOT raw axios) | |
|
|
604
|
+
| ☐ Routes added inside Layout wrapper in App.tsx | |
|
|
605
|
+
| ☐ Route path follows `/{context}/{application}/{module}` | |
|
|
606
|
+
|
|
607
|
+
### Theme Compliance
|
|
608
|
+
| Check | Status |
|
|
609
|
+
|-------|--------|
|
|
610
|
+
| ☐ Zero hardcoded Tailwind colors (grep: `bg-blue-`, `text-gray-`, etc.) | |
|
|
611
|
+
| ☐ All colors use CSS variables (`var(--*)`) | |
|
|
612
|
+
| ☐ Border radius uses `var(--radius-*)` | |
|
|
613
|
+
| ☐ Buttons follow style-guide.md variants | |
|
|
614
|
+
|
|
615
|
+
### UI Components
|
|
616
|
+
| Check | Status |
|
|
617
|
+
|-------|--------|
|
|
618
|
+
| ☐ Grid view uses `EntityCard` (not custom divs) | |
|
|
619
|
+
| ☐ `ViewToggle` for list/grid switch | |
|
|
620
|
+
| ☐ Loading state (spinner) | |
|
|
621
|
+
| ☐ Error state (AlertCircle + retry button) | |
|
|
622
|
+
| ☐ Empty state (icon + message) | |
|
|
623
|
+
| ☐ Pagination component | |
|
|
624
|
+
|
|
625
|
+
### i18n
|
|
626
|
+
| Check | Status |
|
|
627
|
+
|-------|--------|
|
|
628
|
+
| ☐ French (fr) file created | |
|
|
629
|
+
| ☐ English (en) file created | |
|
|
630
|
+
| ☐ Italian (it) file created | |
|
|
631
|
+
| ☐ German (de) file created | |
|
|
632
|
+
| ☐ All 4 files have identical keys | |
|
|
633
|
+
|
|
634
|
+
### Build
|
|
521
635
|
| Check | Status |
|
|
522
636
|
|-------|--------|
|
|
523
|
-
| ☐
|
|
524
|
-
| ☐
|
|
525
|
-
| ☐ Preferences hook created | |
|
|
526
|
-
| ☐ API service created | |
|
|
527
|
-
| ☐ Routes added to App.tsx | |
|
|
528
|
-
| ☐ Uses apiClient (no direct calls) | |
|
|
529
|
-
| ☐ No Infrastructure imports | |
|
|
530
|
-
| ☐ npm run build successful | |
|
|
531
|
-
| ☐ npm run lint successful | |
|
|
637
|
+
| ☐ `npm run build` successful | |
|
|
638
|
+
| ☐ `npm run lint` successful | |
|
|
@@ -802,6 +802,120 @@ private static Guid GenerateEntityGuid(string uniqueKey)
|
|
|
802
802
|
|
|
803
803
|
---
|
|
804
804
|
|
|
805
|
+
## TEMPLATE: CLIENT SEED DATA PROVIDER
|
|
806
|
+
|
|
807
|
+
> **Usage:** Client projects (projectType: "client") to seed data into the Core schema
|
|
808
|
+
> **Mechanism:** Runtime seeding via IClientSeedDataProvider (no Core migrations required)
|
|
809
|
+
> **When:** Generated automatically at step 03b if the project is of type client
|
|
810
|
+
|
|
811
|
+
### Difference with HasData()
|
|
812
|
+
|
|
813
|
+
| Aspect | HasData() (core) | IClientSeedDataProvider (client) |
|
|
814
|
+
|--------|-----------------|----------------------------------|
|
|
815
|
+
| When | EF Core migration | Application startup |
|
|
816
|
+
| Where | In Core migrations | Runtime via CoreDbContext |
|
|
817
|
+
| Who | SmartStack.app | Client project |
|
|
818
|
+
| Idempotent | Yes (single migration) | Yes (check existence) |
|
|
819
|
+
| Core migration | Yes | No |
|
|
820
|
+
| Entities created with | Anonymous objects | Factory methods (mandatory) |
|
|
821
|
+
|
|
822
|
+
### Implementation Pattern
|
|
823
|
+
|
|
824
|
+
The provider is generated in:
|
|
825
|
+
`Infrastructure/Persistence/Seeding/{AppPascalName}SeedDataProvider.cs`
|
|
826
|
+
|
|
827
|
+
It consumes the SeedData files generated in steps 01-03:
|
|
828
|
+
- `{Module}NavigationSeedData.cs` (GUIDs + navigation data)
|
|
829
|
+
- `{Module}NavigationTranslationSeedData.cs` (4-language translations)
|
|
830
|
+
- `{Module}PermissionSeedData.cs` (RBAC permissions)
|
|
831
|
+
- `{Module}RolePermissionSeedData.cs` (role -> permission mappings)
|
|
832
|
+
|
|
833
|
+
### DI Registration
|
|
834
|
+
|
|
835
|
+
In `Infrastructure/DependencyInjection.cs`:
|
|
836
|
+
```csharp
|
|
837
|
+
services.AddScoped<IClientSeedDataProvider, {AppPascalName}SeedDataProvider>();
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
### Execution Pipeline
|
|
841
|
+
|
|
842
|
+
```
|
|
843
|
+
InitializeSmartStackAsync()
|
|
844
|
+
1. MigrateAsync() -> Core schema created
|
|
845
|
+
2. IDatabaseSeeder.SeedAsync() -> System users, tenant
|
|
846
|
+
3. IClientSeedDataProvider.Seed*() -> Navigation + Permissions + Roles CLIENT
|
|
847
|
+
4. IDevDataSeeder.SeedAsync() -> Dev data
|
|
848
|
+
5. InitializeNavigationRoutingAsync -> Routes loaded (including client routes)
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
### Provider Template
|
|
852
|
+
|
|
853
|
+
```csharp
|
|
854
|
+
using Microsoft.EntityFrameworkCore;
|
|
855
|
+
using SmartStack.Application.Common.Interfaces;
|
|
856
|
+
using SmartStack.Domain.Navigation;
|
|
857
|
+
using SmartStack.Domain.Platform.Administration.Roles;
|
|
858
|
+
|
|
859
|
+
namespace {BaseNamespace}.Infrastructure.Persistence.Seeding;
|
|
860
|
+
|
|
861
|
+
public class {AppPascalName}SeedDataProvider : IClientSeedDataProvider
|
|
862
|
+
{
|
|
863
|
+
public int Order => 100;
|
|
864
|
+
|
|
865
|
+
public async Task SeedNavigationAsync(ICoreDbContext context, CancellationToken ct)
|
|
866
|
+
{
|
|
867
|
+
var exists = await context.NavigationApplications
|
|
868
|
+
.AnyAsync(a => a.Code == "{app_code}", ct);
|
|
869
|
+
if (exists) return;
|
|
870
|
+
|
|
871
|
+
var parentContext = await context.NavigationContexts
|
|
872
|
+
.FirstAsync(c => c.Code == "{context_code}", ct);
|
|
873
|
+
|
|
874
|
+
var app = NavigationApplication.Create(
|
|
875
|
+
parentContext.Id, "{app_code}", "{app_label_en}",
|
|
876
|
+
"{app_desc_en}", "{app_icon}", IconType.Lucide,
|
|
877
|
+
"/{context_code}/{app_code}", {display_order});
|
|
878
|
+
context.NavigationApplications.Add(app);
|
|
879
|
+
await ((DbContext)context).SaveChangesAsync(ct);
|
|
880
|
+
|
|
881
|
+
// Create modules from {Module}NavigationSeedData
|
|
882
|
+
// Create translations from {Module}NavigationTranslationSeedData
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
public async Task SeedPermissionsAsync(ICoreDbContext context, CancellationToken ct)
|
|
886
|
+
{
|
|
887
|
+
var exists = await context.Permissions
|
|
888
|
+
.AnyAsync(p => p.Path == "{full_path}.*", ct);
|
|
889
|
+
if (exists) return;
|
|
890
|
+
|
|
891
|
+
// Create permissions from {Module}PermissionSeedData
|
|
892
|
+
// Use Permission.CreateForModule(...) and Permission.CreateWildcard(...)
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
public async Task SeedRolePermissionsAsync(ICoreDbContext context, CancellationToken ct)
|
|
896
|
+
{
|
|
897
|
+
var exists = await context.RolePermissions
|
|
898
|
+
.AnyAsync(rp => rp.Permission!.Path.StartsWith("{full_path}."), ct);
|
|
899
|
+
if (exists) return;
|
|
900
|
+
|
|
901
|
+
// Create role-permissions from {Module}RolePermissionSeedData
|
|
902
|
+
// Use RolePermission.Create(roleId, permissionId, "system")
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
```
|
|
906
|
+
|
|
907
|
+
### Critical Rules
|
|
908
|
+
|
|
909
|
+
| Rule | Description |
|
|
910
|
+
|------|-------------|
|
|
911
|
+
| Factory methods | `NavigationModule.Create(...)`, `Permission.CreateForModule(...)` - NEVER `new Entity()` |
|
|
912
|
+
| Idempotence | Each Seed method checks existence before inserting |
|
|
913
|
+
| SaveChanges per group | Navigation -> save -> Permissions -> save -> RolePermissions -> save |
|
|
914
|
+
| Deterministic GUIDs | Use IDs from SeedData classes (not `Guid.NewGuid()`) |
|
|
915
|
+
| FK resolution by Code | Parent modules found by `Code`, not hardcoded GUID |
|
|
916
|
+
|
|
917
|
+
---
|
|
918
|
+
|
|
805
919
|
## SEED CHECKLIST
|
|
806
920
|
|
|
807
921
|
| Check | Status |
|
|
@@ -818,6 +932,8 @@ private static Guid GenerateEntityGuid(string uniqueKey)
|
|
|
818
932
|
| ☐ RolePermissions assigned | |
|
|
819
933
|
| ☐ Entity SeedData.cs created (if user opted in) | |
|
|
820
934
|
| ☐ DevDataSeeder.cs updated (if user opted in) | |
|
|
935
|
+
| ☐ IClientSeedDataProvider generated (client projects only) | |
|
|
936
|
+
| ☐ Provider registered in DI (client projects only) | |
|
|
821
937
|
|
|
822
938
|
---
|
|
823
939
|
|
|
@@ -240,7 +240,7 @@ Load ONLY relevant categories based on feature type:
|
|
|
240
240
|
| Template | File | Used in step |
|
|
241
241
|
| -------- | ----------------------------------- | ------------ |
|
|
242
242
|
| schema | `schemas/feature-schema.json` | All steps |
|
|
243
|
-
|
|
|
243
|
+
| spec | `templates/tpl-frd.md` | 02 |
|
|
244
244
|
| handoff | `templates/tpl-handoff.md` | 04 |
|
|
245
245
|
| suggestions | `patterns/suggestion-catalog.md` | 01 |
|
|
246
246
|
|
|
@@ -23,6 +23,15 @@
|
|
|
23
23
|
| Q7.7 | Key information per screen? | Per screen |
|
|
24
24
|
| Q7.8 | Possible actions per screen? | Per screen |
|
|
25
25
|
|
|
26
|
+
## 7.3 Dashboards & Analytics
|
|
27
|
+
|
|
28
|
+
| # | Question | Answer Type |
|
|
29
|
+
|---|----------|-------------|
|
|
30
|
+
| Q7.9 | Dashboards ou tableaux de bord requis ? | List (noms + descriptions) |
|
|
31
|
+
| Q7.10 | KPIs à afficher par dashboard ? | Per dashboard: nom, métrique, format (number/currency/percent), seuils |
|
|
32
|
+
| Q7.11 | Types de graphiques souhaités ? | Per KPI: bar, line, pie, area, scatter, kpi-card |
|
|
33
|
+
| Q7.12 | Filtres temporels ? | Période par défaut + plages disponibles (jour/semaine/mois/trimestre/année) |
|
|
34
|
+
|
|
26
35
|
---
|
|
27
36
|
|
|
28
37
|
## UI Components
|
|
@@ -38,6 +47,9 @@
|
|
|
38
47
|
| Detail view | DetailPanel | Side panel ou page dédiée |
|
|
39
48
|
| Status/Workflow | StatusBadge + Timeline | Affichage état + historique transitions |
|
|
40
49
|
| Tooltips/Infobulles | Tooltip | Info contextuelle sur champs/actions |
|
|
50
|
+
| KPI Card | StatCard | Valeur numérique + tendance + icône |
|
|
51
|
+
| Chart (Bar/Line/Pie) | RechartsChart | Graphique interactif via Recharts |
|
|
52
|
+
| Dashboard layout | DashboardGrid | Grille responsive de KPI + charts |
|
|
41
53
|
|
|
42
54
|
## Elicitation Guide
|
|
43
55
|
|
|
@@ -50,6 +62,8 @@
|
|
|
50
62
|
| Q7.5 (screens) | Pas de mention dashboard | "L'utilisateur a-t-il besoin d'un tableau de bord avec des KPIs avant de plonger dans les données ?" |
|
|
51
63
|
| Q7.7 (key info) | "Toutes les colonnes" | "Sur mobile/petit écran, quelles 3-4 colonnes sont indispensables ? Le reste = détail secondaire" |
|
|
52
64
|
| Q7.8 (actions) | "CRUD classique" | "Actions métier spécifiques ? (valider, dupliquer, archiver, changer statut, assigner)" |
|
|
65
|
+
| Q7.9 (dashboards) | "Juste des chiffres" | "Les tendances/comparaisons ne seraient-elles pas plus lisibles en graphique ? Bar chart pour comparer, line chart pour évoluer ?" |
|
|
66
|
+
| Q7.10 (KPIs) | Pas de seuils définis | "À partir de quel seuil un KPI est-il en alerte ? En critique ? (ex: stock < 10 = warning, < 3 = critique)" |
|
|
53
67
|
|
|
54
68
|
### Anti-patterns to Detect
|
|
55
69
|
|
|
@@ -59,3 +73,4 @@
|
|
|
59
73
|
| Aucune mention de feedback utilisateur | **UX silencieuse** | "Quels messages de succès/erreur ? Toast, notification, redirect ?" |
|
|
60
74
|
| "Identique à [autre app]" | **Copier sans adapter** | "Quelles différences de contexte ? L'utilisateur fait-il le même parcours ?" |
|
|
61
75
|
| Actions sans confirmation | **Actions destructives non protégées** | "La suppression nécessite-t-elle une confirmation ? Un motif ? Une double validation ?" |
|
|
76
|
+
| "Juste des chiffres sur la page" | **Dashboard sans visualisation** | "Les tendances/comparaisons ne seraient-elles pas plus lisibles en graphique ? Bar chart pour comparer, line chart pour évoluer ?" |
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
| Type | Generated by | Format |
|
|
22
22
|
|------|-------------|--------|
|
|
23
|
-
|
|
|
23
|
+
| Documentation module | `/documentation` (auto step-08) | React page intégrée via doc-data.ts + DocRenderer |
|
|
24
24
|
| Swagger API | Automatic (Swashbuckle) | OpenAPI 3.0 |
|
|
25
25
|
| User guide | Manual or AI | Markdown / PDF |
|
|
26
26
|
| Release notes | `/gitflow:11-finish` | Changelog automatique |
|
|
@@ -40,4 +40,4 @@
|
|
|
40
40
|
|
|
41
41
|
| Signal | Anti-pattern | Action |
|
|
42
42
|
|--------|-------------|--------|
|
|
43
|
-
| "La doc se fera après" | **Documentation en dette** | Le
|
|
43
|
+
| "La doc se fera après" | **Documentation en dette** | Le feature.json EST la doc. `/documentation` la génère automatiquement au step-08. |
|
|
@@ -214,7 +214,7 @@
|
|
|
214
214
|
|
|
215
215
|
"analysis": {
|
|
216
216
|
"type": "object",
|
|
217
|
-
"description": "Enriched by step-01-analyse (analysis part, merged from old
|
|
217
|
+
"description": "Enriched by step-01-analyse (analysis part, merged from old business requirements step)",
|
|
218
218
|
"properties": {
|
|
219
219
|
"objectives": {
|
|
220
220
|
"type": "array",
|
|
@@ -569,12 +569,18 @@
|
|
|
569
569
|
},
|
|
570
570
|
"uiWireframes": {
|
|
571
571
|
"type": "array",
|
|
572
|
+
"description": "MANDATORY wireframes for every module section. Each section MUST have at least one wireframe validated by the client. These wireframes are traced through to frontend implementation via linkedWireframes in handoff.filesToCreate.",
|
|
572
573
|
"items": {
|
|
573
574
|
"type": "object",
|
|
575
|
+
"required": ["screen", "mockup", "elements", "section"],
|
|
574
576
|
"properties": {
|
|
575
|
-
"screen": { "type": "string" },
|
|
577
|
+
"screen": { "type": "string", "description": "Unique screen identifier: {module}-{section} (e.g., orders-list, orders-detail)" },
|
|
576
578
|
"description": { "type": "string" },
|
|
577
|
-
"elements": {
|
|
579
|
+
"elements": {
|
|
580
|
+
"type": "array",
|
|
581
|
+
"items": { "type": "string" },
|
|
582
|
+
"description": "UI components in this wireframe (DataGrid, FilterBar, Form, etc.). Used for component mapping."
|
|
583
|
+
},
|
|
578
584
|
"actions": { "type": "array", "items": { "type": "string" } },
|
|
579
585
|
"permissionsRequired": { "type": "array", "items": { "type": "string" } },
|
|
580
586
|
"mockupFormat": {
|
|
@@ -584,15 +590,77 @@
|
|
|
584
590
|
},
|
|
585
591
|
"mockup": {
|
|
586
592
|
"type": "string",
|
|
587
|
-
"description": "ASCII art or SVG markup of the wireframe"
|
|
593
|
+
"description": "ASCII art or SVG markup of the wireframe. MANDATORY: captures the layout validated by the client."
|
|
588
594
|
},
|
|
589
595
|
"section": {
|
|
590
596
|
"type": "string",
|
|
591
|
-
"description": "Navigation section this wireframe belongs to (e.g., list, detail, create)"
|
|
597
|
+
"description": "Navigation section this wireframe belongs to (e.g., list, detail, create, dashboard)"
|
|
598
|
+
},
|
|
599
|
+
"componentMapping": {
|
|
600
|
+
"type": "array",
|
|
601
|
+
"description": "Mapping of wireframe elements to SmartStack React components",
|
|
602
|
+
"items": {
|
|
603
|
+
"type": "object",
|
|
604
|
+
"properties": {
|
|
605
|
+
"wireframeElement": { "type": "string", "description": "Element name from the wireframe (e.g., DataGrid)" },
|
|
606
|
+
"reactComponent": { "type": "string", "description": "SmartStack React component to use (e.g., SmartTable)" }
|
|
607
|
+
}
|
|
608
|
+
}
|
|
592
609
|
}
|
|
593
610
|
}
|
|
594
611
|
}
|
|
595
612
|
},
|
|
613
|
+
"dashboards": {
|
|
614
|
+
"type": "array",
|
|
615
|
+
"description": "Dashboard specifications with KPIs and chart configurations. Captured during step-03 when a 'dashboard' section is selected.",
|
|
616
|
+
"items": {
|
|
617
|
+
"type": "object",
|
|
618
|
+
"required": ["code", "title", "kpis"],
|
|
619
|
+
"properties": {
|
|
620
|
+
"code": { "type": "string", "description": "Dashboard identifier (kebab-case, e.g., fleet-dashboard)" },
|
|
621
|
+
"title": { "type": "string" },
|
|
622
|
+
"description": { "type": "string" },
|
|
623
|
+
"linkedUCs": { "type": "array", "items": { "type": "string" } },
|
|
624
|
+
"refreshMode": { "type": "string", "enum": ["static", "polling", "signalr"], "default": "static" },
|
|
625
|
+
"defaultPeriod": { "type": "string", "enum": ["day", "week", "month", "quarter", "year"], "default": "month" },
|
|
626
|
+
"filters": {
|
|
627
|
+
"type": "array",
|
|
628
|
+
"items": {
|
|
629
|
+
"type": "object",
|
|
630
|
+
"properties": {
|
|
631
|
+
"field": { "type": "string" },
|
|
632
|
+
"type": { "type": "string", "enum": ["dateRange", "select", "multiselect", "search"] },
|
|
633
|
+
"label": { "type": "string" }
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
},
|
|
637
|
+
"kpis": {
|
|
638
|
+
"type": "array",
|
|
639
|
+
"items": {
|
|
640
|
+
"type": "object",
|
|
641
|
+
"required": ["code", "label", "metric", "visualization"],
|
|
642
|
+
"properties": {
|
|
643
|
+
"code": { "type": "string" },
|
|
644
|
+
"label": { "type": "string" },
|
|
645
|
+
"metric": { "type": "string", "description": "Description of calculation (e.g. COUNT(vehicles WHERE status=active))" },
|
|
646
|
+
"format": { "type": "string", "enum": ["number", "currency", "percent", "duration"], "default": "number" },
|
|
647
|
+
"visualization": { "type": "string", "enum": ["kpi-card", "bar", "line", "pie", "area", "donut", "stacked-bar"] },
|
|
648
|
+
"dataSource": { "type": "string", "description": "Entity or endpoint providing data" },
|
|
649
|
+
"dimensions": { "type": "array", "items": { "type": "string" }, "description": "Grouping dimensions (e.g. status, month)" },
|
|
650
|
+
"thresholds": {
|
|
651
|
+
"type": "object",
|
|
652
|
+
"properties": {
|
|
653
|
+
"warning": { "type": "number" },
|
|
654
|
+
"critical": { "type": "number" }
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
},
|
|
660
|
+
"permissionsRequired": { "type": "array", "items": { "type": "string" } }
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
},
|
|
596
664
|
"messages": {
|
|
597
665
|
"type": "array",
|
|
598
666
|
"description": "Business messages (success, error, warning, info) with i18n keys. MANDATORY: ≥4 messages.",
|
|
@@ -862,6 +930,17 @@
|
|
|
862
930
|
}
|
|
863
931
|
},
|
|
864
932
|
|
|
933
|
+
"documentation": {
|
|
934
|
+
"type": "object",
|
|
935
|
+
"description": "Documentation requirements captured during BA (questionnaire 10). Consumed by /application step-08 to auto-generate in-app documentation.",
|
|
936
|
+
"properties": {
|
|
937
|
+
"userDocRequired": { "type": "boolean", "default": true, "description": "Whether in-app user documentation page should be generated" },
|
|
938
|
+
"techDocRequired": { "type": "boolean", "default": false, "description": "Whether technical documentation (ERD, API) should be generated" },
|
|
939
|
+
"generatedAt": { "type": ["string", "null"], "format": "date-time", "description": "Timestamp when documentation was last generated" },
|
|
940
|
+
"status": { "type": "string", "enum": ["pending", "generated", "skipped"], "default": "pending" }
|
|
941
|
+
}
|
|
942
|
+
},
|
|
943
|
+
|
|
865
944
|
"suggestions": {
|
|
866
945
|
"type": "array",
|
|
867
946
|
"description": "Proactive AI suggestions for complementary modules/sections",
|
|
@@ -917,14 +996,24 @@
|
|
|
917
996
|
"required": ["path", "type"],
|
|
918
997
|
"properties": {
|
|
919
998
|
"path": { "type": "string", "description": "Relative file path" },
|
|
920
|
-
"type": { "type": "string", "description": "File type (Entity, Service, DTO, Repository, Migration, HasData, Constants, ApiController, Page, Component, ApiClient, ReduxSlice, UnitTests, IntegrationTests, Enum)" },
|
|
999
|
+
"type": { "type": "string", "description": "File type (Entity, Service, DTO, Repository, Migration, HasData, Constants, ApiController, Page, DashboardPage, Component, ApiClient, ReduxSlice, UnitTests, IntegrationTests, Enum)" },
|
|
921
1000
|
"linkedFRs": { "type": "array", "items": { "type": "string" }, "description": "Linked functional requirement IDs (FR-XXX)" },
|
|
922
1001
|
"linkedUCs": { "type": "array", "items": { "type": "string" }, "description": "Linked use case IDs (UC-XXX)" },
|
|
1002
|
+
"linkedWireframes": {
|
|
1003
|
+
"type": "array",
|
|
1004
|
+
"items": { "type": "string" },
|
|
1005
|
+
"description": "Linked wireframe screen identifiers from specification.uiWireframes[].screen. MANDATORY for Page and DashboardPage types. Ensures BA mockups are traced to implementation."
|
|
1006
|
+
},
|
|
1007
|
+
"wireframeAcceptanceCriteria": {
|
|
1008
|
+
"type": "string",
|
|
1009
|
+
"description": "Acceptance criteria describing how the implementation must match the wireframe layout. MANDATORY for Page and DashboardPage types."
|
|
1010
|
+
},
|
|
923
1011
|
"category": { "type": "string", "enum": ["core", "business"], "description": "SeedData category: core (navigation/permissions) or business (lookup tables)" },
|
|
924
1012
|
"source": { "type": "string", "description": "Source path in feature.json for derivation (e.g., specification.seedDataCore.permissions)" },
|
|
925
1013
|
"description": { "type": "string" },
|
|
926
1014
|
"pattern": { "type": "string", "description": "Reference pattern from existing codebase" },
|
|
927
|
-
"instructions": { "type": "string" }
|
|
1015
|
+
"instructions": { "type": "string" },
|
|
1016
|
+
"dashboardRef": { "type": "string", "description": "Reference to specification.dashboards[].code for DashboardPage type files" }
|
|
928
1017
|
}
|
|
929
1018
|
}
|
|
930
1019
|
}
|