@atlashub/smartstack-cli 4.41.0 → 4.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.documentation/apex.html +2 -2
- package/.documentation/business-analyse.html +26 -27
- package/.documentation/commands.html +6 -6
- package/dist/index.js +24 -13
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/templates/agents/ba-reader.md +2 -2
- package/templates/agents/ba-writer.md +44 -9
- package/templates/hooks/stop-hook.sh +6 -6
- package/templates/ralph/README.md +1 -1
- package/templates/scripts/setup-ralph-loop.sh +2 -2
- package/templates/skills/_resources/context-digest-template.md +1 -1
- package/templates/skills/_shared.md +13 -13
- package/templates/skills/apex/SKILL.md +14 -7
- package/templates/skills/apex/_shared.md +1 -1
- package/templates/skills/apex/references/challenge-questions.md +46 -13
- package/templates/skills/apex/references/core-seed-data.md +4 -4
- package/templates/skills/apex/references/error-classification.md +3 -3
- package/templates/skills/apex/references/smartstack-api.md +1 -1
- package/templates/skills/apex/references/smartstack-layers.md +1 -1
- package/templates/skills/apex/steps/step-00-init.md +46 -8
- package/templates/skills/apex/steps/step-01-analyze.md +1 -1
- package/templates/skills/apex/steps/step-02-plan.md +1 -1
- package/templates/skills/apex/steps/step-03-execute.md +1 -1
- package/templates/skills/business-analyse/SKILL.md +83 -22
- package/templates/skills/business-analyse/_shared.md +12 -9
- package/templates/skills/business-analyse/questionnaire/02-stakeholders-scope.md +24 -9
- package/templates/skills/business-analyse/questionnaire/03-data-ui.md +33 -0
- package/templates/skills/business-analyse/questionnaire/04-risks-metrics.md +1 -1
- package/templates/skills/business-analyse/react/components.md +1 -22
- package/templates/skills/business-analyse/react/schema.md +1 -1
- package/templates/skills/business-analyse/references/acceptance-criteria.md +3 -3
- package/templates/skills/business-analyse/references/consolidation-structural-checks.md +1 -1
- package/templates/skills/business-analyse/references/detection-strategies.md +2 -2
- package/templates/skills/business-analyse/references/entity-architecture-decision.md +1 -1
- package/templates/skills/business-analyse/references/naming-conventions.md +6 -6
- package/templates/skills/business-analyse/references/robustness-checks.md +4 -4
- package/templates/skills/business-analyse/references/spec-auto-inference.md +2 -2
- package/templates/skills/business-analyse/references/validation-checklist.md +3 -3
- package/templates/skills/business-analyse/schemas/feature-schema.json +1 -1
- package/templates/skills/business-analyse/schemas/sections/handoff-schema.json +2 -2
- package/templates/skills/business-analyse/schemas/sections/specification-schema.json +3 -2
- package/templates/skills/business-analyse/steps/step-00-init.md +15 -5
- package/templates/skills/business-analyse/steps/step-01-cadrage.md +15 -6
- package/templates/skills/business-analyse/steps/step-02-structure.md +17 -1
- package/templates/skills/business-analyse/steps/step-03-specify.md +136 -26
- package/templates/skills/business-analyse/steps/step-04-consolidate.md +44 -8
- package/templates/skills/business-analyse/templates/tpl-handoff.md +5 -5
- package/templates/skills/business-analyse/templates/tpl-launch-displays.md +4 -4
- package/templates/skills/business-analyse/templates-frd.md +4 -4
- package/templates/skills/{ba-design-ui → business-analyse-design}/SKILL.md +9 -9
- package/templates/skills/{ba-design-ui → business-analyse-design}/steps/step-01-screens.md +9 -0
- package/templates/skills/{ba-design-ui → business-analyse-design}/steps/step-03-navigation.md +9 -2
- package/templates/skills/business-analyse-develop/SKILL.md +248 -0
- package/templates/skills/{ralph-loop → business-analyse-develop}/references/category-completeness.md +1 -1
- package/templates/skills/{ralph-loop → business-analyse-develop}/references/init-resume-recovery.md +8 -8
- package/templates/skills/{ralph-loop → business-analyse-develop}/references/multi-module-queue.md +1 -1
- package/templates/skills/business-analyse-develop/references/quality-gates.md +70 -0
- package/templates/skills/{ralph-loop → business-analyse-develop}/references/task-transform-legacy.md +1 -1
- package/templates/skills/{ralph-loop → business-analyse-develop}/steps/step-00-init.md +20 -4
- package/templates/skills/{ralph-loop → business-analyse-develop}/steps/step-01-task.md +3 -2
- package/templates/skills/business-analyse-develop/steps/step-01-v4-execute.md +131 -0
- package/templates/skills/business-analyse-develop/steps/step-02-v4-verify.md +156 -0
- package/templates/skills/{ralph-loop → business-analyse-develop}/steps/step-04-check.md +1 -1
- package/templates/skills/{ralph-loop → business-analyse-develop}/steps/step-05-report.md +1 -1
- package/templates/skills/{derive-prd → business-analyse-handoff}/SKILL.md +7 -7
- package/templates/skills/{derive-prd → business-analyse-handoff}/references/acceptance-criteria.md +5 -5
- package/templates/skills/{derive-prd → business-analyse-handoff}/references/handoff-file-templates.md +1 -1
- package/templates/skills/{derive-prd → business-analyse-handoff}/references/handoff-mappings.md +1 -1
- package/templates/skills/{derive-prd → business-analyse-handoff}/references/handoff-seeddata-generation.md +2 -2
- package/templates/skills/{derive-prd → business-analyse-handoff}/references/prd-generation.md +14 -14
- package/templates/skills/{derive-prd → business-analyse-handoff}/schemas/handoff-schema.json +2 -2
- package/templates/skills/{derive-prd → business-analyse-handoff}/steps/step-00-validate.md +7 -7
- package/templates/skills/{derive-prd → business-analyse-handoff}/steps/step-01-transform.md +50 -11
- package/templates/skills/{derive-prd → business-analyse-handoff}/steps/step-02-export.md +36 -16
- package/templates/skills/{ba-generate-html → business-analyse-html}/SKILL.md +4 -4
- package/templates/skills/{ba-generate-html → business-analyse-html}/html/ba-interactive.html +714 -286
- package/templates/skills/{ba-generate-html → business-analyse-html}/html/build-html.js +25 -3
- package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/scripts/01-data-init.js +54 -0
- package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/scripts/02-navigation.js +102 -12
- package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/scripts/03-render-cadrage.js +8 -7
- package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/scripts/04-render-modules.js +7 -7
- package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/scripts/05-render-specs.js +188 -85
- package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/scripts/06-render-consolidation.js +15 -14
- package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/scripts/06-render-mockups.js +19 -19
- package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/scripts/07-render-handoff.js +24 -4
- package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/scripts/08-editing.js +6 -2
- package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/scripts/09-export.js +27 -57
- package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/scripts/10-comments.js +67 -45
- package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/scripts/11-review-panel.js +15 -13
- package/templates/skills/business-analyse-html/html/src/styles/02-layout.css +216 -0
- package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/styles/05-modules.css +36 -0
- package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/template.html +22 -12
- package/templates/skills/{ba-generate-html → business-analyse-html}/references/data-build.md +1 -1
- package/templates/skills/{ba-generate-html → business-analyse-html}/references/data-mapping.md +5 -1
- package/templates/skills/{ba-generate-html → business-analyse-html}/references/output-modes.md +7 -7
- package/templates/skills/{ba-generate-html → business-analyse-html}/steps/step-01-collect.md +25 -1
- package/templates/skills/{ba-generate-html → business-analyse-html}/steps/step-02-build-data.md +33 -5
- package/templates/skills/{ba-generate-html → business-analyse-html}/steps/step-03-render.md +2 -2
- package/templates/skills/{ba-generate-html → business-analyse-html}/steps/step-04-verify.md +2 -2
- package/templates/skills/{ba-review → business-analyse-review}/SKILL.md +11 -10
- package/templates/skills/{ba-review → business-analyse-review}/references/review-data-mapping.md +2 -2
- package/templates/skills/business-analyse-review/steps/step-00-init.md +107 -0
- package/templates/skills/{ba-review → business-analyse-review}/steps/step-01-apply.md +19 -11
- package/templates/skills/business-analyse-status/SKILL.md +118 -0
- package/templates/skills/documentation/SKILL.md +2 -2
- package/templates/skills/sketch/SKILL.md +172 -0
- package/templates/skills/sketch/references/domain-heuristics.md +116 -0
- package/templates/skills/ba-generate-html/html/src/styles/02-layout.css +0 -101
- package/templates/skills/ralph-loop/SKILL.md +0 -240
- /package/templates/skills/{ba-design-ui → business-analyse-design}/steps/step-02-wireframes.md +0 -0
- /package/templates/skills/{ralph-loop → business-analyse-develop}/references/category-rules.md +0 -0
- /package/templates/skills/{ralph-loop → business-analyse-develop}/references/compact-loop.md +0 -0
- /package/templates/skills/{ralph-loop → business-analyse-develop}/references/module-transition.md +0 -0
- /package/templates/skills/{ralph-loop → business-analyse-develop}/references/parallel-execution.md +0 -0
- /package/templates/skills/{ralph-loop → business-analyse-develop}/references/section-splitting.md +0 -0
- /package/templates/skills/{ralph-loop → business-analyse-develop}/references/team-orchestration.md +0 -0
- /package/templates/skills/{ralph-loop → business-analyse-develop}/steps/step-02-execute.md +0 -0
- /package/templates/skills/{ralph-loop → business-analyse-develop}/steps/step-03-commit.md +0 -0
- /package/templates/skills/{derive-prd → business-analyse-handoff}/references/entity-domain-mapping.md +0 -0
- /package/templates/skills/{derive-prd → business-analyse-handoff}/references/readiness-scoring.md +0 -0
- /package/templates/skills/{derive-prd → business-analyse-handoff}/templates/tpl-progress.md +0 -0
- /package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/partials/cadrage-context.html +0 -0
- /package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/partials/cadrage-scope.html +0 -0
- /package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/partials/cadrage-stakeholders.html +0 -0
- /package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/partials/cadrage-success.html +0 -0
- /package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/partials/consol-datamodel.html +0 -0
- /package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/partials/consol-flows.html +0 -0
- /package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/partials/consol-interactions.html +0 -0
- /package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/partials/consol-permissions.html +0 -0
- /package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/partials/decomp-dependencies.html +0 -0
- /package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/partials/decomp-modules.html +0 -0
- /package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/partials/handoff-summary.html +0 -0
- /package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/partials/module-spec-container.html +0 -0
- /package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/styles/01-variables.css +0 -0
- /package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/styles/03-navigation.css +0 -0
- /package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/styles/04-cards.css +0 -0
- /package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/styles/06-wireframes.css +0 -0
- /package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/styles/07-comments.css +0 -0
- /package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/styles/08-review-panel.css +0 -0
- /package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/styles/09-mockups-html.css +0 -0
- /package/templates/skills/{ba-generate-html → business-analyse-html}/references/wireframe-svg-style-guide.md +0 -0
|
@@ -27,23 +27,24 @@ function renderModuleSpecSection(mod) {
|
|
|
27
27
|
|
|
28
28
|
return `
|
|
29
29
|
<div class="section" id="module-spec-${code}" style="display:none;">
|
|
30
|
-
<h2 class="section-title">${mod.name}</h2>
|
|
31
|
-
<p class="section-subtitle">${mod.description || 'Spécification détaillée de ce domaine fonctionnel.'}</p>
|
|
32
|
-
|
|
33
|
-
<div class="tab-bar">
|
|
34
|
-
<button class="tab-btn active" onclick="switchTab('${code}', 'uc')">Cas d'utilisation</button>
|
|
35
|
-
<button class="tab-btn" onclick="switchTab('${code}', 'br')">Règles métier</button>
|
|
36
|
-
<button class="tab-btn" onclick="switchTab('${code}', 'ent')">Données</button>
|
|
37
|
-
<button class="tab-btn" onclick="switchTab('${code}', 'perm')">Droits d'accès</button>
|
|
38
|
-
<button class="tab-btn" onclick="switchTab('${code}', 'mock')">Maquettes</button>
|
|
39
|
-
<button class="tab-btn" onclick="switchTab('${code}', 'notes')">Notes</button>
|
|
40
|
-
<button class="tab-btn" onclick="switchTab('${code}', 'struct')">Structure</button>
|
|
30
|
+
<h2 class="section-title">${escapeHtml(mod.name)}</h2>
|
|
31
|
+
<p class="section-subtitle">${escapeHtml(mod.description || 'Spécification détaillée de ce domaine fonctionnel.')}</p>
|
|
32
|
+
|
|
33
|
+
<div class="tab-bar" role="tablist" aria-label="Spécifications du module ${escapeHtml(mod.name)}">
|
|
34
|
+
<button class="tab-btn active" role="tab" aria-selected="true" aria-controls="tab-${code}-uc" onclick="switchTab('${code}', 'uc')">Cas d'utilisation</button>
|
|
35
|
+
<button class="tab-btn" role="tab" aria-selected="false" aria-controls="tab-${code}-br" onclick="switchTab('${code}', 'br')">Règles métier</button>
|
|
36
|
+
<button class="tab-btn" role="tab" aria-selected="false" aria-controls="tab-${code}-ent" onclick="switchTab('${code}', 'ent')">Données</button>
|
|
37
|
+
<button class="tab-btn" role="tab" aria-selected="false" aria-controls="tab-${code}-perm" onclick="switchTab('${code}', 'perm')">Droits d'accès</button>
|
|
38
|
+
<button class="tab-btn" role="tab" aria-selected="false" aria-controls="tab-${code}-mock" onclick="switchTab('${code}', 'mock')">Maquettes</button>
|
|
39
|
+
<button class="tab-btn" role="tab" aria-selected="false" aria-controls="tab-${code}-notes" onclick="switchTab('${code}', 'notes')">Notes</button>
|
|
40
|
+
<button class="tab-btn" role="tab" aria-selected="false" aria-controls="tab-${code}-struct" onclick="switchTab('${code}', 'struct')">Structure</button>
|
|
41
41
|
</div>
|
|
42
42
|
|
|
43
43
|
<!-- TAB: Cas d'utilisation -->
|
|
44
|
-
<div class="tab-panel active" id="tab-${code}-uc">
|
|
44
|
+
<div class="tab-panel active" id="tab-${code}-uc" role="tabpanel" aria-hidden="false">
|
|
45
45
|
<p style="font-size:0.85rem;color:var(--text-muted);margin-bottom:1rem;">Décrivez ce que chaque type d'utilisateur peut faire dans ce domaine. Un cas d'utilisation = une action concrète.</p>
|
|
46
46
|
<div id="ucList-${code}" class="uc-list">
|
|
47
|
+
${hasHierarchicalSpecs(mod) ? renderSectionGroupedItems(mod, 'useCases', code, renderUseCase) : ''}
|
|
47
48
|
${spec.useCases.map((uc, i) => renderUseCase(code, uc, i)).join('')}
|
|
48
49
|
</div>
|
|
49
50
|
<button class="add-btn" onclick="toggleForm('addUcForm-${code}')">+ Ajouter un cas d'utilisation</button>
|
|
@@ -73,9 +74,10 @@ function renderModuleSpecSection(mod) {
|
|
|
73
74
|
</div>
|
|
74
75
|
|
|
75
76
|
<!-- TAB: Règles métier -->
|
|
76
|
-
<div class="tab-panel" id="tab-${code}-br">
|
|
77
|
+
<div class="tab-panel" id="tab-${code}-br" role="tabpanel" aria-hidden="true">
|
|
77
78
|
<p style="font-size:0.85rem;color:var(--text-muted);margin-bottom:1rem;">Les règles que le système doit respecter. Formulez-les sous forme de conditions : "Si... alors... sinon..."</p>
|
|
78
79
|
<div id="brList-${code}">
|
|
80
|
+
${hasHierarchicalSpecs(mod) ? renderSectionGroupedItems(mod, 'businessRules', code, renderBusinessRule) : ''}
|
|
79
81
|
${spec.businessRules.map((br, i) => renderBusinessRule(code, br, i)).join('')}
|
|
80
82
|
</div>
|
|
81
83
|
<button class="add-btn" onclick="toggleForm('addBrForm-${code}')">+ Ajouter une règle métier</button>
|
|
@@ -111,7 +113,7 @@ function renderModuleSpecSection(mod) {
|
|
|
111
113
|
</div>
|
|
112
114
|
|
|
113
115
|
<!-- TAB: Données (Entités) -->
|
|
114
|
-
<div class="tab-panel" id="tab-${code}-ent">
|
|
116
|
+
<div class="tab-panel" id="tab-${code}-ent" role="tabpanel" aria-hidden="true">
|
|
115
117
|
<p style="font-size:0.85rem;color:var(--text-muted);margin-bottom:1rem;">Les types de données que ce domaine gère. Décrivez les informations importantes à enregistrer pour chaque type.</p>
|
|
116
118
|
<div id="entList-${code}">
|
|
117
119
|
${spec.entities.length > 0
|
|
@@ -148,7 +150,7 @@ function renderModuleSpecSection(mod) {
|
|
|
148
150
|
</div>
|
|
149
151
|
|
|
150
152
|
<!-- TAB: Droits d'accès -->
|
|
151
|
-
<div class="tab-panel" id="tab-${code}-perm">
|
|
153
|
+
<div class="tab-panel" id="tab-${code}-perm" role="tabpanel" aria-hidden="true">
|
|
152
154
|
<p style="font-size:0.85rem;color:var(--text-muted);margin-bottom:1rem;">Définissez qui peut faire quoi dans ce domaine. Cochez les actions autorisées pour chaque profil.</p>
|
|
153
155
|
<div id="permGrid-${code}">
|
|
154
156
|
${renderPermissionGrid(code)}
|
|
@@ -182,7 +184,7 @@ function renderModuleSpecSection(mod) {
|
|
|
182
184
|
</div>
|
|
183
185
|
|
|
184
186
|
<!-- TAB: Maquettes -->
|
|
185
|
-
<div class="tab-panel" id="tab-${code}-mock">
|
|
187
|
+
<div class="tab-panel" id="tab-${code}-mock" role="tabpanel" aria-hidden="true">
|
|
186
188
|
<p style="font-size:0.85rem;color:var(--text-muted);margin-bottom:1rem;">Maquettes validées lors de l'analyse. Ces wireframes montrent la structure exacte des écrans de ce domaine.</p>
|
|
187
189
|
<div id="mockupContainer-${code}">
|
|
188
190
|
${renderModuleMockups(code)}
|
|
@@ -190,7 +192,7 @@ function renderModuleSpecSection(mod) {
|
|
|
190
192
|
</div>
|
|
191
193
|
|
|
192
194
|
<!-- TAB: Notes -->
|
|
193
|
-
<div class="tab-panel" id="tab-${code}-notes">
|
|
195
|
+
<div class="tab-panel" id="tab-${code}-notes" role="tabpanel" aria-hidden="true">
|
|
194
196
|
<p style="font-size:0.85rem;color:var(--text-muted);margin-bottom:1rem;">Notes libres, questions en suspens, éléments à clarifier pour ce domaine.</p>
|
|
195
197
|
<div class="card">
|
|
196
198
|
<div class="editable" contenteditable="true" data-module-code="${code}" data-module-field="notes" data-placeholder="Notez ici tout ce qui concerne ce domaine : questions, précisions, contraintes particulières...">${spec.notes || ''}</div>
|
|
@@ -198,7 +200,7 @@ function renderModuleSpecSection(mod) {
|
|
|
198
200
|
</div>
|
|
199
201
|
|
|
200
202
|
<!-- TAB: Structure (sections/resources) -->
|
|
201
|
-
<div class="tab-panel" id="tab-${code}-struct">
|
|
203
|
+
<div class="tab-panel" id="tab-${code}-struct" role="tabpanel" aria-hidden="true">
|
|
202
204
|
<p style="font-size:0.85rem;color:var(--text-muted);margin-bottom:1rem;">Organisation des écrans et ressources de ce domaine. Structure hiérarchique : sections regroupant des ressources.</p>
|
|
203
205
|
<div id="structContainer-${code}">
|
|
204
206
|
${renderModuleStructure(code)}
|
|
@@ -212,19 +214,19 @@ function renderUseCase(code, uc, index) {
|
|
|
212
214
|
<div class="uc-item">
|
|
213
215
|
<div class="uc-header">
|
|
214
216
|
<span class="uc-id">UC-${String(index + 1).padStart(3, '0')}</span>
|
|
215
|
-
<span class="uc-title">${uc.name}</span>
|
|
217
|
+
<span class="uc-title">${escapeHtml(uc.name)}</span>
|
|
216
218
|
<div class="uc-actions">
|
|
217
219
|
<button class="btn btn-sm" onclick="removeUseCase('${code}',${index})">Supprimer</button>
|
|
218
220
|
</div>
|
|
219
221
|
</div>
|
|
220
|
-
<div class="uc-actors"><div class="uc-actor">${uc.actor}</div></div>
|
|
221
|
-
${uc.steps ? `<div class="uc-detail-label">Déroulement</div><div class="uc-detail">${uc.steps.replace(/\n/g, '<br>')}</div>` : ''}
|
|
222
|
-
${uc.alternative ? `<div class="uc-detail-label">En cas de problème</div><div class="uc-detail" style="color:var(--warning);">${uc.alternative}</div>` : ''}
|
|
222
|
+
<div class="uc-actors"><div class="uc-actor">${escapeHtml(uc.actor)}</div></div>
|
|
223
|
+
${uc.steps ? `<div class="uc-detail-label">Déroulement</div><div class="uc-detail">${escapeHtml(uc.steps).replace(/\n/g, '<br>')}</div>` : ''}
|
|
224
|
+
${uc.alternative ? `<div class="uc-detail-label">En cas de problème</div><div class="uc-detail" style="color:var(--warning);">${escapeHtml(uc.alternative)}</div>` : ''}
|
|
223
225
|
<div style="padding:0.5rem 0.75rem;border-top:1px solid var(--border);background:var(--bg-input);border-radius:0 0 8px 8px;">
|
|
224
226
|
<label style="font-size:0.75rem;color:var(--text-muted);display:block;margin-bottom:0.25rem;">Commentaire / Feedback :</label>
|
|
225
227
|
<textarea class="form-textarea" placeholder="Ajouter un commentaire sur ce cas d'utilisation..."
|
|
226
228
|
onblur="updateSpecComment('${code}','uc',${index},this.value)"
|
|
227
|
-
style="min-height:45px;font-size:0.8rem;resize:vertical;">${getSpecComment(code, 'uc', index)}</textarea>
|
|
229
|
+
style="min-height:45px;font-size:0.8rem;resize:vertical;">${escapeHtml(getSpecComment(code, 'uc', index))}</textarea>
|
|
228
230
|
</div>
|
|
229
231
|
</div>`;
|
|
230
232
|
}
|
|
@@ -247,6 +249,7 @@ function addUseCase(code) {
|
|
|
247
249
|
}
|
|
248
250
|
|
|
249
251
|
function removeUseCase(code, index) {
|
|
252
|
+
if (!confirm('Supprimer ce cas d\'utilisation ?')) return;
|
|
250
253
|
data.moduleSpecs[code].useCases.splice(index, 1);
|
|
251
254
|
renderAllModuleSpecs();
|
|
252
255
|
updateCounts();
|
|
@@ -259,18 +262,18 @@ function renderBusinessRule(code, br, index) {
|
|
|
259
262
|
return `
|
|
260
263
|
<div style="margin-bottom:0.5rem;">
|
|
261
264
|
<div class="br-item" style="margin-bottom:0;border-radius:8px 8px 0 0;">
|
|
262
|
-
<span class="br-category ${catColors[br.category] || 'br-cat-validation'}">${catLabels[br.category] || br.category}</span>
|
|
265
|
+
<span class="br-category ${catColors[br.category] || 'br-cat-validation'}">${escapeHtml(catLabels[br.category] || br.category)}</span>
|
|
263
266
|
<div class="br-text" style="flex:1;">
|
|
264
|
-
<div style="font-weight:600;color:var(--text-bright);margin-bottom:0.2rem;">${br.name}</div>
|
|
265
|
-
<div>${br.statement}</div>
|
|
266
|
-
${br.example ? `<div style="font-size:0.8rem;color:var(--text-muted);margin-top:0.25rem;font-style:italic;">Exemple : ${br.example}</div>` : ''}
|
|
267
|
+
<div style="font-weight:600;color:var(--text-bright);margin-bottom:0.2rem;">${escapeHtml(br.name)}</div>
|
|
268
|
+
<div>${escapeHtml(br.statement)}</div>
|
|
269
|
+
${br.example ? `<div style="font-size:0.8rem;color:var(--text-muted);margin-top:0.25rem;font-style:italic;">Exemple : ${escapeHtml(br.example)}</div>` : ''}
|
|
267
270
|
</div>
|
|
268
271
|
<button class="btn btn-sm" onclick="removeBusinessRule('${code}',${index})" style="opacity:0.5;flex-shrink:0;">✕</button>
|
|
269
272
|
</div>
|
|
270
273
|
<div style="padding:0.4rem 0.75rem 0.6rem;background:var(--bg-input);border:1px solid var(--border);border-top:0;border-radius:0 0 8px 8px;">
|
|
271
274
|
<textarea class="form-textarea" placeholder="Commentaire sur cette règle..."
|
|
272
275
|
onblur="updateSpecComment('${code}','br',${index},this.value)"
|
|
273
|
-
style="min-height:35px;font-size:0.8rem;resize:vertical;">${getSpecComment(code, 'br', index)}</textarea>
|
|
276
|
+
style="min-height:35px;font-size:0.8rem;resize:vertical;">${escapeHtml(getSpecComment(code, 'br', index))}</textarea>
|
|
274
277
|
</div>
|
|
275
278
|
</div>`;
|
|
276
279
|
}
|
|
@@ -292,18 +295,20 @@ function addBusinessRule(code) {
|
|
|
292
295
|
}
|
|
293
296
|
|
|
294
297
|
function removeBusinessRule(code, index) {
|
|
298
|
+
if (!confirm('Supprimer cette règle métier ?')) return;
|
|
295
299
|
data.moduleSpecs[code].businessRules.splice(index, 1);
|
|
296
300
|
renderAllModuleSpecs();
|
|
297
301
|
autoSave();
|
|
298
302
|
}
|
|
299
303
|
|
|
300
304
|
function renderEntity(code, ent, index) {
|
|
305
|
+
var attrFormId = 'addAttrForm-' + code + '-' + index;
|
|
301
306
|
return `
|
|
302
307
|
<div class="entity-block">
|
|
303
308
|
<div class="entity-header">
|
|
304
309
|
<div>
|
|
305
|
-
<div class="entity-name">${ent.name}</div>
|
|
306
|
-
<div class="entity-desc">${ent.description || ''}</div>
|
|
310
|
+
<div class="entity-name">${escapeHtml(ent.name)}</div>
|
|
311
|
+
<div class="entity-desc">${escapeHtml(ent.description || '')}</div>
|
|
307
312
|
</div>
|
|
308
313
|
<button class="btn btn-sm" onclick="removeEntity('${code}',${index})" style="opacity:0.5;">Supprimer</button>
|
|
309
314
|
</div>
|
|
@@ -311,21 +316,26 @@ function renderEntity(code, ent, index) {
|
|
|
311
316
|
<table class="attr-table">
|
|
312
317
|
<thead><tr><th>Information</th><th>Description</th></tr></thead>
|
|
313
318
|
<tbody>
|
|
314
|
-
${ent.attributes.map(a => `<tr><td style="font-weight:500;color:var(--text-bright);">${a.name}</td><td>${a.description || ''}</td></tr>`).join('')}
|
|
319
|
+
${ent.attributes.map(a => `<tr><td style="font-weight:500;color:var(--text-bright);">${escapeHtml(a.name)}</td><td>${escapeHtml(a.description || '')}</td></tr>`).join('')}
|
|
315
320
|
</tbody>
|
|
316
|
-
</table
|
|
321
|
+
</table>` : ''}
|
|
317
322
|
<div style="padding:0.3rem 0.75rem;">
|
|
318
|
-
<button class="add-btn" style="font-size:0.75rem;padding:0.4rem;" onclick="
|
|
319
|
-
|
|
323
|
+
<button class="add-btn" style="font-size:0.75rem;padding:0.4rem;" onclick="toggleForm('${attrFormId}')">+ Ajouter un attribut</button>
|
|
324
|
+
<div class="inline-form" id="${attrFormId}">
|
|
325
|
+
<div class="form-group"><label class="form-label">Nom de l'attribut</label><input type="text" class="form-input" id="attr-name-${code}-${index}" placeholder="Nom de l'attribut"></div>
|
|
326
|
+
<div class="form-group"><label class="form-label">Description (optionnel)</label><input type="text" class="form-input" id="attr-desc-${code}-${index}" placeholder="Description"></div>
|
|
327
|
+
<div class="form-actions"><button class="btn" onclick="toggleForm('${attrFormId}')">Annuler</button><button class="btn btn-primary" onclick="addEntityAttribute('${code}',${index})">Ajouter</button></div>
|
|
328
|
+
</div>
|
|
329
|
+
</div>
|
|
320
330
|
${(ent.relationships || []).length > 0 ? `
|
|
321
331
|
<div style="padding:0.5rem 0.75rem;font-size:0.8rem;color:var(--text-muted);border-top:1px solid var(--border);">
|
|
322
|
-
Relations : ${ent.relationships.map(r => `<span style="color:var(--accent);">${r}</span>`).join(', ')}
|
|
332
|
+
Relations : ${ent.relationships.map(r => `<span style="color:var(--accent);">${escapeHtml(typeof r === 'string' ? r : (r.target || ''))}</span>`).join(', ')}
|
|
323
333
|
</div>` : ''}
|
|
324
334
|
<div style="padding:0.4rem 0.75rem 0.6rem;background:var(--bg-input);border-top:1px solid var(--border);border-radius:0 0 8px 8px;">
|
|
325
335
|
<label style="font-size:0.75rem;color:var(--text-muted);display:block;margin-bottom:0.25rem;">Commentaire :</label>
|
|
326
336
|
<textarea class="form-textarea" placeholder="Commentaire sur cette entité..."
|
|
327
337
|
onblur="updateSpecComment('${code}','ent',${index},this.value)"
|
|
328
|
-
style="min-height:35px;font-size:0.8rem;resize:vertical;">${getSpecComment(code, 'ent', index)}</textarea>
|
|
338
|
+
style="min-height:35px;font-size:0.8rem;resize:vertical;">${escapeHtml(getSpecComment(code, 'ent', index))}</textarea>
|
|
329
339
|
</div>
|
|
330
340
|
</div>`;
|
|
331
341
|
}
|
|
@@ -353,20 +363,21 @@ function addEntity(code) {
|
|
|
353
363
|
}
|
|
354
364
|
|
|
355
365
|
function removeEntity(code, index) {
|
|
366
|
+
if (!confirm('Supprimer ce type de données ?')) return;
|
|
356
367
|
data.moduleSpecs[code].entities.splice(index, 1);
|
|
357
368
|
renderAllModuleSpecs();
|
|
358
369
|
autoSave();
|
|
359
370
|
}
|
|
360
371
|
|
|
361
372
|
function addEntityAttribute(code, entityIndex) {
|
|
362
|
-
var attrName =
|
|
363
|
-
|
|
364
|
-
|
|
373
|
+
var attrName = document.getElementById('attr-name-' + code + '-' + entityIndex);
|
|
374
|
+
var attrDesc = document.getElementById('attr-desc-' + code + '-' + entityIndex);
|
|
375
|
+
if (!attrName || !attrName.value.trim()) return;
|
|
365
376
|
if (!data.moduleSpecs[code]?.entities?.[entityIndex]) return;
|
|
366
377
|
if (!data.moduleSpecs[code].entities[entityIndex].attributes) {
|
|
367
378
|
data.moduleSpecs[code].entities[entityIndex].attributes = [];
|
|
368
379
|
}
|
|
369
|
-
data.moduleSpecs[code].entities[entityIndex].attributes.push({ name: attrName, description: attrDesc });
|
|
380
|
+
data.moduleSpecs[code].entities[entityIndex].attributes.push({ name: attrName.value.trim(), description: (attrDesc?.value || '').trim() });
|
|
370
381
|
renderAllModuleSpecs();
|
|
371
382
|
autoSave();
|
|
372
383
|
}
|
|
@@ -392,37 +403,6 @@ function updateWireframeComment(code, screen, value) {
|
|
|
392
403
|
autoSave();
|
|
393
404
|
}
|
|
394
405
|
|
|
395
|
-
function renderModuleMockups(code) {
|
|
396
|
-
var spec = data.moduleSpecs[code] || {};
|
|
397
|
-
var screens = spec.screens || [];
|
|
398
|
-
var wireframes = EMBEDDED_ARTIFACTS?.wireframes?.[code] || [];
|
|
399
|
-
|
|
400
|
-
// Priority 1: HTML mockups from screens[] specs
|
|
401
|
-
if (screens.length > 0) {
|
|
402
|
-
var html = '';
|
|
403
|
-
if (typeof renderScreenMockups === 'function') {
|
|
404
|
-
html = renderScreenMockups(code);
|
|
405
|
-
}
|
|
406
|
-
// Also show wireframes below if available
|
|
407
|
-
if (wireframes.length > 0) {
|
|
408
|
-
html += '<h3 style="color:var(--text-bright);font-size:1rem;margin:2rem 0 1rem;">Wireframes</h3>';
|
|
409
|
-
html += wireframes.map(function(wf, i) { return renderWireframeMockup(code, wf, i); }).join('');
|
|
410
|
-
}
|
|
411
|
-
return html;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// Priority 2: Wireframes from EMBEDDED_ARTIFACTS
|
|
415
|
-
if (wireframes.length === 0) {
|
|
416
|
-
return `
|
|
417
|
-
<div class="card" style="text-align:center;padding:2rem;color:var(--text-muted);">
|
|
418
|
-
<p>Aucune maquette disponible pour ce module.</p>
|
|
419
|
-
<p style="font-size:0.85rem;margin-top:0.5rem;">Les maquettes seront générées lors de la spécification détaillée.</p>
|
|
420
|
-
</div>`;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
return wireframes.map(function(wf, i) { return renderWireframeMockup(code, wf, i); }).join('');
|
|
424
|
-
}
|
|
425
|
-
|
|
426
406
|
function renderWireframeMockup(code, wf, i) {
|
|
427
407
|
const hasSvg = !!wf.svgContent;
|
|
428
408
|
const wireframeId = `wf-${code}-${i}`;
|
|
@@ -433,7 +413,7 @@ function renderWireframeMockup(code, wf, i) {
|
|
|
433
413
|
<div class="mockup-dot mockup-dot-red"></div>
|
|
434
414
|
<div class="mockup-dot mockup-dot-yellow"></div>
|
|
435
415
|
<div class="mockup-dot mockup-dot-green"></div>
|
|
436
|
-
<span class="mockup-title">${wf.screen || wf.section}</span>
|
|
416
|
+
<span class="mockup-title">${escapeHtml(wf.screen || wf.section)}</span>
|
|
437
417
|
${hasSvg ? `
|
|
438
418
|
<div class="wireframe-toggle">
|
|
439
419
|
<button class="wireframe-toggle-btn active" data-target="${wireframeId}" data-view="svg" onclick="toggleWireframeView('${wireframeId}', 'svg')">SVG</button>
|
|
@@ -450,11 +430,11 @@ function renderWireframeMockup(code, wf, i) {
|
|
|
450
430
|
</div>
|
|
451
431
|
${wf.description ? `
|
|
452
432
|
<div class="wireframe-description">
|
|
453
|
-
<strong>Description:</strong> ${wf.description}
|
|
433
|
+
<strong>Description:</strong> ${escapeHtml(wf.description)}
|
|
454
434
|
</div>` : ''}
|
|
455
435
|
${(wf.elements || []).length > 0 ? `
|
|
456
436
|
<div class="wireframe-metadata">
|
|
457
|
-
<div><strong>Elements:</strong> ${wf.elements.map(e => typeof e === 'string' ? e : (e.type || e.label || e.id || '')).filter(Boolean).join(', ')}</div>
|
|
437
|
+
<div><strong>Elements:</strong> ${wf.elements.map(e => escapeHtml(typeof e === 'string' ? e : (e.type || e.label || e.id || ''))).filter(Boolean).join(', ')}</div>
|
|
458
438
|
</div>` : ''}
|
|
459
439
|
${(() => {
|
|
460
440
|
const cm = wf.componentMapping;
|
|
@@ -468,7 +448,7 @@ function renderWireframeMockup(code, wf, i) {
|
|
|
468
448
|
<thead><tr><th>Élément maquette</th><th>Composant React</th></tr></thead>
|
|
469
449
|
<tbody>
|
|
470
450
|
${mappings.map(m =>
|
|
471
|
-
'<tr><td>' + (m.wireframeElement || '') + '</td><td><code>' + (m.reactComponent || '') + '</code></td></tr>'
|
|
451
|
+
'<tr><td>' + escapeHtml(m.wireframeElement || '') + '</td><td><code>' + escapeHtml(m.reactComponent || '') + '</code></td></tr>'
|
|
472
452
|
).join('')}
|
|
473
453
|
</tbody>
|
|
474
454
|
</table>
|
|
@@ -543,12 +523,12 @@ function renderPermissionGrid(code) {
|
|
|
543
523
|
<table class="mock-table" style="background:var(--bg-card);border-radius:8px;overflow:hidden;">
|
|
544
524
|
<thead><tr>
|
|
545
525
|
<th>Profil</th>
|
|
546
|
-
${actions.map((a, i) => `<th style="text-align:center;">${a}${i >= baseActionsCount ? ' <span onclick="removeCustomAction('+`'${code}','${a}'`+')" style="cursor:pointer;color:var(--danger);font-size:0.7rem;" title="Supprimer cette action">✕</span>' : ''}</th>`).join('')}
|
|
526
|
+
${actions.map((a, i) => `<th style="text-align:center;">${escapeHtml(a)}${i >= baseActionsCount ? ' <span onclick="removeCustomAction('+`'${code}','${a}'`+')" style="cursor:pointer;color:var(--danger);font-size:0.7rem;" title="Supprimer cette action">✕</span>' : ''}</th>`).join('')}
|
|
547
527
|
</tr></thead>
|
|
548
528
|
<tbody>
|
|
549
529
|
${roles.map((role, ri) => `
|
|
550
530
|
<tr>
|
|
551
|
-
<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 rôle">✕</span>' : ''}</td>
|
|
531
|
+
<td style="font-weight:500;color:var(--text-bright);">${escapeHtml(role)}${ri >= baseRolesCount ? ' <span onclick="removeCustomRole('+`'${code}','${role}'`+')" style="cursor:pointer;color:var(--danger);font-size:0.7rem;" title="Supprimer ce rôle">✕</span>' : ''}</td>
|
|
552
532
|
${actions.map(action => {
|
|
553
533
|
const key = role + '|' + action;
|
|
554
534
|
const wildcardKey = role + '|*';
|
|
@@ -642,24 +622,52 @@ function renderModuleStructure(code) {
|
|
|
642
622
|
|
|
643
623
|
return sections.map(function(section) {
|
|
644
624
|
var resources = section.resources || [];
|
|
625
|
+
var sectionUCs = section.useCases || [];
|
|
626
|
+
var sectionBRs = section.businessRules || [];
|
|
645
627
|
var html = '<div class="struct-section">';
|
|
646
628
|
html += '<div class="struct-section-header">';
|
|
647
|
-
html += '<span class="struct-section-code">' + (section.code || section.name || '') + '</span>';
|
|
629
|
+
html += '<span class="struct-section-code">' + escapeHtml(section.code || section.name || '') + '</span>';
|
|
648
630
|
if (section.description) {
|
|
649
|
-
html += '<span class="struct-section-desc">' + section.description + '</span>';
|
|
631
|
+
html += '<span class="struct-section-desc">' + escapeHtml(section.description) + '</span>';
|
|
650
632
|
}
|
|
651
633
|
html += '<span class="struct-section-badge">' + resources.length + ' ressource' + (resources.length !== 1 ? 's' : '') + '</span>';
|
|
652
634
|
html += '</div>';
|
|
653
635
|
|
|
636
|
+
// Section metadata (route, permission)
|
|
637
|
+
if (section.route || section.permission) {
|
|
638
|
+
html += '<div style="padding:0.4rem 1rem;font-size:0.75rem;color:var(--text-muted);display:flex;gap:1rem;border-bottom:1px solid var(--border);">';
|
|
639
|
+
if (section.route) html += '<span>Route: <code style="color:var(--accent);background:var(--bg-input);padding:0.1rem 0.3rem;border-radius:3px;">' + escapeHtml(section.route) + '</code></span>';
|
|
640
|
+
if (section.permission) html += '<span>Permission: <code style="color:var(--accent);background:var(--bg-input);padding:0.1rem 0.3rem;border-radius:3px;">' + escapeHtml(section.permission) + '</code></span>';
|
|
641
|
+
html += '</div>';
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// Section-level UC summary
|
|
645
|
+
if (sectionUCs.length > 0) {
|
|
646
|
+
html += '<div style="padding:0.4rem 1rem;font-size:0.8rem;border-bottom:1px solid var(--border);">';
|
|
647
|
+
html += '<span style="color:var(--text-muted);font-weight:500;">' + sectionUCs.length + ' cas d\'utilisation :</span> ';
|
|
648
|
+
html += sectionUCs.map(function(uc) { return '<span style="color:var(--text-bright);">' + escapeHtml(uc.name || '') + '</span>'; }).join(', ');
|
|
649
|
+
html += '</div>';
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// Section-level BR summary
|
|
653
|
+
if (sectionBRs.length > 0) {
|
|
654
|
+
html += '<div style="padding:0.4rem 1rem;font-size:0.8rem;border-bottom:1px solid var(--border);">';
|
|
655
|
+
html += '<span style="color:var(--text-muted);font-weight:500;">' + sectionBRs.length + ' règle' + (sectionBRs.length > 1 ? 's' : '') + ' métier :</span> ';
|
|
656
|
+
html += sectionBRs.map(function(br) { return '<span style="color:var(--text-bright);">' + escapeHtml(br.name || '') + '</span>'; }).join(', ');
|
|
657
|
+
html += '</div>';
|
|
658
|
+
}
|
|
659
|
+
|
|
654
660
|
if (resources.length > 0) {
|
|
655
661
|
html += '<div class="struct-resources">';
|
|
656
662
|
resources.forEach(function(res) {
|
|
657
663
|
var resName = typeof res === 'string' ? res : (res.code || res.name || '');
|
|
664
|
+
var resType = typeof res === 'object' ? (res.type || '') : '';
|
|
658
665
|
var resDesc = typeof res === 'object' ? (res.description || '') : '';
|
|
659
666
|
html += '<div class="struct-resource">';
|
|
660
667
|
html += '<span class="struct-resource-icon">•</span>';
|
|
661
|
-
html += '<span class="struct-resource-name">' + resName + '</span>';
|
|
662
|
-
if (
|
|
668
|
+
html += '<span class="struct-resource-name">' + escapeHtml(resName) + '</span>';
|
|
669
|
+
if (resType) html += '<span style="font-size:0.7rem;color:var(--accent);background:rgba(6,182,212,0.1);padding:0.1rem 0.3rem;border-radius:3px;">' + escapeHtml(resType) + '</span>';
|
|
670
|
+
if (resDesc) html += '<span class="struct-resource-desc">' + escapeHtml(resDesc) + '</span>';
|
|
663
671
|
html += '</div>';
|
|
664
672
|
});
|
|
665
673
|
html += '</div>';
|
|
@@ -670,14 +678,109 @@ function renderModuleStructure(code) {
|
|
|
670
678
|
}).join('');
|
|
671
679
|
}
|
|
672
680
|
|
|
681
|
+
/* ---------- Section-Grouped Rendering (Hierarchical Mode) ---------- */
|
|
682
|
+
|
|
683
|
+
function renderSectionGroupedItems(mod, itemsKey, code, renderFn) {
|
|
684
|
+
var sections = mod.anticipatedSections || [];
|
|
685
|
+
var html = '';
|
|
686
|
+
sections.forEach(function(section) {
|
|
687
|
+
var items = section[itemsKey] || [];
|
|
688
|
+
if (items.length === 0) return;
|
|
689
|
+
html += '<div class="section-group">';
|
|
690
|
+
html += '<div class="section-group-header">';
|
|
691
|
+
html += '<span class="section-group-icon">▷</span> ';
|
|
692
|
+
html += '<span class="section-group-label">' + escapeHtml(section.code || section.name || '') + '</span>';
|
|
693
|
+
if (section.route) html += '<span class="section-group-route">' + escapeHtml(section.route) + '</span>';
|
|
694
|
+
html += '<span class="nav-badge">' + items.length + '</span>';
|
|
695
|
+
html += '</div>';
|
|
696
|
+
html += items.map(function(item, i) { return renderFn(code, item, i); }).join('');
|
|
697
|
+
html += '</div>';
|
|
698
|
+
});
|
|
699
|
+
return html;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
function renderModuleMockups(code) {
|
|
703
|
+
var spec = data.moduleSpecs[code] || {};
|
|
704
|
+
var screens = spec.screens || [];
|
|
705
|
+
var wireframes = EMBEDDED_ARTIFACTS?.wireframes?.[code] || [];
|
|
706
|
+
var mod = data.modules.find(function(m) { return m.code === code; });
|
|
707
|
+
var sections = mod ? (mod.anticipatedSections || []) : [];
|
|
708
|
+
|
|
709
|
+
// Priority 1: HTML mockups from screens[] specs
|
|
710
|
+
if (screens.length > 0) {
|
|
711
|
+
var html = '';
|
|
712
|
+
if (typeof renderScreenMockups === 'function') {
|
|
713
|
+
html = renderScreenMockups(code);
|
|
714
|
+
}
|
|
715
|
+
if (wireframes.length > 0) {
|
|
716
|
+
html += '<h3 style="color:var(--text-bright);font-size:1rem;margin:2rem 0 1rem;">Wireframes</h3>';
|
|
717
|
+
html += wireframes.map(function(wf, i) { return renderWireframeMockup(code, wf, i); }).join('');
|
|
718
|
+
}
|
|
719
|
+
return html;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// Priority 2: Wireframes grouped by section (if sections exist)
|
|
723
|
+
if (wireframes.length > 0 && sections.length > 1) {
|
|
724
|
+
var grouped = {};
|
|
725
|
+
var ungrouped = [];
|
|
726
|
+
wireframes.forEach(function(wf, i) {
|
|
727
|
+
var sectionCode = wf.section || wf.sectionCode || '';
|
|
728
|
+
var matchingSection = sections.find(function(s) { return s.code === sectionCode; });
|
|
729
|
+
if (matchingSection) {
|
|
730
|
+
if (!grouped[sectionCode]) grouped[sectionCode] = { section: matchingSection, items: [] };
|
|
731
|
+
grouped[sectionCode].items.push({ wf: wf, index: i });
|
|
732
|
+
} else {
|
|
733
|
+
ungrouped.push({ wf: wf, index: i });
|
|
734
|
+
}
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
var html = '';
|
|
738
|
+
Object.keys(grouped).forEach(function(sectionCode) {
|
|
739
|
+
var group = grouped[sectionCode];
|
|
740
|
+
html += '<div class="section-group">';
|
|
741
|
+
html += '<div class="section-group-header">';
|
|
742
|
+
html += '<span class="section-group-icon">▷</span> ';
|
|
743
|
+
html += '<span class="section-group-label">' + escapeHtml(group.section.code || '') + '</span>';
|
|
744
|
+
html += '<span class="nav-badge">' + group.items.length + '</span>';
|
|
745
|
+
html += '</div>';
|
|
746
|
+
group.items.forEach(function(item) {
|
|
747
|
+
html += renderWireframeMockup(code, item.wf, item.index);
|
|
748
|
+
});
|
|
749
|
+
html += '</div>';
|
|
750
|
+
});
|
|
751
|
+
ungrouped.forEach(function(item) {
|
|
752
|
+
html += renderWireframeMockup(code, item.wf, item.index);
|
|
753
|
+
});
|
|
754
|
+
return html;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// Priority 3: Flat wireframe list
|
|
758
|
+
if (wireframes.length === 0) {
|
|
759
|
+
return '<div class="card" style="text-align:center;padding:2rem;color:var(--text-muted);"><p>Aucune maquette disponible pour ce module.</p><p style="font-size:0.85rem;margin-top:0.5rem;">Les maquettes seront générées lors de la spécification détaillée.</p></div>';
|
|
760
|
+
}
|
|
761
|
+
return wireframes.map(function(wf, i) { return renderWireframeMockup(code, wf, i); }).join('');
|
|
762
|
+
}
|
|
763
|
+
|
|
673
764
|
function switchTab(code, tabId) {
|
|
674
765
|
const section = document.getElementById('module-spec-' + code);
|
|
675
766
|
if (!section) return;
|
|
676
|
-
section.querySelectorAll('.tab-btn').forEach(btn
|
|
677
|
-
|
|
767
|
+
section.querySelectorAll('.tab-btn').forEach(function(btn) {
|
|
768
|
+
btn.classList.remove('active');
|
|
769
|
+
btn.setAttribute('aria-selected', 'false');
|
|
770
|
+
});
|
|
771
|
+
section.querySelectorAll('.tab-panel').forEach(function(panel) {
|
|
772
|
+
panel.classList.remove('active');
|
|
773
|
+
panel.setAttribute('aria-hidden', 'true');
|
|
774
|
+
});
|
|
678
775
|
const targetPanel = document.getElementById('tab-' + code + '-' + tabId);
|
|
679
|
-
if (targetPanel)
|
|
776
|
+
if (targetPanel) {
|
|
777
|
+
targetPanel.classList.add('active');
|
|
778
|
+
targetPanel.setAttribute('aria-hidden', 'false');
|
|
779
|
+
}
|
|
680
780
|
const buttons = section.querySelectorAll('.tab-btn');
|
|
681
781
|
const tabIndex = { uc: 0, br: 1, ent: 2, perm: 3, mock: 4, notes: 5, struct: 6 }[tabId];
|
|
682
|
-
if (buttons[tabIndex])
|
|
782
|
+
if (buttons[tabIndex]) {
|
|
783
|
+
buttons[tabIndex].classList.add('active');
|
|
784
|
+
buttons[tabIndex].setAttribute('aria-selected', 'true');
|
|
785
|
+
}
|
|
683
786
|
}
|
|
@@ -53,7 +53,7 @@ function renderDataModel() {
|
|
|
53
53
|
|
|
54
54
|
html += `<div class="dm-module-group">`;
|
|
55
55
|
html += `<div class="dm-module-header">
|
|
56
|
-
<span class="dm-module-name">${m.name || m.code}</span>
|
|
56
|
+
<span class="dm-module-name">${escapeHtml(m.name || m.code)}</span>
|
|
57
57
|
<span class="dm-module-count">${entities.length} entité${entities.length > 1 ? 's' : ''}</span>
|
|
58
58
|
</div>`;
|
|
59
59
|
|
|
@@ -64,15 +64,15 @@ function renderDataModel() {
|
|
|
64
64
|
html += `
|
|
65
65
|
<div class="dm-entity-card">
|
|
66
66
|
<div class="dm-entity-header">
|
|
67
|
-
<span class="dm-entity-name">${ent.name}</span>
|
|
67
|
+
<span class="dm-entity-name">${escapeHtml(ent.name)}</span>
|
|
68
68
|
<span class="dm-entity-attr-count">${attrs.length} champ${attrs.length > 1 ? 's' : ''}</span>
|
|
69
69
|
</div>
|
|
70
|
-
${ent.description ? `<div class="dm-entity-desc">${ent.description}</div>` : ''}
|
|
70
|
+
${ent.description ? `<div class="dm-entity-desc">${escapeHtml(ent.description)}</div>` : ''}
|
|
71
71
|
${attrs.length > 0 ? `
|
|
72
72
|
<table class="dm-attr-table">
|
|
73
73
|
<thead><tr><th>Champ</th><th>Description</th></tr></thead>
|
|
74
74
|
<tbody>
|
|
75
|
-
${attrs.map(a => `<tr><td class="dm-attr-name">${a.name}</td><td class="dm-attr-desc">${a.description || ''}</td></tr>`).join('')}
|
|
75
|
+
${attrs.map(a => `<tr><td class="dm-attr-name">${escapeHtml(a.name)}</td><td class="dm-attr-desc">${escapeHtml(a.description || '')}</td></tr>`).join('')}
|
|
76
76
|
</tbody>
|
|
77
77
|
</table>` : ''}
|
|
78
78
|
${rels.length > 0 ? `
|
|
@@ -80,7 +80,7 @@ function renderDataModel() {
|
|
|
80
80
|
<div class="dm-relations-title">Relations</div>
|
|
81
81
|
${rels.map(r => {
|
|
82
82
|
const relText = typeof r === 'string' ? r : (r.target + ' (' + r.type + ') - ' + (r.description || ''));
|
|
83
|
-
return `<div class="dm-relation-item">${relText}</div>`;
|
|
83
|
+
return `<div class="dm-relation-item">${escapeHtml(relText)}</div>`;
|
|
84
84
|
}).join('')}
|
|
85
85
|
</div>` : ''}
|
|
86
86
|
</div>`;
|
|
@@ -100,11 +100,11 @@ function renderConsolInteractions() {
|
|
|
100
100
|
const toName = data.modules.find(m => m.code === d.to)?.name || d.to;
|
|
101
101
|
return `
|
|
102
102
|
<div class="interaction-item">
|
|
103
|
-
<span style="font-weight:600;color:var(--text-bright);">${fromName}</span>
|
|
103
|
+
<span style="font-weight:600;color:var(--text-bright);">${escapeHtml(fromName)}</span>
|
|
104
104
|
<span class="interaction-arrow">→</span>
|
|
105
|
-
<span style="font-weight:600;color:var(--text-bright);">${toName}</span>
|
|
105
|
+
<span style="font-weight:600;color:var(--text-bright);">${escapeHtml(toName)}</span>
|
|
106
106
|
<span class="interaction-type">Dépendance</span>
|
|
107
|
-
<span style="flex:1;font-size:0.8rem;color:var(--text-muted);">${d.description || ''}</span>
|
|
107
|
+
<span style="flex:1;font-size:0.8rem;color:var(--text-muted);">${escapeHtml(d.description || '')}</span>
|
|
108
108
|
</div>`;
|
|
109
109
|
}).join('');
|
|
110
110
|
}
|
|
@@ -117,11 +117,11 @@ function renderConsolPermissions() {
|
|
|
117
117
|
|
|
118
118
|
let html = '<table class="mock-table" style="background:var(--bg-card);border-radius:8px;overflow:hidden;">';
|
|
119
119
|
html += '<thead><tr><th>Profil</th>';
|
|
120
|
-
data.modules.forEach(m => { html += `<th style="text-align:center;">${m.name || m.code}</th>`; });
|
|
120
|
+
data.modules.forEach(m => { html += `<th style="text-align:center;">${escapeHtml(m.name || m.code)}</th>`; });
|
|
121
121
|
html += '</tr></thead><tbody>';
|
|
122
122
|
|
|
123
123
|
roles.forEach(role => {
|
|
124
|
-
html += `<tr><td style="font-weight:500;color:var(--text-bright);">${role}</td>`;
|
|
124
|
+
html += `<tr><td style="font-weight:500;color:var(--text-bright);">${escapeHtml(role)}</td>`;
|
|
125
125
|
data.modules.forEach(m => {
|
|
126
126
|
const allPerms = data.moduleSpecs[m.code]?.permissions || [];
|
|
127
127
|
const hasWildcard = allPerms.includes(role + '|*');
|
|
@@ -165,6 +165,7 @@ function addE2EFlow() {
|
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
function removeE2EFlow(index) {
|
|
168
|
+
if (!confirm('Supprimer ce parcours bout en bout ?')) return;
|
|
168
169
|
data.consolidation.e2eFlows.splice(index, 1);
|
|
169
170
|
renderE2EFlows();
|
|
170
171
|
autoSave();
|
|
@@ -177,15 +178,15 @@ function renderE2EFlows() {
|
|
|
177
178
|
container.innerHTML = data.consolidation.e2eFlows.map((flow, fi) => `
|
|
178
179
|
<div class="card" style="margin-bottom:1rem;">
|
|
179
180
|
<div class="card-header">
|
|
180
|
-
<span class="card-title">${flow.name}</span>
|
|
181
|
+
<span class="card-title">${escapeHtml(flow.name)}</span>
|
|
181
182
|
<button class="btn btn-sm" onclick="removeE2EFlow(${fi})" style="opacity:0.5;">Supprimer</button>
|
|
182
183
|
</div>
|
|
183
|
-
${flow.actors ? `<div style="font-size:0.8rem;color:var(--text-muted);margin-bottom:0.5rem;">Intervenants : ${flow.actors}</div>` : ''}
|
|
184
|
+
${flow.actors ? `<div style="font-size:0.8rem;color:var(--text-muted);margin-bottom:0.5rem;">Intervenants : ${escapeHtml(flow.actors)}</div>` : ''}
|
|
184
185
|
<div class="e2e-flow">
|
|
185
186
|
${flow.steps.map((s, i) => `
|
|
186
187
|
<div class="e2e-step">
|
|
187
|
-
<div class="e2e-step-module">${s.module}</div>
|
|
188
|
-
<div class="e2e-step-action">${s.action}</div>
|
|
188
|
+
<div class="e2e-step-module">${escapeHtml(s.module)}</div>
|
|
189
|
+
<div class="e2e-step-action">${escapeHtml(s.action)}</div>
|
|
189
190
|
</div>
|
|
190
191
|
${i < flow.steps.length - 1 ? '<div class="process-arrow">→</div>' : ''}
|
|
191
192
|
`).join('')}
|