@atlashub/smartstack-cli 4.48.0 → 4.50.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/testing-ba-e2e.md +76 -24
- package/package.json +1 -1
- package/templates/agents/gitflow/init.md +26 -0
- package/templates/skills/apex/references/parallel-execution.md +22 -4
- package/templates/skills/apex/steps/step-00-init.md +38 -0
- package/templates/skills/apex/steps/step-03a-layer0-domain.md +21 -0
- package/templates/skills/apex/steps/step-03b-layer1-seed.md +60 -0
- package/templates/skills/apex/steps/step-03c-layer2-backend.md +124 -13
- package/templates/skills/apex/steps/step-03d-layer3-frontend.md +32 -0
- package/templates/skills/application/references/backend-controller-hierarchy.md +14 -4
- package/templates/skills/business-analyse/patterns/suggestion-catalog.md +2 -0
- package/templates/skills/business-analyse/questionnaire/02-stakeholders-scope.md +2 -0
- package/templates/skills/business-analyse/schemas/application-schema.json +36 -1
- package/templates/skills/business-analyse/schemas/sections/specification-schema.json +19 -0
- package/templates/skills/business-analyse/steps/step-00-init.md +64 -14
- package/templates/skills/business-analyse/steps/step-01-cadrage.md +49 -2
- package/templates/skills/business-analyse/steps/step-02-structure.md +41 -17
- package/templates/skills/business-analyse/steps/step-04-consolidate.md +171 -0
- package/templates/skills/business-analyse-develop/references/quality-gates.md +91 -1
- package/templates/skills/business-analyse-develop/steps/step-01-task.md +147 -1
- package/templates/skills/business-analyse-handoff/references/acceptance-criteria.md +53 -1
- package/templates/skills/business-analyse-handoff/references/handoff-file-templates.md +42 -0
- package/templates/skills/business-analyse-handoff/references/handoff-mappings.md +15 -1
- package/templates/skills/business-analyse-handoff/references/prd-generation.md +59 -0
- package/templates/skills/business-analyse-handoff/steps/step-01-transform.md +25 -1
- package/templates/skills/business-analyse-handoff/steps/step-02-export.md +32 -4
- package/templates/skills/business-analyse-html/html/ba-interactive.html +80 -11
- package/templates/skills/business-analyse-html/html/src/scripts/01-data-init.js +4 -2
- package/templates/skills/business-analyse-html/html/src/scripts/02-navigation.js +16 -2
- package/templates/skills/business-analyse-html/html/src/scripts/05-render-specs.js +19 -4
- package/templates/skills/business-analyse-html/html/src/scripts/06-render-mockups.js +1 -1
- package/templates/skills/business-analyse-html/html/src/styles/03-navigation.css +2 -2
- package/templates/skills/business-analyse-html/html/src/styles/05-modules.css +38 -0
- package/templates/skills/business-analyse-html/references/data-build.md +4 -1
- package/templates/skills/business-analyse-html/references/data-mapping.md +4 -1
- package/templates/skills/business-analyse-html/steps/step-02-build-data.md +113 -1
- package/templates/skills/business-analyse-html/steps/step-04-verify.md +17 -1
- package/templates/skills/controller/references/mcp-scaffold-workflow.md +8 -4
- package/templates/skills/controller/steps/step-05-validate.md +2 -2
- package/templates/skills/controller/templates.md +4 -3
- package/templates/skills/feature-full/steps/step-01-implementation.md +18 -5
|
@@ -208,6 +208,64 @@ Each file entry in a category array must have:
|
|
|
208
208
|
|
|
209
209
|
---
|
|
210
210
|
|
|
211
|
+
### 6. Specification Files (MANDATORY)
|
|
212
|
+
|
|
213
|
+
Each PRD MUST include a `specificationFiles` object and 5 companion files in `.ralph/`:
|
|
214
|
+
|
|
215
|
+
```json
|
|
216
|
+
{
|
|
217
|
+
"$version": "3.0.0",
|
|
218
|
+
"implementation": { "filesToCreate": { /* 8 categories */ } },
|
|
219
|
+
"brToCodeMapping": [ /* enriched with statement, example, formula */ ],
|
|
220
|
+
"apiEndpointSummary": [ /* ... */ ],
|
|
221
|
+
"specificationFiles": {
|
|
222
|
+
"entities": "prd-{moduleCode}.entities.json",
|
|
223
|
+
"rules": "prd-{moduleCode}.rules.json",
|
|
224
|
+
"usecases": "prd-{moduleCode}.usecases.json",
|
|
225
|
+
"screens": "prd-{moduleCode}.screens.json",
|
|
226
|
+
"permissions": "prd-{moduleCode}.permissions.json"
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Companion files** are VERBATIM copies of BA source files, colocated in `.ralph/`:
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
.ralph/
|
|
235
|
+
prd-Employees.json # PRD manifest (compact)
|
|
236
|
+
prd-Employees.entities.json # VERBATIM copy of entities.json
|
|
237
|
+
prd-Employees.rules.json # VERBATIM copy of rules.json
|
|
238
|
+
prd-Employees.usecases.json # VERBATIM copy of usecases.json
|
|
239
|
+
prd-Employees.screens.json # VERBATIM copy of screens.json
|
|
240
|
+
prd-Employees.permissions.json # VERBATIM copy of permissions.json
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**Validation:**
|
|
244
|
+
```javascript
|
|
245
|
+
const specFiles = prd.specificationFiles;
|
|
246
|
+
if (!specFiles) {
|
|
247
|
+
BLOCKING_ERROR('specificationFiles missing from PRD');
|
|
248
|
+
}
|
|
249
|
+
const specKeys = ['entities', 'rules', 'usecases', 'screens', 'permissions'];
|
|
250
|
+
for (const key of specKeys) {
|
|
251
|
+
if (!specFiles[key]) {
|
|
252
|
+
BLOCKING_ERROR(`specificationFiles.${key} missing`);
|
|
253
|
+
}
|
|
254
|
+
const filePath = path.join('.ralph', specFiles[key]);
|
|
255
|
+
if (!fs.existsSync(filePath)) {
|
|
256
|
+
BLOCKING_ERROR(`Companion file not found: ${filePath}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**Why companion files matter:**
|
|
262
|
+
- `/apex` loads only the specs needed per layer (entities for L0, rules+usecases for L2, screens for L3)
|
|
263
|
+
- Context per layer is ~200-500 lines instead of ~2000 lines monolithic
|
|
264
|
+
- Zero risk of specs being lost at the bottom of a large context window
|
|
265
|
+
- Full BA detail available for oneshot generation (attributes, formulas, screen columns)
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
211
269
|
## Validation Checklist
|
|
212
270
|
|
|
213
271
|
Use this checklist when validating PRD files:
|
|
@@ -219,6 +277,7 @@ Use this checklist when validating PRD files:
|
|
|
219
277
|
| **Categories** | All 8 categories present | PASS/FAIL |
|
|
220
278
|
| **File Counts** | PRD counts match index.json handoff.filesToCreate | PASS/FAIL |
|
|
221
279
|
| **File Schemas** | All files have path, type, module | PASS/FAIL |
|
|
280
|
+
| **Spec Files** | `specificationFiles` present with 5 paths, all companion files exist | PASS/FAIL |
|
|
222
281
|
|
|
223
282
|
**All checks must PASS before proceeding to /business-analyse-develop**
|
|
224
283
|
|
|
@@ -151,6 +151,25 @@ Write via ba-writer.enrichModuleHandoff with the complete handoff payload:
|
|
|
151
151
|
- totalFiles, totalTasks, handedOffAt (ISO timestamp)
|
|
152
152
|
- featureDescription: {featureDescription}
|
|
153
153
|
|
|
154
|
+
### Specification Files (MANDATORY)
|
|
155
|
+
After writing the handoff data, ALSO write 5 companion files:
|
|
156
|
+
1. `.ralph/prd-{moduleCode}.entities.json` = VERBATIM copy of `{moduleDir}/entities.json`
|
|
157
|
+
2. `.ralph/prd-{moduleCode}.rules.json` = VERBATIM copy of `{moduleDir}/rules.json`
|
|
158
|
+
3. `.ralph/prd-{moduleCode}.usecases.json` = VERBATIM copy of `{moduleDir}/usecases.json`
|
|
159
|
+
4. `.ralph/prd-{moduleCode}.screens.json` = VERBATIM copy of `{moduleDir}/screens.json`
|
|
160
|
+
5. `.ralph/prd-{moduleCode}.permissions.json` = VERBATIM copy of `{moduleDir}/permissions.json`
|
|
161
|
+
|
|
162
|
+
Add `specificationFiles` to the PRD referencing these files (relative names, all in `.ralph/`):
|
|
163
|
+
```json
|
|
164
|
+
"specificationFiles": {
|
|
165
|
+
"entities": "prd-{moduleCode}.entities.json",
|
|
166
|
+
"rules": "prd-{moduleCode}.rules.json",
|
|
167
|
+
"usecases": "prd-{moduleCode}.usecases.json",
|
|
168
|
+
"screens": "prd-{moduleCode}.screens.json",
|
|
169
|
+
"permissions": "prd-{moduleCode}.permissions.json"
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
154
173
|
### POST-CHECK (BLOCKING)
|
|
155
174
|
After writing, verify:
|
|
156
175
|
1. Handoff not empty
|
|
@@ -159,7 +178,12 @@ After writing, verify:
|
|
|
159
178
|
4. Section resources have entity field
|
|
160
179
|
5. SeedData contains CORE entries (NavigationModuleSeedData, NavigationSectionSeedData if sections exist, PermissionsSeedData, RolesSeedData)
|
|
161
180
|
6. For FIRST module only: SeedData contains APP-LEVEL CORE entries (NavigationApplicationSeedData, ApplicationRolesSeedData)
|
|
162
|
-
|
|
181
|
+
7. All 5 companion files exist in `.ralph/` and are non-empty
|
|
182
|
+
8. `specificationFiles` present in the PRD with all 5 paths
|
|
183
|
+
9. Entity count in companion matches source: `prd-{moduleCode}.entities.json` entities[] count = `{moduleDir}/entities.json` entities[] count
|
|
184
|
+
10. BR count in companion matches source: `prd-{moduleCode}.rules.json` rules[] count = `{moduleDir}/rules.json` rules[] count
|
|
185
|
+
11. Each `brToCodeMapping[].statement` is non-empty (not just a title paraphrase)
|
|
186
|
+
Display: "POST-CHECK PASS: {moduleCode} -- 8 categories, {brCount} BRs mapped, {coreCount} core seeds, 5 companion files"
|
|
163
187
|
```
|
|
164
188
|
|
|
165
189
|
### 3. Display Progress
|
|
@@ -91,6 +91,26 @@ for (const cat of categories) {
|
|
|
91
91
|
BLOCKING_ERROR(`${cat}: prd=${prdCount} but feature=${featureCount}`);
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
|
+
|
|
95
|
+
// Check 5: Specification files (companion files)
|
|
96
|
+
const specFiles = prd.specificationFiles;
|
|
97
|
+
if (!specFiles) {
|
|
98
|
+
BLOCKING_ERROR("specificationFiles missing from PRD");
|
|
99
|
+
}
|
|
100
|
+
const specKeys = ['entities', 'rules', 'usecases', 'screens', 'permissions'];
|
|
101
|
+
for (const key of specKeys) {
|
|
102
|
+
if (!specFiles[key]) {
|
|
103
|
+
BLOCKING_ERROR(`specificationFiles.${key} path missing`);
|
|
104
|
+
}
|
|
105
|
+
const companionPath = `.ralph/${specFiles[key]}`;
|
|
106
|
+
if (!fileExists(companionPath)) {
|
|
107
|
+
BLOCKING_ERROR(`Companion file not found: ${companionPath}`);
|
|
108
|
+
}
|
|
109
|
+
const companionSize = fileSize(companionPath);
|
|
110
|
+
if (companionSize < 10) {
|
|
111
|
+
BLOCKING_ERROR(`Companion file empty or too small: ${companionPath} (${companionSize} bytes)`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
94
114
|
```
|
|
95
115
|
|
|
96
116
|
Display verification table showing all 8 categories match between module JSON files and prd.json.
|
|
@@ -187,10 +207,15 @@ Readiness Scores:
|
|
|
187
207
|
└──────────────┴────────────┴──────────────┘
|
|
188
208
|
|
|
189
209
|
Artifacts generated:
|
|
190
|
-
.ralph/prd-{module}.json
|
|
191
|
-
.ralph/
|
|
192
|
-
.ralph/
|
|
193
|
-
|
|
210
|
+
.ralph/prd-{module}.json — Task breakdown per module
|
|
211
|
+
.ralph/prd-{module}.entities.json — Entity specifications (companion)
|
|
212
|
+
.ralph/prd-{module}.rules.json — Business rules (companion)
|
|
213
|
+
.ralph/prd-{module}.usecases.json — Use cases (companion)
|
|
214
|
+
.ralph/prd-{module}.screens.json — Screen specifications (companion)
|
|
215
|
+
.ralph/prd-{module}.permissions.json — Permissions (companion)
|
|
216
|
+
.ralph/progress.txt — Progression tracker
|
|
217
|
+
.ralph/modules-queue.json — Module execution order (if multi-module)
|
|
218
|
+
docs/index.json — BA manifest (status: handed-off)
|
|
194
219
|
|
|
195
220
|
[NEXT] Development:
|
|
196
221
|
1. Run /business-analyse-develop to begin implementation
|
|
@@ -210,5 +235,8 @@ Artifacts generated:
|
|
|
210
235
|
4. `docs/index.json` updated with correct entry count and status "handed-off"
|
|
211
236
|
5. All PRD files have $version=3.0.0 or 4.0.0, file manifest present, 8 categories
|
|
212
237
|
6. File counts match between PRD and handoff for all categories
|
|
238
|
+
7. For EACH module: 5 companion files exist in `.ralph/` (entities, rules, usecases, screens, permissions)
|
|
239
|
+
8. Each PRD contains `specificationFiles` with all 5 paths
|
|
240
|
+
9. Each companion file is non-empty (>10 bytes)
|
|
213
241
|
|
|
214
242
|
**IF any check fails -> fix before completing.**
|
|
@@ -378,13 +378,13 @@ body {
|
|
|
378
378
|
.nav-icon-section { font-size: 0.5rem; color: var(--accent); }
|
|
379
379
|
.nav-icon-resource { font-size: 0.7rem; color: var(--border-light); }
|
|
380
380
|
.nav-resource-link {
|
|
381
|
-
cursor:
|
|
381
|
+
cursor: pointer;
|
|
382
382
|
font-size: 0.72rem;
|
|
383
383
|
color: var(--text-muted);
|
|
384
384
|
padding-top: 0.2rem;
|
|
385
385
|
padding-bottom: 0.2rem;
|
|
386
386
|
}
|
|
387
|
-
.nav-resource-link:hover { background:
|
|
387
|
+
.nav-resource-link:hover { background: var(--bg-hover); color: var(--accent); }
|
|
388
388
|
.nav-resources { margin-left: 0.5rem; }
|
|
389
389
|
|
|
390
390
|
|
|
@@ -1031,6 +1031,44 @@ body {
|
|
|
1031
1031
|
color: var(--text-muted);
|
|
1032
1032
|
}
|
|
1033
1033
|
|
|
1034
|
+
/* ============================================
|
|
1035
|
+
STRUCTURE LEGEND
|
|
1036
|
+
============================================ */
|
|
1037
|
+
.struct-legend {
|
|
1038
|
+
background: var(--bg-card);
|
|
1039
|
+
border: 1px solid var(--border);
|
|
1040
|
+
border-radius: 10px;
|
|
1041
|
+
padding: 0.75rem 1rem;
|
|
1042
|
+
margin-bottom: 1.25rem;
|
|
1043
|
+
}
|
|
1044
|
+
.struct-legend-title {
|
|
1045
|
+
font-size: 0.8rem;
|
|
1046
|
+
font-weight: 600;
|
|
1047
|
+
color: var(--text-muted);
|
|
1048
|
+
margin-bottom: 0.5rem;
|
|
1049
|
+
}
|
|
1050
|
+
.struct-legend-grid {
|
|
1051
|
+
display: grid;
|
|
1052
|
+
grid-template-columns: 1fr 1fr;
|
|
1053
|
+
gap: 0.4rem 1.5rem;
|
|
1054
|
+
}
|
|
1055
|
+
.struct-legend-item {
|
|
1056
|
+
font-size: 0.78rem;
|
|
1057
|
+
color: var(--text-muted);
|
|
1058
|
+
line-height: 1.4;
|
|
1059
|
+
}
|
|
1060
|
+
.struct-legend-code {
|
|
1061
|
+
display: inline-block;
|
|
1062
|
+
font-weight: 600;
|
|
1063
|
+
color: var(--accent);
|
|
1064
|
+
background: rgba(6,182,212,0.1);
|
|
1065
|
+
padding: 0.05rem 0.4rem;
|
|
1066
|
+
border-radius: 3px;
|
|
1067
|
+
font-size: 0.72rem;
|
|
1068
|
+
margin-right: 0.3rem;
|
|
1069
|
+
font-family: monospace;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1034
1072
|
/* ============================================
|
|
1035
1073
|
SECTION GROUPS (Hierarchical Mode)
|
|
1036
1074
|
============================================ */
|
|
@@ -2416,8 +2454,10 @@ data.moduleSpecs = data.moduleSpecs || {};
|
|
|
2416
2454
|
if (!data.moduleSpecs[m.code]) {
|
|
2417
2455
|
data.moduleSpecs[m.code] = { useCases: [], businessRules: [], entities: [], permissions: [], notes: '' };
|
|
2418
2456
|
}
|
|
2419
|
-
// Ensure anticipatedSections array exists
|
|
2420
|
-
m.anticipatedSections = m.anticipatedSections || []
|
|
2457
|
+
// Ensure anticipatedSections array exists and normalize strings to objects
|
|
2458
|
+
m.anticipatedSections = (m.anticipatedSections || []).map(function(s) {
|
|
2459
|
+
return typeof s === 'string' ? { code: s, name: s, description: '', resources: [] } : s;
|
|
2460
|
+
});
|
|
2421
2461
|
m.applicationCode = m.applicationCode || '';
|
|
2422
2462
|
// Initialize screens array for interface specs
|
|
2423
2463
|
if (!data.moduleSpecs[m.code].screens) {
|
|
@@ -2729,7 +2769,9 @@ function renderModuleNavItem(mod) {
|
|
|
2729
2769
|
var ucCount = (spec.useCases || []).length;
|
|
2730
2770
|
var brCount = (spec.businessRules || []).length;
|
|
2731
2771
|
var entCount = (spec.entities || []).length;
|
|
2732
|
-
var sections = mod.anticipatedSections || []
|
|
2772
|
+
var sections = (mod.anticipatedSections || []).map(function(s) {
|
|
2773
|
+
return typeof s === 'string' ? { code: s, name: s, resources: [] } : s;
|
|
2774
|
+
});
|
|
2733
2775
|
var groupId = 'mod-' + code;
|
|
2734
2776
|
var collapsed = navCollapseState[groupId] === true;
|
|
2735
2777
|
|
|
@@ -2762,7 +2804,7 @@ function renderModuleNavItem(mod) {
|
|
|
2762
2804
|
html += '<div class="nav-children nav-resources">';
|
|
2763
2805
|
resources.forEach(function(res) {
|
|
2764
2806
|
var resName = typeof res === 'string' ? res : (res.code || res.name || '');
|
|
2765
|
-
html += '<a class="nav-item nav-resource-link">';
|
|
2807
|
+
html += '<a class="nav-item nav-resource-link" onclick="showSection(\'module-spec-' + code + '\');switchTab(\'' + code + '\',\'mock\');scrollToMockup(\'' + code + '\',\'' + section.code + '\')">';
|
|
2766
2808
|
html += '<span class="nav-icon nav-icon-resource">•</span> ' + escapeHtml(resName);
|
|
2767
2809
|
html += '</a>';
|
|
2768
2810
|
});
|
|
@@ -2785,6 +2827,18 @@ function renderModuleTabNavItem(code, tabId, label, badge) {
|
|
|
2785
2827
|
'</a>';
|
|
2786
2828
|
}
|
|
2787
2829
|
|
|
2830
|
+
function scrollToMockup(moduleCode, sectionCode) {
|
|
2831
|
+
setTimeout(function() {
|
|
2832
|
+
var el = document.getElementById('screen-' + moduleCode + '-' + sectionCode);
|
|
2833
|
+
if (el) {
|
|
2834
|
+
el.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
2835
|
+
el.style.outline = '2px solid var(--accent)';
|
|
2836
|
+
el.style.borderRadius = '10px';
|
|
2837
|
+
setTimeout(function() { el.style.outline = ''; el.style.borderRadius = ''; }, 2000);
|
|
2838
|
+
}
|
|
2839
|
+
}, 150);
|
|
2840
|
+
}
|
|
2841
|
+
|
|
2788
2842
|
/* ---------- Collapse/Expand ---------- */
|
|
2789
2843
|
|
|
2790
2844
|
function toggleNavGroup(groupId) {
|
|
@@ -3907,7 +3961,9 @@ function refreshAllPermGrids() {
|
|
|
3907
3961
|
|
|
3908
3962
|
function renderModuleStructure(code) {
|
|
3909
3963
|
const mod = data.modules.find(m => m.code === code);
|
|
3910
|
-
const sections = mod ? (mod.anticipatedSections || [])
|
|
3964
|
+
const sections = mod ? (mod.anticipatedSections || []).map(function(s) {
|
|
3965
|
+
return typeof s === 'string' ? { code: s, name: s, description: '', resources: [] } : s;
|
|
3966
|
+
}) : [];
|
|
3911
3967
|
|
|
3912
3968
|
if (sections.length === 0) {
|
|
3913
3969
|
return '<div class="card" style="text-align:center;padding:2rem;color:var(--text-muted);">' +
|
|
@@ -3916,7 +3972,16 @@ function renderModuleStructure(code) {
|
|
|
3916
3972
|
'</div>';
|
|
3917
3973
|
}
|
|
3918
3974
|
|
|
3919
|
-
|
|
3975
|
+
var legend = '<div class="struct-legend">' +
|
|
3976
|
+
'<div class="struct-legend-title">ⓘ Types de sections</div>' +
|
|
3977
|
+
'<div class="struct-legend-grid">' +
|
|
3978
|
+
'<div class="struct-legend-item"><span class="struct-legend-code">list</span> Page principale — grille de données avec filtres, tri et actions (créer, exporter)</div>' +
|
|
3979
|
+
'<div class="struct-legend-item"><span class="struct-legend-code">detail</span> Fiche détaillée — affichée au clic sur une ligne, avec onglets (infos, relations, historique)</div>' +
|
|
3980
|
+
'<div class="struct-legend-item"><span class="struct-legend-code">dashboard</span> Tableau de bord — KPIs, graphiques et métriques du module</div>' +
|
|
3981
|
+
'<div class="struct-legend-item"><span class="struct-legend-code">approve</span> Workflow — file de validation avec actions (approuver, rejeter, mettre en attente)</div>' +
|
|
3982
|
+
'</div></div>';
|
|
3983
|
+
|
|
3984
|
+
return legend + sections.map(function(section) {
|
|
3920
3985
|
var resources = section.resources || [];
|
|
3921
3986
|
var sectionUCs = section.useCases || [];
|
|
3922
3987
|
var sectionBRs = section.businessRules || [];
|
|
@@ -3977,7 +4042,9 @@ function renderModuleStructure(code) {
|
|
|
3977
4042
|
/* ---------- Section-Grouped Rendering (Hierarchical Mode) ---------- */
|
|
3978
4043
|
|
|
3979
4044
|
function renderSectionGroupedItems(mod, itemsKey, code, renderFn) {
|
|
3980
|
-
var sections = mod.anticipatedSections || []
|
|
4045
|
+
var sections = (mod.anticipatedSections || []).map(function(s) {
|
|
4046
|
+
return typeof s === 'string' ? { code: s, name: s, resources: [] } : s;
|
|
4047
|
+
});
|
|
3981
4048
|
var html = '';
|
|
3982
4049
|
sections.forEach(function(section) {
|
|
3983
4050
|
var items = section[itemsKey] || [];
|
|
@@ -4000,7 +4067,9 @@ function renderModuleMockups(code) {
|
|
|
4000
4067
|
var screens = spec.screens || [];
|
|
4001
4068
|
var wireframes = EMBEDDED_ARTIFACTS?.wireframes?.[code] || [];
|
|
4002
4069
|
var mod = data.modules.find(function(m) { return m.code === code; });
|
|
4003
|
-
var sections = mod ? (mod.anticipatedSections || [])
|
|
4070
|
+
var sections = mod ? (mod.anticipatedSections || []).map(function(s) {
|
|
4071
|
+
return typeof s === 'string' ? { code: s, name: s, resources: [] } : s;
|
|
4072
|
+
}) : [];
|
|
4004
4073
|
|
|
4005
4074
|
// Priority 1: HTML mockups from screens[] specs
|
|
4006
4075
|
if (screens.length > 0) {
|
|
@@ -4295,7 +4364,7 @@ function renderScreenMockups(code) {
|
|
|
4295
4364
|
|
|
4296
4365
|
return screens.map(function(screen, si) {
|
|
4297
4366
|
var resources = screen.resources || [];
|
|
4298
|
-
return '<div class="screen-section" style="margin-bottom:2rem;">' +
|
|
4367
|
+
return '<div class="screen-section" id="screen-' + code + '-' + screen.sectionCode + '" style="margin-bottom:2rem;">' +
|
|
4299
4368
|
'<h3 style="color:var(--text-bright);font-size:1rem;margin-bottom:1rem;">' +
|
|
4300
4369
|
'<span style="color:var(--accent);">▸</span> ' + escapeHtml(screen.sectionLabel || screen.sectionCode) +
|
|
4301
4370
|
'</h3>' +
|
|
@@ -66,8 +66,10 @@ data.moduleSpecs = data.moduleSpecs || {};
|
|
|
66
66
|
if (!data.moduleSpecs[m.code]) {
|
|
67
67
|
data.moduleSpecs[m.code] = { useCases: [], businessRules: [], entities: [], permissions: [], notes: '' };
|
|
68
68
|
}
|
|
69
|
-
// Ensure anticipatedSections array exists
|
|
70
|
-
m.anticipatedSections = m.anticipatedSections || []
|
|
69
|
+
// Ensure anticipatedSections array exists and normalize strings to objects
|
|
70
|
+
m.anticipatedSections = (m.anticipatedSections || []).map(function(s) {
|
|
71
|
+
return typeof s === 'string' ? { code: s, name: s, description: '', resources: [] } : s;
|
|
72
|
+
});
|
|
71
73
|
m.applicationCode = m.applicationCode || '';
|
|
72
74
|
// Initialize screens array for interface specs
|
|
73
75
|
if (!data.moduleSpecs[m.code].screens) {
|
|
@@ -149,7 +149,9 @@ function renderModuleNavItem(mod) {
|
|
|
149
149
|
var ucCount = (spec.useCases || []).length;
|
|
150
150
|
var brCount = (spec.businessRules || []).length;
|
|
151
151
|
var entCount = (spec.entities || []).length;
|
|
152
|
-
var sections = mod.anticipatedSections || []
|
|
152
|
+
var sections = (mod.anticipatedSections || []).map(function(s) {
|
|
153
|
+
return typeof s === 'string' ? { code: s, name: s, resources: [] } : s;
|
|
154
|
+
});
|
|
153
155
|
var groupId = 'mod-' + code;
|
|
154
156
|
var collapsed = navCollapseState[groupId] === true;
|
|
155
157
|
|
|
@@ -182,7 +184,7 @@ function renderModuleNavItem(mod) {
|
|
|
182
184
|
html += '<div class="nav-children nav-resources">';
|
|
183
185
|
resources.forEach(function(res) {
|
|
184
186
|
var resName = typeof res === 'string' ? res : (res.code || res.name || '');
|
|
185
|
-
html += '<a class="nav-item nav-resource-link">';
|
|
187
|
+
html += '<a class="nav-item nav-resource-link" onclick="showSection(\'module-spec-' + code + '\');switchTab(\'' + code + '\',\'mock\');scrollToMockup(\'' + code + '\',\'' + section.code + '\')">';
|
|
186
188
|
html += '<span class="nav-icon nav-icon-resource">•</span> ' + escapeHtml(resName);
|
|
187
189
|
html += '</a>';
|
|
188
190
|
});
|
|
@@ -205,6 +207,18 @@ function renderModuleTabNavItem(code, tabId, label, badge) {
|
|
|
205
207
|
'</a>';
|
|
206
208
|
}
|
|
207
209
|
|
|
210
|
+
function scrollToMockup(moduleCode, sectionCode) {
|
|
211
|
+
setTimeout(function() {
|
|
212
|
+
var el = document.getElementById('screen-' + moduleCode + '-' + sectionCode);
|
|
213
|
+
if (el) {
|
|
214
|
+
el.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
215
|
+
el.style.outline = '2px solid var(--accent)';
|
|
216
|
+
el.style.borderRadius = '10px';
|
|
217
|
+
setTimeout(function() { el.style.outline = ''; el.style.borderRadius = ''; }, 2000);
|
|
218
|
+
}
|
|
219
|
+
}, 150);
|
|
220
|
+
}
|
|
221
|
+
|
|
208
222
|
/* ---------- Collapse/Expand ---------- */
|
|
209
223
|
|
|
210
224
|
function toggleNavGroup(groupId) {
|
|
@@ -611,7 +611,9 @@ function refreshAllPermGrids() {
|
|
|
611
611
|
|
|
612
612
|
function renderModuleStructure(code) {
|
|
613
613
|
const mod = data.modules.find(m => m.code === code);
|
|
614
|
-
const sections = mod ? (mod.anticipatedSections || [])
|
|
614
|
+
const sections = mod ? (mod.anticipatedSections || []).map(function(s) {
|
|
615
|
+
return typeof s === 'string' ? { code: s, name: s, description: '', resources: [] } : s;
|
|
616
|
+
}) : [];
|
|
615
617
|
|
|
616
618
|
if (sections.length === 0) {
|
|
617
619
|
return '<div class="card" style="text-align:center;padding:2rem;color:var(--text-muted);">' +
|
|
@@ -620,7 +622,16 @@ function renderModuleStructure(code) {
|
|
|
620
622
|
'</div>';
|
|
621
623
|
}
|
|
622
624
|
|
|
623
|
-
|
|
625
|
+
var legend = '<div class="struct-legend">' +
|
|
626
|
+
'<div class="struct-legend-title">ⓘ Types de sections</div>' +
|
|
627
|
+
'<div class="struct-legend-grid">' +
|
|
628
|
+
'<div class="struct-legend-item"><span class="struct-legend-code">list</span> Page principale — grille de données avec filtres, tri et actions (créer, exporter)</div>' +
|
|
629
|
+
'<div class="struct-legend-item"><span class="struct-legend-code">detail</span> Fiche détaillée — affichée au clic sur une ligne, avec onglets (infos, relations, historique)</div>' +
|
|
630
|
+
'<div class="struct-legend-item"><span class="struct-legend-code">dashboard</span> Tableau de bord — KPIs, graphiques et métriques du module</div>' +
|
|
631
|
+
'<div class="struct-legend-item"><span class="struct-legend-code">approve</span> Workflow — file de validation avec actions (approuver, rejeter, mettre en attente)</div>' +
|
|
632
|
+
'</div></div>';
|
|
633
|
+
|
|
634
|
+
return legend + sections.map(function(section) {
|
|
624
635
|
var resources = section.resources || [];
|
|
625
636
|
var sectionUCs = section.useCases || [];
|
|
626
637
|
var sectionBRs = section.businessRules || [];
|
|
@@ -681,7 +692,9 @@ function renderModuleStructure(code) {
|
|
|
681
692
|
/* ---------- Section-Grouped Rendering (Hierarchical Mode) ---------- */
|
|
682
693
|
|
|
683
694
|
function renderSectionGroupedItems(mod, itemsKey, code, renderFn) {
|
|
684
|
-
var sections = mod.anticipatedSections || []
|
|
695
|
+
var sections = (mod.anticipatedSections || []).map(function(s) {
|
|
696
|
+
return typeof s === 'string' ? { code: s, name: s, resources: [] } : s;
|
|
697
|
+
});
|
|
685
698
|
var html = '';
|
|
686
699
|
sections.forEach(function(section) {
|
|
687
700
|
var items = section[itemsKey] || [];
|
|
@@ -704,7 +717,9 @@ function renderModuleMockups(code) {
|
|
|
704
717
|
var screens = spec.screens || [];
|
|
705
718
|
var wireframes = EMBEDDED_ARTIFACTS?.wireframes?.[code] || [];
|
|
706
719
|
var mod = data.modules.find(function(m) { return m.code === code; });
|
|
707
|
-
var sections = mod ? (mod.anticipatedSections || [])
|
|
720
|
+
var sections = mod ? (mod.anticipatedSections || []).map(function(s) {
|
|
721
|
+
return typeof s === 'string' ? { code: s, name: s, resources: [] } : s;
|
|
722
|
+
}) : [];
|
|
708
723
|
|
|
709
724
|
// Priority 1: HTML mockups from screens[] specs
|
|
710
725
|
if (screens.length > 0) {
|
|
@@ -11,7 +11,7 @@ function renderScreenMockups(code) {
|
|
|
11
11
|
|
|
12
12
|
return screens.map(function(screen, si) {
|
|
13
13
|
var resources = screen.resources || [];
|
|
14
|
-
return '<div class="screen-section" style="margin-bottom:2rem;">' +
|
|
14
|
+
return '<div class="screen-section" id="screen-' + code + '-' + screen.sectionCode + '" style="margin-bottom:2rem;">' +
|
|
15
15
|
'<h3 style="color:var(--text-bright);font-size:1rem;margin-bottom:1rem;">' +
|
|
16
16
|
'<span style="color:var(--accent);">▸</span> ' + escapeHtml(screen.sectionLabel || screen.sectionCode) +
|
|
17
17
|
'</h3>' +
|
|
@@ -110,11 +110,11 @@
|
|
|
110
110
|
.nav-icon-section { font-size: 0.5rem; color: var(--accent); }
|
|
111
111
|
.nav-icon-resource { font-size: 0.7rem; color: var(--border-light); }
|
|
112
112
|
.nav-resource-link {
|
|
113
|
-
cursor:
|
|
113
|
+
cursor: pointer;
|
|
114
114
|
font-size: 0.72rem;
|
|
115
115
|
color: var(--text-muted);
|
|
116
116
|
padding-top: 0.2rem;
|
|
117
117
|
padding-bottom: 0.2rem;
|
|
118
118
|
}
|
|
119
|
-
.nav-resource-link:hover { background:
|
|
119
|
+
.nav-resource-link:hover { background: var(--bg-hover); color: var(--accent); }
|
|
120
120
|
.nav-resources { margin-left: 0.5rem; }
|
|
@@ -443,6 +443,44 @@
|
|
|
443
443
|
color: var(--text-muted);
|
|
444
444
|
}
|
|
445
445
|
|
|
446
|
+
/* ============================================
|
|
447
|
+
STRUCTURE LEGEND
|
|
448
|
+
============================================ */
|
|
449
|
+
.struct-legend {
|
|
450
|
+
background: var(--bg-card);
|
|
451
|
+
border: 1px solid var(--border);
|
|
452
|
+
border-radius: 10px;
|
|
453
|
+
padding: 0.75rem 1rem;
|
|
454
|
+
margin-bottom: 1.25rem;
|
|
455
|
+
}
|
|
456
|
+
.struct-legend-title {
|
|
457
|
+
font-size: 0.8rem;
|
|
458
|
+
font-weight: 600;
|
|
459
|
+
color: var(--text-muted);
|
|
460
|
+
margin-bottom: 0.5rem;
|
|
461
|
+
}
|
|
462
|
+
.struct-legend-grid {
|
|
463
|
+
display: grid;
|
|
464
|
+
grid-template-columns: 1fr 1fr;
|
|
465
|
+
gap: 0.4rem 1.5rem;
|
|
466
|
+
}
|
|
467
|
+
.struct-legend-item {
|
|
468
|
+
font-size: 0.78rem;
|
|
469
|
+
color: var(--text-muted);
|
|
470
|
+
line-height: 1.4;
|
|
471
|
+
}
|
|
472
|
+
.struct-legend-code {
|
|
473
|
+
display: inline-block;
|
|
474
|
+
font-weight: 600;
|
|
475
|
+
color: var(--accent);
|
|
476
|
+
background: rgba(6,182,212,0.1);
|
|
477
|
+
padding: 0.05rem 0.4rem;
|
|
478
|
+
border-radius: 3px;
|
|
479
|
+
font-size: 0.72rem;
|
|
480
|
+
margin-right: 0.3rem;
|
|
481
|
+
font-family: monospace;
|
|
482
|
+
}
|
|
483
|
+
|
|
446
484
|
/* ============================================
|
|
447
485
|
SECTION GROUPS (Hierarchical Mode)
|
|
448
486
|
============================================ */
|
|
@@ -51,7 +51,10 @@ const FEATURE_DATA = {
|
|
|
51
51
|
featureType: module.featureType || "data-centric",
|
|
52
52
|
estimatedComplexity: module.estimatedComplexity || "medium",
|
|
53
53
|
entities: module.entities || [],
|
|
54
|
-
|
|
54
|
+
// NORMALIZE: source may have strings (coverageMatrix) or objects (module schema)
|
|
55
|
+
anticipatedSections: (module.anticipatedSections || []).map(s =>
|
|
56
|
+
typeof s === 'string' ? { code: s, name: s, description: '', resources: [] } : s
|
|
57
|
+
), // ALWAYS [{code, name, description, resources[]}]
|
|
55
58
|
dependencies: module.dependencies || [],
|
|
56
59
|
dependents: module.dependents || []
|
|
57
60
|
}
|
|
@@ -57,7 +57,10 @@ Build a JSON object following this **exact mapping** from index.json to the HTML
|
|
|
57
57
|
description: m.description || "",
|
|
58
58
|
featureType: m.featureType || "data-centric",
|
|
59
59
|
entities: m.entities || [],
|
|
60
|
-
|
|
60
|
+
// NORMALIZE: source may have strings (coverageMatrix) or objects (module schema)
|
|
61
|
+
anticipatedSections: (m.anticipatedSections || []).map(s =>
|
|
62
|
+
typeof s === 'string' ? { code: s, name: s, description: '', resources: [] } : s
|
|
63
|
+
), // ALWAYS [{code, name, description, resources[]}]
|
|
61
64
|
dependencies: m.dependencies || [],
|
|
62
65
|
dependents: m.dependents || [],
|
|
63
66
|
estimatedComplexity: m.estimatedComplexity || "medium",
|