@atlashub/smartstack-cli 3.8.0 → 3.10.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 (130) hide show
  1. package/dist/index.js +365 -2
  2. package/dist/index.js.map +1 -1
  3. package/package.json +2 -1
  4. package/templates/agents/action.md +1 -0
  5. package/templates/agents/ba-writer.md +211 -0
  6. package/templates/agents/explore-codebase.md +1 -0
  7. package/templates/agents/explore-docs.md +1 -0
  8. package/templates/agents/fix-grammar.md +1 -0
  9. package/templates/agents/snipper.md +1 -0
  10. package/templates/skills/admin/SKILL.md +6 -0
  11. package/templates/skills/ai-prompt/SKILL.md +32 -136
  12. package/templates/skills/ai-prompt/steps/step-01-implementation.md +122 -0
  13. package/templates/skills/apex/SKILL.md +120 -0
  14. package/templates/skills/apex/_shared.md +86 -0
  15. package/templates/skills/apex/references/agent-teams-protocol.md +164 -0
  16. package/templates/skills/apex/references/smartstack-layers.md +173 -0
  17. package/templates/skills/apex/steps/step-00-init.md +156 -0
  18. package/templates/skills/apex/steps/step-01-analyze.md +169 -0
  19. package/templates/skills/apex/steps/step-02-plan.md +160 -0
  20. package/templates/skills/apex/steps/step-03-execute.md +166 -0
  21. package/templates/skills/apex/steps/step-04-validate.md +138 -0
  22. package/templates/skills/apex/steps/step-05-examine.md +124 -0
  23. package/templates/skills/apex/steps/step-06-resolve.md +105 -0
  24. package/templates/skills/apex/steps/step-07-tests.md +130 -0
  25. package/templates/skills/apex/steps/step-08-run-tests.md +115 -0
  26. package/templates/skills/application/SKILL.md +10 -0
  27. package/templates/skills/application/references/application-roles-template.md +227 -0
  28. package/templates/skills/application/references/backend-controller-hierarchy.md +58 -0
  29. package/templates/skills/application/references/backend-entity-seeding.md +72 -0
  30. package/templates/skills/application/references/backend-verification.md +88 -0
  31. package/templates/skills/application/references/frontend-verification.md +111 -0
  32. package/templates/skills/application/references/nav-fallback-procedure.md +200 -0
  33. package/templates/skills/application/references/provider-template.md +158 -0
  34. package/templates/skills/application/references/test-frontend.md +73 -0
  35. package/templates/skills/application/references/test-prerequisites.md +72 -0
  36. package/templates/skills/application/steps/step-01-navigation.md +7 -198
  37. package/templates/skills/application/steps/step-03-roles.md +45 -7
  38. package/templates/skills/application/steps/step-03b-provider.md +15 -132
  39. package/templates/skills/application/steps/step-04-backend.md +20 -350
  40. package/templates/skills/application/steps/step-05-frontend.md +12 -101
  41. package/templates/skills/application/steps/step-07-tests.md +12 -132
  42. package/templates/skills/business-analyse/SKILL.md +67 -6
  43. package/templates/skills/business-analyse/html/ba-interactive.html +176 -14
  44. package/templates/skills/business-analyse/html/src/scripts/01-data-init.js +1 -0
  45. package/templates/skills/business-analyse/html/src/scripts/05-render-specs.js +16 -4
  46. package/templates/skills/business-analyse/html/src/scripts/06-render-consolidation.js +7 -2
  47. package/templates/skills/business-analyse/html/src/scripts/09-export.js +103 -0
  48. package/templates/skills/business-analyse/html/src/scripts/10-comments.js +12 -6
  49. package/templates/skills/business-analyse/html/src/scripts/11-review-panel.js +24 -2
  50. package/templates/skills/business-analyse/html/src/styles/08-review-panel.css +12 -0
  51. package/templates/skills/business-analyse/html/src/template.html +1 -0
  52. package/templates/skills/business-analyse/references/agent-pooling-best-practices.md +477 -0
  53. package/templates/skills/business-analyse/references/cache-warming-strategy.md +578 -0
  54. package/templates/skills/business-analyse/references/cadrage-structure-cards.md +78 -0
  55. package/templates/skills/business-analyse/references/cadrage-vibe-coding.md +97 -0
  56. package/templates/skills/business-analyse/references/consolidation-structural-checks.md +92 -0
  57. package/templates/skills/business-analyse/references/deploy-data-build.md +121 -0
  58. package/templates/skills/business-analyse/references/deploy-modes.md +49 -0
  59. package/templates/skills/business-analyse/references/handoff-file-templates.md +119 -0
  60. package/templates/skills/business-analyse/references/handoff-mappings.md +81 -0
  61. package/templates/skills/business-analyse/references/html-data-mapping.md +10 -2
  62. package/templates/skills/business-analyse/references/init-schema-deployment.md +65 -0
  63. package/templates/skills/business-analyse/references/review-data-mapping.md +363 -0
  64. package/templates/skills/business-analyse/references/robustness-checks.md +538 -0
  65. package/templates/skills/business-analyse/references/spec-auto-inference.md +57 -0
  66. package/templates/skills/business-analyse/references/ui-dashboard-spec.md +85 -0
  67. package/templates/skills/business-analyse/references/ui-resource-cards.md +110 -0
  68. package/templates/skills/business-analyse/references/validate-incremental-html.md +55 -0
  69. package/templates/skills/business-analyse/schemas/sections/specification-schema.json +33 -1
  70. package/templates/skills/business-analyse/steps/step-00-init.md +186 -53
  71. package/templates/skills/business-analyse/steps/step-01-cadrage.md +5 -194
  72. package/templates/skills/business-analyse/steps/step-03a-data.md +42 -49
  73. package/templates/skills/business-analyse/steps/step-03b-ui.md +12 -178
  74. package/templates/skills/business-analyse/steps/step-03c-compile.md +71 -2
  75. package/templates/skills/business-analyse/steps/step-03d-validate.md +277 -48
  76. package/templates/skills/business-analyse/steps/step-04-consolidation.md +175 -104
  77. package/templates/skills/business-analyse/steps/step-05a-handoff.md +66 -438
  78. package/templates/skills/business-analyse/steps/step-05b-deploy.md +35 -184
  79. package/templates/skills/business-analyse/steps/step-05c-ralph-readiness.md +526 -0
  80. package/templates/skills/business-analyse/steps/step-06-review.md +277 -0
  81. package/templates/skills/cc-agent/references/agent-behavior-patterns.md +95 -0
  82. package/templates/skills/cc-agent/steps/step-02-generate.md +5 -78
  83. package/templates/skills/check-version/SKILL.md +7 -0
  84. package/templates/skills/controller/references/controller-code-templates.md +159 -0
  85. package/templates/skills/controller/references/permission-sync-templates.md +152 -0
  86. package/templates/skills/controller/steps/step-03-generate.md +166 -158
  87. package/templates/skills/controller/steps/step-04-perms.md +5 -144
  88. package/templates/skills/controller/templates.md +11 -2
  89. package/templates/skills/debug/SKILL.md +7 -0
  90. package/templates/skills/explore/SKILL.md +6 -0
  91. package/templates/skills/feature-full/SKILL.md +39 -142
  92. package/templates/skills/feature-full/steps/step-01-implementation.md +120 -0
  93. package/templates/skills/gitflow/references/init-config-template.md +135 -0
  94. package/templates/skills/gitflow/references/init-name-normalization.md +103 -0
  95. package/templates/skills/gitflow/references/plan-template.md +69 -0
  96. package/templates/skills/gitflow/references/start-efcore-preflight.md +70 -0
  97. package/templates/skills/gitflow/references/start-local-config.md +110 -0
  98. package/templates/skills/gitflow/steps/step-init.md +18 -289
  99. package/templates/skills/gitflow/steps/step-plan.md +6 -63
  100. package/templates/skills/gitflow/steps/step-start.md +16 -126
  101. package/templates/skills/mcp/SKILL.md +9 -213
  102. package/templates/skills/mcp/steps/step-01-healthcheck.md +108 -0
  103. package/templates/skills/mcp/steps/step-02-tools.md +73 -0
  104. package/templates/skills/notification/SKILL.md +7 -0
  105. package/templates/skills/quick-search/SKILL.md +5 -0
  106. package/templates/skills/ralph-loop/SKILL.md +99 -381
  107. package/templates/skills/ralph-loop/references/category-rules.md +259 -0
  108. package/templates/skills/ralph-loop/references/compact-loop.md +182 -0
  109. package/templates/skills/ralph-loop/references/core-seed-data.md +173 -21
  110. package/templates/skills/ralph-loop/references/task-transform-legacy.md +259 -0
  111. package/templates/skills/ralph-loop/references/team-orchestration.md +189 -0
  112. package/templates/skills/ralph-loop/steps/step-00-init.md +111 -383
  113. package/templates/skills/ralph-loop/steps/step-01-task.md +79 -896
  114. package/templates/skills/ralph-loop/steps/step-02-execute.md +68 -680
  115. package/templates/skills/ralph-loop/steps/step-03-commit.md +47 -277
  116. package/templates/skills/ralph-loop/steps/step-04-check.md +124 -607
  117. package/templates/skills/ralph-loop/steps/step-05-report.md +68 -367
  118. package/templates/skills/refactor/SKILL.md +12 -176
  119. package/templates/skills/refactor/steps/step-01-discover.md +60 -0
  120. package/templates/skills/refactor/steps/step-02-execute.md +67 -0
  121. package/templates/skills/review-code/SKILL.md +19 -257
  122. package/templates/skills/review-code/steps/step-01-smartstack.md +96 -0
  123. package/templates/skills/review-code/steps/step-02-detailed-review.md +80 -0
  124. package/templates/skills/review-code/steps/step-03-react.md +44 -0
  125. package/templates/skills/ui-components/SKILL.md +7 -0
  126. package/templates/skills/utils/SKILL.md +6 -0
  127. package/templates/skills/validate/SKILL.md +6 -0
  128. package/templates/skills/validate-feature/SKILL.md +8 -0
  129. package/templates/skills/workflow/SKILL.md +40 -118
  130. package/templates/skills/workflow/steps/step-01-implementation.md +84 -0
