@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
|
@@ -13,7 +13,7 @@ function renderScreenMockups(code) {
|
|
|
13
13
|
var resources = screen.resources || [];
|
|
14
14
|
return '<div class="screen-section" style="margin-bottom:2rem;">' +
|
|
15
15
|
'<h3 style="color:var(--text-bright);font-size:1rem;margin-bottom:1rem;">' +
|
|
16
|
-
'<span style="color:var(--accent);">▸</span> ' + (screen.sectionLabel || screen.sectionCode) +
|
|
16
|
+
'<span style="color:var(--accent);">▸</span> ' + escapeHtml(screen.sectionLabel || screen.sectionCode) +
|
|
17
17
|
'</h3>' +
|
|
18
18
|
resources.map(function(res, ri) {
|
|
19
19
|
return renderResourceMockup(code, screen.sectionCode, res, ri);
|
|
@@ -31,9 +31,9 @@ function renderResourceMockup(code, sectionCode, res, index) {
|
|
|
31
31
|
html += '<div class="mockup-dot mockup-dot-red"></div>';
|
|
32
32
|
html += '<div class="mockup-dot mockup-dot-yellow"></div>';
|
|
33
33
|
html += '<div class="mockup-dot mockup-dot-green"></div>';
|
|
34
|
-
html += '<span class="mockup-title">' + (res.label || res.code) + ' (' + res.type + ')</span>';
|
|
34
|
+
html += '<span class="mockup-title">' + escapeHtml(res.label || res.code) + ' (' + res.type + ')</span>';
|
|
35
35
|
if (res.permission) {
|
|
36
|
-
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>';
|
|
36
|
+
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;">' + escapeHtml(res.permission) + '</span>';
|
|
37
37
|
}
|
|
38
38
|
html += '</div>';
|
|
39
39
|
|
|
@@ -66,7 +66,7 @@ function renderResourceMockup(code, sectionCode, res, index) {
|
|
|
66
66
|
// Notes
|
|
67
67
|
if (res.notes) {
|
|
68
68
|
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);">';
|
|
69
|
-
html += '<strong>Notes:</strong> ' + res.notes;
|
|
69
|
+
html += '<strong>Notes:</strong> ' + escapeHtml(res.notes);
|
|
70
70
|
html += '</div>';
|
|
71
71
|
}
|
|
72
72
|
|
|
@@ -93,7 +93,7 @@ function renderSmartTableMockup(res) {
|
|
|
93
93
|
|
|
94
94
|
// Header with actions
|
|
95
95
|
html += '<div class="mock-header">';
|
|
96
|
-
html += '<span class="mock-title">' + (res.label || 'Liste') + '</span>';
|
|
96
|
+
html += '<span class="mock-title">' + escapeHtml(res.label || 'Liste') + '</span>';
|
|
97
97
|
html += '<div style="display:flex;gap:0.4rem;">';
|
|
98
98
|
(res.actions || []).forEach(function(action) {
|
|
99
99
|
var isPrimary = action === 'create' || action === 'export';
|
|
@@ -115,7 +115,7 @@ function renderSmartTableMockup(res) {
|
|
|
115
115
|
html += '<table class="mock-table">';
|
|
116
116
|
html += '<thead><tr>';
|
|
117
117
|
columns.forEach(function(col) {
|
|
118
|
-
html += '<th>' + (col.label || col.field);
|
|
118
|
+
html += '<th>' + escapeHtml(col.label || col.field);
|
|
119
119
|
if (col.sortable) html += ' <span style="font-size:0.6rem;color:var(--text-muted);">▲▼</span>';
|
|
120
120
|
html += '</th>';
|
|
121
121
|
});
|
|
@@ -166,7 +166,7 @@ function renderSmartFormMockup(res) {
|
|
|
166
166
|
|
|
167
167
|
// Header
|
|
168
168
|
html += '<div class="mock-header">';
|
|
169
|
-
html += '<span class="mock-title">' + (res.label || 'Formulaire') + '</span>';
|
|
169
|
+
html += '<span class="mock-title">' + escapeHtml(res.label || 'Formulaire') + '</span>';
|
|
170
170
|
html += '<div style="display:flex;gap:0.4rem;">';
|
|
171
171
|
(res.actions || ['save', 'cancel']).forEach(function(action) {
|
|
172
172
|
var isPrimary = action === 'save';
|
|
@@ -178,7 +178,7 @@ function renderSmartFormMockup(res) {
|
|
|
178
178
|
if (tabs.length > 1) {
|
|
179
179
|
html += '<div style="display:flex;gap:0;border-bottom:1px solid var(--border);margin-bottom:1.5rem;">';
|
|
180
180
|
tabs.forEach(function(tab, i) {
|
|
181
|
-
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>';
|
|
181
|
+
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)') + ';">' + escapeHtml(tab.label) + '</span>';
|
|
182
182
|
});
|
|
183
183
|
html += '</div>';
|
|
184
184
|
}
|
|
@@ -197,7 +197,7 @@ function renderSmartFormMockup(res) {
|
|
|
197
197
|
html += '<div class="mock-form-row">';
|
|
198
198
|
row.forEach(function(field) {
|
|
199
199
|
html += '<div class="mock-form-group">';
|
|
200
|
-
html += '<label class="mock-label">' + (field.label || field.field);
|
|
200
|
+
html += '<label class="mock-label">' + escapeHtml(field.label || field.field);
|
|
201
201
|
if (field.required) html += ' <span style="color:var(--error);">*</span>';
|
|
202
202
|
html += '</label>';
|
|
203
203
|
html += renderFormFieldMockup(field);
|
|
@@ -233,11 +233,11 @@ function renderSubtableMockup(field) {
|
|
|
233
233
|
var entity = field.entity || 'Element';
|
|
234
234
|
var html = '<div style="margin:1rem 0;border:1px solid var(--border);border-radius:8px;overflow:hidden;">';
|
|
235
235
|
html += '<div style="display:flex;justify-content:space-between;align-items:center;padding:0.5rem 0.75rem;background:var(--bg-hover);">';
|
|
236
|
-
html += '<span style="font-weight:500;color:var(--text-bright);font-size:0.85rem;">' + entity + '</span>';
|
|
236
|
+
html += '<span style="font-weight:500;color:var(--text-bright);font-size:0.85rem;">' + escapeHtml(entity) + '</span>';
|
|
237
237
|
html += '<span class="mock-btn" style="font-size:0.75rem;padding:0.2rem 0.5rem;">+ Ajouter</span>';
|
|
238
238
|
html += '</div>';
|
|
239
239
|
html += '<table class="mock-table"><thead><tr>';
|
|
240
|
-
cols.forEach(function(c) { html += '<th>' + c + '</th>'; });
|
|
240
|
+
cols.forEach(function(c) { html += '<th>' + escapeHtml(c) + '</th>'; });
|
|
241
241
|
html += '</tr></thead><tbody>';
|
|
242
242
|
html += '<tr>' + cols.map(function() { return '<td style="color:var(--text-muted);font-style:italic;">...</td>'; }).join('') + '</tr>';
|
|
243
243
|
html += '</tbody></table></div>';
|
|
@@ -247,7 +247,7 @@ function renderSubtableMockup(field) {
|
|
|
247
247
|
/* ---------- SmartCard ---------- */
|
|
248
248
|
function renderSmartCardMockup(res) {
|
|
249
249
|
var columns = res.columns || res.fields || [];
|
|
250
|
-
var html = '<div class="mock-header"><span class="mock-title">' + (res.label || 'Cartes') + '</span></div>';
|
|
250
|
+
var html = '<div class="mock-header"><span class="mock-title">' + escapeHtml(res.label || 'Cartes') + '</span></div>';
|
|
251
251
|
html += '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:1rem;">';
|
|
252
252
|
|
|
253
253
|
for (var i = 0; i < 4; i++) {
|
|
@@ -255,9 +255,9 @@ function renderSmartCardMockup(res) {
|
|
|
255
255
|
columns.forEach(function(col, ci) {
|
|
256
256
|
var label = col.label || col.field || col;
|
|
257
257
|
if (ci === 0) {
|
|
258
|
-
html += '<div style="font-weight:600;color:var(--text-bright);margin-bottom:0.5rem;">' + label + ' #' + (i + 1) + '</div>';
|
|
258
|
+
html += '<div style="font-weight:600;color:var(--text-bright);margin-bottom:0.5rem;">' + escapeHtml(label) + ' #' + (i + 1) + '</div>';
|
|
259
259
|
} else {
|
|
260
|
-
html += '<div style="font-size:0.8rem;color:var(--text-muted);margin-bottom:0.25rem;">' + label + ': <span style="color:var(--text);">valeur</span></div>';
|
|
260
|
+
html += '<div style="font-size:0.8rem;color:var(--text-muted);margin-bottom:0.25rem;">' + escapeHtml(label) + ': <span style="color:var(--text);">valeur</span></div>';
|
|
261
261
|
}
|
|
262
262
|
});
|
|
263
263
|
html += '</div>';
|
|
@@ -269,14 +269,14 @@ function renderSmartCardMockup(res) {
|
|
|
269
269
|
/* ---------- SmartKanban ---------- */
|
|
270
270
|
function renderSmartKanbanMockup(res) {
|
|
271
271
|
var options = res.options || res.columns || ['À faire', 'En cours', 'Terminé'];
|
|
272
|
-
var html = '<div class="mock-header"><span class="mock-title">' + (res.label || 'Kanban') + '</span></div>';
|
|
272
|
+
var html = '<div class="mock-header"><span class="mock-title">' + escapeHtml(res.label || 'Kanban') + '</span></div>';
|
|
273
273
|
html += '<div style="display:flex;gap:1rem;overflow-x:auto;padding-bottom:0.5rem;">';
|
|
274
274
|
|
|
275
275
|
options.forEach(function(col, ci) {
|
|
276
276
|
var colLabel = typeof col === 'string' ? col : (col.label || col.field || 'Colonne');
|
|
277
277
|
html += '<div style="min-width:200px;flex:1;background:var(--bg-hover);border-radius:8px;padding:0.75rem;">';
|
|
278
278
|
html += '<div style="font-weight:600;font-size:0.85rem;color:var(--text-bright);margin-bottom:0.75rem;display:flex;justify-content:space-between;">';
|
|
279
|
-
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>';
|
|
279
|
+
html += escapeHtml(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>';
|
|
280
280
|
html += '</div>';
|
|
281
281
|
for (var j = 0; j < Math.max(1, 3 - ci); j++) {
|
|
282
282
|
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;">';
|
|
@@ -292,7 +292,7 @@ function renderSmartKanbanMockup(res) {
|
|
|
292
292
|
|
|
293
293
|
/* ---------- SmartDashboard ---------- */
|
|
294
294
|
function renderSmartDashboardMockup(res) {
|
|
295
|
-
var html = '<div class="mock-header"><span class="mock-title">' + (res.label || 'Tableau de bord') + '</span></div>';
|
|
295
|
+
var html = '<div class="mock-header"><span class="mock-title">' + escapeHtml(res.label || 'Tableau de bord') + '</span></div>';
|
|
296
296
|
|
|
297
297
|
// KPI cards
|
|
298
298
|
html += '<div class="mock-kpi-grid">';
|
|
@@ -303,7 +303,7 @@ function renderSmartDashboardMockup(res) {
|
|
|
303
303
|
{ label: 'Taux', value: '80%' }
|
|
304
304
|
];
|
|
305
305
|
kpis.forEach(function(kpi) {
|
|
306
|
-
html += '<div class="mock-kpi"><div class="mock-kpi-value">' + (kpi.value || '0') + '</div><div class="mock-kpi-label">' + (kpi.label || '') + '</div></div>';
|
|
306
|
+
html += '<div class="mock-kpi"><div class="mock-kpi-value">' + escapeHtml(kpi.value || '0') + '</div><div class="mock-kpi-label">' + escapeHtml(kpi.label || '') + '</div></div>';
|
|
307
307
|
});
|
|
308
308
|
html += '</div>';
|
|
309
309
|
|
|
@@ -319,7 +319,7 @@ function renderSmartFilterMockup(res) {
|
|
|
319
319
|
var html = '<div style="display:flex;gap:0.4rem;flex-wrap:wrap;padding:0.5rem 0;">';
|
|
320
320
|
html += '<span style="padding:0.3rem 0.7rem;border-radius:16px;font-size:0.8rem;background:var(--primary);color:#fff;cursor:pointer;">Tous</span>';
|
|
321
321
|
options.forEach(function(opt) {
|
|
322
|
-
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>';
|
|
322
|
+
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;">' + escapeHtml(opt) + '</span>';
|
|
323
323
|
});
|
|
324
324
|
html += '</div>';
|
|
325
325
|
return html;
|
|
@@ -5,6 +5,7 @@ function renderHandoff() {
|
|
|
5
5
|
renderHandoffStats();
|
|
6
6
|
renderHandoffModules();
|
|
7
7
|
renderCoverageMatrix();
|
|
8
|
+
renderProgressIndicator();
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
function renderHandoffStats() {
|
|
@@ -43,8 +44,8 @@ function renderHandoffModules() {
|
|
|
43
44
|
<div style="display:flex;align-items:center;gap:0.75rem;">
|
|
44
45
|
<div style="width:28px;height:28px;border-radius:50%;background:var(--primary);display:flex;align-items:center;justify-content:center;color:#fff;font-size:0.75rem;font-weight:700;flex-shrink:0;">${i + 1}</div>
|
|
45
46
|
<div style="flex:1;">
|
|
46
|
-
<div style="font-weight:600;color:var(--text-bright);">${m.name}</div>
|
|
47
|
-
<div style="font-size:0.8rem;color:var(--text-muted);">${m.description || ''}</div>
|
|
47
|
+
<div style="font-weight:600;color:var(--text-bright);">${escapeHtml(m.name)}</div>
|
|
48
|
+
<div style="font-size:0.8rem;color:var(--text-muted);">${escapeHtml(m.description || '')}</div>
|
|
48
49
|
</div>
|
|
49
50
|
<div style="display:flex;gap:1rem;font-size:0.75rem;color:var(--text-muted);">
|
|
50
51
|
<span>${(spec.useCases || []).length} cas d'utilisation</span>
|
|
@@ -77,12 +78,31 @@ function renderCoverageMatrix() {
|
|
|
77
78
|
: (data.modules.length > 0 ? data.modules.map(m => m.name).join(', ') : 'À définir');
|
|
78
79
|
return `
|
|
79
80
|
<tr>
|
|
80
|
-
<td>${item.name}</td>
|
|
81
|
+
<td>${escapeHtml(item.name)}</td>
|
|
81
82
|
<td><span class="priority priority-${item.priority}">${formatPriority(item.priority)}</span></td>
|
|
82
|
-
<td style="color:var(--text-muted);">${moduleName}</td>
|
|
83
|
+
<td style="color:var(--text-muted);">${escapeHtml(moduleName)}</td>
|
|
83
84
|
<td style="text-align:center;color:var(--success);">✓</td>
|
|
84
85
|
</tr>`;
|
|
85
86
|
}).join('')}
|
|
86
87
|
</tbody>
|
|
87
88
|
</table>`;
|
|
88
89
|
}
|
|
90
|
+
|
|
91
|
+
function renderProgressIndicator() {
|
|
92
|
+
var container = document.getElementById('progressIndicator');
|
|
93
|
+
if (!container) return;
|
|
94
|
+
var p = computeProgress();
|
|
95
|
+
var html = '<div class="progress-bar-container">';
|
|
96
|
+
html += '<div class="progress-bar-fill" style="width:' + p.pct + '%;"></div>';
|
|
97
|
+
html += '</div>';
|
|
98
|
+
html += '<div class="progress-label">' + p.pct + '% complet — ' + p.doneCount + '/' + p.total + ' critères</div>';
|
|
99
|
+
html += '<div class="progress-checks">';
|
|
100
|
+
p.checks.forEach(function(c) {
|
|
101
|
+
html += '<div class="progress-check ' + (c.done ? 'done' : '') + '">';
|
|
102
|
+
html += '<span class="progress-check-icon">' + (c.done ? '✓' : '○') + '</span> ';
|
|
103
|
+
html += escapeHtml(c.label);
|
|
104
|
+
html += '</div>';
|
|
105
|
+
});
|
|
106
|
+
html += '</div>';
|
|
107
|
+
container.innerHTML = html;
|
|
108
|
+
}
|
package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/scripts/08-editing.js
RENAMED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
/* ============================================
|
|
2
2
|
PERSISTENCE
|
|
3
3
|
============================================ */
|
|
4
|
+
let _saveTimer;
|
|
4
5
|
function autoSave() {
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
clearTimeout(_saveTimer);
|
|
7
|
+
_saveTimer = setTimeout(function() {
|
|
8
|
+
data.metadata.lastModified = new Date().toISOString();
|
|
9
|
+
localStorage.setItem(APP_KEY, JSON.stringify(data));
|
|
10
|
+
}, 500);
|
|
7
11
|
}
|
|
8
12
|
|
|
9
13
|
function saveToLocalStorage() {
|
package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/scripts/09-export.js
RENAMED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
/* ============================================
|
|
2
2
|
EXPORT JSON
|
|
3
3
|
============================================ */
|
|
4
|
-
function
|
|
5
|
-
// Collect all editable fields (cadrage)
|
|
4
|
+
function collectEditableFields() {
|
|
6
5
|
document.querySelectorAll('.editable[data-field]').forEach(el => {
|
|
7
6
|
setNestedValue(data, 'cadrage.' + el.dataset.field, el.textContent.trim());
|
|
8
7
|
});
|
|
9
|
-
|
|
10
|
-
// Collect module editable fields
|
|
11
8
|
document.querySelectorAll('.editable[data-module-field]').forEach(el => {
|
|
12
9
|
const code = el.dataset.moduleCode;
|
|
13
10
|
const field = el.dataset.moduleField;
|
|
@@ -15,17 +12,39 @@ function exportJSON() {
|
|
|
15
12
|
data.moduleSpecs[code][field] = el.textContent.trim();
|
|
16
13
|
}
|
|
17
14
|
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function buildModuleSpecsExport() {
|
|
18
|
+
var result = {};
|
|
19
|
+
data.modules.forEach(function(m) {
|
|
20
|
+
var spec = data.moduleSpecs[m.code] || {};
|
|
21
|
+
result[m.code] = {
|
|
22
|
+
module: m,
|
|
23
|
+
useCases: (spec.useCases || []).map(function(uc, i) {
|
|
24
|
+
return Object.assign({ id: 'UC-' + String(i + 1).padStart(3, '0') }, uc);
|
|
25
|
+
}),
|
|
26
|
+
businessRules: (spec.businessRules || []).map(function(br, i) {
|
|
27
|
+
return Object.assign({ id: 'BR-' + (br.category || 'GEN').toUpperCase().substring(0, 4) + '-' + String(i + 1).padStart(3, '0') }, br);
|
|
28
|
+
}),
|
|
29
|
+
entities: spec.entities || [],
|
|
30
|
+
permissions: spec.permissions || [],
|
|
31
|
+
notes: spec.notes || ''
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
18
36
|
|
|
37
|
+
function exportJSON() {
|
|
38
|
+
collectEditableFields();
|
|
19
39
|
data.metadata.lastModified = new Date().toISOString();
|
|
20
40
|
data.metadata.exportedAt = new Date().toISOString();
|
|
21
41
|
|
|
22
|
-
// Build complete export with structured data
|
|
23
42
|
const exportData = {
|
|
24
43
|
metadata: data.metadata,
|
|
25
44
|
cadrage: data.cadrage,
|
|
26
45
|
modules: data.modules,
|
|
27
46
|
dependencies: data.dependencies,
|
|
28
|
-
moduleSpecifications:
|
|
47
|
+
moduleSpecifications: buildModuleSpecsExport(),
|
|
29
48
|
consolidation: data.consolidation,
|
|
30
49
|
artifacts: EMBEDDED_ARTIFACTS,
|
|
31
50
|
wireframeComments: data.wireframeComments,
|
|
@@ -35,25 +54,6 @@ function exportJSON() {
|
|
|
35
54
|
comments: data.comments
|
|
36
55
|
};
|
|
37
56
|
|
|
38
|
-
// Structure module specs for export
|
|
39
|
-
data.modules.forEach(m => {
|
|
40
|
-
const spec = data.moduleSpecs[m.code] || {};
|
|
41
|
-
exportData.moduleSpecifications[m.code] = {
|
|
42
|
-
module: m,
|
|
43
|
-
useCases: (spec.useCases || []).map((uc, i) => ({
|
|
44
|
-
id: 'UC-' + String(i + 1).padStart(3, '0'),
|
|
45
|
-
...uc
|
|
46
|
-
})),
|
|
47
|
-
businessRules: (spec.businessRules || []).map((br, i) => ({
|
|
48
|
-
id: 'BR-' + br.category.toUpperCase().substring(0, 4) + '-' + String(i + 1).padStart(3, '0'),
|
|
49
|
-
...br
|
|
50
|
-
})),
|
|
51
|
-
entities: spec.entities || [],
|
|
52
|
-
permissions: spec.permissions || [],
|
|
53
|
-
notes: spec.notes || ''
|
|
54
|
-
};
|
|
55
|
-
});
|
|
56
|
-
|
|
57
57
|
const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
|
|
58
58
|
const url = URL.createObjectURL(blob);
|
|
59
59
|
const a = document.createElement('a');
|
|
@@ -93,24 +93,13 @@ function detectChanges(original, current) {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
function saveReviewJSON() {
|
|
96
|
-
|
|
97
|
-
document.querySelectorAll('.editable[data-field]').forEach(el => {
|
|
98
|
-
setNestedValue(data, 'cadrage.' + el.dataset.field, el.textContent.trim());
|
|
99
|
-
});
|
|
100
|
-
document.querySelectorAll('.editable[data-module-field]').forEach(el => {
|
|
101
|
-
const code = el.dataset.moduleCode;
|
|
102
|
-
const field = el.dataset.moduleField;
|
|
103
|
-
if (data.moduleSpecs[code]) {
|
|
104
|
-
data.moduleSpecs[code][field] = el.textContent.trim();
|
|
105
|
-
}
|
|
106
|
-
});
|
|
96
|
+
collectEditableFields();
|
|
107
97
|
|
|
108
98
|
const changes = detectChanges(ORIGINAL_DATA, data);
|
|
109
99
|
const hasChanges = changes.cadrage || changes.modulesAdded.length > 0
|
|
110
100
|
|| changes.modulesRemoved.length > 0 || changes.modulesModified.length > 0
|
|
111
101
|
|| changes.commentsCount > 0;
|
|
112
102
|
|
|
113
|
-
// Build review export with metadata envelope
|
|
114
103
|
const reviewData = {
|
|
115
104
|
_reviewMeta: {
|
|
116
105
|
sourceVersion: data.metadata.version || '1.0',
|
|
@@ -129,7 +118,7 @@ function saveReviewJSON() {
|
|
|
129
118
|
cadrage: data.cadrage,
|
|
130
119
|
modules: data.modules,
|
|
131
120
|
dependencies: data.dependencies,
|
|
132
|
-
moduleSpecifications:
|
|
121
|
+
moduleSpecifications: buildModuleSpecsExport(),
|
|
133
122
|
consolidation: data.consolidation,
|
|
134
123
|
wireframeComments: data.wireframeComments,
|
|
135
124
|
specComments: data.specComments,
|
|
@@ -138,25 +127,6 @@ function saveReviewJSON() {
|
|
|
138
127
|
comments: data.comments
|
|
139
128
|
};
|
|
140
129
|
|
|
141
|
-
// Structure module specs (same logic as exportJSON)
|
|
142
|
-
data.modules.forEach(m => {
|
|
143
|
-
const spec = data.moduleSpecs[m.code] || {};
|
|
144
|
-
reviewData.moduleSpecifications[m.code] = {
|
|
145
|
-
module: m,
|
|
146
|
-
useCases: (spec.useCases || []).map((uc, i) => ({
|
|
147
|
-
id: 'UC-' + String(i + 1).padStart(3, '0'),
|
|
148
|
-
...uc
|
|
149
|
-
})),
|
|
150
|
-
businessRules: (spec.businessRules || []).map((br, i) => ({
|
|
151
|
-
id: 'BR-' + br.category.toUpperCase().substring(0, 4) + '-' + String(i + 1).padStart(3, '0'),
|
|
152
|
-
...br
|
|
153
|
-
})),
|
|
154
|
-
entities: spec.entities || [],
|
|
155
|
-
permissions: spec.permissions || [],
|
|
156
|
-
notes: spec.notes || ''
|
|
157
|
-
};
|
|
158
|
-
});
|
|
159
|
-
|
|
160
130
|
const blob = new Blob([JSON.stringify(reviewData, null, 2)], { type: 'application/json' });
|
|
161
131
|
const url = URL.createObjectURL(blob);
|
|
162
132
|
const a = document.createElement('a');
|
package/templates/skills/{ba-generate-html → business-analyse-html}/html/src/scripts/10-comments.js
RENAMED
|
@@ -4,14 +4,24 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Comments are stored in data.comments[] with structure:
|
|
7
|
-
* { id, sectionId,
|
|
7
|
+
* { id, sectionId, elementId, author, timestamp, content, status, category }
|
|
8
8
|
*
|
|
9
|
-
* - sectionId: matches the section div id (e.g. "cadrage-
|
|
10
|
-
* -
|
|
9
|
+
* - sectionId: matches the section div id (e.g. "cadrage-context", "module-spec-Clients")
|
|
10
|
+
* - elementId: stable identifier for the element (name, role, or generated id)
|
|
11
|
+
* - cardIndex: (deprecated, kept for backward compat) positional index
|
|
11
12
|
* - status: "to-review" | "validated"
|
|
12
13
|
* - category: "clarification" | "correction" | "suggestion"
|
|
13
14
|
*/
|
|
14
15
|
|
|
16
|
+
function getElementId(item, sectionId, index) {
|
|
17
|
+
// Extract a stable ID from the element's data
|
|
18
|
+
var title = item.querySelector('.uc-title, .entity-name, .stakeholder-role, .card-title');
|
|
19
|
+
if (title) return title.textContent.trim();
|
|
20
|
+
var nameEl = item.querySelector('[data-field]');
|
|
21
|
+
if (nameEl) return nameEl.dataset.field;
|
|
22
|
+
return sectionId + '-' + index;
|
|
23
|
+
}
|
|
24
|
+
|
|
15
25
|
function initInlineComments() {
|
|
16
26
|
// Cadrage sections: direct card children
|
|
17
27
|
document.querySelectorAll('.section > .card, .section > .stakeholder-card, .section > .uc-item').forEach(function(card) {
|
|
@@ -21,7 +31,8 @@ function initInlineComments() {
|
|
|
21
31
|
var sectionId = section ? section.id : 'unknown';
|
|
22
32
|
var siblings = Array.from(section.querySelectorAll(':scope > .card, :scope > .stakeholder-card, :scope > .uc-item'));
|
|
23
33
|
var index = siblings.indexOf(card);
|
|
24
|
-
card
|
|
34
|
+
var elementId = getElementId(card, sectionId, index);
|
|
35
|
+
card.appendChild(createCommentUI(sectionId, elementId));
|
|
25
36
|
});
|
|
26
37
|
|
|
27
38
|
// Module spec lists: nested items in ucList, brList, entList containers
|
|
@@ -32,7 +43,8 @@ function initInlineComments() {
|
|
|
32
43
|
var listId = list.id;
|
|
33
44
|
var siblings = Array.from(list.children);
|
|
34
45
|
var index = siblings.indexOf(item);
|
|
35
|
-
item
|
|
46
|
+
var elementId = getElementId(item, listId, index);
|
|
47
|
+
item.appendChild(createCommentUI(listId, elementId));
|
|
36
48
|
});
|
|
37
49
|
|
|
38
50
|
// Stakeholder cards in grid
|
|
@@ -44,7 +56,8 @@ function initInlineComments() {
|
|
|
44
56
|
var sectionId = section ? section.id : 'stakeholders';
|
|
45
57
|
var siblings = Array.from(grid.children);
|
|
46
58
|
var index = siblings.indexOf(card);
|
|
47
|
-
card
|
|
59
|
+
var elementId = getElementId(card, sectionId, index);
|
|
60
|
+
card.appendChild(createCommentUI(sectionId, elementId));
|
|
48
61
|
});
|
|
49
62
|
|
|
50
63
|
// Scope items
|
|
@@ -54,36 +67,39 @@ function initInlineComments() {
|
|
|
54
67
|
container.querySelectorAll('.uc-item').forEach(function(item, index) {
|
|
55
68
|
if (item.dataset.commentInitialized) return;
|
|
56
69
|
item.dataset.commentInitialized = 'true';
|
|
57
|
-
item
|
|
70
|
+
var elementId = getElementId(item, containerId, index);
|
|
71
|
+
item.appendChild(createCommentUI(containerId, elementId));
|
|
58
72
|
});
|
|
59
73
|
});
|
|
60
74
|
}
|
|
61
75
|
|
|
62
|
-
function createCommentUI(sectionId,
|
|
63
|
-
const comments = getCommentsForCard(sectionId,
|
|
76
|
+
function createCommentUI(sectionId, elementId) {
|
|
77
|
+
const comments = getCommentsForCard(sectionId, elementId);
|
|
64
78
|
const count = comments.length;
|
|
79
|
+
const safeElId = String(elementId).replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
80
|
+
const threadId = 'comment-thread-' + sectionId + '-' + safeElId;
|
|
65
81
|
|
|
66
82
|
const container = document.createElement('div');
|
|
67
83
|
container.className = 'comment-btn-container';
|
|
68
84
|
container.dataset.sectionId = sectionId;
|
|
69
|
-
container.dataset.
|
|
85
|
+
container.dataset.elementId = elementId;
|
|
70
86
|
|
|
71
87
|
container.innerHTML = `
|
|
72
|
-
<button class="comment-toggle-btn" onclick="toggleCommentThread('${sectionId}', ${
|
|
88
|
+
<button class="comment-toggle-btn" onclick="toggleCommentThread('${sectionId}', '${safeElId}')">
|
|
73
89
|
Commentaires <span class="comment-count ${count === 0 ? 'empty' : ''}">${count}</span>
|
|
74
90
|
</button>
|
|
75
|
-
<div class="comment-thread" id="
|
|
76
|
-
<div class="comment-items" id="comment-items-${sectionId}-${
|
|
77
|
-
${renderCommentItems(sectionId,
|
|
91
|
+
<div class="comment-thread" id="${threadId}">
|
|
92
|
+
<div class="comment-items" id="comment-items-${sectionId}-${safeElId}">
|
|
93
|
+
${renderCommentItems(sectionId, elementId)}
|
|
78
94
|
</div>
|
|
79
95
|
<div class="comment-add-form">
|
|
80
|
-
<textarea id="comment-text-${sectionId}-${
|
|
81
|
-
<select id="comment-cat-${sectionId}-${
|
|
96
|
+
<textarea id="comment-text-${sectionId}-${safeElId}" placeholder="Ajouter un commentaire..."></textarea>
|
|
97
|
+
<select id="comment-cat-${sectionId}-${safeElId}">
|
|
82
98
|
<option value="clarification">Clarification</option>
|
|
83
99
|
<option value="correction">Correction</option>
|
|
84
100
|
<option value="suggestion">Suggestion</option>
|
|
85
101
|
</select>
|
|
86
|
-
<button onclick="addInlineComment('${sectionId}', ${
|
|
102
|
+
<button onclick="addInlineComment('${sectionId}', '${elementId.replace(/'/g, "\\'")}')">Ajouter</button>
|
|
87
103
|
</div>
|
|
88
104
|
</div>
|
|
89
105
|
`;
|
|
@@ -91,25 +107,27 @@ function createCommentUI(sectionId, cardIndex) {
|
|
|
91
107
|
return container;
|
|
92
108
|
}
|
|
93
109
|
|
|
94
|
-
function getCommentsForCard(sectionId,
|
|
95
|
-
return (data.comments || []).filter(c
|
|
96
|
-
c.sectionId === sectionId && c.
|
|
97
|
-
|
|
110
|
+
function getCommentsForCard(sectionId, elementId) {
|
|
111
|
+
return (data.comments || []).filter(function(c) {
|
|
112
|
+
if (c.elementId) return c.sectionId === sectionId && c.elementId === elementId;
|
|
113
|
+
// Backward compat: match by cardIndex if elementId not set
|
|
114
|
+
return c.sectionId === sectionId && c.cardIndex === elementId;
|
|
115
|
+
});
|
|
98
116
|
}
|
|
99
117
|
|
|
100
|
-
function toggleCommentThread(sectionId,
|
|
101
|
-
const thread = document.getElementById('comment-thread-' + sectionId + '-' +
|
|
118
|
+
function toggleCommentThread(sectionId, safeElId) {
|
|
119
|
+
const thread = document.getElementById('comment-thread-' + sectionId + '-' + safeElId);
|
|
102
120
|
if (thread) thread.classList.toggle('visible');
|
|
103
121
|
}
|
|
104
122
|
|
|
105
|
-
function renderCommentItems(sectionId,
|
|
106
|
-
const comments = getCommentsForCard(sectionId,
|
|
123
|
+
function renderCommentItems(sectionId, elementId) {
|
|
124
|
+
const comments = getCommentsForCard(sectionId, elementId);
|
|
107
125
|
if (comments.length === 0) {
|
|
108
126
|
return '<div style="font-size:0.8rem;color:var(--text-muted);padding:0.5rem 0;font-style:italic;">Aucun commentaire</div>';
|
|
109
127
|
}
|
|
110
128
|
|
|
111
|
-
return comments.map((c, i)
|
|
112
|
-
const initials = (c.author || 'U').substring(0, 2).toUpperCase();
|
|
129
|
+
return comments.map(function(c, i) {
|
|
130
|
+
const initials = escapeHtml((c.author || 'U').substring(0, 2).toUpperCase());
|
|
113
131
|
const date = c.timestamp ? new Date(c.timestamp).toLocaleDateString('fr-FR', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }) : '';
|
|
114
132
|
const globalIndex = data.comments.indexOf(c);
|
|
115
133
|
|
|
@@ -118,12 +136,12 @@ function renderCommentItems(sectionId, cardIndex) {
|
|
|
118
136
|
<div class="comment-avatar">${initials}</div>
|
|
119
137
|
<div class="comment-body">
|
|
120
138
|
<div class="comment-meta">
|
|
121
|
-
<span class="comment-author">${c.author || 'Utilisateur'}</span>
|
|
122
|
-
<span class="comment-date">${date}</span>
|
|
123
|
-
<span class="comment-category comment-category-${c.category}">${c.category}</span>
|
|
139
|
+
<span class="comment-author">${escapeHtml(c.author || 'Utilisateur')}</span>
|
|
140
|
+
<span class="comment-date">${escapeHtml(date)}</span>
|
|
141
|
+
<span class="comment-category comment-category-${escapeHtml(c.category)}">${escapeHtml(c.category)}</span>
|
|
124
142
|
<span class="comment-status comment-status-${c.status}">${c.status === 'validated' ? 'Validé' : 'À revoir'}</span>
|
|
125
143
|
</div>
|
|
126
|
-
<div class="comment-text">${c.content}</div>
|
|
144
|
+
<div class="comment-text">${escapeHtml(c.content)}</div>
|
|
127
145
|
<div class="comment-actions">
|
|
128
146
|
<button class="comment-action-btn" onclick="toggleCommentStatus(${globalIndex})">${c.status === 'validated' ? 'Remettre à revoir' : 'Valider'}</button>
|
|
129
147
|
<button class="comment-action-btn" onclick="deleteComment(${globalIndex})" style="color:var(--error);">Supprimer</button>
|
|
@@ -134,16 +152,17 @@ function renderCommentItems(sectionId, cardIndex) {
|
|
|
134
152
|
}).join('');
|
|
135
153
|
}
|
|
136
154
|
|
|
137
|
-
function addInlineComment(sectionId,
|
|
138
|
-
|
|
139
|
-
const
|
|
155
|
+
function addInlineComment(sectionId, elementId) {
|
|
156
|
+
var safeElId = String(elementId).replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
157
|
+
const textEl = document.getElementById('comment-text-' + sectionId + '-' + safeElId);
|
|
158
|
+
const catEl = document.getElementById('comment-cat-' + sectionId + '-' + safeElId);
|
|
140
159
|
const content = textEl.value.trim();
|
|
141
160
|
if (!content) return;
|
|
142
161
|
|
|
143
162
|
const comment = {
|
|
144
163
|
id: 'comment-' + Date.now(),
|
|
145
164
|
sectionId: sectionId,
|
|
146
|
-
|
|
165
|
+
elementId: elementId,
|
|
147
166
|
author: 'Utilisateur',
|
|
148
167
|
timestamp: new Date().toISOString(),
|
|
149
168
|
content: content,
|
|
@@ -154,7 +173,7 @@ function addInlineComment(sectionId, cardIndex) {
|
|
|
154
173
|
data.comments.push(comment);
|
|
155
174
|
textEl.value = '';
|
|
156
175
|
|
|
157
|
-
refreshCommentUI(sectionId,
|
|
176
|
+
refreshCommentUI(sectionId, elementId);
|
|
158
177
|
renderReviewPanel();
|
|
159
178
|
autoSave();
|
|
160
179
|
}
|
|
@@ -163,32 +182,35 @@ function toggleCommentStatus(globalIndex) {
|
|
|
163
182
|
const comment = data.comments[globalIndex];
|
|
164
183
|
if (!comment) return;
|
|
165
184
|
comment.status = comment.status === 'validated' ? 'to-review' : 'validated';
|
|
166
|
-
|
|
185
|
+
var elId = comment.elementId || comment.cardIndex;
|
|
186
|
+
refreshCommentUI(comment.sectionId, elId);
|
|
167
187
|
renderReviewPanel();
|
|
168
188
|
autoSave();
|
|
169
189
|
}
|
|
170
190
|
|
|
171
191
|
function deleteComment(globalIndex) {
|
|
192
|
+
if (!confirm('Supprimer ce commentaire ?')) return;
|
|
172
193
|
const comment = data.comments[globalIndex];
|
|
173
194
|
if (!comment) return;
|
|
174
|
-
|
|
175
|
-
|
|
195
|
+
var sectionId = comment.sectionId;
|
|
196
|
+
var elId = comment.elementId || comment.cardIndex;
|
|
176
197
|
data.comments.splice(globalIndex, 1);
|
|
177
|
-
refreshCommentUI(sectionId,
|
|
198
|
+
refreshCommentUI(sectionId, elId);
|
|
178
199
|
renderReviewPanel();
|
|
179
200
|
autoSave();
|
|
180
201
|
}
|
|
181
202
|
|
|
182
|
-
function refreshCommentUI(sectionId,
|
|
203
|
+
function refreshCommentUI(sectionId, elementId) {
|
|
204
|
+
var safeElId = String(elementId).replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
183
205
|
// Update comment items
|
|
184
|
-
const itemsContainer = document.getElementById('comment-items-' + sectionId + '-' +
|
|
206
|
+
const itemsContainer = document.getElementById('comment-items-' + sectionId + '-' + safeElId);
|
|
185
207
|
if (itemsContainer) {
|
|
186
|
-
itemsContainer.innerHTML = renderCommentItems(sectionId,
|
|
208
|
+
itemsContainer.innerHTML = renderCommentItems(sectionId, elementId);
|
|
187
209
|
}
|
|
188
210
|
|
|
189
211
|
// Update count badge
|
|
190
|
-
const count = getCommentsForCard(sectionId,
|
|
191
|
-
const container = document.querySelector(
|
|
212
|
+
const count = getCommentsForCard(sectionId, elementId).length;
|
|
213
|
+
const container = document.querySelector('.comment-btn-container[data-section-id="' + sectionId + '"][data-element-id="' + elementId + '"]');
|
|
192
214
|
if (container) {
|
|
193
215
|
const badge = container.querySelector('.comment-count');
|
|
194
216
|
if (badge) {
|