@atlashub/smartstack-cli 3.4.0 → 3.4.1
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/dist/index.js +14 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/project/appsettings.json.template +12 -2
- package/templates/skills/business-analyse/SKILL.md +47 -143
- package/templates/skills/business-analyse/_shared.md +1 -1
- package/templates/skills/business-analyse/html/ba-interactive.html +269 -28
- package/templates/skills/business-analyse/schemas/application-schema.json +3 -4
- package/templates/skills/business-analyse/schemas/sections/metadata-schema.json +1 -2
- package/templates/skills/business-analyse/steps/step-00-init.md +130 -398
- package/templates/skills/business-analyse/steps/step-01-cadrage.md +6 -14
- package/templates/skills/business-analyse/steps/step-05b-deploy.md +22 -69
- package/templates/skills/business-analyse/templates/tpl-handoff.md +3 -13
- package/templates/skills/business-analyse/templates/tpl-launch-displays.md +23 -128
- package/templates/skills/business-analyse/templates-frd.md +3 -19
- package/templates/skills/business-analyse/steps/step-06-extract.md +0 -648
|
@@ -160,8 +160,8 @@
|
|
|
160
160
|
============================================ */
|
|
161
161
|
.main {
|
|
162
162
|
flex: 1;
|
|
163
|
-
padding: 2rem
|
|
164
|
-
max-width:
|
|
163
|
+
padding: 2rem 3rem;
|
|
164
|
+
max-width: 1400px;
|
|
165
165
|
overflow-y: auto;
|
|
166
166
|
height: calc(100vh - var(--header-height));
|
|
167
167
|
}
|
|
@@ -374,6 +374,13 @@
|
|
|
374
374
|
min-height: 200px;
|
|
375
375
|
}
|
|
376
376
|
|
|
377
|
+
.wireframe-comment {
|
|
378
|
+
padding: 0.75rem 1.5rem 1rem;
|
|
379
|
+
border-top: 1px solid var(--border);
|
|
380
|
+
background: var(--bg-input);
|
|
381
|
+
border-radius: 0 0 10px 10px;
|
|
382
|
+
}
|
|
383
|
+
|
|
377
384
|
/* Wireframe rendering */
|
|
378
385
|
.ascii-wireframe {
|
|
379
386
|
font-family: 'Courier New', 'Consolas', monospace;
|
|
@@ -1202,25 +1209,45 @@
|
|
|
1202
1209
|
<span style="color: #f87171;">■</span> Fonctionnalites indispensables
|
|
1203
1210
|
</h3>
|
|
1204
1211
|
<div id="scopeVital" class="uc-list"></div>
|
|
1205
|
-
<button class="add-btn" onclick="
|
|
1212
|
+
<button class="add-btn" onclick="toggleForm('addScopeForm-vital')">+ Ajouter une fonctionnalite indispensable</button>
|
|
1213
|
+
<div class="inline-form" id="addScopeForm-vital">
|
|
1214
|
+
<div class="form-group"><label class="form-label">Nom de la fonctionnalite</label><input type="text" class="form-input" id="scope-name-vital" placeholder="Ex: Gestion des commandes"></div>
|
|
1215
|
+
<div class="form-group"><label class="form-label">Description (optionnel)</label><input type="text" class="form-input" id="scope-desc-vital" placeholder="Courte description"></div>
|
|
1216
|
+
<div class="form-actions"><button class="btn" onclick="toggleForm('addScopeForm-vital')">Annuler</button><button class="btn btn-primary" onclick="addScopeItem('vital')">Ajouter</button></div>
|
|
1217
|
+
</div>
|
|
1206
1218
|
|
|
1207
1219
|
<h3 style="color: var(--text-bright); font-size: 1rem; margin: 1.5rem 0 0.75rem;">
|
|
1208
1220
|
<span style="color: #facc15;">■</span> Fonctionnalites importantes
|
|
1209
1221
|
</h3>
|
|
1210
1222
|
<div id="scopeImportant" class="uc-list"></div>
|
|
1211
|
-
<button class="add-btn" onclick="
|
|
1223
|
+
<button class="add-btn" onclick="toggleForm('addScopeForm-important')">+ Ajouter une fonctionnalite importante</button>
|
|
1224
|
+
<div class="inline-form" id="addScopeForm-important">
|
|
1225
|
+
<div class="form-group"><label class="form-label">Nom de la fonctionnalite</label><input type="text" class="form-input" id="scope-name-important" placeholder="Ex: Export PDF"></div>
|
|
1226
|
+
<div class="form-group"><label class="form-label">Description (optionnel)</label><input type="text" class="form-input" id="scope-desc-important" placeholder="Courte description"></div>
|
|
1227
|
+
<div class="form-actions"><button class="btn" onclick="toggleForm('addScopeForm-important')">Annuler</button><button class="btn btn-primary" onclick="addScopeItem('important')">Ajouter</button></div>
|
|
1228
|
+
</div>
|
|
1212
1229
|
|
|
1213
1230
|
<h3 style="color: var(--text-bright); font-size: 1rem; margin: 1.5rem 0 0.75rem;">
|
|
1214
1231
|
<span style="color: #4ade80;">■</span> Fonctionnalites optionnelles
|
|
1215
1232
|
</h3>
|
|
1216
1233
|
<div id="scopeOptional" class="uc-list"></div>
|
|
1217
|
-
<button class="add-btn" onclick="
|
|
1234
|
+
<button class="add-btn" onclick="toggleForm('addScopeForm-optional')">+ Ajouter une fonctionnalite optionnelle</button>
|
|
1235
|
+
<div class="inline-form" id="addScopeForm-optional">
|
|
1236
|
+
<div class="form-group"><label class="form-label">Nom de la fonctionnalite</label><input type="text" class="form-input" id="scope-name-optional" placeholder="Ex: Calendrier partage"></div>
|
|
1237
|
+
<div class="form-group"><label class="form-label">Description (optionnel)</label><input type="text" class="form-input" id="scope-desc-optional" placeholder="Courte description"></div>
|
|
1238
|
+
<div class="form-actions"><button class="btn" onclick="toggleForm('addScopeForm-optional')">Annuler</button><button class="btn btn-primary" onclick="addScopeItem('optional')">Ajouter</button></div>
|
|
1239
|
+
</div>
|
|
1218
1240
|
|
|
1219
1241
|
<h3 style="color: var(--text-bright); font-size: 1rem; margin: 1.5rem 0 0.75rem;">
|
|
1220
1242
|
<span style="color: #94a3b8;">■</span> Hors perimetre
|
|
1221
1243
|
</h3>
|
|
1222
1244
|
<div id="scopeExcluded" class="uc-list"></div>
|
|
1223
|
-
<button class="add-btn" onclick="
|
|
1245
|
+
<button class="add-btn" onclick="toggleForm('addScopeForm-excluded')">+ Ajouter une exclusion</button>
|
|
1246
|
+
<div class="inline-form" id="addScopeForm-excluded">
|
|
1247
|
+
<div class="form-group"><label class="form-label">Element hors perimetre</label><input type="text" class="form-input" id="scope-name-excluded" placeholder="Ex: Gestion de la paie"></div>
|
|
1248
|
+
<div class="form-group"><label class="form-label">Raison (optionnel)</label><input type="text" class="form-input" id="scope-desc-excluded" placeholder="Pourquoi hors perimetre"></div>
|
|
1249
|
+
<div class="form-actions"><button class="btn" onclick="toggleForm('addScopeForm-excluded')">Annuler</button><button class="btn btn-primary" onclick="addScopeItem('excluded')">Ajouter</button></div>
|
|
1250
|
+
</div>
|
|
1224
1251
|
</div>
|
|
1225
1252
|
|
|
1226
1253
|
<!-- SECTION: Risques et hypotheses -->
|
|
@@ -1296,6 +1323,15 @@
|
|
|
1296
1323
|
<div class="card-label">Conditions minimales de mise en service</div>
|
|
1297
1324
|
<div class="editable" contenteditable="true" data-field="success.minimumConditions" data-placeholder="Quelles conditions minimales pour mettre le systeme en service ? (fonctionnalites presentes, donnees migrees, formation faite...)"></div>
|
|
1298
1325
|
</div>
|
|
1326
|
+
|
|
1327
|
+
<h3 style="color: var(--text-bright); font-size: 1rem; margin: 2rem 0 0.75rem;">Criteres d'acceptation</h3>
|
|
1328
|
+
<p style="font-size:0.85rem;color:var(--text-muted);margin-bottom:1rem;">Criteres concrets et mesurables pour valider le succes du projet.</p>
|
|
1329
|
+
<div id="criteriaList" class="uc-list"></div>
|
|
1330
|
+
<button class="add-btn" onclick="toggleForm('addCriterionForm')">+ Ajouter un critere d'acceptation</button>
|
|
1331
|
+
<div class="inline-form" id="addCriterionForm">
|
|
1332
|
+
<div class="form-group"><label class="form-label">Critere (mesurable)</label><input type="text" class="form-input" id="criterion-text" placeholder="Ex: 80% des employes saisissent leurs heures chaque semaine"></div>
|
|
1333
|
+
<div class="form-actions"><button class="btn" onclick="toggleForm('addCriterionForm')">Annuler</button><button class="btn btn-primary" onclick="addCriterion()">Ajouter</button></div>
|
|
1334
|
+
</div>
|
|
1299
1335
|
</div>
|
|
1300
1336
|
|
|
1301
1337
|
<!-- ================================================================
|
|
@@ -1498,6 +1534,13 @@
|
|
|
1498
1534
|
let data = {{FEATURE_DATA}};
|
|
1499
1535
|
const EMBEDDED_ARTIFACTS = {{EMBEDDED_ARTIFACTS}};
|
|
1500
1536
|
|
|
1537
|
+
// Initialize optional data structures
|
|
1538
|
+
data.wireframeComments = data.wireframeComments || {};
|
|
1539
|
+
data.specComments = data.specComments || {};
|
|
1540
|
+
data.customRoles = data.customRoles || [];
|
|
1541
|
+
data.customActions = data.customActions || [];
|
|
1542
|
+
data.cadrage.criteria = data.cadrage.criteria || [];
|
|
1543
|
+
|
|
1501
1544
|
/* ============================================
|
|
1502
1545
|
INITIALIZATION
|
|
1503
1546
|
============================================ */
|
|
@@ -1507,6 +1550,7 @@
|
|
|
1507
1550
|
renderStakeholders();
|
|
1508
1551
|
renderScope();
|
|
1509
1552
|
renderRisks();
|
|
1553
|
+
renderCriteria();
|
|
1510
1554
|
renderModules();
|
|
1511
1555
|
renderDependencies();
|
|
1512
1556
|
renderAllModuleSpecs();
|
|
@@ -1610,12 +1654,15 @@
|
|
|
1610
1654
|
SCOPE ITEMS
|
|
1611
1655
|
============================================ */
|
|
1612
1656
|
function addScopeItem(priority) {
|
|
1613
|
-
const name =
|
|
1657
|
+
const name = document.getElementById('scope-name-' + priority).value.trim();
|
|
1614
1658
|
if (!name) return;
|
|
1615
|
-
const description =
|
|
1659
|
+
const description = document.getElementById('scope-desc-' + priority).value.trim();
|
|
1616
1660
|
|
|
1617
1661
|
data.cadrage.scope[priority].push({ name, description });
|
|
1618
1662
|
renderScope();
|
|
1663
|
+
toggleForm('addScopeForm-' + priority);
|
|
1664
|
+
document.getElementById('scope-name-' + priority).value = '';
|
|
1665
|
+
document.getElementById('scope-desc-' + priority).value = '';
|
|
1619
1666
|
autoSave();
|
|
1620
1667
|
}
|
|
1621
1668
|
|
|
@@ -1643,6 +1690,45 @@
|
|
|
1643
1690
|
autoSave();
|
|
1644
1691
|
}
|
|
1645
1692
|
|
|
1693
|
+
/* ============================================
|
|
1694
|
+
ACCEPTANCE CRITERIA
|
|
1695
|
+
============================================ */
|
|
1696
|
+
function addCriterion() {
|
|
1697
|
+
const text = document.getElementById('criterion-text').value.trim();
|
|
1698
|
+
if (!text) return;
|
|
1699
|
+
if (!data.cadrage.criteria) data.cadrage.criteria = [];
|
|
1700
|
+
data.cadrage.criteria.push({ text, validated: false });
|
|
1701
|
+
renderCriteria();
|
|
1702
|
+
toggleForm('addCriterionForm');
|
|
1703
|
+
document.getElementById('criterion-text').value = '';
|
|
1704
|
+
autoSave();
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
function removeCriterion(index) {
|
|
1708
|
+
data.cadrage.criteria.splice(index, 1);
|
|
1709
|
+
renderCriteria();
|
|
1710
|
+
autoSave();
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
function toggleCriterion(index) {
|
|
1714
|
+
data.cadrage.criteria[index].validated = !data.cadrage.criteria[index].validated;
|
|
1715
|
+
renderCriteria();
|
|
1716
|
+
autoSave();
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
function renderCriteria() {
|
|
1720
|
+
const container = document.getElementById('criteriaList');
|
|
1721
|
+
if (!container) return;
|
|
1722
|
+
const criteria = data.cadrage.criteria || [];
|
|
1723
|
+
container.innerHTML = criteria.map((c, i) => `
|
|
1724
|
+
<div class="uc-item" style="display:flex;align-items:center;gap:0.75rem;">
|
|
1725
|
+
<input type="checkbox" ${c.validated ? 'checked' : ''} onchange="toggleCriterion(${i})" style="cursor:pointer;width:18px;height:18px;flex-shrink:0;">
|
|
1726
|
+
<span style="flex:1;${c.validated ? 'text-decoration:line-through;color:var(--text-muted);' : ''}">${c.text}</span>
|
|
1727
|
+
<button class="btn btn-sm" onclick="removeCriterion(${i})" style="opacity:0.5;flex-shrink:0;">✕</button>
|
|
1728
|
+
</div>
|
|
1729
|
+
`).join('');
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1646
1732
|
/* ============================================
|
|
1647
1733
|
RISKS
|
|
1648
1734
|
============================================ */
|
|
@@ -1736,6 +1822,10 @@
|
|
|
1736
1822
|
data.moduleSpecs = parsed.moduleSpecs || {};
|
|
1737
1823
|
data.consolidation = { ...data.consolidation, ...(parsed.consolidation || {}) };
|
|
1738
1824
|
data.handoff = parsed.handoff || {};
|
|
1825
|
+
data.wireframeComments = parsed.wireframeComments || {};
|
|
1826
|
+
data.specComments = parsed.specComments || {};
|
|
1827
|
+
data.customRoles = parsed.customRoles || [];
|
|
1828
|
+
data.customActions = parsed.customActions || [];
|
|
1739
1829
|
|
|
1740
1830
|
// Restore editable fields
|
|
1741
1831
|
document.querySelectorAll('.editable[data-field]').forEach(el => {
|
|
@@ -1773,7 +1863,11 @@
|
|
|
1773
1863
|
dependencies: data.dependencies,
|
|
1774
1864
|
moduleSpecifications: {},
|
|
1775
1865
|
consolidation: data.consolidation,
|
|
1776
|
-
artifacts: EMBEDDED_ARTIFACTS
|
|
1866
|
+
artifacts: EMBEDDED_ARTIFACTS,
|
|
1867
|
+
wireframeComments: data.wireframeComments,
|
|
1868
|
+
specComments: data.specComments,
|
|
1869
|
+
customRoles: data.customRoles,
|
|
1870
|
+
customActions: data.customActions
|
|
1777
1871
|
};
|
|
1778
1872
|
|
|
1779
1873
|
// Structure module specs for export
|
|
@@ -2017,7 +2111,7 @@
|
|
|
2017
2111
|
<div class="dep-layer-modules">
|
|
2018
2112
|
${layer.map(code => {
|
|
2019
2113
|
const m = data.modules.find(mod => mod.code === code);
|
|
2020
|
-
return `<div class="dep-module">${m ? m.name : code}</div>`;
|
|
2114
|
+
return `<div class="dep-module">${m ? (m.name || m.code) : code}</div>`;
|
|
2021
2115
|
}).join('')}
|
|
2022
2116
|
</div>
|
|
2023
2117
|
</div>
|
|
@@ -2065,7 +2159,7 @@
|
|
|
2065
2159
|
return `
|
|
2066
2160
|
<div class="process-step">
|
|
2067
2161
|
<div class="process-step-number">Etape ${i + 1}</div>
|
|
2068
|
-
<div class="process-step-label">${m ? m.name : code}</div>
|
|
2162
|
+
<div class="process-step-label">${m ? (m.name || m.code) : code}</div>
|
|
2069
2163
|
</div>
|
|
2070
2164
|
${i < order.length - 1 ? '<div class="process-arrow">→</div>' : ''}
|
|
2071
2165
|
`;
|
|
@@ -2219,6 +2313,32 @@
|
|
|
2219
2313
|
<div id="permGrid-${code}">
|
|
2220
2314
|
${renderPermissionGrid(code)}
|
|
2221
2315
|
</div>
|
|
2316
|
+
<div style="display:flex;gap:0.75rem;margin-top:1rem;flex-wrap:wrap;">
|
|
2317
|
+
<button class="add-btn" onclick="toggleForm('addRoleForm-${code}')" style="flex:1;min-width:200px;">+ Ajouter un role</button>
|
|
2318
|
+
<button class="add-btn" onclick="toggleForm('addActionForm-${code}')" style="flex:1;min-width:200px;">+ Ajouter une action</button>
|
|
2319
|
+
</div>
|
|
2320
|
+
<div class="inline-form" id="addRoleForm-${code}">
|
|
2321
|
+
<div class="inline-form-title">Nouveau role</div>
|
|
2322
|
+
<div class="form-group">
|
|
2323
|
+
<label class="form-label">Nom du role</label>
|
|
2324
|
+
<input type="text" class="form-input" id="role-name-${code}" placeholder="Exemple : Superviseur, Auditeur...">
|
|
2325
|
+
</div>
|
|
2326
|
+
<div class="form-actions">
|
|
2327
|
+
<button class="btn" onclick="toggleForm('addRoleForm-${code}')">Annuler</button>
|
|
2328
|
+
<button class="btn btn-primary" onclick="addCustomRole('${code}')">Ajouter</button>
|
|
2329
|
+
</div>
|
|
2330
|
+
</div>
|
|
2331
|
+
<div class="inline-form" id="addActionForm-${code}">
|
|
2332
|
+
<div class="inline-form-title">Nouvelle action</div>
|
|
2333
|
+
<div class="form-group">
|
|
2334
|
+
<label class="form-label">Nom de l'action</label>
|
|
2335
|
+
<input type="text" class="form-input" id="action-name-${code}" placeholder="Exemple : Archiver, Imprimer, Approuver...">
|
|
2336
|
+
</div>
|
|
2337
|
+
<div class="form-actions">
|
|
2338
|
+
<button class="btn" onclick="toggleForm('addActionForm-${code}')">Annuler</button>
|
|
2339
|
+
<button class="btn btn-primary" onclick="addCustomAction('${code}')">Ajouter</button>
|
|
2340
|
+
</div>
|
|
2341
|
+
</div>
|
|
2222
2342
|
</div>
|
|
2223
2343
|
|
|
2224
2344
|
<!-- TAB: Maquettes -->
|
|
@@ -2252,6 +2372,12 @@
|
|
|
2252
2372
|
<div class="uc-actors"><div class="uc-actor">${uc.actor}</div></div>
|
|
2253
2373
|
${uc.steps ? `<div class="uc-detail-label">Deroulement</div><div class="uc-detail">${uc.steps.replace(/\n/g, '<br>')}</div>` : ''}
|
|
2254
2374
|
${uc.alternative ? `<div class="uc-detail-label">En cas de probleme</div><div class="uc-detail" style="color:var(--warning);">${uc.alternative}</div>` : ''}
|
|
2375
|
+
<div style="padding:0.5rem 0.75rem;border-top:1px solid var(--border);background:var(--bg-input);border-radius:0 0 8px 8px;">
|
|
2376
|
+
<label style="font-size:0.75rem;color:var(--text-muted);display:block;margin-bottom:0.25rem;">Commentaire / Feedback :</label>
|
|
2377
|
+
<textarea class="form-textarea" placeholder="Ajouter un commentaire sur ce cas d'utilisation..."
|
|
2378
|
+
onblur="updateSpecComment('${code}','uc',${index},this.value)"
|
|
2379
|
+
style="min-height:45px;font-size:0.8rem;resize:vertical;">${getSpecComment(code, 'uc', index)}</textarea>
|
|
2380
|
+
</div>
|
|
2255
2381
|
</div>`;
|
|
2256
2382
|
}
|
|
2257
2383
|
|
|
@@ -2283,14 +2409,21 @@
|
|
|
2283
2409
|
const catColors = { validation: 'br-cat-validation', calculation: 'br-cat-calculation', workflow: 'br-cat-workflow', security: 'br-cat-security', data: 'br-cat-data' };
|
|
2284
2410
|
const catLabels = { validation: 'Verification', calculation: 'Calcul', workflow: 'Processus', security: 'Securite', data: 'Donnees' };
|
|
2285
2411
|
return `
|
|
2286
|
-
<div
|
|
2287
|
-
<
|
|
2288
|
-
|
|
2289
|
-
<div
|
|
2290
|
-
|
|
2291
|
-
|
|
2412
|
+
<div style="margin-bottom:0.5rem;">
|
|
2413
|
+
<div class="br-item" style="margin-bottom:0;border-radius:8px 8px 0 0;">
|
|
2414
|
+
<span class="br-category ${catColors[br.category] || 'br-cat-validation'}">${catLabels[br.category] || br.category}</span>
|
|
2415
|
+
<div class="br-text" style="flex:1;">
|
|
2416
|
+
<div style="font-weight:600;color:var(--text-bright);margin-bottom:0.2rem;">${br.name}</div>
|
|
2417
|
+
<div>${br.statement}</div>
|
|
2418
|
+
${br.example ? `<div style="font-size:0.8rem;color:var(--text-muted);margin-top:0.25rem;font-style:italic;">Exemple : ${br.example}</div>` : ''}
|
|
2419
|
+
</div>
|
|
2420
|
+
<button class="btn btn-sm" onclick="removeBusinessRule('${code}',${index})" style="opacity:0.5;flex-shrink:0;">✕</button>
|
|
2421
|
+
</div>
|
|
2422
|
+
<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;">
|
|
2423
|
+
<textarea class="form-textarea" placeholder="Commentaire sur cette regle..."
|
|
2424
|
+
onblur="updateSpecComment('${code}','br',${index},this.value)"
|
|
2425
|
+
style="min-height:35px;font-size:0.8rem;resize:vertical;">${getSpecComment(code, 'br', index)}</textarea>
|
|
2292
2426
|
</div>
|
|
2293
|
-
<button class="btn btn-sm" onclick="removeBusinessRule('${code}',${index})" style="opacity:0.5;flex-shrink:0;">✕</button>
|
|
2294
2427
|
</div>`;
|
|
2295
2428
|
}
|
|
2296
2429
|
|
|
@@ -2337,6 +2470,12 @@
|
|
|
2337
2470
|
<div style="padding:0.5rem 0.75rem;font-size:0.8rem;color:var(--text-muted);border-top:1px solid var(--border);">
|
|
2338
2471
|
Relations : ${ent.relationships.map(r => `<span style="color:var(--accent);">${r}</span>`).join(', ')}
|
|
2339
2472
|
</div>` : ''}
|
|
2473
|
+
<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;">
|
|
2474
|
+
<label style="font-size:0.75rem;color:var(--text-muted);display:block;margin-bottom:0.25rem;">Commentaire :</label>
|
|
2475
|
+
<textarea class="form-textarea" placeholder="Commentaire sur cette entite..."
|
|
2476
|
+
onblur="updateSpecComment('${code}','ent',${index},this.value)"
|
|
2477
|
+
style="min-height:35px;font-size:0.8rem;resize:vertical;">${getSpecComment(code, 'ent', index)}</textarea>
|
|
2478
|
+
</div>
|
|
2340
2479
|
</div>`;
|
|
2341
2480
|
}
|
|
2342
2481
|
|
|
@@ -2368,6 +2507,27 @@
|
|
|
2368
2507
|
autoSave();
|
|
2369
2508
|
}
|
|
2370
2509
|
|
|
2510
|
+
function getSpecComment(code, type, index) {
|
|
2511
|
+
const key = code + '.' + type + '.' + index;
|
|
2512
|
+
return data.specComments[key] || '';
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2515
|
+
function updateSpecComment(code, type, index, value) {
|
|
2516
|
+
const key = code + '.' + type + '.' + index;
|
|
2517
|
+
data.specComments[key] = value.trim();
|
|
2518
|
+
autoSave();
|
|
2519
|
+
}
|
|
2520
|
+
|
|
2521
|
+
function getWireframeComment(code, screen) {
|
|
2522
|
+
return (data.wireframeComments[code] || {})[screen] || '';
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
function updateWireframeComment(code, screen, value) {
|
|
2526
|
+
if (!data.wireframeComments[code]) data.wireframeComments[code] = {};
|
|
2527
|
+
data.wireframeComments[code][screen] = value.trim();
|
|
2528
|
+
autoSave();
|
|
2529
|
+
}
|
|
2530
|
+
|
|
2371
2531
|
function renderModuleMockups(code) {
|
|
2372
2532
|
// Get embedded wireframes for this module
|
|
2373
2533
|
const wireframes = EMBEDDED_ARTIFACTS?.wireframes?.[code] || [];
|
|
@@ -2413,15 +2573,39 @@
|
|
|
2413
2573
|
</tbody>
|
|
2414
2574
|
</table>
|
|
2415
2575
|
</details>` : ''}
|
|
2576
|
+
<div class="wireframe-comment">
|
|
2577
|
+
<label style="font-size:0.8rem;color:var(--text-muted);display:block;margin-bottom:0.3rem;">Commentaire / Feedback :</label>
|
|
2578
|
+
<textarea class="form-textarea"
|
|
2579
|
+
data-module="${code}"
|
|
2580
|
+
data-screen="${wf.screen}"
|
|
2581
|
+
placeholder="Ajouter un commentaire sur cette maquette (ex: deplacer ce bouton, ajouter une colonne...)"
|
|
2582
|
+
onblur="updateWireframeComment('${code}', '${wf.screen}', this.value)"
|
|
2583
|
+
style="min-height:60px;font-size:0.85rem;resize:vertical;"
|
|
2584
|
+
>${getWireframeComment(code, wf.screen)}</textarea>
|
|
2585
|
+
</div>
|
|
2416
2586
|
</div>
|
|
2417
2587
|
`).join('');
|
|
2418
2588
|
}
|
|
2419
2589
|
|
|
2420
|
-
function
|
|
2421
|
-
const
|
|
2590
|
+
function getPermRoles() {
|
|
2591
|
+
const baseRoles = data.cadrage.stakeholders.length > 0
|
|
2422
2592
|
? data.cadrage.stakeholders.map(s => s.role)
|
|
2423
2593
|
: ['Administrateur', 'Responsable', 'Contributeur', 'Lecteur'];
|
|
2424
|
-
|
|
2594
|
+
return [...baseRoles, ...(data.customRoles || [])];
|
|
2595
|
+
}
|
|
2596
|
+
|
|
2597
|
+
function getPermActions() {
|
|
2598
|
+
const baseActions = ['Consulter', 'Creer', 'Modifier', 'Supprimer', 'Valider', 'Exporter'];
|
|
2599
|
+
return [...baseActions, ...(data.customActions || [])];
|
|
2600
|
+
}
|
|
2601
|
+
|
|
2602
|
+
function renderPermissionGrid(code) {
|
|
2603
|
+
const roles = getPermRoles();
|
|
2604
|
+
const actions = getPermActions();
|
|
2605
|
+
const baseRolesCount = data.cadrage.stakeholders.length > 0
|
|
2606
|
+
? data.cadrage.stakeholders.length
|
|
2607
|
+
: 4;
|
|
2608
|
+
const baseActionsCount = 6;
|
|
2425
2609
|
|
|
2426
2610
|
const perms = data.moduleSpecs[code]?.permissions || [];
|
|
2427
2611
|
|
|
@@ -2429,12 +2613,12 @@
|
|
|
2429
2613
|
<table class="mock-table" style="background:var(--bg-card);border-radius:8px;overflow:hidden;">
|
|
2430
2614
|
<thead><tr>
|
|
2431
2615
|
<th>Profil</th>
|
|
2432
|
-
${actions.map(a => `<th style="text-align:center;">${a}</th>`).join('')}
|
|
2616
|
+
${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('')}
|
|
2433
2617
|
</tr></thead>
|
|
2434
2618
|
<tbody>
|
|
2435
|
-
${roles.map(role => `
|
|
2619
|
+
${roles.map((role, ri) => `
|
|
2436
2620
|
<tr>
|
|
2437
|
-
<td style="font-weight:500;color:var(--text-bright);">${role}</td>
|
|
2621
|
+
<td style="font-weight:500;color:var(--text-bright);">${role}${ri >= baseRolesCount ? ' <span onclick="removeCustomRole('+`'${code}','${role}'`+')" style="cursor:pointer;color:var(--danger);font-size:0.7rem;" title="Supprimer ce role">✕</span>' : ''}</td>
|
|
2438
2622
|
${actions.map(action => {
|
|
2439
2623
|
const key = role + '|' + action;
|
|
2440
2624
|
const checked = perms.includes(key);
|
|
@@ -2457,6 +2641,65 @@
|
|
|
2457
2641
|
autoSave();
|
|
2458
2642
|
}
|
|
2459
2643
|
|
|
2644
|
+
function addCustomRole(code) {
|
|
2645
|
+
const input = document.getElementById('role-name-' + code);
|
|
2646
|
+
const name = input.value.trim();
|
|
2647
|
+
if (!name) return;
|
|
2648
|
+
if (!data.customRoles) data.customRoles = [];
|
|
2649
|
+
if (!data.customRoles.includes(name) && !getPermRoles().includes(name)) {
|
|
2650
|
+
data.customRoles.push(name);
|
|
2651
|
+
}
|
|
2652
|
+
input.value = '';
|
|
2653
|
+
toggleForm('addRoleForm-' + code);
|
|
2654
|
+
refreshAllPermGrids();
|
|
2655
|
+
autoSave();
|
|
2656
|
+
}
|
|
2657
|
+
|
|
2658
|
+
function addCustomAction(code) {
|
|
2659
|
+
const input = document.getElementById('action-name-' + code);
|
|
2660
|
+
const name = input.value.trim();
|
|
2661
|
+
if (!name) return;
|
|
2662
|
+
if (!data.customActions) data.customActions = [];
|
|
2663
|
+
if (!data.customActions.includes(name) && !getPermActions().includes(name)) {
|
|
2664
|
+
data.customActions.push(name);
|
|
2665
|
+
}
|
|
2666
|
+
input.value = '';
|
|
2667
|
+
toggleForm('addActionForm-' + code);
|
|
2668
|
+
refreshAllPermGrids();
|
|
2669
|
+
autoSave();
|
|
2670
|
+
}
|
|
2671
|
+
|
|
2672
|
+
function removeCustomRole(code, role) {
|
|
2673
|
+
data.customRoles = (data.customRoles || []).filter(r => r !== role);
|
|
2674
|
+
// Remove associated permissions
|
|
2675
|
+
data.modules.forEach(m => {
|
|
2676
|
+
if (data.moduleSpecs[m.code]?.permissions) {
|
|
2677
|
+
data.moduleSpecs[m.code].permissions = data.moduleSpecs[m.code].permissions.filter(p => !p.startsWith(role + '|'));
|
|
2678
|
+
}
|
|
2679
|
+
});
|
|
2680
|
+
refreshAllPermGrids();
|
|
2681
|
+
autoSave();
|
|
2682
|
+
}
|
|
2683
|
+
|
|
2684
|
+
function removeCustomAction(code, action) {
|
|
2685
|
+
data.customActions = (data.customActions || []).filter(a => a !== action);
|
|
2686
|
+
// Remove associated permissions
|
|
2687
|
+
data.modules.forEach(m => {
|
|
2688
|
+
if (data.moduleSpecs[m.code]?.permissions) {
|
|
2689
|
+
data.moduleSpecs[m.code].permissions = data.moduleSpecs[m.code].permissions.filter(p => !p.endsWith('|' + action));
|
|
2690
|
+
}
|
|
2691
|
+
});
|
|
2692
|
+
refreshAllPermGrids();
|
|
2693
|
+
autoSave();
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2696
|
+
function refreshAllPermGrids() {
|
|
2697
|
+
data.modules.forEach(m => {
|
|
2698
|
+
const container = document.getElementById('permGrid-' + m.code);
|
|
2699
|
+
if (container) container.innerHTML = renderPermissionGrid(m.code);
|
|
2700
|
+
});
|
|
2701
|
+
}
|
|
2702
|
+
|
|
2460
2703
|
function switchTab(code, tabId) {
|
|
2461
2704
|
const section = document.getElementById('module-spec-' + code);
|
|
2462
2705
|
if (!section) return;
|
|
@@ -2500,13 +2743,11 @@
|
|
|
2500
2743
|
const container = document.getElementById('consolPermissions');
|
|
2501
2744
|
if (!container || data.modules.length === 0) return;
|
|
2502
2745
|
|
|
2503
|
-
const roles =
|
|
2504
|
-
? data.cadrage.stakeholders.map(s => s.role)
|
|
2505
|
-
: ['Administrateur', 'Responsable', 'Contributeur', 'Lecteur'];
|
|
2746
|
+
const roles = getPermRoles();
|
|
2506
2747
|
|
|
2507
2748
|
let html = '<table class="mock-table" style="background:var(--bg-card);border-radius:8px;overflow:hidden;">';
|
|
2508
2749
|
html += '<thead><tr><th>Profil</th>';
|
|
2509
|
-
data.modules.forEach(m => { html += `<th style="text-align:center;">${m.name}</th>`; });
|
|
2750
|
+
data.modules.forEach(m => { html += `<th style="text-align:center;">${m.name || m.code}</th>`; });
|
|
2510
2751
|
html += '</tr></thead><tbody>';
|
|
2511
2752
|
|
|
2512
2753
|
roles.forEach(role => {
|
|
@@ -37,13 +37,12 @@
|
|
|
37
37
|
"context": { "type": "string", "const": "business" },
|
|
38
38
|
"language": { "type": "string", "enum": ["fr", "en", "it", "de"] },
|
|
39
39
|
"featureDescription": { "type": "string" },
|
|
40
|
-
"
|
|
40
|
+
"workflowType": {
|
|
41
41
|
"type": "string",
|
|
42
|
-
"enum": ["
|
|
43
|
-
"description": "
|
|
42
|
+
"enum": ["new", "update"],
|
|
43
|
+
"description": "Whether this is a new application or an update"
|
|
44
44
|
},
|
|
45
45
|
"mcpAvailable": { "type": "boolean" },
|
|
46
|
-
"economyMode": { "type": "boolean" },
|
|
47
46
|
"workflow": {
|
|
48
47
|
"type": "object",
|
|
49
48
|
"description": "Iterative module loop state",
|
|
@@ -20,12 +20,11 @@
|
|
|
20
20
|
"type": "string",
|
|
21
21
|
"enum": ["data-centric", "integration", "ui-centric", "workflow", "reporting", "full-module", "micro"]
|
|
22
22
|
},
|
|
23
|
-
"
|
|
23
|
+
"workflowType": { "type": "string", "enum": ["new", "update"] },
|
|
24
24
|
"permissionBase": { "type": "string", "description": "e.g., business.sales.orders" },
|
|
25
25
|
"previousVersion": { "type": ["string", "null"] },
|
|
26
26
|
"changeReason": { "type": ["string", "null"] },
|
|
27
27
|
"mcpAvailable": { "type": "boolean" },
|
|
28
|
-
"economyMode": { "type": "boolean" },
|
|
29
28
|
"scope": { "type": "string", "enum": ["application", "module"], "default": "module" },
|
|
30
29
|
"applicationRef": { "type": ["string", "null"], "description": "Parent application feature ID (FEAT-XXX)" },
|
|
31
30
|
"applicationPath": { "type": ["string", "null"], "description": "Path to parent application-level feature.json" },
|