@@ -35,12 +35,16 @@ The skill auto-detects which use case applies by scanning existing features in `
35
35
 
36
36
  <parameters>
37
37
 
38
- No flags. The entire input is the `{feature_description}`.
38
+ | Flag | Description |
39
+ |------|-------------|
40
+ | (aucun) | Mode standard — analyse complete ou mise a jour |
41
+ | `-review` | Mode review — lit `ba-review.json`, cree une nouvelle version avec les corrections appliquees |
39
42
 
40
- Step-00 handles all detection automatically:
43
+ Step-00 handles detection automatically:
41
44
  - **New vs Update**: scans `docs/business/` for existing applications
42
45
  - **Single vs Multi-module**: determined during step-02 decomposition
43
46
  - **Language**: detected from config or asked once
47
+ - **Review mode**: if `-review` flag, routes directly to step-06
44
48
 
45
49
  </parameters>
46
50
 
@@ -71,6 +75,52 @@ docs/business/
71
75
  **No intermediate markdown files - all state in feature.json**
72
76
  </output_structure>
73
77
 
78
+ <navigation_hierarchy>
79
+ **SmartStack uses a 5-level navigation structure mapped to database tables:**
80
+
81
+ | Level | Entity | Table DB | Created in Step | Description |
82
+ |-------|--------|----------|-----------------|-------------|
83
+ | 1 | Context | `core.nav_Contexts` | N/A | Fixed ("business", "platform", "system") |
84
+ | 2 | Application | `core.nav_Applications` | 00-01 | Top-level application (e.g., "RessourcesHumaines") |
85
+ | 3 | Module | `core.nav_Modules` | 02 | Functional module (e.g., "Employees", "TimeManagement") |
86
+ | 4 | **Section** | **`core.nav_Sections`** | **03a-03c** | **Page within module (e.g., "list", "detail", "create", "dashboard")** |
87
+ | 5 | **Resource** | **`core.nav_Resources`** | **03b-03c** | **React component (SmartTable, SmartForm, Chart, KpiCard, etc.)** |
88
+
89
+ **Hierarchical relationship:**
90
+ - Context → Application → Module → **Section** → **Resource**
91
+ - Each Section MUST have ≥1 Resource
92
+ - Each Section corresponds to 1 React page route
93
+ - Each Resource corresponds to 1 SmartStack UI component
94
+
95
+ **SeedData generation (step-03c):**
96
+
97
+ The `specification.seedDataCore` contains **7 mandatory arrays** for database seeding:
98
+
99
+ | Array | Table | Source | Description |
100
+ |-------|-------|--------|-------------|
101
+ | `navigationModules` | `core.nav_Modules` | Manual (step-03c) | Module entry (Level 3) |
102
+ | **`navigationSections`** | **`core.nav_Sections`** | **Derived from `specification.sections[]`** | **Section entries (Level 4)** |
103
+ | **`navigationResources`** | **`core.nav_Resources`** | **Derived from `specification.sections[].resources[]`** | **Resource entries (Level 5)** |
104
+ | `navigationTranslations` | `core.nav_Translations` | Manual (i18n) | Multi-language labels |
105
+ | `permissions` | `core.Permissions` | Manual (step-03c) | Permission definitions |
106
+ | `rolePermissions` | `core.RolePermissions` | Manual (step-03c) | Role-permission mappings |
107
+ | `permissionConstants` | `PermissionConstants.cs` | Manual (step-03c) | C# permission constants |
108
+
109
+ **Transform algorithm (step-03c section 8f-bis):**
110
+ - `navigationSections` = transform `specification.sections[]` → flatten to { code, label, icon, route, parentCode, permission, sort }
111
+ - `navigationResources` = transform `specification.sections[].resources[]` → flatten to { code, type, entity, parentCode, permission }
112
+
113
+ **Example:**
114
+ - Module: `Employees` (Level 3)
115
+ - Section: `list` (Level 4) → contains resources:
116
+ - Resource: `employees-grid` (SmartTable) (Level 5)
117
+ - Resource: `status-filter` (FilterBar) (Level 5)
118
+ - Section: `detail` (Level 4) → contains resources:
119
+ - Resource: `employee-card` (DetailCard) (Level 5)
120
+ - Resource: `contract-history` (SmartTable) (Level 5)
121
+
122
+ </navigation_hierarchy>
123
+
74
124
  <resume_workflow>
75
125
  **Update mode (auto-detected):**
76
126
 
@@ -100,14 +150,21 @@ When step-00 detects that the description matches an existing application:
100
150
  - **Step 03c:** Per-module compile: actors, UCs, FRs, permissions, navigation, seed data, i18n
101
151
  - **Step 03d:** Per-module validate: completeness checks, write feature.json, incremental HTML, loop
102
152
  - Loop: 03a → 03b → 03c → 03d → 03a (next module) until all specified (specified)
103
- - **Step 04:** Cross-module consolidation (consolidated)
153
+ - **Step 04a:** Collect: module summaries, cross-module interactions (FK, events, shared data)
154
+ - **Step 04b:** Analyze: permission coherence, semantic validation, E2E flows, global risks
155
+ - **Step 04c:** Decide: final approval, write consolidation section, proceed to handoff (consolidated)
104
156
  - **Step 05a:** Handoff: file mapping (7 categories), BR-to-code mapping, API summary, write to feature.json
105
157
  - **Step 05b:** Deploy: prd.json, progress.txt, manifest, ba-interactive.html pre-populated (handed-off)
158
+ - **Step 05c:** Ralph Readiness Check: validation gate before /ralph-loop (optional but recommended)
106
159
 
107
160
  **Update workflow (same phases, delta focus):**
108
161
  - **Step 00:** Detection, locate existing feature, create version N+1
109
162
  - **Step 01:** Cadrage delta: what changes, impact on existing
110
163
  - **Step 02-05:** Same flow as new, with existing context loaded
164
+
165
+ **Review workflow (`-review` flag):**
166
+ - **Step 00:** Detection, scan for latest application, locate `ba-review.json`
167
+ - **Step 06:** Apply corrections, create new version, reverse-map data, regenerate all artifacts
111
168
  </workflow>
112
169
 
113
170
  <state_variables>
@@ -144,11 +201,15 @@ When step-00 detects that the description matches an existing application:
144
201
  | 02 | `steps/step-02-decomposition.md` | Opus | Module decomposition, dependency graph, client checkpoint |
145
202
  | 03a | `steps/step-03a-data.md` | Opus | Per-module: sections, entities, BRs, questionnaires |
146
203
  | 03b | `steps/step-03b-ui.md` | Opus | Per-module: state machines, wireframes, layouts, dashboards |
147
- | 03c | `steps/step-03c-compile.md` | Opus | Per-module: actors, UCs, FRs, permissions, nav, seed data, i18n |
204
+ | 03c | `steps/step-03c-compile.md` | Opus | Per-module: actors, UCs, FRs, permissions, navigation, seedDataCore (7 arrays), i18n |
148
205
  | 03d | `steps/step-03d-validate.md` | Sonnet | Per-module: validation, write, incremental HTML, loop decision |
149
- | 04 | `steps/step-04-consolidation.md` | Opus | Cross-module validation, E2E flows, permissions coherence |
206
+ | 04a | `steps/step-04a-collect.md` | Opus | Collect module summaries & cross-module interactions |
207
+ | 04b | `steps/step-04b-analyze.md` | Opus | Analyze permission coherence, semantic validation, E2E flows |
208
+ | 04c | `steps/step-04c-decide.md` | Opus | Final approval, write consolidation, proceed to handoff |
150
209
  | 05a | `steps/step-05a-handoff.md` | Sonnet | Handoff: file mapping (7 categories), BR-to-code mapping, API summary, write handoff |
151
- | 05b | `steps/step-05b-deploy.md` | Sonnet | Deploy: prd.json, progress.txt, manifest, ba-interactive.html, auto-launch ralph-loop |
210
+ | 05b | `steps/step-05b-deploy.md` | Sonnet | Deploy: prd.json, progress.txt, manifest, ba-interactive.html |
211
+ | 05c | `steps/step-05c-ralph-readiness.md` | Sonnet | Ralph readiness validation gate (optional but recommended) |
212
+ | 06 | `steps/step-06-review.md` | Opus | Apply review corrections, create new version, regenerate all artifacts |
152
213
 
153
214
  </step_files>
154
215
 
@@ -1380,6 +1380,18 @@ body {
1380
1380
  font-size: 0.65rem;
1381
1381
  }
1382
1382
 
1383
+ /* Review save button */
1384
+ .btn-review {
1385
+ background: var(--success);
1386
+ color: #fff;
1387
+ border-color: var(--success);
1388
+ font-weight: 600;
1389
+ }
1390
+ .btn-review:hover {
1391
+ background: #16a34a;
1392
+ border-color: #16a34a;
1393
+ }
1394
+
1383
1395
  @media (max-width: 768px) {
1384
1396
  .body.review-open {
1385
1397
  grid-template-columns: 1fr;
@@ -1408,6 +1420,7 @@ body {
1408
1420
  <div class="header-spacer"></div>
1409
1421
  <div class="header-actions">
1410
1422
  <button class="btn btn-sm" onclick="saveToLocalStorage()" title="Sauvegarder les modifications dans le navigateur">Sauvegarder</button>
1423
+ <button class="btn btn-sm btn-review" onclick="saveReviewJSON()" title="Sauvegarder les corrections pour creer une nouvelle version">Sauvegarder corrections</button>
1411
1424
  <button class="btn btn-sm btn-primary" onclick="exportJSON()" title="Exporter les donnees au format JSON pour l'extraction">Exporter JSON</button>
1412
1425
  <button class="btn btn-sm review-toggle-btn" id="reviewToggleBtn" onclick="toggleReviewPanel()" title="Ouvrir/fermer le panneau de review">
1413
1426
  Review
@@ -2009,6 +2022,7 @@ body {
2009
2022
  ============================================ */
2010
2023
  const APP_KEY = 'ba-{{APPLICATION_ID}}';
2011
2024
  let data = {{FEATURE_DATA}};
2025
+ const ORIGINAL_DATA = JSON.parse(JSON.stringify(data));
2012
2026
  const EMBEDDED_ARTIFACTS = {{EMBEDDED_ARTIFACTS}};
2013
2027
 
2014
2028
  // Initialize optional data structures
@@ -2605,6 +2619,8 @@ function renderAllModuleSpecs() {
2605
2619
  });
2606
2620
  // Restore currently visible section after re-render
2607
2621
  restoreCurrentSection();
2622
+ // Re-initialize inline comment buttons on module spec cards
2623
+ if (typeof initInlineComments === 'function') initInlineComments();
2608
2624
  }
2609
2625
 
2610
2626
  function renderModuleSpecSection(mod) {
@@ -3007,6 +3023,17 @@ function renderModuleMockups(code) {
3007
3023
  }
3008
3024
 
3009
3025
  function getPermRoles() {
3026
+ // Extract roles from actual permission data (handles English role names from feature.json)
3027
+ const rolesFromPerms = [];
3028
+ const seen = new Set();
3029
+ data.modules.forEach(m => {
3030
+ (data.moduleSpecs[m.code]?.permissions || []).forEach(p => {
3031
+ const role = p.split('|')[0];
3032
+ if (role && !seen.has(role)) { seen.add(role); rolesFromPerms.push(role); }
3033
+ });
3034
+ });
3035
+ if (rolesFromPerms.length > 0) return [...rolesFromPerms, ...(data.customRoles || [])];
3036
+ // Fallback: use stakeholder names if no permission data exists
3010
3037
  const baseRoles = data.cadrage.stakeholders.length > 0
3011
3038
  ? data.cadrage.stakeholders.map(s => s.role)
3012
3039
  : ['Administrateur', 'Responsable', 'Contributeur', 'Lecteur'];
@@ -3021,9 +3048,7 @@ function getPermActions() {
3021
3048
  function renderPermissionGrid(code) {
3022
3049
  const roles = getPermRoles();
3023
3050
  const actions = getPermActions();
3024
- const baseRolesCount = data.cadrage.stakeholders.length > 0
3025
- ? data.cadrage.stakeholders.length
3026
- : 4;
3051
+ const baseRolesCount = roles.length - (data.customRoles || []).length;
3027
3052
  const baseActionsCount = 6;
3028
3053
 
3029
3054
  const perms = data.moduleSpecs[code]?.permissions || [];
@@ -3040,7 +3065,8 @@ function renderPermissionGrid(code) {
3040
3065
  <td style="font-weight:500;color:var(--text-bright);">${role}${ri >= baseRolesCount ? ' <span onclick="removeCustomRole('+`'${code}','${role}'`+')" style="cursor:pointer;color:var(--danger);font-size:0.7rem;" title="Supprimer ce role">&#10005;</span>' : ''}</td>
3041
3066
  ${actions.map(action => {
3042
3067
  const key = role + '|' + action;
3043
- const checked = perms.includes(key);
3068
+ const wildcardKey = role + '|*';
3069
+ const checked = perms.includes(key) || perms.includes(wildcardKey);
3044
3070
  return `<td style="text-align:center;"><input type="checkbox" ${checked ? 'checked' : ''} onchange="togglePermission('${code}','${key}',this.checked)" style="cursor:pointer;width:16px;height:16px;"></td>`;
3045
3071
  }).join('')}
3046
3072
  </tr>
@@ -3171,10 +3197,15 @@ function renderConsolPermissions() {
3171
3197
  roles.forEach(role => {
3172
3198
  html += `<tr><td style="font-weight:500;color:var(--text-bright);">${role}</td>`;
3173
3199
  data.modules.forEach(m => {
3174
- const perms = (data.moduleSpecs[m.code]?.permissions || []).filter(p => p.startsWith(role + '|'));
3200
+ const allPerms = data.moduleSpecs[m.code]?.permissions || [];
3201
+ const hasWildcard = allPerms.includes(role + '|*');
3202
+ const perms = hasWildcard
3203
+ ? getPermActions()
3204
+ : allPerms.filter(p => p.startsWith(role + '|'));
3175
3205
  const count = perms.length;
3176
3206
  const color = count > 4 ? 'var(--success)' : count > 2 ? 'var(--warning)' : count > 0 ? 'var(--text-muted)' : 'var(--border)';
3177
- html += `<td style="text-align:center;color:${color};font-weight:600;">${count > 0 ? count + ' droits' : 'Aucun'}</td>`;
3207
+ const label = hasWildcard ? 'Tous' : (count > 0 ? count + ' droits' : 'Aucun');
3208
+ html += `<td style="text-align:center;color:${color};font-weight:600;">${label}</td>`;
3178
3209
  });
3179
3210
  html += '</tr>';
3180
3211
  });
@@ -3446,6 +3477,109 @@ function exportJSON() {
3446
3477
  showNotification('Export JSON telecharge');
3447
3478
  }
3448
3479
 
3480
+ /* ============================================
3481
+ SAVE REVIEW JSON (corrections for new version)
3482
+ ============================================ */
3483
+ function detectChanges(original, current) {
3484
+ const changes = { cadrage: false, modulesAdded: [], modulesRemoved: [], modulesModified: [], commentsCount: 0 };
3485
+
3486
+ // Cadrage changes
3487
+ if (JSON.stringify(original.cadrage) !== JSON.stringify(current.cadrage)) {
3488
+ changes.cadrage = true;
3489
+ }
3490
+
3491
+ // Module changes
3492
+ const origCodes = (original.modules || []).map(m => m.code);
3493
+ const currCodes = (current.modules || []).map(m => m.code);
3494
+ changes.modulesAdded = currCodes.filter(c => !origCodes.includes(c));
3495
+ changes.modulesRemoved = origCodes.filter(c => !currCodes.includes(c));
3496
+ currCodes.filter(c => origCodes.includes(c)).forEach(code => {
3497
+ const origSpec = JSON.stringify(original.moduleSpecs?.[code] || {});
3498
+ const currSpec = JSON.stringify(current.moduleSpecs?.[code] || {});
3499
+ if (origSpec !== currSpec) changes.modulesModified.push(code);
3500
+ });
3501
+
3502
+ // Comments count
3503
+ changes.commentsCount = (current.comments || []).length;
3504
+
3505
+ return changes;
3506
+ }
3507
+
3508
+ function saveReviewJSON() {
3509
+ // Collect all editable fields (same as exportJSON)
3510
+ document.querySelectorAll('.editable[data-field]').forEach(el => {
3511
+ setNestedValue(data, 'cadrage.' + el.dataset.field, el.textContent.trim());
3512
+ });
3513
+ document.querySelectorAll('.editable[data-module-field]').forEach(el => {
3514
+ const code = el.dataset.moduleCode;
3515
+ const field = el.dataset.moduleField;
3516
+ if (data.moduleSpecs[code]) {
3517
+ data.moduleSpecs[code][field] = el.textContent.trim();
3518
+ }
3519
+ });
3520
+
3521
+ const changes = detectChanges(ORIGINAL_DATA, data);
3522
+ const hasChanges = changes.cadrage || changes.modulesAdded.length > 0
3523
+ || changes.modulesRemoved.length > 0 || changes.modulesModified.length > 0
3524
+ || changes.commentsCount > 0;
3525
+
3526
+ // Build review export with metadata envelope
3527
+ const reviewData = {
3528
+ _reviewMeta: {
3529
+ sourceVersion: data.metadata.version || '1.0',
3530
+ sourceApplicationId: data.metadata.applicationId || '',
3531
+ exportedAt: new Date().toISOString(),
3532
+ hasChanges: hasChanges,
3533
+ changeSummary: {
3534
+ cadrageModified: changes.cadrage,
3535
+ modulesAdded: changes.modulesAdded,
3536
+ modulesRemoved: changes.modulesRemoved,
3537
+ modulesModified: changes.modulesModified,
3538
+ commentsCount: changes.commentsCount
3539
+ }
3540
+ },
3541
+ metadata: data.metadata,
3542
+ cadrage: data.cadrage,
3543
+ modules: data.modules,
3544
+ dependencies: data.dependencies,
3545
+ moduleSpecifications: {},
3546
+ consolidation: data.consolidation,
3547
+ wireframeComments: data.wireframeComments,
3548
+ specComments: data.specComments,
3549
+ customRoles: data.customRoles,
3550
+ customActions: data.customActions,
3551
+ comments: data.comments
3552
+ };
3553
+
3554
+ // Structure module specs (same logic as exportJSON)
3555
+ data.modules.forEach(m => {
3556
+ const spec = data.moduleSpecs[m.code] || {};
3557
+ reviewData.moduleSpecifications[m.code] = {
3558
+ module: m,
3559
+ useCases: (spec.useCases || []).map((uc, i) => ({
3560
+ id: 'UC-' + String(i + 1).padStart(3, '0'),
3561
+ ...uc
3562
+ })),
3563
+ businessRules: (spec.businessRules || []).map((br, i) => ({
3564
+ id: 'BR-' + br.category.toUpperCase().substring(0, 4) + '-' + String(i + 1).padStart(3, '0'),
3565
+ ...br
3566
+ })),
3567
+ entities: spec.entities || [],
3568
+ permissions: spec.permissions || [],
3569
+ notes: spec.notes || ''
3570
+ };
3571
+ });
3572
+
3573
+ const blob = new Blob([JSON.stringify(reviewData, null, 2)], { type: 'application/json' });
3574
+ const url = URL.createObjectURL(blob);
3575
+ const a = document.createElement('a');
3576
+ a.href = url;
3577
+ a.download = 'ba-review.json';
3578
+ a.click();
3579
+ URL.revokeObjectURL(url);
3580
+ showNotification('ba-review.json telecharge — sauvegardez-le a cote du HTML');
3581
+ }
3582
+
3449
3583
 
3450
3584
  /* --- 10-comments.js --- */
3451
3585
  /* ============================================
@@ -3463,14 +3597,20 @@ function exportJSON() {
3463
3597
  */
3464
3598
 
3465
3599
  function initInlineComments() {
3466
- // Add comment buttons under each card and uc-item
3600
+ // Add comment buttons under each card and uc-item (direct children of sections)
3467
3601
  document.querySelectorAll('.section').forEach(section => {
3468
3602
  const sectionId = section.id;
3469
- const cards = section.querySelectorAll(':scope > .card, :scope > .uc-item');
3470
- cards.forEach((card, index) => {
3471
- if (card.querySelector('.comment-btn-container')) return; // already initialized
3472
- const container = createCommentUI(sectionId, index);
3473
- card.appendChild(container);
3603
+ section.querySelectorAll(':scope > .card, :scope > .uc-item').forEach((card, index) => {
3604
+ if (card.querySelector('.comment-btn-container')) return;
3605
+ card.appendChild(createCommentUI(sectionId, index));
3606
+ });
3607
+ });
3608
+ // Second pass: nested list containers in module specs (ucList-*, brList-*, entList-*)
3609
+ document.querySelectorAll('[id^="ucList-"], [id^="brList-"], [id^="entList-"]').forEach(list => {
3610
+ const listId = list.id;
3611
+ list.querySelectorAll(':scope > .uc-item, :scope > .entity-block, :scope > div').forEach((item, index) => {
3612
+ if (item.querySelector('.comment-btn-container')) return;
3613
+ item.appendChild(createCommentUI(listId, index));
3474
3614
  });
3475
3615
  });
3476
3616
  }
@@ -3738,12 +3878,34 @@ function getSectionLabel(sectionId) {
3738
3878
  const mod = data.modules.find(m => m.code === code);
3739
3879
  return mod ? mod.name : code;
3740
3880
  }
3881
+ // Handle list-based sectionIds (ucList-*, brList-*, entList-*)
3882
+ const listMatch = sectionId.match(/^(uc|br|ent)List-(.+)$/);
3883
+ if (listMatch) {
3884
+ const tabLabels = { uc: 'Cas d\'utilisation', br: 'Regles metier', ent: 'Donnees' };
3885
+ const mod = data.modules.find(m => m.code === listMatch[2]);
3886
+ const modName = mod ? mod.name : listMatch[2];
3887
+ return modName + ' > ' + (tabLabels[listMatch[1]] || listMatch[1]);
3888
+ }
3741
3889
  return sectionId;
3742
3890
  }
3743
3891
 
3744
3892
  function navigateToComment(sectionId, cardIndex) {
3745
- showSection(sectionId);
3746
- // Scroll to the card and open its comment thread
3893
+ // Handle list-based sectionIds (ucList-*, brList-*, entList-*) → navigate to module + tab
3894
+ const listMatch = sectionId.match(/^(uc|br|ent)List-(.+)$/);
3895
+ if (listMatch) {
3896
+ const [, tabType, moduleCode] = listMatch;
3897
+ showSection('module-spec-' + moduleCode);
3898
+ setTimeout(() => {
3899
+ switchTab(moduleCode, tabType);
3900
+ scrollToCommentThread(sectionId, cardIndex);
3901
+ }, 150);
3902
+ } else {
3903
+ showSection(sectionId);
3904
+ scrollToCommentThread(sectionId, cardIndex);
3905
+ }
3906
+ }
3907
+
3908
+ function scrollToCommentThread(sectionId, cardIndex) {
3747
3909
  setTimeout(() => {
3748
3910
  const thread = document.getElementById('comment-thread-' + sectionId + '-' + cardIndex);
3749
3911
  if (thread && !thread.classList.contains('visible')) {
@@ -3,6 +3,7 @@
3
3
  ============================================ */
4
4
  const APP_KEY = 'ba-{{APPLICATION_ID}}';
5
5
  let data = {{FEATURE_DATA}};
6
+ const ORIGINAL_DATA = JSON.parse(JSON.stringify(data));
6
7
  const EMBEDDED_ARTIFACTS = {{EMBEDDED_ARTIFACTS}};
7
8
 
8
9
  // Initialize optional data structures
@@ -17,6 +17,8 @@ function renderAllModuleSpecs() {
17
17
  });
18
18
  // Restore currently visible section after re-render
19
19
  restoreCurrentSection();
20
+ // Re-initialize inline comment buttons on module spec cards
21
+ if (typeof initInlineComments === 'function') initInlineComments();
20
22
  }
21
23
 
22
24
  function renderModuleSpecSection(mod) {
@@ -419,6 +421,17 @@ function renderModuleMockups(code) {
419
421
  }
420
422
 
421
423
  function getPermRoles() {
424
+ // Extract roles from actual permission data (handles English role names from feature.json)
425
+ const rolesFromPerms = [];
426
+ const seen = new Set();
427
+ data.modules.forEach(m => {
428
+ (data.moduleSpecs[m.code]?.permissions || []).forEach(p => {
429
+ const role = p.split('|')[0];
430
+ if (role && !seen.has(role)) { seen.add(role); rolesFromPerms.push(role); }
431
+ });
432
+ });
433
+ if (rolesFromPerms.length > 0) return [...rolesFromPerms, ...(data.customRoles || [])];
434
+ // Fallback: use stakeholder names if no permission data exists
422
435
  const baseRoles = data.cadrage.stakeholders.length > 0
423
436
  ? data.cadrage.stakeholders.map(s => s.role)
424
437
  : ['Administrateur', 'Responsable', 'Contributeur', 'Lecteur'];
@@ -433,9 +446,7 @@ function getPermActions() {
433
446
  function renderPermissionGrid(code) {
434
447
  const roles = getPermRoles();
435
448
  const actions = getPermActions();
436
- const baseRolesCount = data.cadrage.stakeholders.length > 0
437
- ? data.cadrage.stakeholders.length
438
- : 4;
449
+ const baseRolesCount = roles.length - (data.customRoles || []).length;
439
450
  const baseActionsCount = 6;
440
451
 
441
452
  const perms = data.moduleSpecs[code]?.permissions || [];
@@ -452,7 +463,8 @@ function renderPermissionGrid(code) {
452
463
  <td style="font-weight:500;color:var(--text-bright);">${role}${ri >= baseRolesCount ? ' <span onclick="removeCustomRole('+`'${code}','${role}'`+')" style="cursor:pointer;color:var(--danger);font-size:0.7rem;" title="Supprimer ce role">&#10005;</span>' : ''}</td>
453
464
  ${actions.map(action => {
454
465
  const key = role + '|' + action;
455
- const checked = perms.includes(key);
466
+ const wildcardKey = role + '|*';
467
+ const checked = perms.includes(key) || perms.includes(wildcardKey);
456
468
  return `<td style="text-align:center;"><input type="checkbox" ${checked ? 'checked' : ''} onchange="togglePermission('${code}','${key}',this.checked)" style="cursor:pointer;width:16px;height:16px;"></td>`;
457
469
  }).join('')}
458
470
  </tr>
@@ -38,10 +38,15 @@ function renderConsolPermissions() {
38
38
  roles.forEach(role => {
39
39
  html += `<tr><td style="font-weight:500;color:var(--text-bright);">${role}</td>`;
40
40
  data.modules.forEach(m => {
41
- const perms = (data.moduleSpecs[m.code]?.permissions || []).filter(p => p.startsWith(role + '|'));
41
+ const allPerms = data.moduleSpecs[m.code]?.permissions || [];
42
+ const hasWildcard = allPerms.includes(role + '|*');
43
+ const perms = hasWildcard
44
+ ? getPermActions()
45
+ : allPerms.filter(p => p.startsWith(role + '|'));
42
46
  const count = perms.length;
43
47
  const color = count > 4 ? 'var(--success)' : count > 2 ? 'var(--warning)' : count > 0 ? 'var(--text-muted)' : 'var(--border)';
44
- html += `<td style="text-align:center;color:${color};font-weight:600;">${count > 0 ? count + ' droits' : 'Aucun'}</td>`;
48
+ const label = hasWildcard ? 'Tous' : (count > 0 ? count + ' droits' : 'Aucun');
49
+ html += `<td style="text-align:center;color:${color};font-weight:600;">${label}</td>`;
45
50
  });
46
51
  html += '</tr>';
47
52
  });
@@ -63,3 +63,106 @@ function exportJSON() {
63
63
  URL.revokeObjectURL(url);
64
64
  showNotification('Export JSON telecharge');
65
65
  }
66
+
67
+ /* ============================================
68
+ SAVE REVIEW JSON (corrections for new version)
69
+ ============================================ */
70
+ function detectChanges(original, current) {
71
+ const changes = { cadrage: false, modulesAdded: [], modulesRemoved: [], modulesModified: [], commentsCount: 0 };
72
+
73
+ // Cadrage changes
74
+ if (JSON.stringify(original.cadrage) !== JSON.stringify(current.cadrage)) {
75
+ changes.cadrage = true;
76
+ }
77
+
78
+ // Module changes
79
+ const origCodes = (original.modules || []).map(m => m.code);
80
+ const currCodes = (current.modules || []).map(m => m.code);
81
+ changes.modulesAdded = currCodes.filter(c => !origCodes.includes(c));
82
+ changes.modulesRemoved = origCodes.filter(c => !currCodes.includes(c));
83
+ currCodes.filter(c => origCodes.includes(c)).forEach(code => {
84
+ const origSpec = JSON.stringify(original.moduleSpecs?.[code] || {});
85
+ const currSpec = JSON.stringify(current.moduleSpecs?.[code] || {});
86
+ if (origSpec !== currSpec) changes.modulesModified.push(code);
87
+ });
88
+
89
+ // Comments count
90
+ changes.commentsCount = (current.comments || []).length;
91
+
92
+ return changes;
93
+ }
94
+
95
+ function saveReviewJSON() {
96
+ // Collect all editable fields (same as exportJSON)
97
+ document.querySelectorAll('.editable[data-field]').forEach(el => {
98
+ setNestedValue(data, 'cadrage.' + el.dataset.field, el.textContent.trim());
99
+ });
100
+ document.querySelectorAll('.editable[data-module-field]').forEach(el => {
101
+ const code = el.dataset.moduleCode;
102
+ const field = el.dataset.moduleField;
103
+ if (data.moduleSpecs[code]) {
104
+ data.moduleSpecs[code][field] = el.textContent.trim();
105
+ }
106
+ });
107
+
108
+ const changes = detectChanges(ORIGINAL_DATA, data);
109
+ const hasChanges = changes.cadrage || changes.modulesAdded.length > 0
110
+ || changes.modulesRemoved.length > 0 || changes.modulesModified.length > 0
111
+ || changes.commentsCount > 0;
112
+
113
+ // Build review export with metadata envelope
114
+ const reviewData = {
115
+ _reviewMeta: {
116
+ sourceVersion: data.metadata.version || '1.0',
117
+ sourceApplicationId: data.metadata.applicationId || '',
118
+ exportedAt: new Date().toISOString(),
119
+ hasChanges: hasChanges,
120
+ changeSummary: {
121
+ cadrageModified: changes.cadrage,
122
+ modulesAdded: changes.modulesAdded,
123
+ modulesRemoved: changes.modulesRemoved,
124
+ modulesModified: changes.modulesModified,
125
+ commentsCount: changes.commentsCount
126
+ }
127
+ },
128
+ metadata: data.metadata,
129
+ cadrage: data.cadrage,
130
+ modules: data.modules,
131
+ dependencies: data.dependencies,
132
+ moduleSpecifications: {},
133
+ consolidation: data.consolidation,
134
+ wireframeComments: data.wireframeComments,
135
+ specComments: data.specComments,
136
+ customRoles: data.customRoles,
137
+ customActions: data.customActions,
138
+ comments: data.comments
139
+ };
140
+
141
+ // Structure module specs (same logic as exportJSON)
142
+ data.modules.forEach(m => {
143
+ const spec = data.moduleSpecs[m.code] || {};
144
+ reviewData.moduleSpecifications[m.code] = {
145
+ module: m,
146
+ useCases: (spec.useCases || []).map((uc, i) => ({
147
+ id: 'UC-' + String(i + 1).padStart(3, '0'),
148
+ ...uc
149
+ })),
150
+ businessRules: (spec.businessRules || []).map((br, i) => ({
151
+ id: 'BR-' + br.category.toUpperCase().substring(0, 4) + '-' + String(i + 1).padStart(3, '0'),
152
+ ...br
153
+ })),
154
+ entities: spec.entities || [],
155
+ permissions: spec.permissions || [],
156
+ notes: spec.notes || ''
157
+ };
158
+ });
159
+
160
+ const blob = new Blob([JSON.stringify(reviewData, null, 2)], { type: 'application/json' });
161
+ const url = URL.createObjectURL(blob);
162
+ const a = document.createElement('a');
163
+ a.href = url;
164
+ a.download = 'ba-review.json';
165
+ a.click();
166
+ URL.revokeObjectURL(url);
167
+ showNotification('ba-review.json telecharge — sauvegardez-le a cote du HTML');
168
+ }
@@ -13,14 +13,20 @@
13
13
  */
14
14
 
15
15
  function initInlineComments() {
16
- // Add comment buttons under each card and uc-item
16
+ // Add comment buttons under each card and uc-item (direct children of sections)
17
17
  document.querySelectorAll('.section').forEach(section => {
18
18
  const sectionId = section.id;
19
- const cards = section.querySelectorAll(':scope > .card, :scope > .uc-item');
20
- cards.forEach((card, index) => {
21
- if (card.querySelector('.comment-btn-container')) return; // already initialized
22
- const container = createCommentUI(sectionId, index);
23
- card.appendChild(container);
19
+ section.querySelectorAll(':scope > .card, :scope > .uc-item').forEach((card, index) => {
20
+ if (card.querySelector('.comment-btn-container')) return;
21
+ card.appendChild(createCommentUI(sectionId, index));
22
+ });
23
+ });
24
+ // Second pass: nested list containers in module specs (ucList-*, brList-*, entList-*)
25
+ document.querySelectorAll('[id^="ucList-"], [id^="brList-"], [id^="entList-"]').forEach(list => {
26
+ const listId = list.id;
27
+ list.querySelectorAll(':scope > .uc-item, :scope > .entity-block, :scope > div').forEach((item, index) => {
28
+ if (item.querySelector('.comment-btn-container')) return;
29
+ item.appendChild(createCommentUI(listId, index));
24
30
  });
25
31
  });
26
32
  }
@@ -120,12 +120,34 @@ function getSectionLabel(sectionId) {
120
120
  const mod = data.modules.find(m => m.code === code);
121
121
  return mod ? mod.name : code;
122
122
  }
123
+ // Handle list-based sectionIds (ucList-*, brList-*, entList-*)
124
+ const listMatch = sectionId.match(/^(uc|br|ent)List-(.+)$/);
125
+ if (listMatch) {
126
+ const tabLabels = { uc: 'Cas d\'utilisation', br: 'Regles metier', ent: 'Donnees' };
127
+ const mod = data.modules.find(m => m.code === listMatch[2]);
128
+ const modName = mod ? mod.name : listMatch[2];
129
+ return modName + ' > ' + (tabLabels[listMatch[1]] || listMatch[1]);
130
+ }
123
131
  return sectionId;
124
132
  }
125
133
 
126
134
  function navigateToComment(sectionId, cardIndex) {
127
- showSection(sectionId);
128
- // Scroll to the card and open its comment thread
135
+ // Handle list-based sectionIds (ucList-*, brList-*, entList-*) → navigate to module + tab
136
+ const listMatch = sectionId.match(/^(uc|br|ent)List-(.+)$/);
137
+ if (listMatch) {
138
+ const [, tabType, moduleCode] = listMatch;
139
+ showSection('module-spec-' + moduleCode);
140
+ setTimeout(() => {
141
+ switchTab(moduleCode, tabType);
142
+ scrollToCommentThread(sectionId, cardIndex);
143
+ }, 150);
144
+ } else {
145
+ showSection(sectionId);
146
+ scrollToCommentThread(sectionId, cardIndex);
147
+ }
148
+ }
149
+
150
+ function scrollToCommentThread(sectionId, cardIndex) {
129
151
  setTimeout(() => {
130
152
  const thread = document.getElementById('comment-thread-' + sectionId + '-' + cardIndex);
131
153
  if (thread && !thread.classList.contains('visible')) {
@@ -215,6 +215,18 @@
215
215
  font-size: 0.65rem;
216
216
  }
217
217
 
218
+ /* Review save button */
219
+ .btn-review {
220
+ background: var(--success);
221
+ color: #fff;
222
+ border-color: var(--success);
223
+ font-weight: 600;
224
+ }
225
+ .btn-review:hover {
226
+ background: #16a34a;
227
+ border-color: #16a34a;
228
+ }
229
+
218
230
  @media (max-width: 768px) {
219
231
  .body.review-open {
220
232
  grid-template-columns: 1fr;