@atlashub/smartstack-cli 4.18.0 → 4.19.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 (160) hide show
  1. package/package.json +1 -1
  2. package/templates/agents/ba-reader.md +86 -80
  3. package/templates/agents/ba-writer.md +318 -415
  4. package/templates/agents/docs-context-reader.md +3 -3
  5. package/templates/mcp-scaffolding/frontend/nav-routes.ts.hbs +133 -0
  6. package/templates/mcp-scaffolding/frontend/routes.tsx.hbs +126 -0
  7. package/templates/skills/apex/SKILL.md +29 -16
  8. package/templates/skills/apex/_shared.md +62 -9
  9. package/templates/skills/apex/references/analysis-methods.md +8 -6
  10. package/templates/skills/apex/references/challenge-questions.md +5 -5
  11. package/templates/skills/apex/references/core-seed-data.md +68 -45
  12. package/templates/skills/apex/references/frontend-route-wiring-app-tsx.md +26 -21
  13. package/templates/skills/apex/references/parallel-execution.md +156 -0
  14. package/templates/skills/apex/references/person-extension-pattern.md +12 -12
  15. package/templates/skills/apex/references/post-checks.md +1748 -1726
  16. package/templates/skills/apex/references/smartstack-api.md +63 -57
  17. package/templates/skills/apex/references/smartstack-frontend-compliance.md +594 -0
  18. package/templates/skills/apex/references/smartstack-frontend.md +1246 -1842
  19. package/templates/skills/apex/references/smartstack-layers.md +98 -145
  20. package/templates/skills/apex/steps/step-00-init.md +30 -6
  21. package/templates/skills/apex/steps/step-01-analyze.md +27 -23
  22. package/templates/skills/apex/steps/step-02-plan.md +12 -12
  23. package/templates/skills/apex/steps/step-03-execute.md +198 -143
  24. package/templates/skills/apex/steps/step-04-examine.md +24 -93
  25. package/templates/skills/apex/steps/step-05-deep-review.md +16 -16
  26. package/templates/skills/apex/steps/step-06-resolve.md +9 -9
  27. package/templates/skills/apex/steps/step-07-tests.md +3 -1
  28. package/templates/skills/apex/steps/step-08-run-tests.md +1 -1
  29. package/templates/skills/business-analyse/SKILL.md +182 -301
  30. package/templates/skills/business-analyse/_shared.md +119 -336
  31. package/templates/skills/business-analyse/html/ba-interactive.html +703 -82
  32. package/templates/skills/business-analyse/html/build-html.js +41 -3
  33. package/templates/skills/business-analyse/html/src/partials/cadrage-context.html +34 -0
  34. package/templates/skills/business-analyse/html/src/partials/cadrage-risks.html +48 -0
  35. package/templates/skills/business-analyse/html/src/partials/cadrage-scope.html +49 -0
  36. package/templates/skills/business-analyse/html/src/partials/cadrage-stakeholders.html +55 -0
  37. package/templates/skills/business-analyse/html/src/partials/cadrage-success.html +34 -0
  38. package/templates/skills/business-analyse/html/src/partials/consol-datamodel.html +8 -0
  39. package/templates/skills/business-analyse/html/src/partials/consol-flows.html +29 -0
  40. package/templates/skills/business-analyse/html/src/partials/consol-interactions.html +8 -0
  41. package/templates/skills/business-analyse/html/src/partials/consol-permissions.html +8 -0
  42. package/templates/skills/business-analyse/html/src/partials/decomp-dependencies.html +38 -0
  43. package/templates/skills/business-analyse/html/src/partials/decomp-modules.html +51 -0
  44. package/templates/skills/business-analyse/html/src/partials/handoff-summary.html +24 -0
  45. package/templates/skills/business-analyse/html/src/partials/module-spec-container.html +4 -0
  46. package/templates/skills/business-analyse/html/src/scripts/01-data-init.js +17 -1
  47. package/templates/skills/business-analyse/html/src/scripts/02-navigation.js +31 -5
  48. package/templates/skills/business-analyse/html/src/scripts/05-render-specs.js +100 -63
  49. package/templates/skills/business-analyse/html/src/scripts/06-render-mockups.js +372 -0
  50. package/templates/skills/business-analyse/html/src/scripts/10-comments.js +41 -13
  51. package/templates/skills/business-analyse/html/src/styles/09-mockups-html.css +136 -0
  52. package/templates/skills/business-analyse/patterns/suggestion-catalog.md +7 -5
  53. package/templates/skills/business-analyse/questionnaire/02-stakeholders-scope.md +142 -0
  54. package/templates/skills/business-analyse/questionnaire/03-data-ui.md +94 -0
  55. package/templates/skills/business-analyse/questionnaire/04-risks-metrics.md +150 -0
  56. package/templates/skills/business-analyse/questionnaire/05-cross-module.md +69 -0
  57. package/templates/skills/business-analyse/questionnaire.md +23 -280
  58. package/templates/skills/business-analyse/react/application-viewer.md +2 -2
  59. package/templates/skills/business-analyse/react/components.md +4 -4
  60. package/templates/skills/business-analyse/react/i18n-template.md +1 -1
  61. package/templates/skills/business-analyse/react/schema.md +14 -14
  62. package/templates/skills/business-analyse/references/acceptance-criteria.md +21 -21
  63. package/templates/skills/business-analyse/references/analysis-semantic-checks.md +3 -3
  64. package/templates/skills/business-analyse/references/compilation-structure-cards.md +1 -1
  65. package/templates/skills/business-analyse/references/consolidation-structural-checks.md +5 -5
  66. package/templates/skills/business-analyse/references/deploy-data-build.md +12 -11
  67. package/templates/skills/business-analyse/references/deploy-modes.md +10 -10
  68. package/templates/skills/business-analyse/references/detection-strategies.md +6 -6
  69. package/templates/skills/business-analyse/references/html-data-mapping.md +15 -15
  70. package/templates/skills/business-analyse/references/naming-conventions.md +4 -4
  71. package/templates/skills/business-analyse/references/review-data-mapping.md +29 -29
  72. package/templates/skills/business-analyse/references/robustness-checks.md +36 -36
  73. package/templates/skills/business-analyse/references/spec-auto-inference.md +2 -2
  74. package/templates/skills/business-analyse/references/ui-dashboard-spec.md +1 -1
  75. package/templates/skills/business-analyse/references/ui-resource-cards.md +1 -1
  76. package/templates/skills/business-analyse/references/validation-checklist.md +3 -3
  77. package/templates/skills/business-analyse/references/wireframe-svg-style-guide.md +2 -2
  78. package/templates/skills/business-analyse/schemas/application-schema.json +8 -8
  79. package/templates/skills/business-analyse/schemas/feature-schema.json +3 -3
  80. package/templates/skills/business-analyse/schemas/index-schema.json +47 -0
  81. package/templates/skills/business-analyse/schemas/project-schema.json +6 -6
  82. package/templates/skills/business-analyse/schemas/sections/analysis-schema.json +1 -1
  83. package/templates/skills/business-analyse/schemas/sections/handoff-schema.json +5 -3
  84. package/templates/skills/business-analyse/schemas/sections/metadata-schema.json +4 -4
  85. package/templates/skills/business-analyse/schemas/sections/specification-schema.json +1 -1
  86. package/templates/skills/business-analyse/schemas/shared/common-defs.json +4 -4
  87. package/templates/skills/business-analyse/steps/step-00-init.md +68 -77
  88. package/templates/skills/business-analyse/steps/step-01-cadrage.md +54 -180
  89. package/templates/skills/business-analyse/steps/step-02-structure.md +175 -0
  90. package/templates/skills/business-analyse/steps/step-03-specify.md +198 -0
  91. package/templates/skills/business-analyse/steps/step-04-consolidate.md +478 -0
  92. package/templates/skills/business-analyse/steps/step-05-deploy.md +220 -0
  93. package/templates/skills/business-analyse/steps/step-06-review.md +51 -69
  94. package/templates/skills/business-analyse/templates/tpl-frd.md +1 -1
  95. package/templates/skills/business-analyse/templates/tpl-handoff.md +20 -17
  96. package/templates/skills/business-analyse/templates/tpl-launch-displays.md +2 -2
  97. package/templates/skills/business-analyse/templates-react.md +2 -2
  98. package/templates/skills/derive-prd/SKILL.md +92 -0
  99. package/templates/skills/derive-prd/references/acceptance-criteria.md +169 -0
  100. package/templates/skills/derive-prd/references/entity-domain-mapping.md +115 -0
  101. package/templates/skills/{business-analyse → derive-prd}/references/handoff-file-templates.md +131 -120
  102. package/templates/skills/{business-analyse → derive-prd}/references/handoff-mappings.md +95 -95
  103. package/templates/skills/{business-analyse → derive-prd}/references/handoff-seeddata-generation.md +312 -312
  104. package/templates/skills/{business-analyse → derive-prd}/references/prd-generation.md +262 -263
  105. package/templates/skills/derive-prd/references/readiness-scoring.md +104 -0
  106. package/templates/skills/derive-prd/schemas/handoff-schema.json +95 -0
  107. package/templates/skills/derive-prd/steps/step-00-validate.md +130 -0
  108. package/templates/skills/derive-prd/steps/step-01-transform.md +206 -0
  109. package/templates/skills/derive-prd/steps/step-02-export.md +181 -0
  110. package/templates/skills/{business-analyse → derive-prd}/templates/tpl-progress.md +172 -172
  111. package/templates/skills/ralph-loop/SKILL.md +2 -1
  112. package/templates/skills/ralph-loop/references/init-resume-recovery.md +1 -1
  113. package/templates/skills/ralph-loop/steps/step-01-task.md +2 -2
  114. package/templates/skills/apex/references/agent-teams-protocol.md +0 -203
  115. package/templates/skills/business-analyse/_architecture.md +0 -124
  116. package/templates/skills/business-analyse/_elicitation.md +0 -206
  117. package/templates/skills/business-analyse/_module-loop.md +0 -115
  118. package/templates/skills/business-analyse/_rules.md +0 -142
  119. package/templates/skills/business-analyse/_suggestions.md +0 -34
  120. package/templates/skills/business-analyse/questionnaire/00-application.md +0 -160
  121. package/templates/skills/business-analyse/questionnaire/00b-project.md +0 -85
  122. package/templates/skills/business-analyse/questionnaire/02-stakeholders.md +0 -189
  123. package/templates/skills/business-analyse/questionnaire/03-scope.md +0 -164
  124. package/templates/skills/business-analyse/questionnaire/04-data.md +0 -88
  125. package/templates/skills/business-analyse/questionnaire/05-integrations.md +0 -58
  126. package/templates/skills/business-analyse/questionnaire/06-security.md +0 -68
  127. package/templates/skills/business-analyse/questionnaire/07-ui.md +0 -76
  128. package/templates/skills/business-analyse/questionnaire/08-performance.md +0 -42
  129. package/templates/skills/business-analyse/questionnaire/09-constraints.md +0 -45
  130. package/templates/skills/business-analyse/questionnaire/10-documentation.md +0 -58
  131. package/templates/skills/business-analyse/questionnaire/11-data-lifecycle.md +0 -59
  132. package/templates/skills/business-analyse/questionnaire/12-migration.md +0 -58
  133. package/templates/skills/business-analyse/questionnaire/13-cross-module.md +0 -69
  134. package/templates/skills/business-analyse/questionnaire/14-risk-assumptions.md +0 -135
  135. package/templates/skills/business-analyse/questionnaire/15-success-metrics.md +0 -136
  136. package/templates/skills/business-analyse/references/agent-module-prompt.md +0 -366
  137. package/templates/skills/business-analyse/references/agent-pooling-best-practices.md +0 -557
  138. package/templates/skills/business-analyse/references/cache-warming-strategy.md +0 -566
  139. package/templates/skills/business-analyse/references/cadrage-challenge-patterns.md +0 -41
  140. package/templates/skills/business-analyse/references/cadrage-coverage-matrix.md +0 -74
  141. package/templates/skills/business-analyse/references/cadrage-pre-analysis.md +0 -115
  142. package/templates/skills/business-analyse/references/cadrage-shared-modules.md +0 -68
  143. package/templates/skills/business-analyse/references/cadrage-structure-cards.md +0 -85
  144. package/templates/skills/business-analyse/references/team-orchestration.md +0 -1093
  145. package/templates/skills/business-analyse/references/validate-incremental-html.md +0 -121
  146. package/templates/skills/business-analyse/steps/step-01b-applications.md +0 -419
  147. package/templates/skills/business-analyse/steps/step-02-decomposition.md +0 -387
  148. package/templates/skills/business-analyse/steps/step-03a-data.md +0 -16
  149. package/templates/skills/business-analyse/steps/step-03a1-setup.md +0 -486
  150. package/templates/skills/business-analyse/steps/step-03a2-analysis.md +0 -300
  151. package/templates/skills/business-analyse/steps/step-03b-ui.md +0 -405
  152. package/templates/skills/business-analyse/steps/step-03c-compile.md +0 -516
  153. package/templates/skills/business-analyse/steps/step-03d-validate.md +0 -691
  154. package/templates/skills/business-analyse/steps/step-04-consolidation.md +0 -17
  155. package/templates/skills/business-analyse/steps/step-04a-collect.md +0 -415
  156. package/templates/skills/business-analyse/steps/step-04b-analyze.md +0 -163
  157. package/templates/skills/business-analyse/steps/step-04c-decide.md +0 -186
  158. package/templates/skills/business-analyse/steps/step-05a-handoff.md +0 -937
  159. package/templates/skills/business-analyse/steps/step-05b-deploy.md +0 -522
  160. package/templates/skills/business-analyse/steps/step-05c-ralph-readiness.md +0 -703
