@atlashub/smartstack-cli 4.23.0 → 4.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/package.json +1 -1
  2. package/templates/skills/ba-generate-html/html/ba-interactive.html +950 -1055
  3. package/templates/skills/ba-generate-html/html/src/scripts/01-data-init.js +1 -2
  4. package/templates/skills/ba-generate-html/html/src/scripts/02-navigation.js +0 -1
  5. package/templates/skills/ba-generate-html/html/src/scripts/03-render-cadrage.js +0 -39
  6. package/templates/skills/ba-generate-html/html/src/scripts/05-render-specs.js +0 -1
  7. package/templates/skills/ba-generate-html/html/src/scripts/07-render-handoff.js +0 -1
  8. package/templates/skills/ba-generate-html/html/src/scripts/08-editing.js +133 -135
  9. package/templates/skills/ba-generate-html/html/src/scripts/10-comments.js +199 -199
  10. package/templates/skills/ba-generate-html/html/src/scripts/11-review-panel.js +165 -166
  11. package/templates/skills/ba-generate-html/html/src/styles/05-modules.css +444 -454
  12. package/templates/skills/ba-generate-html/html/src/template.html +0 -49
  13. package/templates/skills/ba-generate-html/references/data-build.md +176 -182
  14. package/templates/skills/ba-generate-html/references/data-mapping.md +295 -301
  15. package/templates/skills/ba-generate-html/steps/step-01-collect.md +1 -1
  16. package/templates/skills/ba-generate-html/steps/step-02-build-data.md +0 -9
  17. package/templates/skills/derive-prd/SKILL.md +9 -9
  18. package/templates/skills/derive-prd/references/acceptance-criteria.md +166 -116
  19. package/templates/skills/derive-prd/references/entity-domain-mapping.md +5 -5
  20. package/templates/skills/derive-prd/references/handoff-file-templates.md +12 -12
  21. package/templates/skills/derive-prd/references/handoff-mappings.md +13 -14
  22. package/templates/skills/derive-prd/references/handoff-seeddata-generation.md +1 -1
  23. package/templates/skills/derive-prd/references/readiness-scoring.md +41 -50
  24. package/templates/skills/derive-prd/schemas/handoff-schema.json +2 -2
  25. package/templates/skills/derive-prd/steps/step-00-validate.md +73 -52
  26. package/templates/skills/derive-prd/steps/step-01-transform.md +86 -43
  27. package/templates/skills/ba-generate-html/html/src/partials/cadrage-risks.html +0 -48
