@atlashub/smartstack-cli 4.74.0 → 4.76.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 +152 -31
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +14 -3
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/agents/ba-reader.md +17 -15
- package/templates/agents/ba-writer.md +49 -51
- package/templates/skills/apex/SKILL.md +2 -2
- package/templates/skills/apex/_shared.md +1 -1
- package/templates/skills/apex/references/checks/backend-checks.sh +21 -7
- package/templates/skills/apex/references/checks/frontend-checks.sh +26 -0
- package/templates/skills/apex/references/checks/infrastructure-checks.sh +47 -10
- package/templates/skills/apex/references/checks/seed-checks.sh +47 -7
- package/templates/skills/apex/references/core-seed-data.md +20 -18
- package/templates/skills/apex/references/frontend-route-wiring-app-tsx.md +3 -0
- package/templates/skills/apex/references/post-checks.md +23 -3
- package/templates/skills/apex/references/smartstack-api.md +4 -4
- package/templates/skills/apex/references/smartstack-frontend.md +54 -8
- package/templates/skills/apex/references/smartstack-layers.md +6 -6
- package/templates/skills/apex/steps/step-00-init.md +75 -1
- package/templates/skills/apex/steps/step-03-execute.md +16 -4
- package/templates/skills/apex/steps/step-03b-layer1-seed.md +65 -6
- package/templates/skills/apex/steps/step-03c-layer2-backend.md +50 -5
- package/templates/skills/apex/steps/step-03d-layer3-frontend.md +226 -4
- package/templates/skills/apex/steps/step-04-examine.md +163 -0
- package/templates/skills/apex-verify/SKILL.md +110 -0
- package/templates/skills/apex-verify/references/audit-rules.md +50 -0
- package/templates/skills/apex-verify/steps/step-00-init.md +119 -0
- package/templates/skills/apex-verify/steps/step-01-nav-audit.md +92 -0
- package/templates/skills/apex-verify/steps/step-02-crud-audit.md +127 -0
- package/templates/skills/apex-verify/steps/step-03-perm-audit.md +119 -0
- package/templates/skills/apex-verify/steps/step-04-route-audit.md +98 -0
- package/templates/skills/apex-verify/steps/step-05-report.md +110 -0
- package/templates/skills/application/references/frontend-route-wiring-app-tsx.md +3 -0
- package/templates/skills/application/templates-frontend.md +2 -2
- package/templates/skills/business-analyse/SKILL.md +17 -3
- package/templates/skills/business-analyse/_shared.md +64 -0
- package/templates/skills/business-analyse/patterns/suggestion-catalog.md +34 -26
- package/templates/skills/business-analyse/questionnaire/01-context.md +13 -9
- package/templates/skills/business-analyse/questionnaire/02-stakeholders-scope.md +20 -27
- package/templates/skills/business-analyse/questionnaire.md +86 -9
- package/templates/skills/business-analyse/references/03-json-schemas.md +221 -0
- package/templates/skills/business-analyse/references/03-post-check-validation.md +208 -0
- package/templates/skills/business-analyse/references/03-smartstack-entity-guards.md +32 -0
- package/templates/skills/business-analyse/references/04-cross-module-validation.md +95 -0
- package/templates/skills/business-analyse/references/04-file-allocation.md +162 -0
- package/templates/skills/business-analyse/references/04-naming-audit-checks.md +174 -0
- package/templates/skills/business-analyse/references/04-semantic-validation-matrix.md +118 -0
- package/templates/skills/business-analyse/references/canonical-json-formats.md +7 -3
- package/templates/skills/business-analyse/references/domain-research-playbook.md +234 -0
- package/templates/skills/business-analyse/references/entity-sourcing-presentation.md +166 -0
- package/templates/skills/business-analyse/references/init-resume-logic.md +70 -0
- package/templates/skills/business-analyse/references/module-completeness-challenge.md +174 -0
- package/templates/skills/business-analyse/references/multi-app-detection.md +149 -0
- package/templates/skills/business-analyse/references/portal-classification.md +52 -0
- package/templates/skills/business-analyse/references/robustness-checks.md +1 -1
- package/templates/skills/business-analyse/references/validation-checklist.md +35 -6
- package/templates/skills/business-analyse/schemas/sections/analysis-schema.json +50 -6
- package/templates/skills/business-analyse/steps/step-00-init.md +22 -190
- package/templates/skills/business-analyse/steps/step-01-cadrage.md +365 -269
- package/templates/skills/business-analyse/steps/step-02-structure.md +98 -20
- package/templates/skills/business-analyse/steps/step-03-specify.md +810 -229
- package/templates/skills/business-analyse/steps/step-04-consolidate.md +509 -278
- package/templates/skills/business-analyse-design/SKILL.md +10 -0
- package/templates/skills/business-analyse-design/references/screens-post-check.md +221 -0
- package/templates/skills/business-analyse-design/references/screens-type-mapping.md +138 -0
- package/templates/skills/business-analyse-design/references/smartcomponents-templates.md +225 -0
- package/templates/skills/{business-analyse → business-analyse-design}/references/spec-auto-inference.md +117 -117
- package/templates/skills/business-analyse-design/steps/step-01-screens.md +36 -162
- package/templates/skills/business-analyse-design/steps/step-02-wireframes.md +8 -7
- package/templates/skills/business-analyse-design/steps/step-03-navigation.md +89 -42
- package/templates/skills/business-analyse-develop/references/compact-loop.md +9 -0
- package/templates/skills/business-analyse-develop/references/handoff-quality-gate.md +132 -0
- package/templates/skills/business-analyse-develop/references/prd-v3-transformation.md +326 -0
- package/templates/skills/business-analyse-develop/references/report-reconciliation.md +140 -0
- package/templates/skills/business-analyse-develop/references/report-template.md +142 -0
- package/templates/skills/business-analyse-develop/steps/step-01-task.md +5 -177
- package/templates/skills/business-analyse-develop/steps/step-02-execute.md +17 -4
- package/templates/skills/business-analyse-develop/steps/step-03-commit.md +6 -2
- package/templates/skills/business-analyse-develop/steps/step-04-check.md +6 -0
- package/templates/skills/business-analyse-develop/steps/step-05-report.md +3 -269
- package/templates/skills/business-analyse-handoff/SKILL.md +10 -0
- package/templates/skills/business-analyse-handoff/references/agent-handoff-transform-prompt.md +211 -0
- package/templates/skills/business-analyse-handoff/references/context-isolation-pattern.md +47 -0
- package/templates/skills/business-analyse-handoff/references/handoff-file-inventory.md +49 -0
- package/templates/skills/business-analyse-handoff/references/handoff-global-validation.md +142 -0
- package/templates/skills/business-analyse-handoff/references/prd-validation-checks.md +125 -0
- package/templates/skills/business-analyse-handoff/references/project-index-update.md +98 -0
- package/templates/skills/business-analyse-handoff/steps/step-01-transform.md +9 -160
- package/templates/skills/business-analyse-handoff/steps/step-02-export.md +10 -99
- package/templates/skills/business-analyse-html/SKILL.md +10 -0
- package/templates/skills/business-analyse-html/html/ba-interactive.html +504 -97
- package/templates/skills/business-analyse-html/html/src/scripts/01-data-init.js +79 -2
- package/templates/skills/business-analyse-html/html/src/scripts/02-navigation.js +6 -46
- package/templates/skills/business-analyse-html/html/src/scripts/05-render-specs.js +80 -11
- package/templates/skills/business-analyse-html/html/src/scripts/06-render-consolidation.js +2 -2
- package/templates/skills/business-analyse-html/html/src/scripts/06-render-mockups.js +94 -36
- package/templates/skills/business-analyse-html/html/src/scripts/12-render-diagrams.js +162 -0
- package/templates/skills/business-analyse-html/html/src/styles/10-diagrams.css +73 -0
- package/templates/skills/business-analyse-html/html/src/template.html +2 -0
- package/templates/skills/business-analyse-html/references/02-embedded-artifacts-building.md +144 -0
- package/templates/skills/business-analyse-html/references/02-feature-data-building.md +143 -0
- package/templates/skills/business-analyse-html/references/02-mapping-tables.md +442 -0
- package/templates/skills/business-analyse-html/references/02-normalization-helpers.md +139 -0
- package/templates/skills/business-analyse-html/references/02-screen-format-detection.md +283 -0
- package/templates/skills/business-analyse-html/references/02-self-check-validation.md +199 -0
- package/templates/skills/business-analyse-html/references/data-build.md +24 -1
- package/templates/skills/business-analyse-html/references/data-mapping.md +119 -17
- package/templates/skills/business-analyse-html/steps/step-02-build-data.md +18 -555
- package/templates/skills/business-analyse-html/steps/step-04-verify.md +92 -3
- package/templates/skills/business-analyse-quick/SKILL.md +807 -0
- package/templates/skills/{sketch → business-analyse-quick}/references/domain-heuristics.md +59 -3
- package/templates/skills/business-analyse-quick/references/prd-schema.md +268 -0
- package/templates/skills/business-analyse-review/SKILL.md +10 -0
- package/templates/skills/business-analyse-review/references/review-data-mapping.md +6 -0
- package/templates/skills/business-analyse-status/SKILL.md +8 -0
- package/templates/skills/dev-start/SKILL.md +143 -307
- package/templates/skills/efcore/SKILL.md +13 -0
- package/templates/skills/sketch/SKILL.md +15 -153
- package/templates/skills/ui-components/SKILL.md +1 -1
- package/templates/skills/ui-components/patterns/data-table.md +1 -1
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>{{APPLICATION_NAME}} - Analyse métier</title>
|
|
7
|
+
<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>
|
|
8
|
+
<script>mermaid.initialize({ startOnLoad: false, theme: 'dark', securityLevel: 'loose' });</script>
|
|
7
9
|
<style>
|
|
8
10
|
/* --- 01-variables.css --- */
|
|
9
11
|
/* ============================================
|
|
@@ -2024,6 +2026,82 @@ body {
|
|
|
2024
2026
|
.mock-form-tab-content.active {
|
|
2025
2027
|
display: block;
|
|
2026
2028
|
}
|
|
2029
|
+
|
|
2030
|
+
|
|
2031
|
+
/* --- 10-diagrams.css --- */
|
|
2032
|
+
/* ==========================================
|
|
2033
|
+
MERMAID DIAGRAMS
|
|
2034
|
+
========================================== */
|
|
2035
|
+
|
|
2036
|
+
.diagram-container {
|
|
2037
|
+
margin: 1.5rem 0;
|
|
2038
|
+
padding: 1rem;
|
|
2039
|
+
background: var(--bg-card);
|
|
2040
|
+
border-radius: 8px;
|
|
2041
|
+
border: 1px solid var(--border);
|
|
2042
|
+
overflow-x: auto;
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
.diagram-container h3,
|
|
2046
|
+
.diagram-container h4 {
|
|
2047
|
+
color: var(--text-bright);
|
|
2048
|
+
margin-bottom: 1rem;
|
|
2049
|
+
font-size: 1.1rem;
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
.diagram-container .mermaid {
|
|
2053
|
+
display: flex;
|
|
2054
|
+
justify-content: center;
|
|
2055
|
+
min-height: 200px;
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2058
|
+
.diagram-container .mermaid svg {
|
|
2059
|
+
max-width: 100%;
|
|
2060
|
+
height: auto;
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
/* ERD specific */
|
|
2064
|
+
.diagram-erd {
|
|
2065
|
+
margin-bottom: 2rem;
|
|
2066
|
+
border-left: 3px solid var(--primary);
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
/* State machine specific */
|
|
2070
|
+
.diagram-state-machine {
|
|
2071
|
+
border-left: 3px solid var(--accent);
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
/* Sequence diagram specific */
|
|
2075
|
+
.diagram-sequence {
|
|
2076
|
+
border-left: 3px solid var(--success);
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
/* Diagram section header */
|
|
2080
|
+
.diagram-section-header {
|
|
2081
|
+
display: flex;
|
|
2082
|
+
align-items: center;
|
|
2083
|
+
gap: 0.5rem;
|
|
2084
|
+
margin-bottom: 1rem;
|
|
2085
|
+
color: var(--text-bright);
|
|
2086
|
+
font-size: 1.2rem;
|
|
2087
|
+
font-weight: 600;
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
.diagram-section-header::before {
|
|
2091
|
+
content: '';
|
|
2092
|
+
width: 4px;
|
|
2093
|
+
height: 1.2em;
|
|
2094
|
+
background: var(--primary);
|
|
2095
|
+
border-radius: 2px;
|
|
2096
|
+
}
|
|
2097
|
+
|
|
2098
|
+
/* No diagrams fallback */
|
|
2099
|
+
.diagram-empty {
|
|
2100
|
+
color: var(--text-muted);
|
|
2101
|
+
font-style: italic;
|
|
2102
|
+
padding: 1rem;
|
|
2103
|
+
text-align: center;
|
|
2104
|
+
}
|
|
2027
2105
|
|
|
2028
2106
|
</style>
|
|
2029
2107
|
</head>
|
|
@@ -2515,9 +2593,22 @@ if (!data.cadrage.scope || (!data.cadrage.scope.inscope?.length && data.cadrage.
|
|
|
2515
2593
|
outofscope: (gs.outOfScope || []).map(s => typeof s === 'string' ? { name: s, description: '' } : s)
|
|
2516
2594
|
};
|
|
2517
2595
|
} else {
|
|
2596
|
+
function normScopeItem(s) {
|
|
2597
|
+
if (typeof s === 'string') return { name: s, description: '' };
|
|
2598
|
+
if (s.name) return s;
|
|
2599
|
+
var nm = s.feature || s.label || s.title || JSON.stringify(s);
|
|
2600
|
+
var parts = [];
|
|
2601
|
+
if (s.priority) parts.push(s.priority.toUpperCase());
|
|
2602
|
+
if (s.module) parts.push('Module: ' + s.module);
|
|
2603
|
+
return { name: nm, description: parts.join(' — ') };
|
|
2604
|
+
}
|
|
2518
2605
|
data.cadrage.scope = {
|
|
2519
|
-
inscope: (gs.inScope || []).map(
|
|
2520
|
-
outofscope: (gs.outOfScope || []).map(s
|
|
2606
|
+
inscope: (gs.inScope || []).map(normScopeItem),
|
|
2607
|
+
outofscope: (gs.outOfScope || []).map(function(s) {
|
|
2608
|
+
if (typeof s === 'string') return { name: s, description: '' };
|
|
2609
|
+
if (s.name) return s;
|
|
2610
|
+
return { name: s.feature || s.reason || JSON.stringify(s), description: s.reason || '' };
|
|
2611
|
+
})
|
|
2521
2612
|
};
|
|
2522
2613
|
}
|
|
2523
2614
|
}
|
|
@@ -2555,6 +2646,70 @@ data.moduleSpecs = data.moduleSpecs || {};
|
|
|
2555
2646
|
});
|
|
2556
2647
|
});
|
|
2557
2648
|
|
|
2649
|
+
// Normalize permissions: convert any object format to "Role|Action" pipe-delimited strings
|
|
2650
|
+
// Handles 5 formats:
|
|
2651
|
+
// A: "Role|Action" strings (already correct)
|
|
2652
|
+
// B: {role, permissions: ["Read:all", ...]} with role-permission pairs
|
|
2653
|
+
// C: {role, permissions: ["Module.Entity.Read", ...]} with path-based permissions
|
|
2654
|
+
// D: {code, label, description} permission definitions without role assignment
|
|
2655
|
+
// E: {role: "", permissions: []} empty entries (skip)
|
|
2656
|
+
(data.modules || []).forEach(function(m) {
|
|
2657
|
+
var spec = data.moduleSpecs[m.code];
|
|
2658
|
+
if (!spec || !spec.permissions || spec.permissions.length === 0) return;
|
|
2659
|
+
// Skip if already normalized (first element is a pipe-delimited string)
|
|
2660
|
+
if (typeof spec.permissions[0] === 'string' && spec.permissions[0].indexOf('|') !== -1) return;
|
|
2661
|
+
if (typeof spec.permissions[0] !== 'object') return;
|
|
2662
|
+
|
|
2663
|
+
var actionMap = {
|
|
2664
|
+
'read': 'Consulter', 'create': 'Créer', 'update': 'Modifier',
|
|
2665
|
+
'delete': 'Supprimer', 'approve': 'Valider', 'validate': 'Valider',
|
|
2666
|
+
'export': 'Exporter', 'admin': 'Administrer', 'import': 'Importer',
|
|
2667
|
+
'viewsalary': 'Consulter', 'assignteam': 'Modifier', 'cancel': 'Supprimer',
|
|
2668
|
+
'viewbalance': 'Consulter', 'invoicetime': 'Valider'
|
|
2669
|
+
};
|
|
2670
|
+
var normalized = [];
|
|
2671
|
+
var first = spec.permissions[0];
|
|
2672
|
+
|
|
2673
|
+
// Detect Format D: {code, label, description} — permission codes without roles
|
|
2674
|
+
if (first.code && !first.role && !first.permissions) {
|
|
2675
|
+
// Extract actions from permission codes, assign to stakeholder roles
|
|
2676
|
+
var roles = (data.cadrage.stakeholders || []).map(function(s) { return s.role; }).filter(Boolean);
|
|
2677
|
+
if (roles.length === 0) roles = ['Administrateur', 'Responsable', 'Contributeur', 'Lecteur'];
|
|
2678
|
+
spec.permissions.forEach(function(permDef) {
|
|
2679
|
+
var code = permDef.code || '';
|
|
2680
|
+
var raw = code.split('.').pop(); // "Module.Entity.Read" → "Read"
|
|
2681
|
+
var action = actionMap[raw.toLowerCase()] || raw;
|
|
2682
|
+
// Assign to first role (admin) by default — better than empty
|
|
2683
|
+
if (roles[0]) {
|
|
2684
|
+
var key = roles[0] + '|' + action;
|
|
2685
|
+
if (normalized.indexOf(key) === -1) normalized.push(key);
|
|
2686
|
+
}
|
|
2687
|
+
});
|
|
2688
|
+
} else {
|
|
2689
|
+
// Format B/C: {role, permissions: [...]}
|
|
2690
|
+
spec.permissions.forEach(function(entry) {
|
|
2691
|
+
var role = entry.role || entry.name || '';
|
|
2692
|
+
if (!role) return; // Skip Format E (empty role)
|
|
2693
|
+
var perms = entry.permissions || entry.actions || [];
|
|
2694
|
+
if (perms.length === 0 && entry.permissionPattern && entry.permissionPattern.endsWith('*')) {
|
|
2695
|
+
['Consulter', 'Créer', 'Modifier', 'Supprimer', 'Valider', 'Exporter'].forEach(function(a) {
|
|
2696
|
+
normalized.push(role + '|' + a);
|
|
2697
|
+
});
|
|
2698
|
+
return;
|
|
2699
|
+
}
|
|
2700
|
+
perms.forEach(function(perm) {
|
|
2701
|
+
if (typeof perm !== 'string') return;
|
|
2702
|
+
var raw = perm.split(':')[0]; // "Read:all" → "Read"
|
|
2703
|
+
if (raw.indexOf('.') !== -1) raw = raw.split('.').pop(); // "Module.Entity.Read" → "Read"
|
|
2704
|
+
var action = actionMap[raw.toLowerCase()] || raw;
|
|
2705
|
+
var key = role + '|' + action;
|
|
2706
|
+
if (normalized.indexOf(key) === -1) normalized.push(key);
|
|
2707
|
+
});
|
|
2708
|
+
});
|
|
2709
|
+
}
|
|
2710
|
+
spec.permissions = normalized;
|
|
2711
|
+
});
|
|
2712
|
+
|
|
2558
2713
|
// Detect if modules use section-level specs (hierarchical mode)
|
|
2559
2714
|
function hasHierarchicalSpecs(mod) {
|
|
2560
2715
|
return (mod.anticipatedSections || []).some(function(s) {
|
|
@@ -2851,54 +3006,14 @@ function renderModuleNavItem(mod) {
|
|
|
2851
3006
|
var ucCount = (spec.useCases || []).length;
|
|
2852
3007
|
var brCount = (spec.businessRules || []).length;
|
|
2853
3008
|
var entCount = (spec.entities || []).length;
|
|
2854
|
-
var sections = (mod.anticipatedSections || []).map(function(s) {
|
|
2855
|
-
return typeof s === 'string' ? { code: s, name: s, resources: [] } : s;
|
|
2856
|
-
});
|
|
2857
|
-
var groupId = 'mod-' + code;
|
|
2858
|
-
var collapsed = navCollapseState[groupId] === true;
|
|
2859
|
-
|
|
2860
|
-
var html = '<div class="nav-module" data-group-id="' + groupId + '">';
|
|
2861
|
-
|
|
2862
|
-
// Module header (clickable to expand + navigate to module spec)
|
|
2863
3009
|
var totalItems = ucCount + brCount + entCount;
|
|
2864
|
-
html += '<a class="nav-item nav-module-header" onclick="toggleNavGroup(\'' + groupId + '\');showSection(\'module-spec-' + code + '\')" data-section="module-spec-' + code + '">';
|
|
2865
|
-
html += '<span class="nav-chevron ' + (collapsed ? '' : 'expanded') + '">▸</span> ';
|
|
2866
|
-
html += (mod.name || mod.code);
|
|
2867
|
-
if (totalItems > 0) html += ' <span class="nav-badge">' + totalItems + '</span>';
|
|
2868
|
-
html += '</a>';
|
|
2869
|
-
|
|
2870
|
-
// Children: sections/resources (navigable sub-tree)
|
|
2871
|
-
// Tab-level items (UC, BR, Données, etc.) are NOT shown here — they are accessed
|
|
2872
|
-
// via the tab bar in the module content area to avoid redundancy.
|
|
2873
|
-
html += '<div class="nav-children"' + (collapsed ? ' style="display:none;"' : '') + '>';
|
|
2874
|
-
if (sections.length > 0) {
|
|
2875
|
-
sections.forEach(function(section) {
|
|
2876
|
-
var resources = section.resources || [];
|
|
2877
|
-
var sectionUCs = (section.useCases || []).length;
|
|
2878
|
-
var sectionBRs = (section.businessRules || []).length;
|
|
2879
|
-
var contentCount = sectionUCs + sectionBRs + resources.length;
|
|
2880
|
-
html += '<div class="nav-section-item">';
|
|
2881
|
-
html += '<a class="nav-item nav-section-link" onclick="showSection(\'module-spec-' + code + '\');switchTab(\'' + code + '\',\'struct\')" data-section="module-struct-' + code + '-' + section.code + '">';
|
|
2882
|
-
html += '<span class="nav-icon nav-icon-section">▷</span> ' + escapeHtml(section.code || section.name || '');
|
|
2883
|
-
if (contentCount > 0) html += ' <span class="nav-badge">' + contentCount + '</span>';
|
|
2884
|
-
html += '</a>';
|
|
2885
|
-
if (resources.length > 0) {
|
|
2886
|
-
html += '<div class="nav-children nav-resources">';
|
|
2887
|
-
resources.forEach(function(res) {
|
|
2888
|
-
var resName = typeof res === 'string' ? res : (res.code || res.name || '');
|
|
2889
|
-
html += '<a class="nav-item nav-resource-link" onclick="showSection(\'module-spec-' + code + '\');switchTab(\'' + code + '\',\'mock\');scrollToMockup(\'' + code + '\',\'' + section.code + '\')">';
|
|
2890
|
-
html += '<span class="nav-icon nav-icon-resource">•</span> ' + escapeHtml(resName);
|
|
2891
|
-
html += '</a>';
|
|
2892
|
-
});
|
|
2893
|
-
html += '</div>';
|
|
2894
|
-
}
|
|
2895
|
-
html += '</div>';
|
|
2896
|
-
});
|
|
2897
|
-
}
|
|
2898
3010
|
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
3011
|
+
// Flat module link — sections/resources are accessible via tabs (Structure, Maquettes)
|
|
3012
|
+
return '<a class="nav-item nav-module-header" onclick="showSection(\'module-spec-' + code + '\')" data-section="module-spec-' + code + '">' +
|
|
3013
|
+
'<span class="nav-icon">●</span> ' +
|
|
3014
|
+
(mod.name || mod.code) +
|
|
3015
|
+
(totalItems > 0 ? ' <span class="nav-badge">' + totalItems + '</span>' : '') +
|
|
3016
|
+
'</a>';
|
|
2902
3017
|
}
|
|
2903
3018
|
|
|
2904
3019
|
function renderModuleTabNavItem(code, tabId, label, badge) {
|
|
@@ -3642,6 +3757,11 @@ function renderModuleSpecSection(mod) {
|
|
|
3642
3757
|
}
|
|
3643
3758
|
|
|
3644
3759
|
function renderUseCase(code, uc, index) {
|
|
3760
|
+
var preconditions = uc.preconditions || [];
|
|
3761
|
+
var postconditions = uc.postconditions || [];
|
|
3762
|
+
var errorScenarios = uc.errorScenarios || [];
|
|
3763
|
+
var description = uc.description || '';
|
|
3764
|
+
|
|
3645
3765
|
return `
|
|
3646
3766
|
<div class="uc-item">
|
|
3647
3767
|
<div class="uc-header">
|
|
@@ -3652,8 +3772,12 @@ function renderUseCase(code, uc, index) {
|
|
|
3652
3772
|
</div>
|
|
3653
3773
|
</div>
|
|
3654
3774
|
<div class="uc-actors"><div class="uc-actor">${escapeHtml(uc.actor)}</div></div>
|
|
3655
|
-
${
|
|
3656
|
-
${
|
|
3775
|
+
${description ? `<div class="uc-detail-label">Description</div><div class="uc-detail" style="font-style:italic;color:var(--text-muted);">${escapeHtml(description)}</div>` : ''}
|
|
3776
|
+
${preconditions.length > 0 ? `<div class="uc-detail-label">Préconditions</div><div class="uc-detail"><ul style="margin:0;padding-left:1.2rem;">${preconditions.map(p => '<li>' + escapeHtml(typeof p === 'string' ? p : p.description || '') + '</li>').join('')}</ul></div>` : ''}
|
|
3777
|
+
${uc.steps ? `<div class="uc-detail-label">Déroulement principal</div><div class="uc-detail">${escapeHtml(uc.steps).replace(/\n/g, '<br>')}</div>` : ''}
|
|
3778
|
+
${uc.alternative ? `<div class="uc-detail-label">Scénarios alternatifs</div><div class="uc-detail" style="color:var(--warning);">${escapeHtml(uc.alternative)}</div>` : ''}
|
|
3779
|
+
${errorScenarios.length > 0 ? `<div class="uc-detail-label">Scénarios d'erreur</div><div class="uc-detail" style="color:var(--danger);"><ul style="margin:0;padding-left:1.2rem;">${errorScenarios.map(e => '<li><strong>' + escapeHtml(typeof e === 'string' ? e : e.name || '') + '</strong>' + (typeof e === 'object' && e.steps ? ' : ' + escapeHtml(Array.isArray(e.steps) ? e.steps.join(', ') : e.steps) : '') + '</li>').join('')}</ul></div>` : ''}
|
|
3780
|
+
${postconditions.length > 0 ? `<div class="uc-detail-label">Postconditions</div><div class="uc-detail"><ul style="margin:0;padding-left:1.2rem;">${postconditions.map(p => '<li>' + escapeHtml(typeof p === 'string' ? p : p.description || '') + '</li>').join('')}</ul></div>` : ''}
|
|
3657
3781
|
<div style="padding:0.5rem 0.75rem;border-top:1px solid var(--border);background:var(--bg-input);border-radius:0 0 8px 8px;">
|
|
3658
3782
|
<label style="font-size:0.75rem;color:var(--text-muted);display:block;margin-bottom:0.25rem;">Commentaire / Feedback :</label>
|
|
3659
3783
|
<textarea class="form-textarea" placeholder="Ajouter un commentaire sur ce cas d'utilisation..."
|
|
@@ -3746,15 +3870,16 @@ function renderEntity(code, ent, index) {
|
|
|
3746
3870
|
</div>
|
|
3747
3871
|
${(ent.attributes || []).length > 0 ? `
|
|
3748
3872
|
<table class="attr-table">
|
|
3749
|
-
<thead><tr><th>Information</th><th>Description</th></tr></thead>
|
|
3873
|
+
<thead><tr><th>Information</th><th style="width:100px;">Type</th><th>Description</th></tr></thead>
|
|
3750
3874
|
<tbody>
|
|
3751
|
-
${ent.attributes.map(a => `<tr><td style="font-weight:500;color:var(--text-bright);">${escapeHtml(a.name)}</td><td>${escapeHtml(a.description || '')}</td></tr>`).join('')}
|
|
3875
|
+
${ent.attributes.map(a => `<tr><td style="font-weight:500;color:var(--text-bright);">${escapeHtml(a.name)}</td><td style="font-family:monospace;font-size:0.8rem;color:var(--accent);">${escapeHtml(a.type || 'string')}</td><td>${escapeHtml(a.description || '')}</td></tr>`).join('')}
|
|
3752
3876
|
</tbody>
|
|
3753
3877
|
</table>` : ''}
|
|
3754
3878
|
<div style="padding:0.3rem 0.75rem;">
|
|
3755
3879
|
<button class="add-btn" style="font-size:0.75rem;padding:0.4rem;" onclick="toggleForm('${attrFormId}')">+ Ajouter un attribut</button>
|
|
3756
3880
|
<div class="inline-form" id="${attrFormId}">
|
|
3757
3881
|
<div class="form-group"><label class="form-label">Nom de l'attribut</label><input type="text" class="form-input" id="attr-name-${code}-${index}" placeholder="Nom de l'attribut"></div>
|
|
3882
|
+
<div class="form-group"><label class="form-label">Type</label><input type="text" class="form-input" id="attr-type-${code}-${index}" placeholder="string, guid, int, decimal, date, enum, boolean" value="string"></div>
|
|
3758
3883
|
<div class="form-group"><label class="form-label">Description (optionnel)</label><input type="text" class="form-input" id="attr-desc-${code}-${index}" placeholder="Description"></div>
|
|
3759
3884
|
<div class="form-actions"><button class="btn" onclick="toggleForm('${attrFormId}')">Annuler</button><button class="btn btn-primary" onclick="addEntityAttribute('${code}',${index})">Ajouter</button></div>
|
|
3760
3885
|
</div>
|
|
@@ -3779,7 +3904,7 @@ function addEntity(code) {
|
|
|
3779
3904
|
|
|
3780
3905
|
const attrs = document.getElementById('ent-attrs-' + code).value.split('\n').filter(l => l.trim()).map(l => {
|
|
3781
3906
|
const parts = l.split(' - ');
|
|
3782
|
-
return { name: parts[0]?.trim() || l.trim(), description: parts.slice(1).join(' - ').trim() };
|
|
3907
|
+
return { name: parts[0]?.trim() || l.trim(), type: parts[1]?.trim() || 'string', description: parts.slice(2).join(' - ').trim() || parts.slice(1).join(' - ').trim() };
|
|
3783
3908
|
});
|
|
3784
3909
|
const rels = document.getElementById('ent-rels-' + code).value.split('\n').filter(l => l.trim());
|
|
3785
3910
|
|
|
@@ -3803,13 +3928,14 @@ function removeEntity(code, index) {
|
|
|
3803
3928
|
|
|
3804
3929
|
function addEntityAttribute(code, entityIndex) {
|
|
3805
3930
|
var attrName = document.getElementById('attr-name-' + code + '-' + entityIndex);
|
|
3931
|
+
var attrType = document.getElementById('attr-type-' + code + '-' + entityIndex);
|
|
3806
3932
|
var attrDesc = document.getElementById('attr-desc-' + code + '-' + entityIndex);
|
|
3807
3933
|
if (!attrName || !attrName.value.trim()) return;
|
|
3808
3934
|
if (!data.moduleSpecs[code]?.entities?.[entityIndex]) return;
|
|
3809
3935
|
if (!data.moduleSpecs[code].entities[entityIndex].attributes) {
|
|
3810
3936
|
data.moduleSpecs[code].entities[entityIndex].attributes = [];
|
|
3811
3937
|
}
|
|
3812
|
-
data.moduleSpecs[code].entities[entityIndex].attributes.push({ name: attrName.value.trim(), description: (attrDesc?.value || '').trim() });
|
|
3938
|
+
data.moduleSpecs[code].entities[entityIndex].attributes.push({ name: attrName.value.trim(), type: (attrType?.value || 'string').trim(), description: (attrDesc?.value || '').trim() });
|
|
3813
3939
|
renderAllModuleSpecs();
|
|
3814
3940
|
autoSave();
|
|
3815
3941
|
}
|
|
@@ -3920,13 +4046,68 @@ function toggleWireframeView(wireframeId, view) {
|
|
|
3920
4046
|
}
|
|
3921
4047
|
}
|
|
3922
4048
|
|
|
4049
|
+
// Normalize permissions array: handles 3 formats:
|
|
4050
|
+
// Format 1 (string): "Role|Action" → already correct
|
|
4051
|
+
// Format 2 (object): {role, permissions: ["Read:all", "Create:all", ...]}
|
|
4052
|
+
// Format 3 (object): {role, permissions: ["Module.Entity.Read", ...]}
|
|
4053
|
+
function normalizePermissions(permsArray) {
|
|
4054
|
+
if (!permsArray || permsArray.length === 0) return [];
|
|
4055
|
+
// If first element is a string with '|', already normalized
|
|
4056
|
+
if (typeof permsArray[0] === 'string' && permsArray[0].includes('|')) return permsArray;
|
|
4057
|
+
// If first element is a string without '|', return as-is (unknown format)
|
|
4058
|
+
if (typeof permsArray[0] === 'string') return permsArray;
|
|
4059
|
+
// Format 2/3: objects with {role, permissions[]}
|
|
4060
|
+
const actionMap = {
|
|
4061
|
+
'read': 'Consulter', 'create': 'Créer', 'update': 'Modifier',
|
|
4062
|
+
'delete': 'Supprimer', 'approve': 'Valider', 'validate': 'Valider',
|
|
4063
|
+
'export': 'Exporter', 'admin': 'Administrer', 'import': 'Importer',
|
|
4064
|
+
'viewsalary': 'Consulter', 'assignteam': 'Modifier', 'cancel': 'Supprimer'
|
|
4065
|
+
};
|
|
4066
|
+
const normalized = [];
|
|
4067
|
+
permsArray.forEach(function(entry) {
|
|
4068
|
+
if (typeof entry !== 'object' || !entry) return;
|
|
4069
|
+
var role = entry.role || entry.name || '';
|
|
4070
|
+
if (!role) return;
|
|
4071
|
+
var perms = entry.permissions || entry.actions || [];
|
|
4072
|
+
if (perms.length === 0 && entry.permissionPattern && entry.permissionPattern.endsWith('*')) {
|
|
4073
|
+
// Wildcard: expand to all standard actions
|
|
4074
|
+
['Consulter', 'Créer', 'Modifier', 'Supprimer', 'Valider', 'Exporter'].forEach(function(a) {
|
|
4075
|
+
normalized.push(role + '|' + a);
|
|
4076
|
+
});
|
|
4077
|
+
return;
|
|
4078
|
+
}
|
|
4079
|
+
perms.forEach(function(perm) {
|
|
4080
|
+
var action = '';
|
|
4081
|
+
if (typeof perm === 'string') {
|
|
4082
|
+
// "Read:all" → extract "Read"
|
|
4083
|
+
var raw = perm.split(':')[0];
|
|
4084
|
+
// "Module.Entity.Read" → extract "Read"
|
|
4085
|
+
if (raw.includes('.')) raw = raw.split('.').pop();
|
|
4086
|
+
action = actionMap[raw.toLowerCase()] || raw;
|
|
4087
|
+
}
|
|
4088
|
+
if (action) {
|
|
4089
|
+
var key = role + '|' + action;
|
|
4090
|
+
if (normalized.indexOf(key) === -1) normalized.push(key);
|
|
4091
|
+
}
|
|
4092
|
+
});
|
|
4093
|
+
});
|
|
4094
|
+
return normalized;
|
|
4095
|
+
}
|
|
4096
|
+
|
|
3923
4097
|
function getPermRoles() {
|
|
3924
|
-
// Extract roles from actual permission data (handles
|
|
4098
|
+
// Extract roles from actual permission data (handles multiple formats)
|
|
3925
4099
|
const rolesFromPerms = [];
|
|
3926
4100
|
const seen = new Set();
|
|
3927
4101
|
data.modules.forEach(m => {
|
|
3928
|
-
|
|
3929
|
-
|
|
4102
|
+
var perms = data.moduleSpecs[m.code]?.permissions || [];
|
|
4103
|
+
// Handle object format directly for role extraction
|
|
4104
|
+
perms.forEach(p => {
|
|
4105
|
+
var role = '';
|
|
4106
|
+
if (typeof p === 'string') {
|
|
4107
|
+
role = p.split('|')[0];
|
|
4108
|
+
} else if (typeof p === 'object' && p) {
|
|
4109
|
+
role = p.role || p.name || '';
|
|
4110
|
+
}
|
|
3930
4111
|
if (role && !seen.has(role)) { seen.add(role); rolesFromPerms.push(role); }
|
|
3931
4112
|
});
|
|
3932
4113
|
});
|
|
@@ -3949,7 +4130,9 @@ function renderPermissionGrid(code) {
|
|
|
3949
4130
|
const baseRolesCount = roles.length - (data.customRoles || []).length;
|
|
3950
4131
|
const baseActionsCount = 6;
|
|
3951
4132
|
|
|
3952
|
-
|
|
4133
|
+
// Normalize permissions to "Role|Action" format (handles object and string formats)
|
|
4134
|
+
const rawPerms = data.moduleSpecs[code]?.permissions || [];
|
|
4135
|
+
const perms = normalizePermissions(rawPerms);
|
|
3953
4136
|
|
|
3954
4137
|
return `
|
|
3955
4138
|
<table class="mock-table" style="background:var(--bg-card);border-radius:8px;overflow:hidden;">
|
|
@@ -4154,7 +4337,8 @@ function renderModuleMockups(code) {
|
|
|
4154
4337
|
}) : [];
|
|
4155
4338
|
|
|
4156
4339
|
// Priority 1: HTML mockups from screens[] specs (wireframes NOT shown when screens exist)
|
|
4157
|
-
|
|
4340
|
+
var hasResources = screens.some(function(s) { return (s.resources || []).length > 0; });
|
|
4341
|
+
if (screens.length > 0 && hasResources) {
|
|
4158
4342
|
var html = '';
|
|
4159
4343
|
if (typeof renderScreenMockups === 'function') {
|
|
4160
4344
|
html = renderScreenMockups(code);
|
|
@@ -4302,9 +4486,9 @@ function renderDataModel() {
|
|
|
4302
4486
|
${ent.description ? `<div class="dm-entity-desc">${escapeHtml(ent.description)}</div>` : ''}
|
|
4303
4487
|
${attrs.length > 0 ? `
|
|
4304
4488
|
<table class="dm-attr-table">
|
|
4305
|
-
<thead><tr><th>Champ</th><th>Description</th></tr></thead>
|
|
4489
|
+
<thead><tr><th>Champ</th><th style="width:100px;">Type</th><th>Description</th></tr></thead>
|
|
4306
4490
|
<tbody>
|
|
4307
|
-
${attrs.map(a => `<tr><td class="dm-attr-name">${escapeHtml(a.name)}</td><td class="dm-attr-desc">${escapeHtml(a.description || '')}</td></tr>`).join('')}
|
|
4491
|
+
${attrs.map(a => `<tr><td class="dm-attr-name">${escapeHtml(a.name)}</td><td style="font-family:monospace;font-size:0.8rem;color:var(--accent);">${escapeHtml(a.type || 'string')}</td><td class="dm-attr-desc">${escapeHtml(a.description || '')}</td></tr>`).join('')}
|
|
4308
4492
|
</tbody>
|
|
4309
4493
|
</table>` : ''}
|
|
4310
4494
|
${rels.length > 0 ? `
|
|
@@ -4444,13 +4628,16 @@ function renderScreenMockups(code) {
|
|
|
4444
4628
|
|
|
4445
4629
|
return screens.map(function(screen, si) {
|
|
4446
4630
|
var resources = screen.resources || [];
|
|
4631
|
+
var content = resources.length > 0
|
|
4632
|
+
? resources.map(function(res, ri) {
|
|
4633
|
+
return renderResourceMockup(code, screen.sectionCode, res, ri);
|
|
4634
|
+
}).join('')
|
|
4635
|
+
: '<div class="card" style="padding:1.5rem;color:var(--text-muted);text-align:center;"><p>Maquette en attente de spécification pour cette section.</p></div>';
|
|
4447
4636
|
return '<div class="screen-section" id="screen-' + code + '-' + screen.sectionCode + '" style="margin-bottom:2rem;">' +
|
|
4448
4637
|
'<h3 style="color:var(--text-bright);font-size:1rem;margin-bottom:1rem;">' +
|
|
4449
4638
|
'<span style="color:var(--accent);">▸</span> ' + escapeHtml(screen.sectionLabel || screen.sectionCode) +
|
|
4450
4639
|
'</h3>' +
|
|
4451
|
-
|
|
4452
|
-
return renderResourceMockup(code, screen.sectionCode, res, ri);
|
|
4453
|
-
}).join('') +
|
|
4640
|
+
content +
|
|
4454
4641
|
'</div>';
|
|
4455
4642
|
}).join('');
|
|
4456
4643
|
}
|
|
@@ -4625,31 +4812,36 @@ function renderSmartFormMockup(res) {
|
|
|
4625
4812
|
html += '<div class="mock-form-tab-content' + (ti === 0 || isOnly ? ' active' : '') + '"';
|
|
4626
4813
|
html += ' data-mockup="' + mockupId + '" data-tab="' + ti + '">';
|
|
4627
4814
|
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4815
|
+
// Tab-level subtable: the entire tab is a subtable (type: "subtable", entity, columns)
|
|
4816
|
+
if (tab.type === 'subtable') {
|
|
4817
|
+
html += renderSubtableMockup(tab);
|
|
4818
|
+
} else {
|
|
4819
|
+
var fields = tab.fields || [];
|
|
4820
|
+
var rows = [];
|
|
4821
|
+
for (var i = 0; i < fields.length; i += 2) {
|
|
4822
|
+
rows.push(fields.slice(i, i + 2));
|
|
4823
|
+
}
|
|
4633
4824
|
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4825
|
+
rows.forEach(function(row) {
|
|
4826
|
+
if (row.length === 1 && row[0].type === 'subtable') {
|
|
4827
|
+
html += renderSubtableMockup(row[0]);
|
|
4828
|
+
} else {
|
|
4829
|
+
html += '<div class="mock-form-row">';
|
|
4830
|
+
row.forEach(function(field) {
|
|
4831
|
+
html += '<div class="mock-form-group">';
|
|
4832
|
+
html += '<label class="mock-label">' + escapeHtml(field.label || field.field);
|
|
4833
|
+
if (field.required) html += ' <span style="color:var(--error);">*</span>';
|
|
4834
|
+
html += '</label>';
|
|
4835
|
+
html += renderFormFieldMockup(field);
|
|
4836
|
+
html += '</div>';
|
|
4837
|
+
});
|
|
4645
4838
|
html += '</div>';
|
|
4646
|
-
}
|
|
4647
|
-
|
|
4648
|
-
}
|
|
4649
|
-
});
|
|
4839
|
+
}
|
|
4840
|
+
});
|
|
4650
4841
|
|
|
4651
|
-
|
|
4652
|
-
|
|
4842
|
+
if (fields.length === 0) {
|
|
4843
|
+
html += '<div style="padding:2rem;text-align:center;color:var(--text-muted);font-style:italic;">Contenu de l\'onglet « ' + escapeHtml(tab.label) + ' »</div>';
|
|
4844
|
+
}
|
|
4653
4845
|
}
|
|
4654
4846
|
|
|
4655
4847
|
html += '</div>';
|
|
@@ -4694,20 +4886,39 @@ function renderSubtableMockup(field) {
|
|
|
4694
4886
|
|
|
4695
4887
|
/* ---------- SmartCard ---------- */
|
|
4696
4888
|
function renderSmartCardMockup(res) {
|
|
4697
|
-
var
|
|
4889
|
+
var fields = res.fields || res.columns || [];
|
|
4698
4890
|
var html = '<div class="mock-header"><span class="mock-title">' + escapeHtml(res.label || 'Cartes') + '</span></div>';
|
|
4699
4891
|
html += '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:1rem;">';
|
|
4700
4892
|
|
|
4701
4893
|
for (var i = 0; i < 4; i++) {
|
|
4702
4894
|
html += '<div style="background:var(--bg-hover);border:1px solid var(--border);border-radius:8px;padding:1rem;">';
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4895
|
+
// Title/Subtitle pattern (SmartCard with title + subtitle + fields + actions)
|
|
4896
|
+
if (res.title) {
|
|
4897
|
+
html += '<div style="font-weight:600;color:var(--text-bright);margin-bottom:0.25rem;">' + escapeHtml(res.title) + ' #' + (i + 1) + '</div>';
|
|
4898
|
+
}
|
|
4899
|
+
if (res.subtitle) {
|
|
4900
|
+
html += '<div style="font-size:0.8rem;color:var(--accent);margin-bottom:0.5rem;">' + escapeHtml(res.subtitle) + '</div>';
|
|
4901
|
+
}
|
|
4902
|
+
// Fields
|
|
4903
|
+
fields.forEach(function(col, ci) {
|
|
4904
|
+
var label = col.label || col.field || (typeof col === 'string' ? col : '');
|
|
4905
|
+
var iconMap = { calendar: '📅 ', 'dollar-sign': '💰 ', 'alert-circle': '⚠ ', mail: '✉ ', building: '🏢 ', user: '👤 ' };
|
|
4906
|
+
var icon = col.icon ? (iconMap[col.icon] || '● ') : '';
|
|
4907
|
+
if (!res.title && ci === 0) {
|
|
4706
4908
|
html += '<div style="font-weight:600;color:var(--text-bright);margin-bottom:0.5rem;">' + escapeHtml(label) + ' #' + (i + 1) + '</div>';
|
|
4707
4909
|
} else {
|
|
4708
|
-
html += '<div style="font-size:0.8rem;color:var(--text-muted);margin-bottom:0.25rem;">' + escapeHtml(label) + ': <span style="color:var(--text);">valeur</span></div>';
|
|
4910
|
+
html += '<div style="font-size:0.8rem;color:var(--text-muted);margin-bottom:0.25rem;">' + icon + escapeHtml(label) + ': <span style="color:var(--text);">valeur</span></div>';
|
|
4709
4911
|
}
|
|
4710
4912
|
});
|
|
4913
|
+
// Actions
|
|
4914
|
+
if (res.actions && res.actions.length > 0) {
|
|
4915
|
+
html += '<div style="margin-top:0.75rem;display:flex;gap:0.4rem;">';
|
|
4916
|
+
res.actions.forEach(function(a) {
|
|
4917
|
+
var actionLabel = typeof a === 'string' ? a.replace(/-/g, ' ') : (a.label || a.action || '');
|
|
4918
|
+
html += '<span class="mock-btn" style="font-size:0.75rem;">' + escapeHtml(actionLabel) + '</span>';
|
|
4919
|
+
});
|
|
4920
|
+
html += '</div>';
|
|
4921
|
+
}
|
|
4711
4922
|
html += '</div>';
|
|
4712
4923
|
}
|
|
4713
4924
|
html += '</div>';
|
|
@@ -4716,7 +4927,7 @@ function renderSmartCardMockup(res) {
|
|
|
4716
4927
|
|
|
4717
4928
|
/* ---------- SmartKanban ---------- */
|
|
4718
4929
|
function renderSmartKanbanMockup(res) {
|
|
4719
|
-
var options = res.options
|
|
4930
|
+
var options = (res.options && res.options.length > 0) ? res.options : (res.columns && res.columns.length > 0) ? res.columns : ['À faire', 'En cours', 'Terminé'];
|
|
4720
4931
|
var html = '<div class="mock-header"><span class="mock-title">' + escapeHtml(res.label || 'Kanban') + '</span></div>';
|
|
4721
4932
|
html += '<div style="display:flex;gap:1rem;overflow-x:auto;padding-bottom:0.5rem;">';
|
|
4722
4933
|
|
|
@@ -4790,11 +5001,42 @@ function kpiDisplayValue(kpi) {
|
|
|
4790
5001
|
|
|
4791
5002
|
/* ---------- SmartFilter ---------- */
|
|
4792
5003
|
function renderSmartFilterMockup(res) {
|
|
4793
|
-
var
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
5004
|
+
var filters = res.filters || [];
|
|
5005
|
+
if (filters.length === 0) {
|
|
5006
|
+
// Fallback: legacy options[] format (tag pills)
|
|
5007
|
+
var options = res.options || [];
|
|
5008
|
+
if (options.length === 0) return '<div style="padding:1rem;text-align:center;color:var(--text-muted);">Aucun filtre défini</div>';
|
|
5009
|
+
var html = '<div style="display:flex;gap:0.4rem;flex-wrap:wrap;padding:0.5rem 0;">';
|
|
5010
|
+
html += '<span style="padding:0.3rem 0.7rem;border-radius:16px;font-size:0.8rem;background:var(--primary);color:#fff;cursor:pointer;">Tous</span>';
|
|
5011
|
+
options.forEach(function(opt) {
|
|
5012
|
+
html += '<span style="padding:0.3rem 0.7rem;border-radius:16px;font-size:0.8rem;background:var(--bg-hover);color:var(--text);border:1px solid var(--border);cursor:pointer;">' + escapeHtml(typeof opt === 'string' ? opt : opt.label || '') + '</span>';
|
|
5013
|
+
});
|
|
5014
|
+
html += '</div>';
|
|
5015
|
+
return html;
|
|
5016
|
+
}
|
|
5017
|
+
|
|
5018
|
+
// Rich filter bar: render each filter based on its type
|
|
5019
|
+
var html = '<div style="display:flex;gap:0.5rem;flex-wrap:wrap;align-items:flex-end;padding:0.5rem 0;">';
|
|
5020
|
+
filters.forEach(function(f) {
|
|
5021
|
+
var label = typeof f === 'string' ? f : (f.label || f.field || '');
|
|
5022
|
+
var type = typeof f === 'string' ? 'text' : (f.type || 'text');
|
|
5023
|
+
html += '<div style="display:flex;flex-direction:column;gap:0.2rem;">';
|
|
5024
|
+
html += '<span style="font-size:0.7rem;color:var(--text-muted);">' + escapeHtml(label) + '</span>';
|
|
5025
|
+
switch (type) {
|
|
5026
|
+
case 'select':
|
|
5027
|
+
var opts = f.options || ['Option 1', 'Option 2'];
|
|
5028
|
+
html += '<span class="mock-input" style="width:auto;min-width:130px;font-size:0.8rem;color:var(--text-muted);">' + escapeHtml(opts[0]) + ' ▾</span>';
|
|
5029
|
+
break;
|
|
5030
|
+
case 'lookup':
|
|
5031
|
+
html += '<span class="mock-input" style="width:auto;min-width:130px;font-size:0.8rem;color:var(--accent);">' + escapeHtml(f.entity || label) + ' 🔍</span>';
|
|
5032
|
+
break;
|
|
5033
|
+
case 'daterange':
|
|
5034
|
+
html += '<span class="mock-input" style="width:auto;min-width:180px;font-size:0.8rem;color:var(--text-muted);">01/01/2025 — 31/12/2025 📅</span>';
|
|
5035
|
+
break;
|
|
5036
|
+
default:
|
|
5037
|
+
html += '<span class="mock-input" style="width:auto;min-width:160px;font-size:0.8rem;color:var(--text-muted);">Rechercher... 🔍</span>';
|
|
5038
|
+
}
|
|
5039
|
+
html += '</div>';
|
|
4798
5040
|
});
|
|
4799
5041
|
html += '</div>';
|
|
4800
5042
|
return html;
|
|
@@ -5721,6 +5963,171 @@ function scrollToCommentThread(sectionId, safeElId) {
|
|
|
5721
5963
|
}
|
|
5722
5964
|
}, 100);
|
|
5723
5965
|
}
|
|
5966
|
+
|
|
5967
|
+
|
|
5968
|
+
/* --- 12-render-diagrams.js --- */
|
|
5969
|
+
/* ==========================================
|
|
5970
|
+
MERMAID DIAGRAM RENDERING
|
|
5971
|
+
Renders ERD, state machines, and sequence diagrams
|
|
5972
|
+
from consolidation.mermaidDiagrams data.
|
|
5973
|
+
========================================== */
|
|
5974
|
+
|
|
5975
|
+
function renderMermaidDiagrams() {
|
|
5976
|
+
const data = window.FEATURE_DATA;
|
|
5977
|
+
if (!data) return;
|
|
5978
|
+
|
|
5979
|
+
const diagrams = data.consolidation?.mermaidDiagrams;
|
|
5980
|
+
if (!diagrams) return;
|
|
5981
|
+
|
|
5982
|
+
// 1. ERD in consol-datamodel (prepend before entity cards)
|
|
5983
|
+
if (diagrams.erd) {
|
|
5984
|
+
const erdContainer = document.getElementById('dataModelContainer');
|
|
5985
|
+
if (erdContainer) {
|
|
5986
|
+
const erdDiv = document.createElement('div');
|
|
5987
|
+
erdDiv.className = 'diagram-container diagram-erd';
|
|
5988
|
+
erdDiv.innerHTML =
|
|
5989
|
+
'<div class="diagram-section-header">Diagramme Entit\u00e9-Relation (ERD)</div>' +
|
|
5990
|
+
'<div class="mermaid">' + escapeHtml(diagrams.erd) + '</div>';
|
|
5991
|
+
erdContainer.insertBefore(erdDiv, erdContainer.firstChild);
|
|
5992
|
+
}
|
|
5993
|
+
}
|
|
5994
|
+
|
|
5995
|
+
// 1b. MCD (Modèle Conceptuel de Données) — after ERD
|
|
5996
|
+
if (diagrams.mcd) {
|
|
5997
|
+
var erdContainer = document.getElementById('dataModelContainer');
|
|
5998
|
+
if (erdContainer) {
|
|
5999
|
+
var mcdDiv = document.createElement('div');
|
|
6000
|
+
mcdDiv.className = 'diagram-container diagram-mcd';
|
|
6001
|
+
mcdDiv.innerHTML =
|
|
6002
|
+
'<div class="diagram-section-header" style="font-size:0.95rem;font-weight:600;color:var(--text-bright);margin-bottom:0.75rem;">Mod\u00e8le Conceptuel de Donn\u00e9es (MCD)</div>' +
|
|
6003
|
+
'<div class="mermaid">' + escapeHtml(diagrams.mcd) + '</div>';
|
|
6004
|
+
var erdDiv = erdContainer.querySelector('.diagram-erd');
|
|
6005
|
+
if (erdDiv && erdDiv.nextSibling) {
|
|
6006
|
+
erdContainer.insertBefore(mcdDiv, erdDiv.nextSibling);
|
|
6007
|
+
} else {
|
|
6008
|
+
erdContainer.appendChild(mcdDiv);
|
|
6009
|
+
}
|
|
6010
|
+
}
|
|
6011
|
+
}
|
|
6012
|
+
|
|
6013
|
+
// 2. State machine diagrams — inject in module spec sections or consol-datamodel
|
|
6014
|
+
if (diagrams.stateMachines && Object.keys(diagrams.stateMachines).length > 0) {
|
|
6015
|
+
const smContainer = document.getElementById('dataModelContainer');
|
|
6016
|
+
if (smContainer) {
|
|
6017
|
+
const smSection = document.createElement('div');
|
|
6018
|
+
smSection.innerHTML = '<div class="diagram-section-header">Cycles de vie (State Machines)</div>';
|
|
6019
|
+
|
|
6020
|
+
Object.entries(diagrams.stateMachines).forEach(function(entry) {
|
|
6021
|
+
var entity = entry[0];
|
|
6022
|
+
var def = entry[1];
|
|
6023
|
+
var smDiv = document.createElement('div');
|
|
6024
|
+
smDiv.className = 'diagram-container diagram-state-machine';
|
|
6025
|
+
smDiv.innerHTML =
|
|
6026
|
+
'<h4>' + escapeHtml(entity) + '</h4>' +
|
|
6027
|
+
'<div class="mermaid">' + escapeHtml(def) + '</div>';
|
|
6028
|
+
smSection.appendChild(smDiv);
|
|
6029
|
+
});
|
|
6030
|
+
|
|
6031
|
+
smContainer.appendChild(smSection);
|
|
6032
|
+
}
|
|
6033
|
+
}
|
|
6034
|
+
|
|
6035
|
+
// 2b. Use Case diagrams — inject in each module's UC tab
|
|
6036
|
+
if (diagrams.useCases && Object.keys(diagrams.useCases).length > 0) {
|
|
6037
|
+
Object.entries(diagrams.useCases).forEach(function(entry) {
|
|
6038
|
+
var moduleCode = entry[0];
|
|
6039
|
+
var def = entry[1];
|
|
6040
|
+
var ucTab = document.getElementById('tab-' + moduleCode + '-uc');
|
|
6041
|
+
if (!ucTab) {
|
|
6042
|
+
// Try kebab-case version
|
|
6043
|
+
var kebab = moduleCode.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
|
6044
|
+
ucTab = document.getElementById('tab-' + kebab + '-uc');
|
|
6045
|
+
}
|
|
6046
|
+
if (ucTab) {
|
|
6047
|
+
var ucDiagramDiv = document.createElement('div');
|
|
6048
|
+
ucDiagramDiv.className = 'diagram-container diagram-usecase';
|
|
6049
|
+
ucDiagramDiv.innerHTML =
|
|
6050
|
+
'<div class="diagram-section-header" style="font-size:0.95rem;font-weight:600;color:var(--text-bright);margin-bottom:0.75rem;">Diagramme de cas d\'utilisation</div>' +
|
|
6051
|
+
'<div class="mermaid">' + escapeHtml(def) + '</div>';
|
|
6052
|
+
// Insert after the first paragraph (description text)
|
|
6053
|
+
var firstP = ucTab.querySelector('p');
|
|
6054
|
+
if (firstP && firstP.nextSibling) {
|
|
6055
|
+
ucTab.insertBefore(ucDiagramDiv, firstP.nextSibling);
|
|
6056
|
+
} else {
|
|
6057
|
+
ucTab.insertBefore(ucDiagramDiv, ucTab.firstChild);
|
|
6058
|
+
}
|
|
6059
|
+
}
|
|
6060
|
+
});
|
|
6061
|
+
}
|
|
6062
|
+
|
|
6063
|
+
// 3. Sequence diagrams in consol-flows
|
|
6064
|
+
if (diagrams.sequences && Object.keys(diagrams.sequences).length > 0) {
|
|
6065
|
+
var flowsContainer = document.getElementById('consolFlowsContainer');
|
|
6066
|
+
// Fallback: try the consol-flows section
|
|
6067
|
+
if (!flowsContainer) {
|
|
6068
|
+
var consolFlows = document.getElementById('consol-flows');
|
|
6069
|
+
if (consolFlows) {
|
|
6070
|
+
flowsContainer = consolFlows.querySelector('.section-body') || consolFlows;
|
|
6071
|
+
}
|
|
6072
|
+
}
|
|
6073
|
+
|
|
6074
|
+
if (flowsContainer) {
|
|
6075
|
+
var seqSection = document.createElement('div');
|
|
6076
|
+
seqSection.innerHTML = '<div class="diagram-section-header">Diagrammes de s\u00e9quence</div>';
|
|
6077
|
+
|
|
6078
|
+
Object.entries(diagrams.sequences).forEach(function(entry) {
|
|
6079
|
+
var flowName = entry[0];
|
|
6080
|
+
var def = entry[1];
|
|
6081
|
+
var seqDiv = document.createElement('div');
|
|
6082
|
+
seqDiv.className = 'diagram-container diagram-sequence';
|
|
6083
|
+
seqDiv.innerHTML =
|
|
6084
|
+
'<h4>' + escapeHtml(flowName) + '</h4>' +
|
|
6085
|
+
'<div class="mermaid">' + escapeHtml(def) + '</div>';
|
|
6086
|
+
seqSection.appendChild(seqDiv);
|
|
6087
|
+
});
|
|
6088
|
+
|
|
6089
|
+
flowsContainer.appendChild(seqSection);
|
|
6090
|
+
}
|
|
6091
|
+
}
|
|
6092
|
+
|
|
6093
|
+
// 4. Render all mermaid elements
|
|
6094
|
+
try {
|
|
6095
|
+
if (typeof mermaid !== 'undefined' && mermaid.run) {
|
|
6096
|
+
mermaid.run({ querySelector: '.mermaid' });
|
|
6097
|
+
}
|
|
6098
|
+
} catch (e) {
|
|
6099
|
+
console.warn('Mermaid rendering failed:', e);
|
|
6100
|
+
// Fallback: show raw text
|
|
6101
|
+
document.querySelectorAll('.mermaid').forEach(function(el) {
|
|
6102
|
+
if (!el.querySelector('svg')) {
|
|
6103
|
+
el.style.whiteSpace = 'pre-wrap';
|
|
6104
|
+
el.style.fontFamily = 'monospace';
|
|
6105
|
+
el.style.fontSize = '0.85rem';
|
|
6106
|
+
el.style.color = 'var(--text-muted)';
|
|
6107
|
+
}
|
|
6108
|
+
});
|
|
6109
|
+
}
|
|
6110
|
+
}
|
|
6111
|
+
|
|
6112
|
+
// Escape HTML for safe injection
|
|
6113
|
+
function escapeHtml(text) {
|
|
6114
|
+
if (!text) return '';
|
|
6115
|
+
// For mermaid content, we need to preserve the syntax
|
|
6116
|
+
// Only escape actual HTML tags, not mermaid arrows
|
|
6117
|
+
return text.replace(/</g, '<').replace(/>/g, '>');
|
|
6118
|
+
}
|
|
6119
|
+
|
|
6120
|
+
// Auto-run after consolidation rendering
|
|
6121
|
+
(function() {
|
|
6122
|
+
// Wait for DOM and other render functions to complete
|
|
6123
|
+
if (document.readyState === 'loading') {
|
|
6124
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
6125
|
+
setTimeout(renderMermaidDiagrams, 500);
|
|
6126
|
+
});
|
|
6127
|
+
} else {
|
|
6128
|
+
setTimeout(renderMermaidDiagrams, 500);
|
|
6129
|
+
}
|
|
6130
|
+
})();
|
|
5724
6131
|
|
|
5725
6132
|
</script>
|
|
5726
6133
|
</body>
|