@@ -1633,6 +1633,145 @@ body {
1633
1633
  box-shadow: -4px 0 20px rgba(0,0,0,0.3);
1634
1634
  }
1635
1635
  }
1636
+
1637
+
1638
+ /* --- 09-mockups-html.css --- */
1639
+ /* ============================================
1640
+ HTML MOCKUPS - Realistic component previews
1641
+ ============================================ */
1642
+
1643
+ /* Screen section container */
1644
+ .screen-section {
1645
+ margin-bottom: 2rem;
1646
+ }
1647
+
1648
+ .screen-section h3 {
1649
+ display: flex;
1650
+ align-items: center;
1651
+ gap: 0.4rem;
1652
+ }
1653
+
1654
+ /* Mockup frame enhancements for HTML mockups */
1655
+ .mockup-frame .mock-header {
1656
+ margin-bottom: 1rem;
1657
+ }
1658
+
1659
+ /* SmartTable inside mockups */
1660
+ .mockup-content .mock-table {
1661
+ border: 1px solid var(--border);
1662
+ border-radius: 6px;
1663
+ overflow: hidden;
1664
+ }
1665
+
1666
+ .mockup-content .mock-table th {
1667
+ background: var(--bg-hover);
1668
+ }
1669
+
1670
+ /* SmartForm tabs */
1671
+ .mockup-content .form-tab-bar {
1672
+ display: flex;
1673
+ gap: 0;
1674
+ border-bottom: 1px solid var(--border);
1675
+ margin-bottom: 1.5rem;
1676
+ }
1677
+
1678
+ .mockup-content .form-tab {
1679
+ padding: 0.5rem 1rem;
1680
+ font-size: 0.85rem;
1681
+ cursor: pointer;
1682
+ border-bottom: 2px solid transparent;
1683
+ color: var(--text-muted);
1684
+ }
1685
+
1686
+ .mockup-content .form-tab.active {
1687
+ border-bottom-color: var(--primary);
1688
+ color: var(--primary-light);
1689
+ }
1690
+
1691
+ /* SmartKanban columns */
1692
+ .mockup-content .kanban-board {
1693
+ display: flex;
1694
+ gap: 1rem;
1695
+ overflow-x: auto;
1696
+ padding-bottom: 0.5rem;
1697
+ }
1698
+
1699
+ .mockup-content .kanban-column {
1700
+ min-width: 200px;
1701
+ flex: 1;
1702
+ background: var(--bg-hover);
1703
+ border-radius: 8px;
1704
+ padding: 0.75rem;
1705
+ }
1706
+
1707
+ .mockup-content .kanban-column-header {
1708
+ font-weight: 600;
1709
+ font-size: 0.85rem;
1710
+ color: var(--text-bright);
1711
+ margin-bottom: 0.75rem;
1712
+ display: flex;
1713
+ justify-content: space-between;
1714
+ }
1715
+
1716
+ .mockup-content .kanban-card {
1717
+ background: var(--bg-card);
1718
+ border: 1px solid var(--border);
1719
+ border-radius: 6px;
1720
+ padding: 0.5rem;
1721
+ margin-bottom: 0.5rem;
1722
+ font-size: 0.8rem;
1723
+ }
1724
+
1725
+ /* SmartDashboard KPI */
1726
+ .mockup-content .mock-kpi-grid {
1727
+ margin-bottom: 1.5rem;
1728
+ }
1729
+
1730
+ /* SmartFilter pills */
1731
+ .mockup-content .filter-pill {
1732
+ padding: 0.3rem 0.7rem;
1733
+ border-radius: 16px;
1734
+ font-size: 0.8rem;
1735
+ cursor: pointer;
1736
+ transition: all var(--transition-fast);
1737
+ }
1738
+
1739
+ .mockup-content .filter-pill:hover {
1740
+ background: var(--bg-hover);
1741
+ border-color: var(--primary);
1742
+ }
1743
+
1744
+ .mockup-content .filter-pill.active {
1745
+ background: var(--primary);
1746
+ color: #fff;
1747
+ border-color: var(--primary);
1748
+ }
1749
+
1750
+ /* Subtable in forms */
1751
+ .mockup-content .subtable-container {
1752
+ margin: 1rem 0;
1753
+ border: 1px solid var(--border);
1754
+ border-radius: 8px;
1755
+ overflow: hidden;
1756
+ }
1757
+
1758
+ .mockup-content .subtable-header {
1759
+ display: flex;
1760
+ justify-content: space-between;
1761
+ align-items: center;
1762
+ padding: 0.5rem 0.75rem;
1763
+ background: var(--bg-hover);
1764
+ }
1765
+
1766
+ /* Permission annotation in mockup toolbar */
1767
+ .mockup-toolbar .permission-badge {
1768
+ margin-left: auto;
1769
+ font-size: 0.65rem;
1770
+ color: var(--text-muted);
1771
+ background: var(--bg-dark);
1772
+ padding: 0.15rem 0.5rem;
1773
+ border-radius: 4px;
1774
+ }
1636
1775
 