@@ -1,166 +1,165 @@
1
- /* ============================================
2
- REVIEW PANEL
3
- ============================================ */
4
- let reviewPanelOpen = false;
5
- let reviewFilter = 'all';
6
-
7
- function toggleReviewPanel() {
8
- reviewPanelOpen = !reviewPanelOpen;
9
- const panel = document.getElementById('reviewPanel');
10
- const body = document.getElementById('appBody');
11
- const btn = document.getElementById('reviewToggleBtn');
12
-
13
- if (reviewPanelOpen) {
14
- panel.classList.add('visible');
15
- body.classList.add('review-open');
16
- btn.classList.add('active');
17
- } else {
18
- panel.classList.remove('visible');
19
- body.classList.remove('review-open');
20
- btn.classList.remove('active');
21
- }
22
-
23
- renderReviewPanel();
24
- }
25
-
26
- function filterReviewComments(filter) {
27
- reviewFilter = filter;
28
- // Update active filter button
29
- document.querySelectorAll('.review-filter-btn').forEach(btn => {
30
- btn.classList.toggle('active', btn.dataset.filter === filter);
31
- });
32
- renderReviewPanel();
33
- }
34
-
35
- function renderReviewPanel() {
36
- const comments = data.comments || [];
37
-
38
- // Update header badge
39
- const toReviewCount = comments.filter(c => c.status === 'to-review').length;
40
- const badge = document.getElementById('reviewBadge');
41
- if (badge) {
42
- badge.textContent = toReviewCount;
43
- badge.classList.toggle('hidden', toReviewCount === 0);
44
- }
45
-
46
- // Update stats
47
- const totalEl = document.getElementById('reviewStatTotal');
48
- const toReviewEl = document.getElementById('reviewStatToReview');
49
- const validatedEl = document.getElementById('reviewStatValidated');
50
- if (totalEl) totalEl.textContent = comments.length;
51
- if (toReviewEl) toReviewEl.textContent = toReviewCount;
52
- if (validatedEl) validatedEl.textContent = comments.filter(c => c.status === 'validated').length;
53
-
54
- // Filter comments
55
- let filtered = comments;
56
- if (reviewFilter === 'to-review') {
57
- filtered = comments.filter(c => c.status === 'to-review');
58
- } else if (reviewFilter === 'validated') {
59
- filtered = comments.filter(c => c.status === 'validated');
60
- }
61
-
62
- // Render comment list
63
- const container = document.getElementById('reviewCommentsList');
64
- if (!container) return;
65
-
66
- if (filtered.length === 0) {
67
- container.innerHTML = '<div class="review-empty">Aucun commentaire' +
68
- (reviewFilter !== 'all' ? ' avec ce filtre' : '') + '.</div>';
69
- return;
70
- }
71
-
72
- container.innerHTML = filtered.map((c, i) => {
73
- const globalIndex = data.comments.indexOf(c);
74
- const sectionLabel = getSectionLabel(c.sectionId);
75
- const date = c.timestamp ? new Date(c.timestamp).toLocaleDateString('fr-FR', { day: '2-digit', month: '2-digit' }) : '';
76
-
77
- return `
78
- <div class="review-comment-item" onclick="navigateToComment('${c.sectionId}', ${c.cardIndex})">
79
- <div class="review-comment-section">${sectionLabel}</div>
80
- <div class="review-comment-text">${c.content}</div>
81
- <div class="review-comment-footer">
82
- <span class="review-comment-author">${c.author || 'Utilisateur'} - ${date}</span>
83
- <div class="review-comment-actions">
84
- <button class="review-action-btn ${c.status === 'validated' ? 'reject' : 'validate'}"
85
- onclick="event.stopPropagation();toggleCommentStatus(${globalIndex})"
86
- title="${c.status === 'validated' ? 'Remettre a revoir' : 'Valider'}">
87
- ${c.status === 'validated' ? 'A revoir' : 'Valider'}
88
- </button>
89
- <button class="review-action-btn delete"
90
- onclick="event.stopPropagation();deleteComment(${globalIndex})"
91
- title="Supprimer">
92
- &#10005;
93
- </button>
94
- </div>
95
- </div>
96
- </div>
97
- `;
98
- }).join('');
99
- }
100
-
101
- function getSectionLabel(sectionId) {
102
- const labels = {
103
- 'cadrage-context': 'Contexte',
104
- 'cadrage-stakeholders': 'Parties prenantes',
105
- 'cadrage-scope': 'Perimetre',
106
- 'cadrage-risks': 'Risques',
107
- 'cadrage-success': 'Criteres',
108
- 'decomp-modules': 'Domaines',
109
- 'decomp-dependencies': 'Dependances',
110
- 'consol-interactions': 'Interactions',
111
- 'consol-permissions': 'Acces',
112
- 'consol-flows': 'Parcours',
113
- 'handoff-summary': 'Synthese'
114
- };
115
- if (labels[sectionId]) return labels[sectionId];
116
- if (sectionId.startsWith('module-spec-')) {
117
- const code = sectionId.replace('module-spec-', '');
118
- const mod = data.modules.find(m => m.code === code);
119
- return mod ? mod.name : code;
120
- }
121
- // Handle module structure sections (module-struct-{code}-{section})
122
- const structMatch = sectionId.match(/^module-struct-(.+?)-(.+)$/);
123
- if (structMatch) {
124
- const mod = data.modules.find(m => m.code === structMatch[1]);
125
- const modName = mod ? mod.name : structMatch[1];
126
- return modName + ' > Structure > ' + structMatch[2];
127
- }
128
- // Handle list-based sectionIds (ucList-*, brList-*, entList-*)
129
- const listMatch = sectionId.match(/^(uc|br|ent)List-(.+)$/);
130
- if (listMatch) {
131
- const tabLabels = { uc: 'Cas d\'utilisation', br: 'Regles metier', ent: 'Donnees' };
132
- const mod = data.modules.find(m => m.code === listMatch[2]);
133
- const modName = mod ? mod.name : listMatch[2];
134
- return modName + ' > ' + (tabLabels[listMatch[1]] || listMatch[1]);
135
- }
136
- return sectionId;
137
- }
138
-
139
- function navigateToComment(sectionId, cardIndex) {
140
- // Handle list-based sectionIds (ucList-*, brList-*, entList-*) → navigate to module + tab
141
- const listMatch = sectionId.match(/^(uc|br|ent)List-(.+)$/);
142
- if (listMatch) {
143
- const [, tabType, moduleCode] = listMatch;
144
- showSection('module-spec-' + moduleCode);
145
- setTimeout(() => {
146
- switchTab(moduleCode, tabType);
147
- scrollToCommentThread(sectionId, cardIndex);
148
- }, 150);
149
- } else {
150
- showSection(sectionId);
151
- scrollToCommentThread(sectionId, cardIndex);
152
- }
153
- }
154
-
155
- function scrollToCommentThread(sectionId, cardIndex) {
156
- setTimeout(() => {
157
- const thread = document.getElementById('comment-thread-' + sectionId + '-' + cardIndex);
158
- if (thread && !thread.classList.contains('visible')) {
159
- thread.classList.add('visible');
160
- }
161
- const container = document.querySelector(`.comment-btn-container[data-section-id="${sectionId}"][data-card-index="${cardIndex}"]`);
162
- if (container) {
163
- container.scrollIntoView({ behavior: 'smooth', block: 'center' });
164
- }
165
- }, 100);
166
- }
1
+ /* ============================================
2
+ REVIEW PANEL
3
+ ============================================ */
4
+ let reviewPanelOpen = false;
5
+ let reviewFilter = 'all';
6
+
7
+ function toggleReviewPanel() {
8
+ reviewPanelOpen = !reviewPanelOpen;
9
+ const panel = document.getElementById('reviewPanel');
10
+ const body = document.getElementById('appBody');
11
+ const btn = document.getElementById('reviewToggleBtn');
12
+
13
+ if (reviewPanelOpen) {
14
+ panel.classList.add('visible');
15
+ body.classList.add('review-open');
16
+ btn.classList.add('active');
17
+ } else {
18
+ panel.classList.remove('visible');
19
+ body.classList.remove('review-open');
20
+ btn.classList.remove('active');
21
+ }
22
+
23
+ renderReviewPanel();
24
+ }
25
+
26
+ function filterReviewComments(filter) {
27
+ reviewFilter = filter;
28
+ // Update active filter button
29
+ document.querySelectorAll('.review-filter-btn').forEach(btn => {
30
+ btn.classList.toggle('active', btn.dataset.filter === filter);
31
+ });
32
+ renderReviewPanel();
33
+ }
34
+
35
+ function renderReviewPanel() {
36
+ const comments = data.comments || [];
37
+
38
+ // Update header badge
39
+ const toReviewCount = comments.filter(c => c.status === 'to-review').length;
40
+ const badge = document.getElementById('reviewBadge');
41
+ if (badge) {
42
+ badge.textContent = toReviewCount;
43
+ badge.classList.toggle('hidden', toReviewCount === 0);
44
+ }
45
+
46
+ // Update stats
47
+ const totalEl = document.getElementById('reviewStatTotal');
48
+ const toReviewEl = document.getElementById('reviewStatToReview');
49
+ const validatedEl = document.getElementById('reviewStatValidated');
50
+ if (totalEl) totalEl.textContent = comments.length;
51
+ if (toReviewEl) toReviewEl.textContent = toReviewCount;
52
+ if (validatedEl) validatedEl.textContent = comments.filter(c => c.status === 'validated').length;
53
+
54
+ // Filter comments
55
+ let filtered = comments;
56
+ if (reviewFilter === 'to-review') {
57
+ filtered = comments.filter(c => c.status === 'to-review');
58
+ } else if (reviewFilter === 'validated') {
59
+ filtered = comments.filter(c => c.status === 'validated');
60
+ }
61
+
62
+ // Render comment list
63
+ const container = document.getElementById('reviewCommentsList');
64
+ if (!container) return;
65
+
66
+ if (filtered.length === 0) {
67
+ container.innerHTML = '<div class="review-empty">Aucun commentaire' +
68
+ (reviewFilter !== 'all' ? ' avec ce filtre' : '') + '.</div>';
69
+ return;
70
+ }
71
+
72
+ container.innerHTML = filtered.map((c, i) => {
73
+ const globalIndex = data.comments.indexOf(c);
74
+ const sectionLabel = getSectionLabel(c.sectionId);
75
+ const date = c.timestamp ? new Date(c.timestamp).toLocaleDateString('fr-FR', { day: '2-digit', month: '2-digit' }) : '';
76
+
77
+ return `
78
+ <div class="review-comment-item" onclick="navigateToComment('${c.sectionId}', ${c.cardIndex})">
79
+ <div class="review-comment-section">${sectionLabel}</div>
80
+ <div class="review-comment-text">${c.content}</div>
81
+ <div class="review-comment-footer">
82
+ <span class="review-comment-author">${c.author || 'Utilisateur'} - ${date}</span>
83
+ <div class="review-comment-actions">
84
+ <button class="review-action-btn ${c.status === 'validated' ? 'reject' : 'validate'}"
85
+ onclick="event.stopPropagation();toggleCommentStatus(${globalIndex})"
86
+ title="${c.status === 'validated' ? 'Remettre a revoir' : 'Valider'}">
87
+ ${c.status === 'validated' ? 'A revoir' : 'Valider'}
88
+ </button>
89
+ <button class="review-action-btn delete"
90
+ onclick="event.stopPropagation();deleteComment(${globalIndex})"
91
+ title="Supprimer">
92
+ &#10005;
93
+ </button>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ `;
98
+ }).join('');
99
+ }
100
+
101
+ function getSectionLabel(sectionId) {
102
+ const labels = {
103
+ 'cadrage-context': 'Contexte',
104
+ 'cadrage-stakeholders': 'Parties prenantes',
105
+ 'cadrage-scope': 'Perimetre',
106
+ 'cadrage-success': 'Criteres',
107
+ 'decomp-modules': 'Domaines',
108
+ 'decomp-dependencies': 'Dependances',
109
+ 'consol-interactions': 'Interactions',
110
+ 'consol-permissions': 'Acces',
111
+ 'consol-flows': 'Parcours',
112
+ 'handoff-summary': 'Synthese'
113
+ };
114
+ if (labels[sectionId]) return labels[sectionId];
115
+ if (sectionId.startsWith('module-spec-')) {
116
+ const code = sectionId.replace('module-spec-', '');
117
+ const mod = data.modules.find(m => m.code === code);
118
+ return mod ? mod.name : code;
119
+ }
120
+ // Handle module structure sections (module-struct-{code}-{section})
121
+ const structMatch = sectionId.match(/^module-struct-(.+?)-(.+)$/);
122
+ if (structMatch) {
123
+ const mod = data.modules.find(m => m.code === structMatch[1]);
124
+ const modName = mod ? mod.name : structMatch[1];
125
+ return modName + ' > Structure > ' + structMatch[2];
126
+ }
127
+ // Handle list-based sectionIds (ucList-*, brList-*, entList-*)
128
+ const listMatch = sectionId.match(/^(uc|br|ent)List-(.+)$/);
129
+ if (listMatch) {
130
+ const tabLabels = { uc: 'Cas d\'utilisation', br: 'Regles metier', ent: 'Donnees' };
131
+ const mod = data.modules.find(m => m.code === listMatch[2]);
132
+ const modName = mod ? mod.name : listMatch[2];
133
+ return modName + ' > ' + (tabLabels[listMatch[1]] || listMatch[1]);
134
+ }
135
+ return sectionId;
136
+ }
137
+
138
+ function navigateToComment(sectionId, cardIndex) {
139
+ // Handle list-based sectionIds (ucList-*, brList-*, entList-*) → navigate to module + tab
140
+ const listMatch = sectionId.match(/^(uc|br|ent)List-(.+)$/);
141
+ if (listMatch) {
142
+ const [, tabType, moduleCode] = listMatch;
143
+ showSection('module-spec-' + moduleCode);
144
+ setTimeout(() => {
145
+ switchTab(moduleCode, tabType);
146
+ scrollToCommentThread(sectionId, cardIndex);
147
+ }, 150);
148
+ } else {
149
+ showSection(sectionId);
150
+ scrollToCommentThread(sectionId, cardIndex);
151
+ }
152
+ }
153
+
154
+ function scrollToCommentThread(sectionId, cardIndex) {
155
+ setTimeout(() => {
156
+ const thread = document.getElementById('comment-thread-' + sectionId + '-' + cardIndex);
157
+ if (thread && !thread.classList.contains('visible')) {
158
+ thread.classList.add('visible');
159
+ }
160
+ const container = document.querySelector(`.comment-btn-container[data-section-id="${sectionId}"][data-card-index="${cardIndex}"]`);
161
+ if (container) {
162
+ container.scrollIntoView({ behavior: 'smooth', block: 'center' });
163
+ }
164
+ }, 100);
165
+ }