@atlashub/smartstack-cli 3.8.0 → 3.10.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/dist/index.js +365 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/templates/agents/action.md +1 -0
- package/templates/agents/ba-writer.md +211 -0
- package/templates/agents/explore-codebase.md +1 -0
- package/templates/agents/explore-docs.md +1 -0
- package/templates/agents/fix-grammar.md +1 -0
- package/templates/agents/snipper.md +1 -0
- package/templates/skills/admin/SKILL.md +6 -0
- package/templates/skills/ai-prompt/SKILL.md +32 -136
- package/templates/skills/ai-prompt/steps/step-01-implementation.md +122 -0
- package/templates/skills/apex/SKILL.md +120 -0
- package/templates/skills/apex/_shared.md +86 -0
- package/templates/skills/apex/references/agent-teams-protocol.md +164 -0
- package/templates/skills/apex/references/smartstack-layers.md +173 -0
- package/templates/skills/apex/steps/step-00-init.md +156 -0
- package/templates/skills/apex/steps/step-01-analyze.md +169 -0
- package/templates/skills/apex/steps/step-02-plan.md +160 -0
- package/templates/skills/apex/steps/step-03-execute.md +166 -0
- package/templates/skills/apex/steps/step-04-validate.md +138 -0
- package/templates/skills/apex/steps/step-05-examine.md +124 -0
- package/templates/skills/apex/steps/step-06-resolve.md +105 -0
- package/templates/skills/apex/steps/step-07-tests.md +130 -0
- package/templates/skills/apex/steps/step-08-run-tests.md +115 -0
- package/templates/skills/application/SKILL.md +10 -0
- package/templates/skills/application/references/application-roles-template.md +227 -0
- package/templates/skills/application/references/backend-controller-hierarchy.md +58 -0
- package/templates/skills/application/references/backend-entity-seeding.md +72 -0
- package/templates/skills/application/references/backend-verification.md +88 -0
- package/templates/skills/application/references/frontend-verification.md +111 -0
- package/templates/skills/application/references/nav-fallback-procedure.md +200 -0
- package/templates/skills/application/references/provider-template.md +158 -0
- package/templates/skills/application/references/test-frontend.md +73 -0
- package/templates/skills/application/references/test-prerequisites.md +72 -0
- package/templates/skills/application/steps/step-01-navigation.md +7 -198
- package/templates/skills/application/steps/step-03-roles.md +45 -7
- package/templates/skills/application/steps/step-03b-provider.md +15 -132
- package/templates/skills/application/steps/step-04-backend.md +20 -350
- package/templates/skills/application/steps/step-05-frontend.md +12 -101
- package/templates/skills/application/steps/step-07-tests.md +12 -132
- package/templates/skills/business-analyse/SKILL.md +67 -6
- package/templates/skills/business-analyse/html/ba-interactive.html +176 -14
- package/templates/skills/business-analyse/html/src/scripts/01-data-init.js +1 -0
- package/templates/skills/business-analyse/html/src/scripts/05-render-specs.js +16 -4
- package/templates/skills/business-analyse/html/src/scripts/06-render-consolidation.js +7 -2
- package/templates/skills/business-analyse/html/src/scripts/09-export.js +103 -0
- package/templates/skills/business-analyse/html/src/scripts/10-comments.js +12 -6
- package/templates/skills/business-analyse/html/src/scripts/11-review-panel.js +24 -2
- package/templates/skills/business-analyse/html/src/styles/08-review-panel.css +12 -0
- package/templates/skills/business-analyse/html/src/template.html +1 -0
- package/templates/skills/business-analyse/references/agent-pooling-best-practices.md +477 -0
- package/templates/skills/business-analyse/references/cache-warming-strategy.md +578 -0
- package/templates/skills/business-analyse/references/cadrage-structure-cards.md +78 -0
- package/templates/skills/business-analyse/references/cadrage-vibe-coding.md +97 -0
- package/templates/skills/business-analyse/references/consolidation-structural-checks.md +92 -0
- package/templates/skills/business-analyse/references/deploy-data-build.md +121 -0
- package/templates/skills/business-analyse/references/deploy-modes.md +49 -0
- package/templates/skills/business-analyse/references/handoff-file-templates.md +119 -0
- package/templates/skills/business-analyse/references/handoff-mappings.md +81 -0
- package/templates/skills/business-analyse/references/html-data-mapping.md +10 -2
- package/templates/skills/business-analyse/references/init-schema-deployment.md +65 -0
- package/templates/skills/business-analyse/references/review-data-mapping.md +363 -0
- package/templates/skills/business-analyse/references/robustness-checks.md +538 -0
- package/templates/skills/business-analyse/references/spec-auto-inference.md +57 -0
- package/templates/skills/business-analyse/references/ui-dashboard-spec.md +85 -0
- package/templates/skills/business-analyse/references/ui-resource-cards.md +110 -0
- package/templates/skills/business-analyse/references/validate-incremental-html.md +55 -0
- package/templates/skills/business-analyse/schemas/sections/specification-schema.json +33 -1
- package/templates/skills/business-analyse/steps/step-00-init.md +186 -53
- package/templates/skills/business-analyse/steps/step-01-cadrage.md +5 -194
- package/templates/skills/business-analyse/steps/step-03a-data.md +42 -49
- package/templates/skills/business-analyse/steps/step-03b-ui.md +12 -178
- package/templates/skills/business-analyse/steps/step-03c-compile.md +71 -2
- package/templates/skills/business-analyse/steps/step-03d-validate.md +277 -48
- package/templates/skills/business-analyse/steps/step-04-consolidation.md +175 -104
- package/templates/skills/business-analyse/steps/step-05a-handoff.md +66 -438
- package/templates/skills/business-analyse/steps/step-05b-deploy.md +35 -184
- package/templates/skills/business-analyse/steps/step-05c-ralph-readiness.md +526 -0
- package/templates/skills/business-analyse/steps/step-06-review.md +277 -0
- package/templates/skills/cc-agent/references/agent-behavior-patterns.md +95 -0
- package/templates/skills/cc-agent/steps/step-02-generate.md +5 -78
- package/templates/skills/check-version/SKILL.md +7 -0
- package/templates/skills/controller/references/controller-code-templates.md +159 -0
- package/templates/skills/controller/references/permission-sync-templates.md +152 -0
- package/templates/skills/controller/steps/step-03-generate.md +166 -158
- package/templates/skills/controller/steps/step-04-perms.md +5 -144
- package/templates/skills/controller/templates.md +11 -2
- package/templates/skills/debug/SKILL.md +7 -0
- package/templates/skills/explore/SKILL.md +6 -0
- package/templates/skills/feature-full/SKILL.md +39 -142
- package/templates/skills/feature-full/steps/step-01-implementation.md +120 -0
- package/templates/skills/gitflow/references/init-config-template.md +135 -0
- package/templates/skills/gitflow/references/init-name-normalization.md +103 -0
- package/templates/skills/gitflow/references/plan-template.md +69 -0
- package/templates/skills/gitflow/references/start-efcore-preflight.md +70 -0
- package/templates/skills/gitflow/references/start-local-config.md +110 -0
- package/templates/skills/gitflow/steps/step-init.md +18 -289
- package/templates/skills/gitflow/steps/step-plan.md +6 -63
- package/templates/skills/gitflow/steps/step-start.md +16 -126
- package/templates/skills/mcp/SKILL.md +9 -213
- package/templates/skills/mcp/steps/step-01-healthcheck.md +108 -0
- package/templates/skills/mcp/steps/step-02-tools.md +73 -0
- package/templates/skills/notification/SKILL.md +7 -0
- package/templates/skills/quick-search/SKILL.md +5 -0
- package/templates/skills/ralph-loop/SKILL.md +99 -381
- package/templates/skills/ralph-loop/references/category-rules.md +259 -0
- package/templates/skills/ralph-loop/references/compact-loop.md +182 -0
- package/templates/skills/ralph-loop/references/core-seed-data.md +173 -21
- package/templates/skills/ralph-loop/references/task-transform-legacy.md +259 -0
- package/templates/skills/ralph-loop/references/team-orchestration.md +189 -0
- package/templates/skills/ralph-loop/steps/step-00-init.md +111 -383
- package/templates/skills/ralph-loop/steps/step-01-task.md +79 -896
- package/templates/skills/ralph-loop/steps/step-02-execute.md +68 -680
- package/templates/skills/ralph-loop/steps/step-03-commit.md +47 -277
- package/templates/skills/ralph-loop/steps/step-04-check.md +124 -607
- package/templates/skills/ralph-loop/steps/step-05-report.md +68 -367
- package/templates/skills/refactor/SKILL.md +12 -176
- package/templates/skills/refactor/steps/step-01-discover.md +60 -0
- package/templates/skills/refactor/steps/step-02-execute.md +67 -0
- package/templates/skills/review-code/SKILL.md +19 -257
- package/templates/skills/review-code/steps/step-01-smartstack.md +96 -0
- package/templates/skills/review-code/steps/step-02-detailed-review.md +80 -0
- package/templates/skills/review-code/steps/step-03-react.md +44 -0
- package/templates/skills/ui-components/SKILL.md +7 -0
- package/templates/skills/utils/SKILL.md +6 -0
- package/templates/skills/validate/SKILL.md +6 -0
- package/templates/skills/validate-feature/SKILL.md +8 -0
- package/templates/skills/workflow/SKILL.md +40 -118
- package/templates/skills/workflow/steps/step-01-implementation.md +84 -0
|
@@ -35,12 +35,16 @@ The skill auto-detects which use case applies by scanning existing features in `
|
|
|
35
35
|
|
|
36
36
|
<parameters>
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
| Flag | Description |
|
|
39
|
+
|------|-------------|
|
|
40
|
+
| (aucun) | Mode standard — analyse complete ou mise a jour |
|
|
41
|
+
| `-review` | Mode review — lit `ba-review.json`, cree une nouvelle version avec les corrections appliquees |
|
|
39
42
|
|
|
40
|
-
Step-00 handles
|
|
43
|
+
Step-00 handles detection automatically:
|
|
41
44
|
- **New vs Update**: scans `docs/business/` for existing applications
|
|
42
45
|
- **Single vs Multi-module**: determined during step-02 decomposition
|
|
43
46
|
- **Language**: detected from config or asked once
|
|
47
|
+
- **Review mode**: if `-review` flag, routes directly to step-06
|
|
44
48
|
|
|
45
49
|
</parameters>
|
|
46
50
|
|
|
@@ -71,6 +75,52 @@ docs/business/
|
|
|
71
75
|
**No intermediate markdown files - all state in feature.json**
|
|
72
76
|
</output_structure>
|
|
73
77
|
|
|
78
|
+
<navigation_hierarchy>
|
|
79
|
+
**SmartStack uses a 5-level navigation structure mapped to database tables:**
|
|
80
|
+
|
|
81
|
+
| Level | Entity | Table DB | Created in Step | Description |
|
|
82
|
+
|-------|--------|----------|-----------------|-------------|
|
|
83
|
+
| 1 | Context | `core.nav_Contexts` | N/A | Fixed ("business", "platform", "system") |
|
|
84
|
+
| 2 | Application | `core.nav_Applications` | 00-01 | Top-level application (e.g., "RessourcesHumaines") |
|
|
85
|
+
| 3 | Module | `core.nav_Modules` | 02 | Functional module (e.g., "Employees", "TimeManagement") |
|
|
86
|
+
| 4 | **Section** | **`core.nav_Sections`** | **03a-03c** | **Page within module (e.g., "list", "detail", "create", "dashboard")** |
|
|
87
|
+
| 5 | **Resource** | **`core.nav_Resources`** | **03b-03c** | **React component (SmartTable, SmartForm, Chart, KpiCard, etc.)** |
|
|
88
|
+
|
|
89
|
+
**Hierarchical relationship:**
|
|
90
|
+
- Context → Application → Module → **Section** → **Resource**
|
|
91
|
+
- Each Section MUST have ≥1 Resource
|
|
92
|
+
- Each Section corresponds to 1 React page route
|
|
93
|
+
- Each Resource corresponds to 1 SmartStack UI component
|
|
94
|
+
|
|
95
|
+
**SeedData generation (step-03c):**
|
|
96
|
+
|
|
97
|
+
The `specification.seedDataCore` contains **7 mandatory arrays** for database seeding:
|
|
98
|
+
|
|
99
|
+
| Array | Table | Source | Description |
|
|
100
|
+
|-------|-------|--------|-------------|
|
|
101
|
+
| `navigationModules` | `core.nav_Modules` | Manual (step-03c) | Module entry (Level 3) |
|
|
102
|
+
| **`navigationSections`** | **`core.nav_Sections`** | **Derived from `specification.sections[]`** | **Section entries (Level 4)** |
|
|
103
|
+
| **`navigationResources`** | **`core.nav_Resources`** | **Derived from `specification.sections[].resources[]`** | **Resource entries (Level 5)** |
|
|
104
|
+
| `navigationTranslations` | `core.nav_Translations` | Manual (i18n) | Multi-language labels |
|
|
105
|
+
| `permissions` | `core.Permissions` | Manual (step-03c) | Permission definitions |
|
|
106
|
+
| `rolePermissions` | `core.RolePermissions` | Manual (step-03c) | Role-permission mappings |
|
|
107
|
+
| `permissionConstants` | `PermissionConstants.cs` | Manual (step-03c) | C# permission constants |
|
|
108
|
+
|
|
109
|
+
**Transform algorithm (step-03c section 8f-bis):**
|
|
110
|
+
- `navigationSections` = transform `specification.sections[]` → flatten to { code, label, icon, route, parentCode, permission, sort }
|
|
111
|
+
- `navigationResources` = transform `specification.sections[].resources[]` → flatten to { code, type, entity, parentCode, permission }
|
|
112
|
+
|
|
113
|
+
**Example:**
|
|
114
|
+
- Module: `Employees` (Level 3)
|
|
115
|
+
- Section: `list` (Level 4) → contains resources:
|
|
116
|
+
- Resource: `employees-grid` (SmartTable) (Level 5)
|
|
117
|
+
- Resource: `status-filter` (FilterBar) (Level 5)
|
|
118
|
+
- Section: `detail` (Level 4) → contains resources:
|
|
119
|
+
- Resource: `employee-card` (DetailCard) (Level 5)
|
|
120
|
+
- Resource: `contract-history` (SmartTable) (Level 5)
|
|
121
|
+
|
|
122
|
+
</navigation_hierarchy>
|
|
123
|
+
|
|
74
124
|
<resume_workflow>
|
|
75
125
|
**Update mode (auto-detected):**
|
|
76
126
|
|
|
@@ -100,14 +150,21 @@ When step-00 detects that the description matches an existing application:
|
|
|
100
150
|
- **Step 03c:** Per-module compile: actors, UCs, FRs, permissions, navigation, seed data, i18n
|
|
101
151
|
- **Step 03d:** Per-module validate: completeness checks, write feature.json, incremental HTML, loop
|
|
102
152
|
- Loop: 03a → 03b → 03c → 03d → 03a (next module) until all specified (specified)
|
|
103
|
-
- **Step
|
|
153
|
+
- **Step 04a:** Collect: module summaries, cross-module interactions (FK, events, shared data)
|
|
154
|
+
- **Step 04b:** Analyze: permission coherence, semantic validation, E2E flows, global risks
|
|
155
|
+
- **Step 04c:** Decide: final approval, write consolidation section, proceed to handoff (consolidated)
|
|
104
156
|
- **Step 05a:** Handoff: file mapping (7 categories), BR-to-code mapping, API summary, write to feature.json
|
|
105
157
|
- **Step 05b:** Deploy: prd.json, progress.txt, manifest, ba-interactive.html pre-populated (handed-off)
|
|
158
|
+
- **Step 05c:** Ralph Readiness Check: validation gate before /ralph-loop (optional but recommended)
|
|
106
159
|
|
|
107
160
|
**Update workflow (same phases, delta focus):**
|
|
108
161
|
- **Step 00:** Detection, locate existing feature, create version N+1
|
|
109
162
|
- **Step 01:** Cadrage delta: what changes, impact on existing
|
|
110
163
|
- **Step 02-05:** Same flow as new, with existing context loaded
|
|
164
|
+
|
|
165
|
+
**Review workflow (`-review` flag):**
|
|
166
|
+
- **Step 00:** Detection, scan for latest application, locate `ba-review.json`
|
|
167
|
+
- **Step 06:** Apply corrections, create new version, reverse-map data, regenerate all artifacts
|
|
111
168
|
</workflow>
|
|
112
169
|
|
|
113
170
|
<state_variables>
|
|
@@ -144,11 +201,15 @@ When step-00 detects that the description matches an existing application:
|
|
|
144
201
|
| 02 | `steps/step-02-decomposition.md` | Opus | Module decomposition, dependency graph, client checkpoint |
|
|
145
202
|
| 03a | `steps/step-03a-data.md` | Opus | Per-module: sections, entities, BRs, questionnaires |
|
|
146
203
|
| 03b | `steps/step-03b-ui.md` | Opus | Per-module: state machines, wireframes, layouts, dashboards |
|
|
147
|
-
| 03c | `steps/step-03c-compile.md` | Opus | Per-module: actors, UCs, FRs, permissions,
|
|
204
|
+
| 03c | `steps/step-03c-compile.md` | Opus | Per-module: actors, UCs, FRs, permissions, navigation, seedDataCore (7 arrays), i18n |
|
|
148
205
|
| 03d | `steps/step-03d-validate.md` | Sonnet | Per-module: validation, write, incremental HTML, loop decision |
|
|
149
|
-
|
|
|
206
|
+
| 04a | `steps/step-04a-collect.md` | Opus | Collect module summaries & cross-module interactions |
|
|
207
|
+
| 04b | `steps/step-04b-analyze.md` | Opus | Analyze permission coherence, semantic validation, E2E flows |
|
|
208
|
+
| 04c | `steps/step-04c-decide.md` | Opus | Final approval, write consolidation, proceed to handoff |
|
|
150
209
|
| 05a | `steps/step-05a-handoff.md` | Sonnet | Handoff: file mapping (7 categories), BR-to-code mapping, API summary, write handoff |
|
|
151
|
-
| 05b | `steps/step-05b-deploy.md` | Sonnet | Deploy: prd.json, progress.txt, manifest, ba-interactive.html
|
|
210
|
+
| 05b | `steps/step-05b-deploy.md` | Sonnet | Deploy: prd.json, progress.txt, manifest, ba-interactive.html |
|
|
211
|
+
| 05c | `steps/step-05c-ralph-readiness.md` | Sonnet | Ralph readiness validation gate (optional but recommended) |
|
|
212
|
+
| 06 | `steps/step-06-review.md` | Opus | Apply review corrections, create new version, regenerate all artifacts |
|
|
152
213
|
|
|
153
214
|
</step_files>
|
|
154
215
|
|
|
@@ -1380,6 +1380,18 @@ body {
|
|
|
1380
1380
|
font-size: 0.65rem;
|
|
1381
1381
|
}
|
|
1382
1382
|
|
|
1383
|
+
/* Review save button */
|
|
1384
|
+
.btn-review {
|
|
1385
|
+
background: var(--success);
|
|
1386
|
+
color: #fff;
|
|
1387
|
+
border-color: var(--success);
|
|
1388
|
+
font-weight: 600;
|
|
1389
|
+
}
|
|
1390
|
+
.btn-review:hover {
|
|
1391
|
+
background: #16a34a;
|
|
1392
|
+
border-color: #16a34a;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1383
1395
|
@media (max-width: 768px) {
|
|
1384
1396
|
.body.review-open {
|
|
1385
1397
|
grid-template-columns: 1fr;
|
|
@@ -1408,6 +1420,7 @@ body {
|
|
|
1408
1420
|
<div class="header-spacer"></div>
|
|
1409
1421
|
<div class="header-actions">
|
|
1410
1422
|
<button class="btn btn-sm" onclick="saveToLocalStorage()" title="Sauvegarder les modifications dans le navigateur">Sauvegarder</button>
|
|
1423
|
+
<button class="btn btn-sm btn-review" onclick="saveReviewJSON()" title="Sauvegarder les corrections pour creer une nouvelle version">Sauvegarder corrections</button>
|
|
1411
1424
|
<button class="btn btn-sm btn-primary" onclick="exportJSON()" title="Exporter les donnees au format JSON pour l'extraction">Exporter JSON</button>
|
|
1412
1425
|
<button class="btn btn-sm review-toggle-btn" id="reviewToggleBtn" onclick="toggleReviewPanel()" title="Ouvrir/fermer le panneau de review">
|
|
1413
1426
|
Review
|
|
@@ -2009,6 +2022,7 @@ body {
|
|
|
2009
2022
|
============================================ */
|
|
2010
2023
|
const APP_KEY = 'ba-{{APPLICATION_ID}}';
|
|
2011
2024
|
let data = {{FEATURE_DATA}};
|
|
2025
|
+
const ORIGINAL_DATA = JSON.parse(JSON.stringify(data));
|
|
2012
2026
|
const EMBEDDED_ARTIFACTS = {{EMBEDDED_ARTIFACTS}};
|
|
2013
2027
|
|
|
2014
2028
|
// Initialize optional data structures
|
|
@@ -2605,6 +2619,8 @@ function renderAllModuleSpecs() {
|
|
|
2605
2619
|
});
|
|
2606
2620
|
// Restore currently visible section after re-render
|
|
2607
2621
|
restoreCurrentSection();
|
|
2622
|
+
// Re-initialize inline comment buttons on module spec cards
|
|
2623
|
+
if (typeof initInlineComments === 'function') initInlineComments();
|
|
2608
2624
|
}
|
|
2609
2625
|
|
|
2610
2626
|
function renderModuleSpecSection(mod) {
|
|
@@ -3007,6 +3023,17 @@ function renderModuleMockups(code) {
|
|
|
3007
3023
|
}
|
|
3008
3024
|
|
|
3009
3025
|
function getPermRoles() {
|
|
3026
|
+
// Extract roles from actual permission data (handles English role names from feature.json)
|
|
3027
|
+
const rolesFromPerms = [];
|
|
3028
|
+
const seen = new Set();
|
|
3029
|
+
data.modules.forEach(m => {
|
|
3030
|
+
(data.moduleSpecs[m.code]?.permissions || []).forEach(p => {
|
|
3031
|
+
const role = p.split('|')[0];
|
|
3032
|
+
if (role && !seen.has(role)) { seen.add(role); rolesFromPerms.push(role); }
|
|
3033
|
+
});
|
|
3034
|
+
});
|
|
3035
|
+
if (rolesFromPerms.length > 0) return [...rolesFromPerms, ...(data.customRoles || [])];
|
|
3036
|
+
// Fallback: use stakeholder names if no permission data exists
|
|
3010
3037
|
const baseRoles = data.cadrage.stakeholders.length > 0
|
|
3011
3038
|
? data.cadrage.stakeholders.map(s => s.role)
|
|
3012
3039
|
: ['Administrateur', 'Responsable', 'Contributeur', 'Lecteur'];
|
|
@@ -3021,9 +3048,7 @@ function getPermActions() {
|
|
|
3021
3048
|
function renderPermissionGrid(code) {
|
|
3022
3049
|
const roles = getPermRoles();
|
|
3023
3050
|
const actions = getPermActions();
|
|
3024
|
-
const baseRolesCount = data.
|
|
3025
|
-
? data.cadrage.stakeholders.length
|
|
3026
|
-
: 4;
|
|
3051
|
+
const baseRolesCount = roles.length - (data.customRoles || []).length;
|
|
3027
3052
|
const baseActionsCount = 6;
|
|
3028
3053
|
|
|
3029
3054
|
const perms = data.moduleSpecs[code]?.permissions || [];
|
|
@@ -3040,7 +3065,8 @@ function renderPermissionGrid(code) {
|
|
|
3040
3065
|
<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>
|
|
3041
3066
|
${actions.map(action => {
|
|
3042
3067
|
const key = role + '|' + action;
|
|
3043
|
-
const
|
|
3068
|
+
const wildcardKey = role + '|*';
|
|
3069
|
+
const checked = perms.includes(key) || perms.includes(wildcardKey);
|
|
3044
3070
|
return `<td style="text-align:center;"><input type="checkbox" ${checked ? 'checked' : ''} onchange="togglePermission('${code}','${key}',this.checked)" style="cursor:pointer;width:16px;height:16px;"></td>`;
|
|
3045
3071
|
}).join('')}
|
|
3046
3072
|
</tr>
|
|
@@ -3171,10 +3197,15 @@ function renderConsolPermissions() {
|
|
|
3171
3197
|
roles.forEach(role => {
|
|
3172
3198
|
html += `<tr><td style="font-weight:500;color:var(--text-bright);">${role}</td>`;
|
|
3173
3199
|
data.modules.forEach(m => {
|
|
3174
|
-
const
|
|
3200
|
+
const allPerms = data.moduleSpecs[m.code]?.permissions || [];
|
|
3201
|
+
const hasWildcard = allPerms.includes(role + '|*');
|
|
3202
|
+
const perms = hasWildcard
|
|
3203
|
+
? getPermActions()
|
|
3204
|
+
: allPerms.filter(p => p.startsWith(role + '|'));
|
|
3175
3205
|
const count = perms.length;
|
|
3176
3206
|
const color = count > 4 ? 'var(--success)' : count > 2 ? 'var(--warning)' : count > 0 ? 'var(--text-muted)' : 'var(--border)';
|
|
3177
|
-
|
|
3207
|
+
const label = hasWildcard ? 'Tous' : (count > 0 ? count + ' droits' : 'Aucun');
|
|
3208
|
+
html += `<td style="text-align:center;color:${color};font-weight:600;">${label}</td>`;
|
|
3178
3209
|
});
|
|
3179
3210
|
html += '</tr>';
|
|
3180
3211
|
});
|
|
@@ -3446,6 +3477,109 @@ function exportJSON() {
|
|
|
3446
3477
|
showNotification('Export JSON telecharge');
|
|
3447
3478
|
}
|
|
3448
3479
|
|
|
3480
|
+
/* ============================================
|
|
3481
|
+
SAVE REVIEW JSON (corrections for new version)
|
|
3482
|
+
============================================ */
|
|
3483
|
+
function detectChanges(original, current) {
|
|
3484
|
+
const changes = { cadrage: false, modulesAdded: [], modulesRemoved: [], modulesModified: [], commentsCount: 0 };
|
|
3485
|
+
|
|
3486
|
+
// Cadrage changes
|
|
3487
|
+
if (JSON.stringify(original.cadrage) !== JSON.stringify(current.cadrage)) {
|
|
3488
|
+
changes.cadrage = true;
|
|
3489
|
+
}
|
|
3490
|
+
|
|
3491
|
+
// Module changes
|
|
3492
|
+
const origCodes = (original.modules || []).map(m => m.code);
|
|
3493
|
+
const currCodes = (current.modules || []).map(m => m.code);
|
|
3494
|
+
changes.modulesAdded = currCodes.filter(c => !origCodes.includes(c));
|
|
3495
|
+
changes.modulesRemoved = origCodes.filter(c => !currCodes.includes(c));
|
|
3496
|
+
currCodes.filter(c => origCodes.includes(c)).forEach(code => {
|
|
3497
|
+
const origSpec = JSON.stringify(original.moduleSpecs?.[code] || {});
|
|
3498
|
+
const currSpec = JSON.stringify(current.moduleSpecs?.[code] || {});
|
|
3499
|
+
if (origSpec !== currSpec) changes.modulesModified.push(code);
|
|
3500
|
+
});
|
|
3501
|
+
|
|
3502
|
+
// Comments count
|
|
3503
|
+
changes.commentsCount = (current.comments || []).length;
|
|
3504
|
+
|
|
3505
|
+
return changes;
|
|
3506
|
+
}
|
|
3507
|
+
|
|
3508
|
+
function saveReviewJSON() {
|
|
3509
|
+
// Collect all editable fields (same as exportJSON)
|
|
3510
|
+
document.querySelectorAll('.editable[data-field]').forEach(el => {
|
|
3511
|
+
setNestedValue(data, 'cadrage.' + el.dataset.field, el.textContent.trim());
|
|
3512
|
+
});
|
|
3513
|
+
document.querySelectorAll('.editable[data-module-field]').forEach(el => {
|
|
3514
|
+
const code = el.dataset.moduleCode;
|
|
3515
|
+
const field = el.dataset.moduleField;
|
|
3516
|
+
if (data.moduleSpecs[code]) {
|
|
3517
|
+
data.moduleSpecs[code][field] = el.textContent.trim();
|
|
3518
|
+
}
|
|
3519
|
+
});
|
|
3520
|
+
|
|
3521
|
+
const changes = detectChanges(ORIGINAL_DATA, data);
|
|
3522
|
+
const hasChanges = changes.cadrage || changes.modulesAdded.length > 0
|
|
3523
|
+
|| changes.modulesRemoved.length > 0 || changes.modulesModified.length > 0
|
|
3524
|
+
|| changes.commentsCount > 0;
|
|
3525
|
+
|
|
3526
|
+
// Build review export with metadata envelope
|
|
3527
|
+
const reviewData = {
|
|
3528
|
+
_reviewMeta: {
|
|
3529
|
+
sourceVersion: data.metadata.version || '1.0',
|
|
3530
|
+
sourceApplicationId: data.metadata.applicationId || '',
|
|
3531
|
+
exportedAt: new Date().toISOString(),
|
|
3532
|
+
hasChanges: hasChanges,
|
|
3533
|
+
changeSummary: {
|
|
3534
|
+
cadrageModified: changes.cadrage,
|
|
3535
|
+
modulesAdded: changes.modulesAdded,
|
|
3536
|
+
modulesRemoved: changes.modulesRemoved,
|
|
3537
|
+
modulesModified: changes.modulesModified,
|
|
3538
|
+
commentsCount: changes.commentsCount
|
|
3539
|
+
}
|
|
3540
|
+
},
|
|
3541
|
+
metadata: data.metadata,
|
|
3542
|
+
cadrage: data.cadrage,
|
|
3543
|
+
modules: data.modules,
|
|
3544
|
+
dependencies: data.dependencies,
|
|
3545
|
+
moduleSpecifications: {},
|
|
3546
|
+
consolidation: data.consolidation,
|
|
3547
|
+
wireframeComments: data.wireframeComments,
|
|
3548
|
+
specComments: data.specComments,
|
|
3549
|
+
customRoles: data.customRoles,
|
|
3550
|
+
customActions: data.customActions,
|
|
3551
|
+
comments: data.comments
|
|
3552
|
+
};
|
|
3553
|
+
|
|
3554
|
+
// Structure module specs (same logic as exportJSON)
|
|
3555
|
+
data.modules.forEach(m => {
|
|
3556
|
+
const spec = data.moduleSpecs[m.code] || {};
|
|
3557
|
+
reviewData.moduleSpecifications[m.code] = {
|
|
3558
|
+
module: m,
|
|
3559
|
+
useCases: (spec.useCases || []).map((uc, i) => ({
|
|
3560
|
+
id: 'UC-' + String(i + 1).padStart(3, '0'),
|
|
3561
|
+
...uc
|
|
3562
|
+
})),
|
|
3563
|
+
businessRules: (spec.businessRules || []).map((br, i) => ({
|
|
3564
|
+
id: 'BR-' + br.category.toUpperCase().substring(0, 4) + '-' + String(i + 1).padStart(3, '0'),
|
|
3565
|
+
...br
|
|
3566
|
+
})),
|
|
3567
|
+
entities: spec.entities || [],
|
|
3568
|
+
permissions: spec.permissions || [],
|
|
3569
|
+
notes: spec.notes || ''
|
|
3570
|
+
};
|
|
3571
|
+
});
|
|
3572
|
+
|
|
3573
|
+
const blob = new Blob([JSON.stringify(reviewData, null, 2)], { type: 'application/json' });
|
|
3574
|
+
const url = URL.createObjectURL(blob);
|
|
3575
|
+
const a = document.createElement('a');
|
|
3576
|
+
a.href = url;
|
|
3577
|
+
a.download = 'ba-review.json';
|
|
3578
|
+
a.click();
|
|
3579
|
+
URL.revokeObjectURL(url);
|
|
3580
|
+
showNotification('ba-review.json telecharge — sauvegardez-le a cote du HTML');
|
|
3581
|
+
}
|
|
3582
|
+
|
|
3449
3583
|
|
|
3450
3584
|
/* --- 10-comments.js --- */
|
|
3451
3585
|
/* ============================================
|
|
@@ -3463,14 +3597,20 @@ function exportJSON() {
|
|
|
3463
3597
|
*/
|
|
3464
3598
|
|
|
3465
3599
|
function initInlineComments() {
|
|
3466
|
-
// Add comment buttons under each card and uc-item
|
|
3600
|
+
// Add comment buttons under each card and uc-item (direct children of sections)
|
|
3467
3601
|
document.querySelectorAll('.section').forEach(section => {
|
|
3468
3602
|
const sectionId = section.id;
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3603
|
+
section.querySelectorAll(':scope > .card, :scope > .uc-item').forEach((card, index) => {
|
|
3604
|
+
if (card.querySelector('.comment-btn-container')) return;
|
|
3605
|
+
card.appendChild(createCommentUI(sectionId, index));
|
|
3606
|
+
});
|
|
3607
|
+
});
|
|
3608
|
+
// Second pass: nested list containers in module specs (ucList-*, brList-*, entList-*)
|
|
3609
|
+
document.querySelectorAll('[id^="ucList-"], [id^="brList-"], [id^="entList-"]').forEach(list => {
|
|
3610
|
+
const listId = list.id;
|
|
3611
|
+
list.querySelectorAll(':scope > .uc-item, :scope > .entity-block, :scope > div').forEach((item, index) => {
|
|
3612
|
+
if (item.querySelector('.comment-btn-container')) return;
|
|
3613
|
+
item.appendChild(createCommentUI(listId, index));
|
|
3474
3614
|
});
|
|
3475
3615
|
});
|
|
3476
3616
|
}
|
|
@@ -3738,12 +3878,34 @@ function getSectionLabel(sectionId) {
|
|
|
3738
3878
|
const mod = data.modules.find(m => m.code === code);
|
|
3739
3879
|
return mod ? mod.name : code;
|
|
3740
3880
|
}
|
|
3881
|
+
// Handle list-based sectionIds (ucList-*, brList-*, entList-*)
|
|
3882
|
+
const listMatch = sectionId.match(/^(uc|br|ent)List-(.+)$/);
|
|
3883
|
+
if (listMatch) {
|
|
3884
|
+
const tabLabels = { uc: 'Cas d\'utilisation', br: 'Regles metier', ent: 'Donnees' };
|
|
3885
|
+
const mod = data.modules.find(m => m.code === listMatch[2]);
|
|
3886
|
+
const modName = mod ? mod.name : listMatch[2];
|
|
3887
|
+
return modName + ' > ' + (tabLabels[listMatch[1]] || listMatch[1]);
|
|
3888
|
+
}
|
|
3741
3889
|
return sectionId;
|
|
3742
3890
|
}
|
|
3743
3891
|
|
|
3744
3892
|
function navigateToComment(sectionId, cardIndex) {
|
|
3745
|
-
|
|
3746
|
-
|
|
3893
|
+
// Handle list-based sectionIds (ucList-*, brList-*, entList-*) → navigate to module + tab
|
|
3894
|
+
const listMatch = sectionId.match(/^(uc|br|ent)List-(.+)$/);
|
|
3895
|
+
if (listMatch) {
|
|
3896
|
+
const [, tabType, moduleCode] = listMatch;
|
|
3897
|
+
showSection('module-spec-' + moduleCode);
|
|
3898
|
+
setTimeout(() => {
|
|
3899
|
+
switchTab(moduleCode, tabType);
|
|
3900
|
+
scrollToCommentThread(sectionId, cardIndex);
|
|
3901
|
+
}, 150);
|
|
3902
|
+
} else {
|
|
3903
|
+
showSection(sectionId);
|
|
3904
|
+
scrollToCommentThread(sectionId, cardIndex);
|
|
3905
|
+
}
|
|
3906
|
+
}
|
|
3907
|
+
|
|
3908
|
+
function scrollToCommentThread(sectionId, cardIndex) {
|
|
3747
3909
|
setTimeout(() => {
|
|
3748
3910
|
const thread = document.getElementById('comment-thread-' + sectionId + '-' + cardIndex);
|
|
3749
3911
|
if (thread && !thread.classList.contains('visible')) {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
============================================ */
|
|
4
4
|
const APP_KEY = 'ba-{{APPLICATION_ID}}';
|
|
5
5
|
let data = {{FEATURE_DATA}};
|
|
6
|
+
const ORIGINAL_DATA = JSON.parse(JSON.stringify(data));
|
|
6
7
|
const EMBEDDED_ARTIFACTS = {{EMBEDDED_ARTIFACTS}};
|
|
7
8
|
|
|
8
9
|
// Initialize optional data structures
|
|
@@ -17,6 +17,8 @@ function renderAllModuleSpecs() {
|
|
|
17
17
|
});
|
|
18
18
|
// Restore currently visible section after re-render
|
|
19
19
|
restoreCurrentSection();
|
|
20
|
+
// Re-initialize inline comment buttons on module spec cards
|
|
21
|
+
if (typeof initInlineComments === 'function') initInlineComments();
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
function renderModuleSpecSection(mod) {
|
|
@@ -419,6 +421,17 @@ function renderModuleMockups(code) {
|
|
|
419
421
|
}
|
|
420
422
|
|
|
421
423
|
function getPermRoles() {
|
|
424
|
+
// Extract roles from actual permission data (handles English role names from feature.json)
|
|
425
|
+
const rolesFromPerms = [];
|
|
426
|
+
const seen = new Set();
|
|
427
|
+
data.modules.forEach(m => {
|
|
428
|
+
(data.moduleSpecs[m.code]?.permissions || []).forEach(p => {
|
|
429
|
+
const role = p.split('|')[0];
|
|
430
|
+
if (role && !seen.has(role)) { seen.add(role); rolesFromPerms.push(role); }
|
|
431
|
+
});
|
|
432
|
+
});
|
|
433
|
+
if (rolesFromPerms.length > 0) return [...rolesFromPerms, ...(data.customRoles || [])];
|
|
434
|
+
// Fallback: use stakeholder names if no permission data exists
|
|
422
435
|
const baseRoles = data.cadrage.stakeholders.length > 0
|
|
423
436
|
? data.cadrage.stakeholders.map(s => s.role)
|
|
424
437
|
: ['Administrateur', 'Responsable', 'Contributeur', 'Lecteur'];
|
|
@@ -433,9 +446,7 @@ function getPermActions() {
|
|
|
433
446
|
function renderPermissionGrid(code) {
|
|
434
447
|
const roles = getPermRoles();
|
|
435
448
|
const actions = getPermActions();
|
|
436
|
-
const baseRolesCount = data.
|
|
437
|
-
? data.cadrage.stakeholders.length
|
|
438
|
-
: 4;
|
|
449
|
+
const baseRolesCount = roles.length - (data.customRoles || []).length;
|
|
439
450
|
const baseActionsCount = 6;
|
|
440
451
|
|
|
441
452
|
const perms = data.moduleSpecs[code]?.permissions || [];
|
|
@@ -452,7 +463,8 @@ function renderPermissionGrid(code) {
|
|
|
452
463
|
<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>
|
|
453
464
|
${actions.map(action => {
|
|
454
465
|
const key = role + '|' + action;
|
|
455
|
-
const
|
|
466
|
+
const wildcardKey = role + '|*';
|
|
467
|
+
const checked = perms.includes(key) || perms.includes(wildcardKey);
|
|
456
468
|
return `<td style="text-align:center;"><input type="checkbox" ${checked ? 'checked' : ''} onchange="togglePermission('${code}','${key}',this.checked)" style="cursor:pointer;width:16px;height:16px;"></td>`;
|
|
457
469
|
}).join('')}
|
|
458
470
|
</tr>
|
|
@@ -38,10 +38,15 @@ function renderConsolPermissions() {
|
|
|
38
38
|
roles.forEach(role => {
|
|
39
39
|
html += `<tr><td style="font-weight:500;color:var(--text-bright);">${role}</td>`;
|
|
40
40
|
data.modules.forEach(m => {
|
|
41
|
-
const
|
|
41
|
+
const allPerms = data.moduleSpecs[m.code]?.permissions || [];
|
|
42
|
+
const hasWildcard = allPerms.includes(role + '|*');
|
|
43
|
+
const perms = hasWildcard
|
|
44
|
+
? getPermActions()
|
|
45
|
+
: allPerms.filter(p => p.startsWith(role + '|'));
|
|
42
46
|
const count = perms.length;
|
|
43
47
|
const color = count > 4 ? 'var(--success)' : count > 2 ? 'var(--warning)' : count > 0 ? 'var(--text-muted)' : 'var(--border)';
|
|
44
|
-
|
|
48
|
+
const label = hasWildcard ? 'Tous' : (count > 0 ? count + ' droits' : 'Aucun');
|
|
49
|
+
html += `<td style="text-align:center;color:${color};font-weight:600;">${label}</td>`;
|
|
45
50
|
});
|
|
46
51
|
html += '</tr>';
|
|
47
52
|
});
|
|
@@ -63,3 +63,106 @@ function exportJSON() {
|
|
|
63
63
|
URL.revokeObjectURL(url);
|
|
64
64
|
showNotification('Export JSON telecharge');
|
|
65
65
|
}
|
|
66
|
+
|
|
67
|
+
/* ============================================
|
|
68
|
+
SAVE REVIEW JSON (corrections for new version)
|
|
69
|
+
============================================ */
|
|
70
|
+
function detectChanges(original, current) {
|
|
71
|
+
const changes = { cadrage: false, modulesAdded: [], modulesRemoved: [], modulesModified: [], commentsCount: 0 };
|
|
72
|
+
|
|
73
|
+
// Cadrage changes
|
|
74
|
+
if (JSON.stringify(original.cadrage) !== JSON.stringify(current.cadrage)) {
|
|
75
|
+
changes.cadrage = true;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Module changes
|
|
79
|
+
const origCodes = (original.modules || []).map(m => m.code);
|
|
80
|
+
const currCodes = (current.modules || []).map(m => m.code);
|
|
81
|
+
changes.modulesAdded = currCodes.filter(c => !origCodes.includes(c));
|
|
82
|
+
changes.modulesRemoved = origCodes.filter(c => !currCodes.includes(c));
|
|
83
|
+
currCodes.filter(c => origCodes.includes(c)).forEach(code => {
|
|
84
|
+
const origSpec = JSON.stringify(original.moduleSpecs?.[code] || {});
|
|
85
|
+
const currSpec = JSON.stringify(current.moduleSpecs?.[code] || {});
|
|
86
|
+
if (origSpec !== currSpec) changes.modulesModified.push(code);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Comments count
|
|
90
|
+
changes.commentsCount = (current.comments || []).length;
|
|
91
|
+
|
|
92
|
+
return changes;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function saveReviewJSON() {
|
|
96
|
+
// Collect all editable fields (same as exportJSON)
|
|
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
|
+
});
|
|
107
|
+
|
|
108
|
+
const changes = detectChanges(ORIGINAL_DATA, data);
|
|
109
|
+
const hasChanges = changes.cadrage || changes.modulesAdded.length > 0
|
|
110
|
+
|| changes.modulesRemoved.length > 0 || changes.modulesModified.length > 0
|
|
111
|
+
|| changes.commentsCount > 0;
|
|
112
|
+
|
|
113
|
+
// Build review export with metadata envelope
|
|
114
|
+
const reviewData = {
|
|
115
|
+
_reviewMeta: {
|
|
116
|
+
sourceVersion: data.metadata.version || '1.0',
|
|
117
|
+
sourceApplicationId: data.metadata.applicationId || '',
|
|
118
|
+
exportedAt: new Date().toISOString(),
|
|
119
|
+
hasChanges: hasChanges,
|
|
120
|
+
changeSummary: {
|
|
121
|
+
cadrageModified: changes.cadrage,
|
|
122
|
+
modulesAdded: changes.modulesAdded,
|
|
123
|
+
modulesRemoved: changes.modulesRemoved,
|
|
124
|
+
modulesModified: changes.modulesModified,
|
|
125
|
+
commentsCount: changes.commentsCount
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
metadata: data.metadata,
|
|
129
|
+
cadrage: data.cadrage,
|
|
130
|
+
modules: data.modules,
|
|
131
|
+
dependencies: data.dependencies,
|
|
132
|
+
moduleSpecifications: {},
|
|
133
|
+
consolidation: data.consolidation,
|
|
134
|
+
wireframeComments: data.wireframeComments,
|
|
135
|
+
specComments: data.specComments,
|
|
136
|
+
customRoles: data.customRoles,
|
|
137
|
+
customActions: data.customActions,
|
|
138
|
+
comments: data.comments
|
|
139
|
+
};
|
|
140
|
+
|
|
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
|
+
const blob = new Blob([JSON.stringify(reviewData, null, 2)], { type: 'application/json' });
|
|
161
|
+
const url = URL.createObjectURL(blob);
|
|
162
|
+
const a = document.createElement('a');
|
|
163
|
+
a.href = url;
|
|
164
|
+
a.download = 'ba-review.json';
|
|
165
|
+
a.click();
|
|
166
|
+
URL.revokeObjectURL(url);
|
|
167
|
+
showNotification('ba-review.json telecharge — sauvegardez-le a cote du HTML');
|
|
168
|
+
}
|
|
@@ -13,14 +13,20 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
function initInlineComments() {
|
|
16
|
-
// Add comment buttons under each card and uc-item
|
|
16
|
+
// Add comment buttons under each card and uc-item (direct children of sections)
|
|
17
17
|
document.querySelectorAll('.section').forEach(section => {
|
|
18
18
|
const sectionId = section.id;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
section.querySelectorAll(':scope > .card, :scope > .uc-item').forEach((card, index) => {
|
|
20
|
+
if (card.querySelector('.comment-btn-container')) return;
|
|
21
|
+
card.appendChild(createCommentUI(sectionId, index));
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
// Second pass: nested list containers in module specs (ucList-*, brList-*, entList-*)
|
|
25
|
+
document.querySelectorAll('[id^="ucList-"], [id^="brList-"], [id^="entList-"]').forEach(list => {
|
|
26
|
+
const listId = list.id;
|
|
27
|
+
list.querySelectorAll(':scope > .uc-item, :scope > .entity-block, :scope > div').forEach((item, index) => {
|
|
28
|
+
if (item.querySelector('.comment-btn-container')) return;
|
|
29
|
+
item.appendChild(createCommentUI(listId, index));
|
|
24
30
|
});
|
|
25
31
|
});
|
|
26
32
|
}
|
|
@@ -120,12 +120,34 @@ function getSectionLabel(sectionId) {
|
|
|
120
120
|
const mod = data.modules.find(m => m.code === code);
|
|
121
121
|
return mod ? mod.name : code;
|
|
122
122
|
}
|
|
123
|
+
// Handle list-based sectionIds (ucList-*, brList-*, entList-*)
|
|
124
|
+
const listMatch = sectionId.match(/^(uc|br|ent)List-(.+)$/);
|
|
125
|
+
if (listMatch) {
|
|
126
|
+
const tabLabels = { uc: 'Cas d\'utilisation', br: 'Regles metier', ent: 'Donnees' };
|
|
127
|
+
const mod = data.modules.find(m => m.code === listMatch[2]);
|
|
128
|
+
const modName = mod ? mod.name : listMatch[2];
|
|
129
|
+
return modName + ' > ' + (tabLabels[listMatch[1]] || listMatch[1]);
|
|
130
|
+
}
|
|
123
131
|
return sectionId;
|
|
124
132
|
}
|
|
125
133
|
|
|
126
134
|
function navigateToComment(sectionId, cardIndex) {
|
|
127
|
-
|
|
128
|
-
|
|
135
|
+
// Handle list-based sectionIds (ucList-*, brList-*, entList-*) → navigate to module + tab
|
|
136
|
+
const listMatch = sectionId.match(/^(uc|br|ent)List-(.+)$/);
|
|
137
|
+
if (listMatch) {
|
|
138
|
+
const [, tabType, moduleCode] = listMatch;
|
|
139
|
+
showSection('module-spec-' + moduleCode);
|
|
140
|
+
setTimeout(() => {
|
|
141
|
+
switchTab(moduleCode, tabType);
|
|
142
|
+
scrollToCommentThread(sectionId, cardIndex);
|
|
143
|
+
}, 150);
|
|
144
|
+
} else {
|
|
145
|
+
showSection(sectionId);
|
|
146
|
+
scrollToCommentThread(sectionId, cardIndex);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function scrollToCommentThread(sectionId, cardIndex) {
|
|
129
151
|
setTimeout(() => {
|
|
130
152
|
const thread = document.getElementById('comment-thread-' + sectionId + '-' + cardIndex);
|
|
131
153
|
if (thread && !thread.classList.contains('visible')) {
|
|
@@ -215,6 +215,18 @@
|
|
|
215
215
|
font-size: 0.65rem;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
/* Review save button */
|
|
219
|
+
.btn-review {
|
|
220
|
+
background: var(--success);
|
|
221
|
+
color: #fff;
|
|
222
|
+
border-color: var(--success);
|
|
223
|
+
font-weight: 600;
|
|
224
|
+
}
|
|
225
|
+
.btn-review:hover {
|
|
226
|
+
background: #16a34a;
|
|
227
|
+
border-color: #16a34a;
|
|
228
|
+
}
|
|
229
|
+
|
|
218
230
|
@media (max-width: 768px) {
|
|
219
231
|
.body.review-open {
|
|
220
232
|
grid-template-columns: 1fr;
|