1637
1776
  </style>
1638
1777
  </head>
@@ -2147,6 +2286,9 @@ let data = {{FEATURE_DATA}};
2147
2286
  const ORIGINAL_DATA = JSON.parse(JSON.stringify(data));
2148
2287
  const EMBEDDED_ARTIFACTS = {{EMBEDDED_ARTIFACTS}};
2149
2288
 
2289
+ // Initialize applications array (multi-app support)
2290
+ data.applications = data.applications || [];
2291
+
2150
2292
  // Initialize optional data structures
2151
2293
  data.wireframeComments = data.wireframeComments || {};
2152
2294
  data.specComments = data.specComments || {};
@@ -2173,7 +2315,7 @@ data.cadrage.context = data.cadrage.context || { problem: '', trigger: '', curre
2173
2315
  // Initialize comments array
2174
2316
  data.comments = data.comments || [];
2175
2317
 
2176
- // Defensive mapping: globalScope (feature.json format) -> scope (HTML format)
2318
+ // Defensive mapping: globalScope (JSON format) -> scope (HTML format)
2177
2319
  // Handles cases where FEATURE_DATA contains raw globalScope instead of pre-mapped scope
2178
2320
  if (!data.cadrage.scope || (!data.cadrage.scope.vital?.length && data.cadrage.globalScope)) {
2179
2321
  const gs = data.cadrage.globalScope || {};
@@ -2186,6 +2328,14 @@ if (!data.cadrage.scope || (!data.cadrage.scope.vital?.length && data.cadrage.gl
2186
2328
  }
2187
2329
  data.cadrage.scope = data.cadrage.scope || { vital: [], important: [], optional: [], excluded: [] };
2188
2330
 
2331
+ // Backward compat: mustHave/shouldHave/couldHave/wontHave → vital/important/optional/excluded
2332
+ if (data.cadrage.scope.mustHave && !data.cadrage.scope.vital?.length) {
2333
+ data.cadrage.scope.vital = data.cadrage.scope.mustHave;
2334
+ data.cadrage.scope.important = data.cadrage.scope.shouldHave || data.cadrage.scope.important || [];
2335
+ data.cadrage.scope.optional = data.cadrage.scope.couldHave || data.cadrage.scope.optional || [];
2336
+ data.cadrage.scope.excluded = data.cadrage.scope.wontHave || data.cadrage.scope.excluded || [];
2337
+ }
2338
+
2189
2339
  // Defensive init: moduleSpecs (may be missing if LLM didn't follow mapping)
2190
2340
  data.moduleSpecs = data.moduleSpecs || {};
2191
2341
  // Ensure ALL modules have a moduleSpecs entry (prevents missing schemas)
@@ -2195,6 +2345,11 @@ data.moduleSpecs = data.moduleSpecs || {};
2195
2345
  }
2196
2346
  // Ensure anticipatedSections array exists
2197
2347
  m.anticipatedSections = m.anticipatedSections || [];
2348
+ m.applicationCode = m.applicationCode || '';
2349
+ // Initialize screens array for interface specs
2350
+ if (!data.moduleSpecs[m.code].screens) {
2351
+ data.moduleSpecs[m.code].screens = [];
2352
+ }
2198
2353
  });
2199
2354
 
2200
2355
  // Vibe coding mode: hide non-relevant sections
