@atlashub/smartstack-cli 4.76.0 → 4.79.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.
Files changed (34) hide show
  1. package/package.json +1 -1
  2. package/templates/project/claude-md/root.CLAUDE.md.template +1 -1
  3. package/templates/skills/ai-prompt/SKILL.md +64 -0
  4. package/templates/skills/ai-prompt/references/ai-agent-modes.md +89 -0
  5. package/templates/skills/ai-prompt/references/eval-framework.md +129 -0
  6. package/templates/skills/apex/references/checks/frontend-checks.sh +97 -11
  7. package/templates/skills/apex/references/checks/seed-checks.sh +34 -0
  8. package/templates/skills/apex/references/core-seed-data.md +7 -4
  9. package/templates/skills/apex/references/domain-events-pattern.md +45 -0
  10. package/templates/skills/apex/references/entity-hooks-pattern.md +68 -0
  11. package/templates/skills/apex/references/licensing-enforcement.md +52 -0
  12. package/templates/skills/apex/references/smartstack-api.md +112 -1
  13. package/templates/skills/apex-verify/steps/step-01-nav-audit.md +4 -0
  14. package/templates/skills/application/references/contexts-cheatsheet.md +86 -0
  15. package/templates/skills/application/references/extensions-system.md +158 -0
  16. package/templates/skills/application/references/frontend-route-naming.md +7 -5
  17. package/templates/skills/application/references/frontend-verification.md +7 -5
  18. package/templates/skills/application/references/provider-template.md +4 -2
  19. package/templates/skills/application/references/smartstack-provider.md +118 -0
  20. package/templates/skills/application/references/themes-db-driven.md +484 -0
  21. package/templates/skills/application/templates-seed.md +4 -2
  22. package/templates/skills/audit-route/references/routing-pattern.md +3 -1
  23. package/templates/skills/business-analyse/react/components.md +30 -28
  24. package/templates/skills/business-analyse/templates-react.md +15 -15
  25. package/templates/skills/cli-app-sync/SKILL.md +105 -4
  26. package/templates/skills/cli-app-sync/references/comparison-map.md +13 -0
  27. package/templates/skills/cli-app-sync/references/diff-entities.md +162 -0
  28. package/templates/skills/documentation/templates.md +16 -16
  29. package/templates/skills/migrate/SKILL.md +312 -0
  30. package/templates/skills/migrate/references/v3.34-to-v3.46.md +289 -0
  31. package/templates/skills/smoke-generation/SKILL.md +313 -0
  32. package/templates/skills/ui-components/SKILL.md +10 -0
  33. package/templates/skills/ui-components/references/component-catalog.md +82 -0
  34. package/templates/skills/workflow/SKILL.md +70 -1
@@ -0,0 +1,52 @@
1
+ # Licensing — `[RequiresLicense]` attribute (v3.46+)
2
+
3
+ > Extracted from `smartstack-api.md` for clarity. Loaded only when generating code that needs license gating.
4
+
5
+ Mark a Command or Query as requiring a valid license / feature / write capability.
6
+
7
+ ## Attribute definition
8
+
9
+ ```csharp
10
+ namespace SmartStack.Application.Common.Licensing;
11
+
12
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
13
+ public class RequiresLicenseAttribute : Attribute
14
+ {
15
+ public string? Feature { get; }
16
+ public bool IsWriteOperation { get; }
17
+
18
+ public RequiresLicenseAttribute() { /* just needs valid license */ }
19
+ public RequiresLicenseAttribute(bool isWriteOperation) { /* blocked in read-only mode */ }
20
+ public RequiresLicenseAttribute(string feature, bool isWriteOperation = false) { /* both */ }
21
+ }
22
+ ```
23
+
24
+ ## Usage on a Command
25
+
26
+ ```csharp
27
+ [RequiresLicense(LicenseFeatures.AdvancedWorkflows, isWriteOperation: true)]
28
+ public record CreateAdvancedWorkflowCommand(string Name, ...) : IRequest<WorkflowDto>;
29
+ ```
30
+
31
+ ## Behavior at runtime
32
+
33
+ - A MediatR pipeline behavior reads the attribute on the request type.
34
+ - `Feature` → checked against the cached `License.Features` (resolved per-tenant from JWT).
35
+ - `IsWriteOperation = true` → blocked when the system is in read-only mode (license expired but in grace period, license downgraded, …).
36
+ - Failures throw a typed exception → 403 Forbidden with a stable error code (`LICENSE_FEATURE_DISABLED`, `LICENSE_READ_ONLY`).
37
+
38
+ ## Where to read feature constants
39
+
40
+ `SmartStack.Domain.Licensing.LicenseFeatures` (string consts — `Entra`, `AdvancedWorkflows`, `AiAdvanced`, …).
41
+
42
+ ## DO / DON'T
43
+
44
+ - DO put `[RequiresLicense]` on the request (Command/Query), not on the handler
45
+ - DO use `LicenseFeatures.X` constants — never magic strings
46
+ - DON'T add `[RequiresLicense]` on `[AllowAnonymous]` endpoints — they bypass the pipeline
47
+ - DON'T duplicate the check in the handler body — the pipeline already enforced it
48
+
49
+ ## Sources
50
+
51
+ - `D:/01 - projets/SmartStack.app/features/IA-Workflow/src/SmartStack.Application/Common/Licensing/RequiresLicenseAttribute.cs`
52
+ - `D:/01 - projets/SmartStack.app/features/IA-Workflow/src/SmartStack.Domain/Licensing/LicenseFeatures.cs`
@@ -16,10 +16,46 @@ public abstract class BaseEntity
16
16
  public Guid Id { get; set; }