@@ -2345,12 +2500,38 @@ function buildNavTree() {
2345
2500
  const nav = document.getElementById('sidebarNav');
2346
2501
  if (!nav) return;
2347
2502
 
2348
- nav.innerHTML =
2349
- renderNavGroup('cadrage', 'Cadrage', buildCadrageItems()) +
2350
- renderNavGroup('modules', 'Modules (' + data.modules.length + ')', buildModuleItems()) +
2351
- renderNavGroup('consolidation', 'Consolidation', buildConsolidationItems()) +
2352
- renderNavGroup('synthese', 'Synthese', buildSyntheseItems());
2503
+ var html = renderNavGroup('cadrage', 'Cadrage', buildCadrageItems());
2353
2504
 
2505
+ // Multi-app: group modules by application
2506
+ if (data.applications && data.applications.length > 1) {
2507
+ data.applications.forEach(function(app) {
2508
+ var appModules = data.modules.filter(function(m) { return m.applicationCode === app.code; });
2509
+ var appModuleItems = '';
2510
+ appModules.forEach(function(mod) {
2511
+ appModuleItems += renderModuleNavItem(mod);
2512
+ });
2513
+ // Global views at bottom of each app group
2514
+ appModuleItems += renderNavItem('decomp-modules', 'Vue d\'ensemble', data.modules.length);
2515
+ html += renderNavGroup('app-' + app.code, app.name || app.code, appModuleItems);
2516
+ });
2517
+ // Modules without applicationCode
2518
+ var orphanModules = data.modules.filter(function(m) { return !m.applicationCode; });
2519
+ if (orphanModules.length > 0) {
2520
+ var orphanItems = '';
2521
+ orphanModules.forEach(function(mod) {
2522
+ orphanItems += renderModuleNavItem(mod);
2523
+ });
2524
+ html += renderNavGroup('modules-other', 'Autres modules (' + orphanModules.length + ')', orphanItems);
2525
+ }
2526
+ html += renderNavItem('decomp-dependencies', 'Dependances', data.dependencies.length);
2527
+ } else {
2528
+ html += renderNavGroup('modules', 'Modules (' + data.modules.length + ')', buildModuleItems());
2529
+ }
2530
+
2531
+ html += renderNavGroup('consolidation', 'Consolidation', buildConsolidationItems());
2532
+ html += renderNavGroup('synthese', 'Synthese', buildSyntheseItems());
2533
+
2534
+ nav.innerHTML = html;
2354
2535
  restoreNavState();
2355
2536
  highlightActiveNavItem();
2356
2537
  }