17
17
  public DateTime CreatedAt { get; set; }
18
18
  public DateTime? UpdatedAt { get; set; }
19
+
20
+ // v3.46+ : JSON storage for SDK extension fields. Default "{}".
21
+ public string ExtensionData { get; private set; } = "{}";
19
22
  }
20
23
  ```
21
24
 
22
- **ONLY 3 properties.** No Code, no IsDeleted, no RowVersion, no SoftDelete, no CreatedBy/UpdatedBy.
25
+ **4 inherited properties** (v3.46+). No Code, no IsDeleted, no RowVersion, no SoftDelete, no CreatedBy/UpdatedBy. `Code` is a business property — add it on the concrete entity. `CreatedBy`/`UpdatedBy` come from `IAuditableEntity` (separate interface).
26
+
27
+ ### ExtensionData — SDK custom fields (v3.46+)
28
+
29
+ `BaseEntity.ExtensionData` is a JSON string allowing SDK clients to attach custom fields to ANY entity without schema migrations. The base class exposes 7 typed methods:
30
+
31
+ ```csharp
32
+ // Read a typed value
33
+ var color = entity.GetExtensionValue<string>("color");
34
+ var meta = entity.GetExtensionValue<MyMetadata>("meta");
35
+
36
+ // Write
37
+ entity.SetExtensionValue("color", "blue");
38
+ entity.SetExtensionValue("meta", new MyMetadata { Tag = "vip" });
39
+
40
+ // Inspect
41
+ if (entity.HasExtensionValue("color")) { ... }
42
+ var all = entity.GetAllExtensionData(); // IReadOnlyDictionary<string, JsonElement>
43
+
44
+ // Bulk replace
45
+ entity.SetAllExtensionData(new Dictionary<string, object?> { ["a"] = 1, ["b"] = 2 });
46
+
47
+ // Cleanup
48
+ entity.RemoveExtensionValue("color"); // returns bool
49
+ entity.ClearExtensionData(); // resets to "{}"
50
+ ```
51
+
52
+ **Conventions:**
53
+ - Serialization uses `JsonNamingPolicy.CamelCase` — `MyField` is stored as `"myField"`. Frontend consumers see camelCase.
54
+ - Mutating methods automatically update `UpdatedAt`.
55
+ - Backed by SQL Server `nvarchar(max)`. Map with `builder.Property(x => x.ExtensionData).HasColumnType("nvarchar(max)").HasDefaultValue("{}");` in EF config.
56
+ - `ExtensionData` is for **client SDK extensions** — NOT for storing application-controlled data (use real columns for those).
57
+
58
+ > Source : `D:/01 - projets/SmartStack.app/features/IA-Workflow/src/SmartStack.Domain/Common/BaseEntity.cs`
23
59
 
24
60
  ---
25
61
 
@@ -90,6 +126,38 @@ Verify in `Program.cs`: `JsonSerializerOptions.Converters.Add(new JsonStringEnum
90
126
 
91
127
  ---
92
128
 
129
+ ## File Storage — `StorageType` enum (v3.46+)
130
+
131
+ ```csharp
132
+ namespace SmartStack.Domain.Common;
133
+
134
+ public enum StorageType
135
+ {
136
+ Normal = 0, // Standard Azure Blob — deletable any time
137
+ Legal = 1 // Azure Legal Hold — immutable, 10-year retention (Swiss law Art. 958f CO)
138
+ }
139
+ ```
140
+
141
+ **When to use `Legal`:**
142
+ - Contracts, invoices, accounting documents (Swiss CO art. 958f → 10 years)
143
+ - Audit logs that must survive deletion attempts (compliance, GDPR Art. 30)
144
+ - Anything required by regulation to outlive the user's delete intent
145
+
146
+ **When `Normal` is enough:**
147
+ - User-uploaded avatars, attachments to tickets, message attachments, theme assets
148
+ - Anything the user owns and can legitimately delete
149
+
150
+ **Storage routing (Infrastructure):**
151
+ - `Normal` → standard container (deletable, free tier eligible)
152
+ - `Legal` → container with **Azure Blob immutable storage policy** + retention lock applied at upload
153
+ - The choice is made by the entity author when creating the file — not a runtime toggle
154
+
155
+ **AppSettings:** `appsettings.json` exposes both containers under `AzureStorage.Normal` and `AzureStorage.Legal` (with `EnableLegalHold` and `EnableRetentionPolicy` flags). Local dev uses `FileStorage.Normal/Legal` folders.
156
+
157
+ > Source : `D:/01 - projets/SmartStack.app/features/IA-Workflow/src/SmartStack.Domain/Common/StorageType.cs`
158
+
159
+ ---
160
+
93
161
  ## Entity Patterns
94
162
 
95
163
  ### Tenant-scoped (most common)
@@ -333,6 +401,49 @@ public class {Name}Controller : ControllerBase
333
401
 
334
402
  **Sub-resource completeness:** If a parent page has a navigate() to a sub-resource route, the frontend MUST include a page for that route. Otherwise → dead link → white screen. Prefer separate controllers (with Suffix) over sub-endpoints in parent controller.
335
403
 
404
+ ### Core Controllers Exception (Bootstrap / Navigation / Config)
405
+
406
+ A small set of **bootstrap controllers** uses `[Route("api/[controller]")]` instead of `[NavRoute]`. These are routed BEFORE the navigation registry is built from the DB, so they cannot use NavRoute.
407
+
408
+ | Controller | Route | Reason |
409
+ |---|---|---|
410
+ | `BootstrapController` | `api/bootstrap` | Returns initial config (CSRF token, public flags) |
411
+ | `NavigationController` | `api/navigation` | Returns the navigation menu — must run before NavRoute resolves |
412
+ | `ConfigController` (if present) | `api/config` | Public app config (read-only) |
413
+ | `AuthController` | `api/auth` | Login/refresh — runs before authenticated routes resolve |
414
+
415
+ **Rules for core controllers:**
416
+ - Use the classical `[Route("api/[controller]")]` attribute
417
+ - Still apply `[Authorize]` and `[RequirePermission]` when applicable (most are anonymous-allowed)
418
+ - Do NOT add to navigation seed data
419
+ - POST-CHECK ignores these specific controller names when validating NavRoute usage
420
+
421
+ **Do not extend this list arbitrarily.** Application/business controllers MUST use `[NavRoute]`. If you think a new controller belongs to the bootstrap set, justify it: the controller must run before navigation is loaded.
422
+
423
+ ---
424
+
425
+ ## Entity Lifecycle Hooks (v3.46+)
426
+
427
+ 6 typed hook interfaces + 1 executor in `SmartStack.Application.Common.Interfaces.Hooks` : `IBeforeCreate<T>`, `IAfterCreate<T>`, `IBeforeUpdate<T>`, `IAfterUpdate<T>`, `IBeforeDelete<T>`, `IAfterDelete<T>`, `IHookExecutor`. `IBefore*` may throw to cancel ; `IAfter*` runs post-commit (no rollback). Each has an `Order` property (default 0).
428
+
429
+ **See [entity-hooks-pattern.md](entity-hooks-pattern.md)** for full interface signatures, DI registration, usage example, and DO/DON'T list.
430
+
431
+ ---
432
+
433
+ ## Domain Events (lightweight pub-sub)
434
+
435
+ Minimal pattern in `SmartStack.Domain.Support.Events` : `IDomainEvent { OccurredAt }` + `DomainEvent` abstract base. Use **events** for business facts independent of CRUD ; use **hooks** for cross-cutting reactions tied to the entity lifecycle. Dispatch is explicit (no auto-publication from `SaveChangesAsync` in v3.46).
436
+
437
+ **See [domain-events-pattern.md](domain-events-pattern.md)** for the full when-to-use table, naming convention, and dispatch example.
438
+
439
+ ---
440
+
441
+ ## Licensing — `[RequiresLicense]` attribute (v3.46+)
442
+
443
+ Mark a Command or Query class as requiring a valid license, a specific feature (`LicenseFeatures.X`), or write capability (blocked in read-only mode). MediatR pipeline reads the attribute and throws 403 with stable error codes (`LICENSE_FEATURE_DISABLED`, `LICENSE_READ_ONLY`).
444
+
445
+ **See [licensing-enforcement.md](licensing-enforcement.md)** for the attribute definition (3 constructors), usage on Commands, runtime behavior, feature constants location, and DO/DON'T.
446
+
336
447
  ---
337
448
 
338
449
  ## Navigation Seed Data (routes must be full paths)
@@ -78,6 +78,10 @@ For each issue found, create a finding:
78
78
  | NAV-003 | BLOCKING | Route contains forbidden segment (/detail/, /create/, /edit/) |
79
79
  | NAV-004 | WARNING | Reserved section code with IsActive = false (acceptable but unnecessary) |
80
80
  | NAV-005 | WARNING | Section code not in kebab-case |
81
+ | NAV-006 | WARNING (v3.46) | `ApplicationZone` enum referenced in seed data — must use `IsPersonal`/`IsOpen` flags (cf. seed-checks.sh check **C66**, will become BLOCKING in v3.47) |
82
+ | NAV-007 | WARNING (v3.46) | Legacy layouts `AdminLayout`/`BusinessLayout`/`UserLayout`/`HRLayout`/`SalesLayout` referenced — must use `AppLayout` (cf. seed-checks.sh check **C67**, will become BLOCKING in v3.47) |
83
+
84
+ **Note** : NAV-006 and NAV-007 mirror the bash post-checks **C66**/**C67** in `apex/references/checks/seed-checks.sh`. The bash checks emit `WARNING` (no FAIL) during the v3.46 grace period — apex-verify should align with the same severity.
81
85
 
82
86
  ---
83
87
 
@@ -0,0 +1,86 @@
1
+ # SmartStack Contexts — Cheatsheet
2
+
3
+ > **Reference for `application` skill** — quick lookup of the 10 system contexts exposed by `<SmartStackProvider>`.
4
+
5
+ ## When This Reference Applies
6
+
7
+ - You generate a component that needs to read auth/tenant/theme state
8
+ - You audit a page that re-implements existing context state
9
+ - The user asks "how do I read the current tenant", "how do I check a permission", "where is the SignalR connection"
10
+
11
+ ---
12
+
13
+ ## The 10 contexts (state map)
14
+
15
+ | # | Context | Hook | State exposed | Example use case |
16
+ |---|---|---|---|---|
17
+ | 1 | AuthContext | `useAuth()` | `user`, `roles`, `permissions`, `isAuthenticated`, `login()`, `logout()`, `refresh()`, `hasPermission(path)` | Read current user, check permission gate |
18
+ | 2 | TenantContext | `useTenant()` | `currentTenant`, `userTenants`, `isGlobalView`, `switchTenant(id)` | Display tenant switcher, scope a query |
19
+ | 3 | NavigationContext | `useNavigation()` | `menu` (MenuDto), `reload()`, `currentApp`, `currentModule`, `currentSection`, `isLoading`, `getApplicationsByZone()` (back-compat alias) | Render sidebar, breadcrumb, route resolution |
20
+ | 4 | ThemeContext | `useTheme()` | `mode`, `selectedTheme`, `selectedPreset`, `setMode()`, `setTheme(id)`, `setPreset(id)` | Toggle dark mode, switch theme |
21
+ | 5 | LicenseContext | `useLicense()` | `license`, `isFeatureEnabled(code)`, `hasModule(code)` | Gate a feature behind license |
22
+ | 6 | SignalRContext | `useSignalR()` | `isConnected`, `on(event, handler)`, `off(event, handler)`, `emit(event, payload)` | Real-time notifications |
23
+ | 7 | FavoritesContext | `useFavorites()` | `favorites`, `toggleFavorite(item)`, `isFavorite(id)` | Bookmark a page/entity |
24
+ | 8 | SidebarContext | `useSidebar()` | `isCollapsed`, `toggle()`, `setCollapsed(value)` | Sidebar collapse button |
25
+ | 9 | FeatureConfigContext | `useFeatureConfig()` | `features` (record from `/api/config/public`) | Feature flag, A/B test |
26
+ | 10 | DocPanelContext | `useDocPanel()` | `isOpen`, `toggle()`, `openDoc(slug)` | Contextual help panel |
27
+
28
+ ---
29
+
30
+ ## Common patterns
31
+
32
+ ### Permission check — render guard
33
+
34
+ ```tsx
35
+ const { hasPermission } = useAuth();
36
+ if (!hasPermission('administration.users.create')) return <Forbidden />;
37
+ ```
38
+
39
+ ### Tenant-scoped query
40
+
41
+ ```tsx
42
+ const { currentTenant } = useTenant();
43
+ const { data } = useQuery(
44
+ ['orders', currentTenant?.id],
45
+ () => orderApi.list({ tenantId: currentTenant?.id })
46
+ );
47
+ ```
48
+
49
+ ### Real-time updates
50
+
51
+ ```tsx
52
+ const { on, off } = useSignalR();
53
+ useEffect(() => {
54
+ const handler = (notif) => toast(notif.message);
55
+ on('NotificationReceived', handler);
56
+ return () => off('NotificationReceived', handler);
57
+ }, []);
58
+ ```
59
+
60
+ ### Feature flag
61
+
62
+ ```tsx
63
+ const { isFeatureEnabled } = useLicense();
64
+ if (isFeatureEnabled('ai-assistant')) return <AiAssistant />;
65
+ ```
66
+
67
+ ---
68
+
69
+ ## DO / DON'T
70
+
71
+ | ✅ DO | ❌ DON'T |
72
+ |---|---|
73
+ | Consume contexts via the provided hooks | `useContext(SmartStackContext)` directly |
74
+ | Memoize derived values from context | Re-compute on every render |
75
+ | Subscribe/unsubscribe SignalR handlers in `useEffect` cleanup | Forget to `off()` (memory leak) |
76
+ | Treat `permissions` as opaque strings | Parse permissions client-side |
77
+ | Read tenant from `currentTenant`, not URL | Trust the URL slug as a source of truth |
78
+
79
+ ---
80
+
81
+ ## Reference source files (read-only)
82
+
83
+ - `D:\01 - projets\SmartStack.app\features\IA-Workflow\web\smartstack-web\src\contexts\` (all 10 files)
84
+ - `D:\01 - projets\SmartStack.app\features\IA-Workflow\web\smartstack-web\src\provider\SmartStackProvider.tsx` (wrapping order)
85
+
86
+ See [smartstack-provider.md](smartstack-provider.md) for the wrapping order.
@@ -0,0 +1,158 @@
1
+ # Extension System — Slot/Fill, Page Overrides, Hooks
2
+
3
+ > **Reference for `application` skill** — how a client project enriches a SmartStack page without forking the SDK.
4
+
5
+ ## When This Reference Applies
6
+
7
+ - Client wants to inject content into an existing SmartStack page (header action, table column, side panel)
8
+ - Client wants to fully replace a built-in page with a custom one
9
+ - Client needs to react to navigation/auth/tenant lifecycle events
10
+ - The user asks "how do I add a column to UsersList", "how do I override the AdministrationDashboard page", "how do I customize a SmartStack form"
11
+
12
+ ---
13
+
14
+ ## Three extension mechanisms
15
+
16
+ ### 1. Page overrides — `componentRegistry` + `PageRegistry`
17
+
18
+ Override a built-in page by registering your component under the same key :
19
+
20
+ ```tsx
21
+ // In your client componentRegistry.generated.ts (or a custom registry.ts)
22
+ import { PageRegistry } from '@atlashub/smartstack';
23
+ import { lazy } from 'react';
24
+
25
+ PageRegistry.register(
26
+ 'administration.users', // built-in key — replaces SmartStack page
27
+ lazy(() => import('@/pages/MyCustomUsers')), // your component
28
+ );
29
+
30
+ // Or via SmartStackConfig.extensions.pages (declarative)
31
+ const config: SmartStackConfig = {
32
+ apiUrl: '...',
33
+ extensions: {
34
+ pages: {
35
+ 'administration.users': () => import('@/pages/MyCustomUsers'),
36
+ },
37
+ },
38
+ };
39
+ ```
40
+
41
+ The `DynamicRouter` calls `PageRegistry.resolve(componentKey)` and your override wins.
42
+
43
+ > **Tip** — for net-new pages on client-defined routes, prefer the MCP tool `mcp__smartstack__scaffold_routes` to keep `componentRegistry.generated.ts` consistent.
44
+
45
+ ### 2. Slot / Fill — render-prop injection
46
+
47
+ Inject content into a named slot exposed by a SmartStack page :
48
+
49
+ ```tsx
50
+ import { Fill } from '@atlashub/smartstack';
51
+
52
+ // Inject into the slot 'users.header.actions'
53
+ <Fill slot="users.header.actions">
54
+ <button onClick={...}>My custom action</button>
55
+ </Fill>
56
+ ```
57
+
58
+ Available slot names follow the convention `{section}.{location}.{purpose}` (see `extensions/types.ts` SlotName union for the full list — examples : `users.header.actions`, `users.table.columns.before`, `users.table.row.actions`, `users.form.fields.after`, `users.detail.tabs.after`).
59
+
60
+ You can declare slots once via `SmartStackConfig.extensions.slots` :
61
+
62
+ ```ts
63
+ extensions: {
64
+ slots: {
65
+ 'users.header.actions': MyHeaderWidget,
66
+ 'dashboard.kpi.before': MyKpi,
67
+ }
68
+ }
69
+ ```
70
+
71
+ ### 3. Lifecycle hooks
72
+
73
+ Subscribe to platform events from anywhere :
74
+
75
+ ```ts
76
+ extensions: {
77
+ hooks: {
78
+ onAuthSuccess: (user) => analytics.identify(user.id),
79
+ onTenantSwitch: (tenant) => router.navigate('/dashboard'),
80
+ onNavigationChanged: (menu) => console.log('menu refreshed'),
81
+ }
82
+ }
83
+ ```
84
+
85
+ ---
86
+
87
+ ## Table columns & form fields extensions
88
+
89
+ Extend the standard `DataTable` or `EntityForm` of any built-in section :
90
+
91
+ ```ts
92
+ extensions: {
93
+ tableColumns: {
94
+ 'administration.users.list': [
95
+ {
96
+ key: 'department',
97
+ label: 'Department',
98
+ render: (row) => row.department?.name,
99
+ align: 'left',
100
+ sortable: true,
101
+ },
102
+ ],
103
+ },
104
+ formFields: {
105
+ 'administration.users.create': [
106
+ {
107
+ name: 'department',
108
+ type: 'select',
109
+ label: 'Department',
110
+ options: () => departmentApi.list(),
111
+ validation: { required: true },
112
+ },
113
+ ],
114
+ },
115
+ }
116
+ ```
117
+
118
+ These extensions are consumed by the corresponding hooks in the SmartStack page :
119
+
120
+ ```tsx
121
+ const extraColumns = useTableColumnExtensions('administration.users.list');
122
+ const extraFields = useFormFieldExtensions('administration.users.create');
123
+ ```
124
+
125
+ ---
126
+
127
+ ## DO / DON'T
128
+
129
+ | ✅ DO | ❌ DON'T |
130
+ |---|---|
131
+ | Use `PageRegistry.register()` once at startup | Register the same key twice (last wins, but ambiguous) |
132
+ | Use lazy imports for page overrides | Import override pages eagerly (kills code splitting) |
133
+ | Match exact componentKey from the navigation menu | Guess the key — read it from `/api/navigation/menu` response |
134
+ | Use slots for additive content | Use slots to remove content (use page override instead) |
135
+ | Type custom column data via `row` typing | Cast `row as any` |
136
+
137
+ ---
138
+
139
+ ## MCP delegation
140
+
141
+ For complex extension scaffolds (page override + slots + tests), prefer the MCP tool over hand-written code :
142
+
143
+ | Goal | MCP tool |
144
+ |---|---|
145
+ | Generate a page override + register it | `mcp__smartstack__scaffold_frontend_extension` (type: `page-override`) |
146
+ | Generate a custom Slot component | `mcp__smartstack__scaffold_frontend_extension` (type: `slot`) |
147
+ | Generate a custom column extension | `mcp__smartstack__scaffold_frontend_extension` (type: `table-column`) |
148
+
149
+ ---
150
+
151
+ ## Reference source files (read-only)
152
+
153
+ - `D:\01 - projets\SmartStack.app\features\IA-Workflow\web\smartstack-web\src\extensions\PageRegistry.tsx`
154
+ - `D:\01 - projets\SmartStack.app\features\IA-Workflow\web\smartstack-web\src\extensions\types.ts` (SlotName, ColumnExtension, FormFieldExtension)
155
+ - `D:\01 - projets\SmartStack.app\features\IA-Workflow\web\smartstack-web\src\extensions\ExtensionContext.tsx` (useExtensions, useTableColumnExtensions, useFormFieldExtensions)
156
+ - `D:\01 - projets\SmartStack.app\features\IA-Workflow\web\smartstack-web\src\extensions\SlotFill.tsx` (Slot, Fill, SlotFillProvider)
157
+
158
+ See also [smartstack-provider.md](smartstack-provider.md) for the SmartStackConfig shape that hosts `extensions`.
@@ -79,19 +79,21 @@ const applicationRoutes: ApplicationRouteExtensions = {
79
79
 
80
80
  ## Implementation Pattern
81
81
 
82
+ > **v3.46+ note** — The domain-specific layouts `AdminLayout`, `BusinessLayout`, `UserLayout`, `HRLayout`, `SalesLayout` are REMOVED. `AppLayout` is now the SOLE shell for authenticated application routes. Routes are resolved dynamically by `DynamicRouter` via `componentRegistry.generated.ts` — the JSX `<Route>` blocks below are illustrative for naming convention only, not for manual declaration. See `references/smartstack-provider.md` for the full provider/router setup.
83
+
82
84
  When generating routes, apply kebab-case to all multi-word segments:
83
85
 
84
86
  ```tsx
85
- // CORRECT
86
- <Route path="/human-resources" element={<HRLayout />}>
87
+ // CORRECT — kebab-case convention (illustrative — actual routes flow through DynamicRouter)
88
+ <Route path="/human-resources" element={<AppLayout />}>
87
89
  <Route path="employees" element={<EmployeesPage />} />
88
90
  <Route path="time-management" element={<TimeManagementPage />} />
89
91
  </Route>
90
92
 
91
93
  // FORBIDDEN
92
- <Route path="/humanresources" element={<HRLayout />} />
93
- <Route path="/HR" element={<HRLayout />} />
94
- <Route path="/human_resources" element={<HRLayout />} />
94
+ <Route path="/humanresources" element={<AppLayout />} />
95
+ <Route path="/HR" element={<AppLayout />} />
96
+ <Route path="/human_resources" element={<AppLayout />} />
95
97
  ```
96
98
 
97
99
  ---
@@ -4,13 +4,15 @@
4
4
 
5
5
  ## Routing Rules
6
6
 
7
+ > ⚠️ **DEPRECATED PATTERN — v3.46+** : Domain-specific layouts (`SalesLayout`, `AdminLayout`, `BusinessLayout`, `UserLayout`, `HRLayout`) are REMOVED. There are now **only 4 layouts** : `AppLayout` (sole shell for authenticated app routes), `AuthLayout` (login/register), `PublicLayout` (public pages), `DocsLayout` (documentation). The "manual `<Route>` declaration" examples below are LEGACY ; on v3.46+ routes are resolved by `DynamicRouter` via `componentRegistry.generated.ts` (see Section 5b below + `references/smartstack-provider.md`). The examples are kept for projects on v3.6 and earlier.
8
+
7
9
  ### Rule 1: Routes MUST be inside Layout wrappers
8
10
 
9
- **CRITICAL:** All routes MUST be nested inside the appropriate context layout. This is what provides the SmartStack shell (header with AvatarMenu, sidebar, navigation).
11
+ **CRITICAL (legacy v3.6 only):** All routes MUST be nested inside the appropriate context layout. This is what provides the SmartStack shell (header with AvatarMenu, sidebar, navigation). On v3.46+, `AppLayout` is the SOLE shell.
10
12
 
11
13
  ```tsx
12
- // CORRECT - Inside SalesLayout (shell renders: header + AvatarMenu + sidebar)
13
- <Route path="/sales" element={<SalesLayout />}>
14
+ // CORRECT (v3.46+) - Inside AppLayout (shell renders: header + AvatarMenu + sidebar)
15
+ <Route path="/sales" element={<AppLayout />}>
14
16
  <Route path="products" element={<ProductsPage />} />
15
17
  </Route>
16
18
 
@@ -23,8 +25,8 @@
23
25
  **CRITICAL:** SmartStack requires NESTED routes within the layout:
24
26
 
25
27
  ```tsx
26
- // CORRECT - Nested routes inside layout
27
- <Route path="/sales" element={<SalesLayout />}>
28
+ // CORRECT - Nested routes inside layout (v3.46+ uses AppLayout)
29
+ <Route path="/sales" element={<AppLayout />}>
28
30
  <Route path="products">
29
31
  <Route index element={<Navigate to="products" replace />} />
30
32
  <Route path="products" element={<ProductsPage />} />
@@ -42,10 +42,12 @@ public class {AppPascalName}SeedDataProvider : IClientSeedDataProvider
42
42
  }
43
43
  else
44
44
  {
45
+ // v3.46+ signature : Zone removed. New flags isOpen + isPersonal at the END (optional, default false).
45
46
  app = NavigationApplication.Create(
46
- ApplicationZone.Business, appEntry.Code, appEntry.Label,
47
+ appEntry.Code, appEntry.Label,
47
48
  appEntry.Description, appEntry.Icon, appEntry.IconType,
48
- appEntry.Route, appEntry.DisplayOrder);
49
+ appEntry.Route, appEntry.DisplayOrder,
50
+ isOpen: appEntry.IsOpen, isPersonal: appEntry.IsPersonal);
49
51
  context.NavigationApplications.Add(app);
50
52
  await ((DbContext)context).SaveChangesAsync(ct);
51
53
 
@@ -0,0 +1,118 @@
1
+ # SmartStackProvider — Wrapping the Client App
2
+
3
+ > **Reference for `application` skill** — how to bootstrap a SmartStack client project (npm `@atlashub/smartstack`) using the SDK provider.
4
+
5
+ ## When This Reference Applies
6
+
7
+ - The client project consumes `@atlashub/smartstack` as an npm package (v3.7+)
8
+ - You are generating or auditing `App.tsx` / `main.tsx` / the application bootstrap
9
+ - The user asks "how do I wrap my app", "where do I put providers", "how do I configure SmartStack"
10
+
11
+ If the project is the SmartStack platform itself (`SmartStack.app`), the platform's internal `App.tsx` already inlines the providers and does NOT use `<SmartStackProvider>` — only client projects use it.
12
+
13
+ ---
14
+
15
+ ## SmartStackConfig shape
16
+
17
+ ```ts
18
+ interface SmartStackConfig {
19
+ apiUrl: string; // REQUIRED — backend root URL (e.g. "https://api.client.com")
20
+ defaultLanguage?: 'fr' | 'en' | 'it' | 'de'; // default 'fr'
21
+ debug?: boolean; // verbose logging
22
+ logo?: ReactNode; // override branding logo
23
+ appName?: string; // displayed in title/header fallback
24
+ licenseKey?: string; // license string (validated against /api/license)
25
+ extensions?: ExtensionConfig; // see references/extensions-system.md
26
+ }
27
+ ```
28
+
29
+ ---
30
+
31
+ ## Wrapping order (Outer → Inner)
32
+
33
+ ```tsx
34
+ import { BrowserRouter } from 'react-router-dom';
35
+ import { SmartStackProvider, DynamicRouter } from '@atlashub/smartstack';
36
+ import '@atlashub/smartstack/theme.css';
37
+
38
+ const config: SmartStackConfig = {
39
+ apiUrl: import.meta.env.VITE_API_URL,
40
+ defaultLanguage: 'fr',
41
+ appName: 'My Client App',
42
+ };
43
+
44
+ createRoot(document.getElementById('root')!).render(
45
+ <StrictMode>
46
+ <BrowserRouter>
47
+ <SmartStackProvider config={config}>
48
+ <DynamicRouter />
49
+ </SmartStackProvider>
50
+ </BrowserRouter>
51
+ </StrictMode>,
52
+ );
53
+ ```
54
+
55
+ `<SmartStackProvider>` internally nests, in this order :
56
+
57
+ ```
58
+ SmartStackContext (config)
59
+ └─ GlobalErrorBoundary
60
+ └─ ExtensionProvider
61
+ └─ ThemeProvider
62
+ └─ FeatureConfigProvider
63
+ └─ AuthProvider
64
+ └─ TenantProvider
65
+ └─ LicenseProvider
66
+ └─ FavoritesProvider
67
+ └─ SidebarProvider
68
+ └─ SessionProvider
69
+ └─ ThemeSync
70
+ └─ NavigationProvider
71
+ └─ SignalRProvider
72
+ └─ {children}
73
+ ```
74
+
75
+ **Do NOT re-wrap any of these providers manually.** They are exported only for advanced cases (e.g., Storybook).
76
+
77
+ ---
78
+
79
+ ## Hooks consumable from any descendant
80
+
81
+ | Hook | Context | Returns |
82
+ |---|---|---|
83
+ | `useSmartStack()` | SmartStackContext | `{ config }` |
84
+ | `useAuth()` | AuthContext | `{ user, login, logout, refresh, hasPermission }` |
85
+ | `useTenant()` | TenantContext | `{ currentTenant, userTenants, switchTenant, isGlobalView }` |
86
+ | `useNavigation()` | NavigationContext | `{ menu, reload, currentApp, currentModule, isLoading }` |
87
+ | `useTheme()` | ThemeContext | `{ mode, selectedTheme, selectedPreset, setTheme, setPreset, toggleMode }` |
88
+ | `useLicense()` | LicenseContext | `{ license, isFeatureEnabled, hasModule }` |
89
+ | `useSignalR()` | SignalRContext | `{ isConnected, on, off, emit }` |
90
+ | `useFavorites()` | FavoritesContext | `{ favorites, toggleFavorite, isFavorite }` |
91
+ | `useSidebar()` | SidebarContext | `{ isCollapsed, toggle, setCollapsed }` |
92
+ | `useFeatureConfig()` | FeatureConfigContext | `{ features }` |
93
+ | `useDocPanel()` | DocPanelContext | `{ isOpen, toggle, openDoc }` |
94
+
95
+ See [contexts-cheatsheet.md](contexts-cheatsheet.md) for usage examples.
96
+
97
+ ---
98
+
99
+ ## DO / DON'T
100
+
101
+ | ✅ DO | ❌ DON'T |
102
+ |---|---|
103
+ | Place `<SmartStackProvider>` exactly once at root | Wrap the same provider twice |
104
+ | Put `<BrowserRouter>` ABOVE `<SmartStackProvider>` | Put `<SmartStackProvider>` above the router |
105
+ | Read config via `useSmartStack()` from any descendant | Pass config down via props |
106
+ | Import `componentRegistry.generated` in `main.tsx` BEFORE the render | Forget the import (DynamicRouter will not resolve pages) |
107
+ | Wait for `i18nReady` promise before render | Render before i18n loaded (causes FOUC) |
108
+ | Provide `apiUrl` via `import.meta.env.VITE_API_URL` | Hardcode the backend URL |
109
+
110
+ ---
111
+
112
+ ## Reference source files (read-only — DO NOT copy verbatim)
113
+
114
+ - `D:\01 - projets\SmartStack.app\features\IA-Workflow\web\smartstack-web\src\provider\SmartStackProvider.tsx`
115
+ - `D:\01 - projets\SmartStack.app\features\IA-Workflow\web\smartstack-web\src\router\types.ts` (SmartStackConfig)
116
+ - `D:\01 - projets\SmartStack.app\features\IA-Workflow\web\smartstack-web\src\main.tsx` (i18nReady + componentRegistry import)
117
+
118
+ For overrides via `extensions`, see [extensions-system.md](extensions-system.md).