@@ -3247,7 +3428,10 @@ function renderEntity(code, ent, index) {
3247
3428
  <tbody>
3248
3429
  ${ent.attributes.map(a => `<tr><td style="font-weight:500;color:var(--text-bright);">${a.name}</td><td>${a.description || ''}</td></tr>`).join('')}
3249
3430
  </tbody>
3250
- </table>` : ''}
3431
+ </table>
3432
+ <div style="padding:0.3rem 0.75rem;">
3433
+ <button class="add-btn" style="font-size:0.75rem;padding:0.4rem;" onclick="addEntityAttribute('${code}',${index})">+ Ajouter un attribut</button>
3434
+ </div>` : ''}
3251
3435
  ${(ent.relationships || []).length > 0 ? `
3252
3436
  <div style="padding:0.5rem 0.75rem;font-size:0.8rem;color:var(--text-muted);border-top:1px solid var(--border);">
3253
3437
  Relations : ${ent.relationships.map(r => `<span style="color:var(--accent);">${r}</span>`).join(', ')}
@@ -3289,6 +3473,19 @@ function removeEntity(code, index) {
3289
3473
  autoSave();
3290
3474
  }
3291
3475
 
3476
+ function addEntityAttribute(code, entityIndex) {
3477
+ var attrName = prompt('Nom de l\'attribut :');
3478
+ if (!attrName) return;
3479
+ var attrDesc = prompt('Description (optionnel) :') || '';
3480
+ if (!data.moduleSpecs[code]?.entities?.[entityIndex]) return;
3481
+ if (!data.moduleSpecs[code].entities[entityIndex].attributes) {
3482
+ data.moduleSpecs[code].entities[entityIndex].attributes = [];
3483
+ }
3484
+ data.moduleSpecs[code].entities[entityIndex].attributes.push({ name: attrName, description: attrDesc });
3485
+ renderAllModuleSpecs();
3486
+ autoSave();
3487
+ }
3488
+
3292
3489
  function getSpecComment(code, type, index) {
3293
3490
  const key = code + '.' + type + '.' + index;
3294
3491
  return data.specComments[key] || '';
@@ -3311,8 +3508,25 @@ function updateWireframeComment(code, screen, value) {
3311
3508
  }
3312
3509
 
3313
3510
  function renderModuleMockups(code) {
3314
- const wireframes = EMBEDDED_ARTIFACTS?.wireframes?.[code] || [];
3511
+ var spec = data.moduleSpecs[code] || {};
3512
+ var screens = spec.screens || [];
3513
+ var wireframes = EMBEDDED_ARTIFACTS?.wireframes?.[code] || [];
3514
+
3515
+ // Priority 1: HTML mockups from screens[] specs
3516
+ if (screens.length > 0) {
3517
+ var html = '';
3518
+ if (typeof renderScreenMockups === 'function') {
3519
+ html = renderScreenMockups(code);
3520
+ }
3521
+ // Also show wireframes below if available
3522
+ if (wireframes.length > 0) {
3523
+ html += '<h3 style="color:var(--text-bright);font-size:1rem;margin:2rem 0 1rem;">Wireframes</h3>';
3524
+ html += wireframes.map(function(wf, i) { return renderWireframeMockup(code, wf, i); }).join('');
3525
+ }
3526
+ return html;
3527
+ }
3315
3528
 
3529
+ // Priority 2: Wireframes from EMBEDDED_ARTIFACTS
3316
3530
  if (wireframes.length === 0) {
3317
3531
  return `
3318
3532
  <div class="card" style="text-align:center;padding:2rem;color:var(--text-muted);">
@@ -3321,69 +3535,73 @@ function renderModuleMockups(code) {
3321
3535
  </div>`;
3322
3536
  }
3323
3537
 
3324
- return wireframes.map((wf, i) => {
3325
- const hasSvg = !!wf.svgContent;
3326
- const wireframeId = `wf-${code}-${i}`;
3538
+ return wireframes.map(function(wf, i) { return renderWireframeMockup(code, wf, i); }).join('');
3539
+ }
3327
3540
 
3328
- return `
3329
- <div class="mockup-frame" style="${i > 0 ? 'margin-top:1.5rem;' : ''}">
3330
- <div class="mockup-toolbar">
3331
- <div class="mockup-dot mockup-dot-red"></div>
3332
- <div class="mockup-dot mockup-dot-yellow"></div>
3333
- <div class="mockup-dot mockup-dot-green"></div>
3334
- <span class="mockup-title">${wf.screen || wf.section}</span>
3335
- ${hasSvg ? `
3336
- <div class="wireframe-toggle">
3337
- <button class="wireframe-toggle-btn active" data-target="${wireframeId}" data-view="svg" onclick="toggleWireframeView('${wireframeId}', 'svg')">SVG</button>
3338
- <button class="wireframe-toggle-btn" data-target="${wireframeId}" data-view="ascii" onclick="toggleWireframeView('${wireframeId}', 'ascii')">ASCII</button>
3339
- </div>` : ''}
3340
- </div>
3341
- <div class="mockup-content" id="${wireframeId}">
3342
- ${hasSvg
3343
- ? `<div class="svg-wireframe wireframe-view active" data-view="svg">${wf.svgContent}</div>
3344
- <pre class="ascii-wireframe wireframe-view" data-view="ascii" style="display:none;">${wf.content || ''}</pre>`
3345
- : (wf.format === 'ascii'
3346
- ? `<pre class="ascii-wireframe">${wf.content || ''}</pre>`
3347
- : `<div class="svg-wireframe">${wf.content || ''}</div>`)}
3348
- </div>
3349
- ${wf.description ? `
3350
- <div class="wireframe-description">
3351
- <strong>Description:</strong> ${wf.description}
3352
- </div>` : ''}
3353
- ${(wf.elements || []).length > 0 ? `
3354
- <div class="wireframe-metadata">
3355
- <div><strong>Elements:</strong> ${wf.elements.map(e => typeof e === 'string' ? e : (e.type || e.label || e.id || '')).filter(Boolean).join(', ')}</div>
3541
+ function renderWireframeMockup(code, wf, i) {
3542
+ const hasSvg = !!wf.svgContent;
3543
+ const wireframeId = `wf-${code}-${i}`;
3544
+
3545
+ return `
3546
+ <div class="mockup-frame" style="${i > 0 ? 'margin-top:1.5rem;' : ''}">
3547
+ <div class="mockup-toolbar">
3548
+ <div class="mockup-dot mockup-dot-red"></div>
3549
+ <div class="mockup-dot mockup-dot-yellow"></div>
3550
+ <div class="mockup-dot mockup-dot-green"></div>
3551
+ <span class="mockup-title">${wf.screen || wf.section}</span>
3552
+ ${hasSvg ? `
3553
+ <div class="wireframe-toggle">
3554
+ <button class="wireframe-toggle-btn active" data-target="${wireframeId}" data-view="svg" onclick="toggleWireframeView('${wireframeId}', 'svg')">SVG</button>
3555
+ <button class="wireframe-toggle-btn" data-target="${wireframeId}" data-view="ascii" onclick="toggleWireframeView('${wireframeId}', 'ascii')">ASCII</button>
3356
3556
  </div>` : ''}
3357
- ${(() => {
3358
- const cm = wf.componentMapping;
3359
- const mappings = Array.isArray(cm) ? cm
3360
- : (typeof cm === 'object' && cm !== null) ? Object.entries(cm).map(([k,v]) => ({wireframeElement: k, reactComponent: v}))
3361
- : [];
3362
- return mappings.length > 0 ? `
3363
- <details class="wireframe-details">
3364
- <summary>Mapping composants SmartStack</summary>
3365
- <table class="mapping-table">
3366
- <thead><tr><th>Element maquette</th><th>Composant React</th></tr></thead>
3367
- <tbody>
3368
- ${mappings.map(m =>
3369
- '<tr><td>' + (m.wireframeElement || '') + '</td><td><code>' + (m.reactComponent || '') + '</code></td></tr>'
3370
- ).join('')}
3371
- </tbody>
3372
- </table>
3373
- </details>` : '';
3374
- })()}
3375
- <div class="wireframe-comment">
3376
- <label style="font-size:0.8rem;color:var(--text-muted);display:block;margin-bottom:0.3rem;">Commentaire / Feedback :</label>
3377
- <textarea class="form-textarea"
3378
- data-module="${code}"
3379
- data-screen="${wf.screen}"
3380
- placeholder="Ajouter un commentaire sur cette maquette (ex: deplacer ce bouton, ajouter une colonne...)"
3381
- onblur="updateWireframeComment('${code}', '${wf.screen}', this.value)"
3382
- style="min-height:60px;font-size:0.85rem;resize:vertical;"
3383
- >${getWireframeComment(code, wf.screen)}</textarea>
3384
- </div>
3385
3557
  </div>
3386
- `}).join('');
3558
+ <div class="mockup-content" id="${wireframeId}">
3559
+ ${hasSvg
3560
+ ? `<div class="svg-wireframe wireframe-view active" data-view="svg">${wf.svgContent}</div>
3561
+ <pre class="ascii-wireframe wireframe-view" data-view="ascii" style="display:none;">${wf.content || ''}</pre>`
3562
+ : (wf.format === 'ascii'
3563
+ ? `<pre class="ascii-wireframe">${wf.content || ''}</pre>`
3564
+ : `<div class="svg-wireframe">${wf.content || ''}</div>`)}
3565
+ </div>
3566
+ ${wf.description ? `
3567
+ <div class="wireframe-description">
3568
+ <strong>Description:</strong> ${wf.description}
3569
+ </div>` : ''}
3570
+ ${(wf.elements || []).length > 0 ? `
3571
+ <div class="wireframe-metadata">
3572
+ <div><strong>Elements:</strong> ${wf.elements.map(e => typeof e === 'string' ? e : (e.type || e.label || e.id || '')).filter(Boolean).join(', ')}</div>
3573
+ </div>` : ''}
3574
+ ${(() => {
3575
+ const cm = wf.componentMapping;
3576
+ const mappings = Array.isArray(cm) ? cm
3577
+ : (typeof cm === 'object' && cm !== null) ? Object.entries(cm).map(([k,v]) => ({wireframeElement: k, reactComponent: v}))
3578
+ : [];
3579
+ return mappings.length > 0 ? `
3580
+ <details class="wireframe-details">
3581
+ <summary>Mapping composants SmartStack</summary>
3582
+ <table class="mapping-table">
3583
+ <thead><tr><th>Element maquette</th><th>Composant React</th></tr></thead>
3584
+ <tbody>
3585
+ ${mappings.map(m =>
3586
+ '<tr><td>' + (m.wireframeElement || '') + '</td><td><code>' + (m.reactComponent || '') + '</code></td></tr>'
3587
+ ).join('')}
3588
+ </tbody>
3589
+ </table>
3590
+ </details>` : '';
3591
+ })()}
3592
+ <div class="wireframe-comment">
3593
+ <label style="font-size:0.8rem;color:var(--text-muted);display:block;margin-bottom:0.3rem;">Commentaire / Feedback :</label>
3594
+ <textarea class="form-textarea"
3595
+ data-module="${code}"
3596
+ data-screen="${wf.screen}"
3597
+ placeholder="Ajouter un commentaire sur cette maquette (ex: deplacer ce bouton, ajouter une colonne...)"
3598
+ onblur="updateWireframeComment('${code}', '${wf.screen}', this.value)"
3599
+ style="min-height:60px;font-size:0.85rem;resize:vertical;"
3600
+ >${getWireframeComment(code, wf.screen)}</textarea>
3601
+ </div>
3602
+ </div>
3603
+ `;
3604
+ }
3387
3605
  }
3388
3606
 
3389
3607
  function toggleWireframeView(wireframeId, view) {
@@ -3407,7 +3625,7 @@ function toggleWireframeView(wireframeId, view) {
3407
3625
  }
3408
3626
 
3409
3627
  function getPermRoles() {
3410
- // Extract roles from actual permission data (handles English role names from feature.json)
3628
+ // Extract roles from actual permission data (handles English role names from JSON data)
3411
3629
  const rolesFromPerms = [];
3412
3630
  const seen = new Set();
3413
3631
  data.modules.forEach(m => {
@@ -3779,6 +3997,381 @@ function renderE2EFlows() {
3779
3997
  }
3780
3998
 
3781
3999
 
4000
+ /* --- 06-render-mockups.js --- */
4001
+ /* ============================================
4002
+ HTML MOCKUP RENDERER
4003
+ Generates realistic HTML mockups from screens[] specs
4004
+ ============================================ */
4005
+
4006
+ function renderScreenMockups(code) {
4007
+ var spec = data.moduleSpecs[code] || {};
4008
+ var screens = spec.screens || [];
4009
+
4010
+ if (screens.length === 0) return '';
4011
+
4012
+ return screens.map(function(screen, si) {
4013
+ var resources = screen.resources || [];
4014
+ return '<div class="screen-section" style="margin-bottom:2rem;">' +
4015
+ '<h3 style="color:var(--text-bright);font-size:1rem;margin-bottom:1rem;">' +
4016
+ '<span style="color:var(--accent);">&#9656;</span> ' + (screen.sectionLabel || screen.sectionCode) +
4017
+ '</h3>' +
4018
+ resources.map(function(res, ri) {
4019
+ return renderResourceMockup(code, screen.sectionCode, res, ri);
4020
+ }).join('') +
4021
+ '</div>';
4022
+ }).join('');
4023
+ }
4024
+
4025
+ function renderResourceMockup(code, sectionCode, res, index) {
4026
+ var mockupId = 'mockup-' + code + '-' + sectionCode + '-' + index;
4027
+ var html = '<div class="mockup-frame" style="margin-bottom:1.5rem;">';
4028
+
4029
+ // Browser chrome
4030
+ html += '<div class="mockup-toolbar">';
4031
+ html += '<div class="mockup-dot mockup-dot-red"></div>';
4032
+ html += '<div class="mockup-dot mockup-dot-yellow"></div>';
4033
+ html += '<div class="mockup-dot mockup-dot-green"></div>';
4034
+ html += '<span class="mockup-title">' + (res.label || res.code) + ' (' + res.type + ')</span>';
4035
+ if (res.permission) {
4036
+ html += '<span style="margin-left:auto;font-size:0.65rem;color:var(--text-muted);background:var(--bg-dark);padding:0.15rem 0.5rem;border-radius:4px;">' + res.permission + '</span>';
4037
+ }
4038
+ html += '</div>';
4039
+
4040
+ // Mockup content
4041
+ html += '<div class="mockup-content" id="' + mockupId + '">';
4042
+ switch (res.type) {
4043
+ case 'SmartTable':
4044
+ html += renderSmartTableMockup(res);
4045
+ break;
4046
+ case 'SmartForm':
4047
+ html += renderSmartFormMockup(res);
4048
+ break;
4049
+ case 'SmartCard':
4050
+ html += renderSmartCardMockup(res);
4051
+ break;
4052
+ case 'SmartKanban':
4053
+ html += renderSmartKanbanMockup(res);
4054
+ break;
4055
+ case 'SmartDashboard':
4056
+ html += renderSmartDashboardMockup(res);
4057
+ break;
4058
+ case 'SmartFilter':
4059
+ html += renderSmartFilterMockup(res);
4060
+ break;
4061
+ default:
4062
+ html += '<div style="padding:2rem;text-align:center;color:var(--text-muted);">Composant ' + res.type + ' - maquette non disponible</div>';
4063
+ }
4064
+ html += '</div>';
4065
+
4066
+ // Notes
4067
+ if (res.notes) {
4068
+ html += '<div style="padding:0.5rem 1rem;font-size:0.8rem;color:var(--text-muted);border-top:1px solid var(--border);background:var(--bg-input);">';
4069
+ html += '<strong>Notes:</strong> ' + res.notes;
4070
+ html += '</div>';
4071
+ }
4072
+
4073
+ // Comment area
4074
+ html += '<div class="wireframe-comment">';
4075
+ html += '<label style="font-size:0.8rem;color:var(--text-muted);display:block;margin-bottom:0.3rem;">Commentaire :</label>';
4076
+ html += '<textarea class="form-textarea" placeholder="Commentaire sur cette maquette..."';
4077
+ html += ' onblur="updateWireframeComment(\'' + code + '\', \'' + (res.code || sectionCode + '-' + index) + '\', this.value)"';
4078
+ html += ' style="min-height:50px;font-size:0.85rem;resize:vertical;">';
4079
+ html += getWireframeComment(code, res.code || sectionCode + '-' + index);
4080
+ html += '</textarea>';
4081
+ html += '</div>';
4082
+
4083
+ html += '</div>';
4084
+ return html;
4085
+ }
4086
+
4087
+ /* ---------- SmartTable ---------- */
4088
+ function renderSmartTableMockup(res) {
4089
+ var columns = res.columns || [];
4090
+ if (columns.length === 0) return '<div style="padding:2rem;text-align:center;color:var(--text-muted);">Table sans colonnes definies</div>';
4091
+
4092
+ var html = '';
4093
+
4094
+ // Header with actions
4095
+ html += '<div class="mock-header">';
4096
+ html += '<span class="mock-title">' + (res.label || 'Liste') + '</span>';
4097
+ html += '<div style="display:flex;gap:0.4rem;">';
4098
+ (res.actions || []).forEach(function(action) {
4099
+ var isPrimary = action === 'create' || action === 'export';
4100
+ html += '<span class="mock-btn' + (isPrimary ? '' : '" style="background:var(--bg-hover);color:var(--text)') + '">' + formatActionLabel(action) + '</span>';
4101
+ });
4102
+ html += '</div></div>';
4103
+
4104
+ // Filters
4105
+ if (res.filters && res.filters.length > 0) {
4106
+ html += '<div style="display:flex;gap:0.5rem;margin-bottom:1rem;">';
4107
+ res.filters.forEach(function(f) {
4108
+ html += '<span class="mock-input" style="width:auto;min-width:120px;font-size:0.8rem;color:var(--text-muted);">' + f + ' &#9662;</span>';
4109
+ });
4110
+ html += '<span class="mock-input" style="width:auto;min-width:200px;font-size:0.8rem;color:var(--text-muted);">Rechercher...</span>';
4111
+ html += '</div>';
4112
+ }
4113
+
4114
+ // Table
4115
+ html += '<table class="mock-table">';
4116
+ html += '<thead><tr>';
4117
+ columns.forEach(function(col) {
4118
+ html += '<th>' + (col.label || col.field);
4119
+ if (col.sortable) html += ' <span style="font-size:0.6rem;color:var(--text-muted);">&#9650;&#9660;</span>';
4120
+ html += '</th>';
4121
+ });
4122
+ html += '<th style="width:80px;">Actions</th>';
4123
+ html += '</tr></thead>';
4124
+
4125
+ // Sample rows
4126
+ html += '<tbody>';
4127
+ var sampleData = generateSampleRows(columns, 4);
4128
+ sampleData.forEach(function(row) {
4129
+ html += '<tr>';
4130
+ columns.forEach(function(col) {
4131
+ var val = row[col.field] || '';
4132
+ if (col.type === 'badge') {
4133
+ html += '<td><span class="mock-status mock-status-active">' + val + '</span></td>';
4134
+ } else if (col.type === 'lookup') {
4135
+ html += '<td style="color:var(--accent);">' + val + '</td>';
4136
+ } else {
4137
+ html += '<td>' + val + '</td>';
4138
+ }
4139
+ });
4140
+ html += '<td style="text-align:center;"><span style="cursor:pointer;color:var(--text-muted);">&#9998; &#128465;</span></td>';
4141
+ html += '</tr>';
4142
+ });
4143
+ html += '</tbody></table>';
4144
+
4145
+ // Pagination
4146
+ html += '<div style="display:flex;justify-content:space-between;align-items:center;padding:0.75rem 0;font-size:0.8rem;color:var(--text-muted);">';
4147
+ html += '<span>1-4 sur 24 resultats</span>';
4148
+ html += '<div style="display:flex;gap:0.3rem;">';
4149
+ html += '<span class="mock-btn" style="background:var(--primary);font-size:0.75rem;padding:0.2rem 0.5rem;">1</span>';
4150
+ html += '<span class="mock-btn" style="background:var(--bg-hover);font-size:0.75rem;padding:0.2rem 0.5rem;">2</span>';
4151
+ html += '<span class="mock-btn" style="background:var(--bg-hover);font-size:0.75rem;padding:0.2rem 0.5rem;">3</span>';
4152
+ html += '</div></div>';
4153
+
4154
+ return html;
4155
+ }
4156
+
4157
+ /* ---------- SmartForm ---------- */
4158
+ function renderSmartFormMockup(res) {
4159
+ var tabs = res.tabs || [];
4160
+ if (tabs.length === 0 && res.fields) {
4161
+ tabs = [{ label: 'Informations', fields: res.fields }];
4162
+ }
4163
+ if (tabs.length === 0) return '<div style="padding:2rem;text-align:center;color:var(--text-muted);">Formulaire sans champs definis</div>';
4164
+
4165
+ var html = '';
4166
+
4167
+ // Header
4168
+ html += '<div class="mock-header">';
4169
+ html += '<span class="mock-title">' + (res.label || 'Formulaire') + '</span>';
4170
+ html += '<div style="display:flex;gap:0.4rem;">';
4171
+ (res.actions || ['save', 'cancel']).forEach(function(action) {
4172
+ var isPrimary = action === 'save';
4173
+ html += '<span class="mock-btn' + (isPrimary ? '' : '" style="background:var(--bg-hover);color:var(--text)') + '">' + formatActionLabel(action) + '</span>';
4174
+ });
4175
+ html += '</div></div>';
4176
+
4177
+ // Tabs
4178
+ if (tabs.length > 1) {
4179
+ html += '<div style="display:flex;gap:0;border-bottom:1px solid var(--border);margin-bottom:1.5rem;">';
4180
+ tabs.forEach(function(tab, i) {
4181
+ html += '<span style="padding:0.5rem 1rem;font-size:0.85rem;cursor:pointer;border-bottom:2px solid ' + (i === 0 ? 'var(--primary)' : 'transparent') + ';color:' + (i === 0 ? 'var(--primary-light)' : 'var(--text-muted)') + ';">' + tab.label + '</span>';
4182
+ });
4183
+ html += '</div>';
4184
+ }
4185
+
4186
+ // Fields (first tab only)
4187
+ var fields = tabs[0].fields || [];
4188
+ var rows = [];
4189
+ for (var i = 0; i < fields.length; i += 2) {
4190
+ rows.push(fields.slice(i, i + 2));
4191
+ }
4192
+
4193
+ rows.forEach(function(row) {
4194
+ if (row.length === 1 && row[0].type === 'subtable') {
4195
+ html += renderSubtableMockup(row[0]);
4196
+ } else {
4197
+ html += '<div class="mock-form-row">';
4198
+ row.forEach(function(field) {
4199
+ html += '<div class="mock-form-group">';
4200
+ html += '<label class="mock-label">' + (field.label || field.field);
4201
+ if (field.required) html += ' <span style="color:var(--error);">*</span>';
4202
+ html += '</label>';
4203
+ html += renderFormFieldMockup(field);
4204
+ html += '</div>';
4205
+ });
4206
+ html += '</div>';
4207
+ }
4208
+ });
4209
+
4210
+ return html;
4211
+ }
4212
+
4213
+ function renderFormFieldMockup(field) {
4214
+ var val = generateSampleValue(field);
4215
+ switch (field.type) {
4216
+ case 'select':
4217
+ return '<span class="mock-input" style="display:block;">' + val + ' &#9662;</span>';
4218
+ case 'lookup':
4219
+ return '<span class="mock-input" style="display:block;color:var(--accent);">' + val + ' &#128269;</span>';
4220
+ case 'date':
4221
+ return '<span class="mock-input" style="display:block;">' + val + ' &#128197;</span>';
4222
+ case 'textarea':
4223
+ return '<span class="mock-input" style="display:block;min-height:60px;">' + val + '</span>';
4224
+ case 'checkbox':
4225
+ return '<div style="display:flex;align-items:center;gap:0.5rem;"><input type="checkbox" checked disabled style="width:16px;height:16px;"><span style="font-size:0.85rem;color:var(--text);">' + val + '</span></div>';
4226
+ default:
4227
+ return '<span class="mock-input" style="display:block;' + (field.readonly ? 'opacity:0.6;' : '') + '">' + val + '</span>';
4228
+ }
4229
+ }
4230
+
4231
+ function renderSubtableMockup(field) {
4232
+ var cols = field.columns || [];
4233
+ var entity = field.entity || 'Element';
4234
+ var html = '<div style="margin:1rem 0;border:1px solid var(--border);border-radius:8px;overflow:hidden;">';
4235
+ html += '<div style="display:flex;justify-content:space-between;align-items:center;padding:0.5rem 0.75rem;background:var(--bg-hover);">';
4236
+ html += '<span style="font-weight:500;color:var(--text-bright);font-size:0.85rem;">' + entity + '</span>';
4237
+ html += '<span class="mock-btn" style="font-size:0.75rem;padding:0.2rem 0.5rem;">+ Ajouter</span>';
4238
+ html += '</div>';
4239
+ html += '<table class="mock-table"><thead><tr>';
4240
+ cols.forEach(function(c) { html += '<th>' + c + '</th>'; });
4241
+ html += '</tr></thead><tbody>';
4242
+ html += '<tr>' + cols.map(function() { return '<td style="color:var(--text-muted);font-style:italic;">...</td>'; }).join('') + '</tr>';
4243
+ html += '</tbody></table></div>';
4244
+ return html;
4245
+ }
4246
+
4247
+ /* ---------- SmartCard ---------- */
4248
+ function renderSmartCardMockup(res) {
4249
+ var columns = res.columns || res.fields || [];
4250
+ var html = '<div class="mock-header"><span class="mock-title">' + (res.label || 'Cartes') + '</span></div>';
4251
+ html += '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:1rem;">';
4252
+
4253
+ for (var i = 0; i < 4; i++) {
4254
+ html += '<div style="background:var(--bg-hover);border:1px solid var(--border);border-radius:8px;padding:1rem;">';
4255
+ columns.forEach(function(col, ci) {
4256
+ var label = col.label || col.field || col;
4257
+ if (ci === 0) {
4258
+ html += '<div style="font-weight:600;color:var(--text-bright);margin-bottom:0.5rem;">' + label + ' #' + (i + 1) + '</div>';
4259
+ } else {
4260
+ html += '<div style="font-size:0.8rem;color:var(--text-muted);margin-bottom:0.25rem;">' + label + ': <span style="color:var(--text);">valeur</span></div>';
4261
+ }
4262
+ });
4263
+ html += '</div>';
4264
+ }
4265
+ html += '</div>';
4266
+ return html;
4267
+ }
4268
+
4269
+ /* ---------- SmartKanban ---------- */
4270
+ function renderSmartKanbanMockup(res) {
4271
+ var options = res.options || res.columns || ['A faire', 'En cours', 'Termine'];
4272
+ var html = '<div class="mock-header"><span class="mock-title">' + (res.label || 'Kanban') + '</span></div>';
4273
+ html += '<div style="display:flex;gap:1rem;overflow-x:auto;padding-bottom:0.5rem;">';
4274
+
4275
+ options.forEach(function(col, ci) {
4276
+ var colLabel = typeof col === 'string' ? col : (col.label || col.field || 'Colonne');
4277
+ html += '<div style="min-width:200px;flex:1;background:var(--bg-hover);border-radius:8px;padding:0.75rem;">';
4278
+ html += '<div style="font-weight:600;font-size:0.85rem;color:var(--text-bright);margin-bottom:0.75rem;display:flex;justify-content:space-between;">';
4279
+ html += colLabel + ' <span style="font-size:0.7rem;background:var(--bg-card);padding:0.1rem 0.4rem;border-radius:4px;color:var(--text-muted);">' + (3 - ci) + '</span>';
4280
+ html += '</div>';
4281
+ for (var j = 0; j < Math.max(1, 3 - ci); j++) {
4282
+ html += '<div style="background:var(--bg-card);border:1px solid var(--border);border-radius:6px;padding:0.5rem;margin-bottom:0.5rem;font-size:0.8rem;">';
4283
+ html += '<div style="color:var(--text-bright);font-weight:500;">Element ' + (j + 1) + '</div>';
4284
+ html += '<div style="color:var(--text-muted);font-size:0.7rem;margin-top:0.25rem;">Description...</div>';
4285
+ html += '</div>';
4286
+ }
4287
+ html += '</div>';
4288
+ });
4289
+ html += '</div>';
4290
+ return html;
4291
+ }
4292
+
4293
+ /* ---------- SmartDashboard ---------- */
4294
+ function renderSmartDashboardMockup(res) {
4295
+ var html = '<div class="mock-header"><span class="mock-title">' + (res.label || 'Tableau de bord') + '</span></div>';
4296
+
4297
+ // KPI cards
4298
+ html += '<div class="mock-kpi-grid">';
4299
+ var kpis = res.kpis || [
4300
+ { label: 'Total', value: '1,234' },
4301
+ { label: 'Actifs', value: '987' },
4302
+ { label: 'En attente', value: '156' },
4303
+ { label: 'Taux', value: '80%' }
4304
+ ];
4305
+ kpis.forEach(function(kpi) {
4306
+ html += '<div class="mock-kpi"><div class="mock-kpi-value">' + (kpi.value || '0') + '</div><div class="mock-kpi-label">' + (kpi.label || '') + '</div></div>';
4307
+ });
4308
+ html += '</div>';
4309
+
4310
+ // Chart placeholder
4311
+ html += '<div class="mock-chart-placeholder">Graphique</div>';
4312
+
4313
+ return html;
4314
+ }
4315
+
4316
+ /* ---------- SmartFilter ---------- */
4317
+ function renderSmartFilterMockup(res) {
4318
+ var options = res.options || [];
4319
+ var html = '<div style="display:flex;gap:0.4rem;flex-wrap:wrap;padding:0.5rem 0;">';
4320
+ html += '<span style="padding:0.3rem 0.7rem;border-radius:16px;font-size:0.8rem;background:var(--primary);color:#fff;cursor:pointer;">Tous</span>';
4321
+ options.forEach(function(opt) {
4322
+ html += '<span style="padding:0.3rem 0.7rem;border-radius:16px;font-size:0.8rem;background:var(--bg-hover);color:var(--text);border:1px solid var(--border);cursor:pointer;">' + opt + '</span>';
4323
+ });
4324
+ html += '</div>';
4325
+ return html;
4326
+ }
4327
+
4328
+ /* ---------- Helpers ---------- */
4329
+ function formatActionLabel(action) {
4330
+ var labels = {
4331
+ create: 'Nouveau', edit: 'Modifier', delete: 'Supprimer',
4332
+ export: 'Exporter', save: 'Enregistrer', cancel: 'Annuler',
4333
+ validate: 'Valider', archive: 'Archiver', print: 'Imprimer'
4334
+ };
4335
+ return labels[action] || action;
4336
+ }
4337
+
4338
+ function generateSampleRows(columns, count) {
4339
+ var rows = [];
4340
+ for (var i = 0; i < count; i++) {
4341
+ var row = {};
4342
+ columns.forEach(function(col) {
4343
+ row[col.field] = generateSampleValue(col, i);
4344
+ });
4345
+ rows.push(row);
4346
+ }
4347
+ return rows;
4348
+ }
4349
+
4350
+ function generateSampleValue(field, index) {
4351
+ var idx = (index || 0) + 1;
4352
+ var t = field.type || 'text';
4353
+ var f = field.field || '';
4354
+
4355
+ if (field.options && field.options.length > 0) {
4356
+ return field.options[idx % field.options.length];
4357
+ }
4358
+
4359
+ if (t === 'date') return '0' + idx + '/03/2026';
4360
+ if (t === 'badge' || t === 'select') return 'Actif';
4361
+ if (t === 'lookup') return f.replace(/Id$/, '') + ' #' + idx;
4362
+ if (t === 'number') return String(idx * 100);
4363
+ if (t === 'checkbox') return idx % 2 === 0 ? 'Oui' : 'Non';
4364
+
4365
+ if (f.toLowerCase().includes('code')) return 'CODE-' + String(idx).padStart(3, '0');
4366
+ if (f.toLowerCase().includes('name') || f.toLowerCase().includes('nom')) return 'Nom ' + idx;
4367
+ if (f.toLowerCase().includes('email')) return 'user' + idx + '@example.com';
4368
+ if (f.toLowerCase().includes('date')) return '0' + idx + '/03/2026';
4369
+ if (f.toLowerCase().includes('status') || f.toLowerCase().includes('statut')) return 'Actif';
4370
+
4371
+ return f + ' ' + idx;
4372
+ }
4373
+
4374
+
3782
4375
  /* --- 07-render-handoff.js --- */
3783
4376
  /* ============================================
3784
4377
  HANDOFF / SYNTHESE
@@ -4199,20 +4792,48 @@ function saveReviewJSON() {
4199
4792
  */
4200
4793
 
4201
4794
  function initInlineComments() {
4202
- // Add comment buttons under each card and uc-item (direct children of sections)
4203
- document.querySelectorAll('.section').forEach(section => {
4204
- const sectionId = section.id;
4205
- section.querySelectorAll(':scope > .card, :scope > .uc-item').forEach((card, index) => {
4206
- if (card.querySelector('.comment-btn-container')) return;
4207
- card.appendChild(createCommentUI(sectionId, index));
4208
- });
4795
+ // Cadrage sections: direct card children
4796
+ document.querySelectorAll('.section > .card, .section > .stakeholder-card, .section > .uc-item, .section > .risk-item').forEach(function(card) {
4797
+ if (card.dataset.commentInitialized) return;
4798
+ card.dataset.commentInitialized = 'true';
4799
+ var section = card.closest('.section');
4800
+ var sectionId = section ? section.id : 'unknown';
4801
+ var siblings = Array.from(section.querySelectorAll(':scope > .card, :scope > .stakeholder-card, :scope > .uc-item, :scope > .risk-item'));
4802
+ var index = siblings.indexOf(card);
4803
+ card.appendChild(createCommentUI(sectionId, index));
4804
+ });
4805
+
4806
+ // Module spec lists: nested items in ucList, brList, entList containers
4807
+ document.querySelectorAll('[id^="ucList-"] > .uc-item, [id^="brList-"] > div, [id^="entList-"] > .entity-block').forEach(function(item) {
4808
+ if (item.dataset.commentInitialized) return;
4809
+ item.dataset.commentInitialized = 'true';
4810
+ var list = item.parentElement;
4811
+ var listId = list.id;
4812
+ var siblings = Array.from(list.children);
4813
+ var index = siblings.indexOf(item);
4814
+ item.appendChild(createCommentUI(listId, index));
4209
4815
  });
4210
- // Second pass: nested list containers in module specs (ucList-*, brList-*, entList-*)
4211
- document.querySelectorAll('[id^="ucList-"], [id^="brList-"], [id^="entList-"]').forEach(list => {
4212
- const listId = list.id;
4213
- list.querySelectorAll(':scope > .uc-item, :scope > .entity-block, :scope > div').forEach((item, index) => {
4214
- if (item.querySelector('.comment-btn-container')) return;
4215
- item.appendChild(createCommentUI(listId, index));
4816
+
4817
+ // Stakeholder cards in grid
4818
+ document.querySelectorAll('.stakeholder-grid > .stakeholder-card').forEach(function(card) {
4819
+ if (card.dataset.commentInitialized) return;
4820
+ card.dataset.commentInitialized = 'true';
4821
+ var grid = card.parentElement;
4822
+ var section = card.closest('.section');
4823
+ var sectionId = section ? section.id : 'stakeholders';
4824
+ var siblings = Array.from(grid.children);
4825
+ var index = siblings.indexOf(card);
4826
+ card.appendChild(createCommentUI(sectionId, index));
4827
+ });
4828
+
4829
+ // Scope items
4830
+ ['scopeVital', 'scopeImportant', 'scopeOptional', 'scopeExcluded'].forEach(function(containerId) {
4831
+ var container = document.getElementById(containerId);
4832
+ if (!container) return;
4833
+ container.querySelectorAll('.uc-item').forEach(function(item, index) {
4834
+ if (item.dataset.commentInitialized) return;
4835
+ item.dataset.commentInitialized = 'true';
4836
+ item.appendChild(createCommentUI(containerId, index));
4216
4837
  });
4217
4838
  });
4218
4839
  }