@atlashub/smartstack-cli 4.61.0 → 4.63.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 +198 -52
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/skills/business-analyse-html/html/ba-interactive.html +217 -39
- package/templates/skills/business-analyse-html/html/src/scripts/05-render-specs.js +1 -5
- package/templates/skills/business-analyse-html/html/src/scripts/06-render-mockups.js +132 -25
- package/templates/skills/business-analyse-html/html/src/styles/06-wireframes.css +0 -9
- package/templates/skills/business-analyse-html/html/src/styles/09-mockups-html.css +84 -0
- package/templates/skills/business-analyse-html/references/data-build.md +3 -1
- package/templates/skills/business-analyse-html/steps/step-01-collect.md +2 -1
- package/templates/skills/business-analyse-html/steps/step-02-build-data.md +33 -0
package/package.json
CHANGED
|
@@ -1367,15 +1367,6 @@ body {
|
|
|
1367
1367
|
}
|
|
1368
1368
|
.mock-kpi-value { font-size: 1.3rem; font-weight: 700; color: var(--text-bright); }
|
|
1369
1369
|
.mock-kpi-label { font-size: 0.7rem; color: var(--text-muted); margin-top: 0.2rem; }
|
|
1370
|
-
.mock-chart-placeholder {
|
|
1371
|
-
height: 150px;
|
|
1372
|
-
background: linear-gradient(135deg, rgba(99,102,241,0.05), rgba(6,182,212,0.05));
|
|
1373
|
-
border: 1px dashed var(--border);
|
|
1374
|
-
border-radius: 8px;
|
|
1375
|
-
display: flex; align-items: center; justify-content: center;
|
|
1376
|
-
color: var(--text-muted); font-size: 0.85rem;
|
|
1377
|
-
}
|
|
1378
|
-
|
|
1379
1370
|
@media (max-width: 768px) {
|
|
1380
1371
|
.mock-kpi-grid { grid-template-columns: repeat(2, 1fr); }
|
|
1381
1372
|
}
|
|
@@ -1949,6 +1940,90 @@ body {
|
|
|
1949
1940
|
padding: 0.15rem 0.5rem;
|
|
1950
1941
|
border-radius: 4px;
|
|
1951
1942
|
}
|
|
1943
|
+
|
|
1944
|
+
/* ---- Mock Charts (SVG previews) ---- */
|
|
1945
|
+
.mock-chart-container {
|
|
1946
|
+
margin: 0.75rem 0;
|
|
1947
|
+
border: 1px solid var(--border);
|
|
1948
|
+
border-radius: 8px;
|
|
1949
|
+
overflow: hidden;
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
.mock-chart-header {
|
|
1953
|
+
padding: 0.5rem 0.75rem;
|
|
1954
|
+
font-size: 0.85rem;
|
|
1955
|
+
font-weight: 500;
|
|
1956
|
+
color: var(--text-bright);
|
|
1957
|
+
background: var(--bg-hover);
|
|
1958
|
+
border-bottom: 1px solid var(--border);
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
.mock-chart-body {
|
|
1962
|
+
padding: 1rem;
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
.mock-pie-svg {
|
|
1966
|
+
width: 120px;
|
|
1967
|
+
height: 120px;
|
|
1968
|
+
flex-shrink: 0;
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
.mock-bar-svg,
|
|
1972
|
+
.mock-line-svg {
|
|
1973
|
+
width: 100%;
|
|
1974
|
+
height: 140px;
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
.mock-chart-legend {
|
|
1978
|
+
font-size: 0.8rem;
|
|
1979
|
+
color: var(--text);
|
|
1980
|
+
display: flex;
|
|
1981
|
+
flex-direction: column;
|
|
1982
|
+
gap: 0.4rem;
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
.mock-legend-dot {
|
|
1986
|
+
display: inline-block;
|
|
1987
|
+
width: 10px;
|
|
1988
|
+
height: 10px;
|
|
1989
|
+
border-radius: 2px;
|
|
1990
|
+
margin-right: 0.4rem;
|
|
1991
|
+
vertical-align: middle;
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
/* ---- Form Tab Switching ---- */
|
|
1995
|
+
.mock-form-tab-bar {
|
|
1996
|
+
display: flex;
|
|
1997
|
+
gap: 0;
|
|
1998
|
+
border-bottom: 1px solid var(--border);
|
|
1999
|
+
margin-bottom: 1.5rem;
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
.mock-form-tab {
|
|
2003
|
+
padding: 0.5rem 1rem;
|
|
2004
|
+
font-size: 0.85rem;
|
|
2005
|
+
cursor: pointer;
|
|
2006
|
+
border-bottom: 2px solid transparent;
|
|
2007
|
+
color: var(--text-muted);
|
|
2008
|
+
transition: all var(--transition-fast);
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
.mock-form-tab:hover {
|
|
2012
|
+
color: var(--text);
|
|
2013
|
+
}
|
|
2014
|
+
|
|
2015
|
+
.mock-form-tab.active {
|
|
2016
|
+
border-bottom-color: var(--primary);
|
|
2017
|
+
color: var(--primary-light);
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
.mock-form-tab-content {
|
|
2021
|
+
display: none;
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
.mock-form-tab-content.active {
|
|
2025
|
+
display: block;
|
|
2026
|
+
}
|
|
1952
2027
|
|
|
1953
2028
|
</style>
|
|
1954
2029
|
</head>
|
|
@@ -4078,16 +4153,12 @@ function renderModuleMockups(code) {
|
|
|
4078
4153
|
return typeof s === 'string' ? { code: s, name: s, resources: [] } : s;
|
|
4079
4154
|
}) : [];
|
|
4080
4155
|
|
|
4081
|
-
// Priority 1: HTML mockups from screens[] specs
|
|
4156
|
+
// Priority 1: HTML mockups from screens[] specs (wireframes NOT shown when screens exist)
|
|
4082
4157
|
if (screens.length > 0) {
|
|
4083
4158
|
var html = '';
|
|
4084
4159
|
if (typeof renderScreenMockups === 'function') {
|
|
4085
4160
|
html = renderScreenMockups(code);
|
|
4086
4161
|
}
|
|
4087
|
-
if (wireframes.length > 0) {
|
|
4088
|
-
html += '<h3 style="color:var(--text-bright);font-size:1rem;margin:2rem 0 1rem;">Wireframes</h3>';
|
|
4089
|
-
html += wireframes.map(function(wf, i) { return renderWireframeMockup(code, wf, i); }).join('');
|
|
4090
|
-
}
|
|
4091
4162
|
return html;
|
|
4092
4163
|
}
|
|
4093
4164
|
|
|
@@ -4363,6 +4434,8 @@ function renderE2EFlows() {
|
|
|
4363
4434
|
Generates realistic HTML mockups from screens[] specs
|
|
4364
4435
|
============================================ */
|
|
4365
4436
|
|
|
4437
|
+
var _formTabCounter = 0;
|
|
4438
|
+
|
|
4366
4439
|
function renderScreenMockups(code) {
|
|
4367
4440
|
var spec = data.moduleSpecs[code] || {};
|
|
4368
4441
|
var screens = spec.screens || [];
|
|
@@ -4522,6 +4595,7 @@ function renderSmartFormMockup(res) {
|
|
|
4522
4595
|
}
|
|
4523
4596
|
if (tabs.length === 0) return '<div style="padding:2rem;text-align:center;color:var(--text-muted);">Formulaire sans champs définis</div>';
|
|
4524
4597
|
|
|
4598
|
+
var mockupId = 'form-tabs-' + (++_formTabCounter);
|
|
4525
4599
|
var html = '';
|
|
4526
4600
|
|
|
4527
4601
|
// Header
|
|
@@ -4534,37 +4608,51 @@ function renderSmartFormMockup(res) {
|
|
|
4534
4608
|
});
|
|
4535
4609
|
html += '</div></div>';
|
|
4536
4610
|
|
|
4537
|
-
// Tabs
|
|
4611
|
+
// Tabs (clickable when more than 1)
|
|
4538
4612
|
if (tabs.length > 1) {
|
|
4539
|
-
html += '<div
|
|
4613
|
+
html += '<div class="mock-form-tab-bar" data-mockup="' + mockupId + '">';
|
|
4540
4614
|
tabs.forEach(function(tab, i) {
|
|
4541
|
-
html += '<span
|
|
4615
|
+
html += '<span class="mock-form-tab' + (i === 0 ? ' active' : '') + '"';
|
|
4616
|
+
html += ' onclick="switchFormTab(\'' + mockupId + '\', ' + i + ')"';
|
|
4617
|
+
html += '>' + escapeHtml(tab.label) + '</span>';
|
|
4542
4618
|
});
|
|
4543
4619
|
html += '</div>';
|
|
4544
4620
|
}
|
|
4545
4621
|
|
|
4546
|
-
//
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
}
|
|
4622
|
+
// Tab contents (all rendered, only first visible)
|
|
4623
|
+
tabs.forEach(function(tab, ti) {
|
|
4624
|
+
var isOnly = tabs.length <= 1;
|
|
4625
|
+
html += '<div class="mock-form-tab-content' + (ti === 0 || isOnly ? ' active' : '') + '"';
|
|
4626
|
+
html += ' data-mockup="' + mockupId + '" data-tab="' + ti + '">';
|
|
4552
4627
|
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
html +=
|
|
4628
|
+
var fields = tab.fields || [];
|
|
4629
|
+
var rows = [];
|
|
4630
|
+
for (var i = 0; i < fields.length; i += 2) {
|
|
4631
|
+
rows.push(fields.slice(i, i + 2));
|
|
4632
|
+
}
|
|
4633
|
+
|
|
4634
|
+
rows.forEach(function(row) {
|
|
4635
|
+
if (row.length === 1 && row[0].type === 'subtable') {
|
|
4636
|
+
html += renderSubtableMockup(row[0]);
|
|
4637
|
+
} else {
|
|
4638
|
+
html += '<div class="mock-form-row">';
|
|
4639
|
+
row.forEach(function(field) {
|
|
4640
|
+
html += '<div class="mock-form-group">';
|
|
4641
|
+
html += '<label class="mock-label">' + escapeHtml(field.label || field.field);
|
|
4642
|
+
if (field.required) html += ' <span style="color:var(--error);">*</span>';
|
|
4643
|
+
html += '</label>';
|
|
4644
|
+
html += renderFormFieldMockup(field);
|
|
4645
|
+
html += '</div>';
|
|
4646
|
+
});
|
|
4564
4647
|
html += '</div>';
|
|
4565
|
-
}
|
|
4566
|
-
|
|
4648
|
+
}
|
|
4649
|
+
});
|
|
4650
|
+
|
|
4651
|
+
if (fields.length === 0) {
|
|
4652
|
+
html += '<div style="padding:2rem;text-align:center;color:var(--text-muted);font-style:italic;">Contenu de l\'onglet « ' + escapeHtml(tab.label) + ' »</div>';
|
|
4567
4653
|
}
|
|
4654
|
+
|
|
4655
|
+
html += '</div>';
|
|
4568
4656
|
});
|
|
4569
4657
|
|
|
4570
4658
|
return html;
|
|
@@ -4668,10 +4756,10 @@ function renderSmartDashboardMockup(res) {
|
|
|
4668
4756
|
});
|
|
4669
4757
|
html += '</div>';
|
|
4670
4758
|
|
|
4671
|
-
//
|
|
4672
|
-
var charts = res.charts || [{ label: 'Graphique' }];
|
|
4759
|
+
// Charts (SVG mock previews)
|
|
4760
|
+
var charts = res.charts || [{ label: 'Graphique', type: 'bar' }];
|
|
4673
4761
|
charts.forEach(function(chart) {
|
|
4674
|
-
html +=
|
|
4762
|
+
html += renderMockChart(chart);
|
|
4675
4763
|
});
|
|
4676
4764
|
|
|
4677
4765
|
return html;
|
|
@@ -4712,6 +4800,96 @@ function renderSmartFilterMockup(res) {
|
|
|
4712
4800
|
return html;
|
|
4713
4801
|
}
|
|
4714
4802
|
|
|
4803
|
+
/* ---------- Mock Charts ---------- */
|
|
4804
|
+
function renderMockChart(chart) {
|
|
4805
|
+
var type = (chart.type || '').toLowerCase();
|
|
4806
|
+
var label = chart.label || chart.type || 'Graphique';
|
|
4807
|
+
var html = '<div class="mock-chart-container">';
|
|
4808
|
+
html += '<div class="mock-chart-header">' + escapeHtml(label) + '</div>';
|
|
4809
|
+
html += '<div class="mock-chart-body">';
|
|
4810
|
+
if (type === 'pie' || type === 'donut') {
|
|
4811
|
+
html += renderMockPieChart();
|
|
4812
|
+
} else if (type === 'line' || type === 'area') {
|
|
4813
|
+
html += renderMockLineChart();
|
|
4814
|
+
} else {
|
|
4815
|
+
html += renderMockBarChart();
|
|
4816
|
+
}
|
|
4817
|
+
html += '</div></div>';
|
|
4818
|
+
return html;
|
|
4819
|
+
}
|
|
4820
|
+
|
|
4821
|
+
function renderMockPieChart() {
|
|
4822
|
+
var html = '<div style="display:flex;align-items:center;gap:1.5rem;">';
|
|
4823
|
+
html += '<svg viewBox="0 0 42 42" class="mock-pie-svg">';
|
|
4824
|
+
html += '<circle cx="21" cy="21" r="15.9" fill="transparent" stroke="var(--primary)" stroke-width="8" stroke-dasharray="35 65" stroke-dashoffset="25"/>';
|
|
4825
|
+
html += '<circle cx="21" cy="21" r="15.9" fill="transparent" stroke="var(--success)" stroke-width="8" stroke-dasharray="25 75" stroke-dashoffset="-10"/>';
|
|
4826
|
+
html += '<circle cx="21" cy="21" r="15.9" fill="transparent" stroke="var(--warning)" stroke-width="8" stroke-dasharray="22 78" stroke-dashoffset="-35"/>';
|
|
4827
|
+
html += '<circle cx="21" cy="21" r="15.9" fill="transparent" stroke="var(--error)" stroke-width="8" stroke-dasharray="18 82" stroke-dashoffset="-57"/>';
|
|
4828
|
+
html += '</svg>';
|
|
4829
|
+
html += '<div class="mock-chart-legend">';
|
|
4830
|
+
html += '<div><span class="mock-legend-dot" style="background:var(--primary);"></span> Cat\u00e9gorie A (35%)</div>';
|
|
4831
|
+
html += '<div><span class="mock-legend-dot" style="background:var(--success);"></span> Cat\u00e9gorie B (25%)</div>';
|
|
4832
|
+
html += '<div><span class="mock-legend-dot" style="background:var(--warning);"></span> Cat\u00e9gorie C (22%)</div>';
|
|
4833
|
+
html += '<div><span class="mock-legend-dot" style="background:var(--error);"></span> Cat\u00e9gorie D (18%)</div>';
|
|
4834
|
+
html += '</div></div>';
|
|
4835
|
+
return html;
|
|
4836
|
+
}
|
|
4837
|
+
|
|
4838
|
+
function renderMockBarChart() {
|
|
4839
|
+
var bars = [85, 60, 45, 70, 55, 90];
|
|
4840
|
+
var labels = ['Jan', 'F\u00e9v', 'Mar', 'Avr', 'Mai', 'Jun'];
|
|
4841
|
+
var html = '<svg viewBox="0 0 300 140" class="mock-bar-svg">';
|
|
4842
|
+
html += '<line x1="30" y1="10" x2="30" y2="120" stroke="var(--border)" stroke-width="1"/>';
|
|
4843
|
+
html += '<line x1="30" y1="120" x2="290" y2="120" stroke="var(--border)" stroke-width="1"/>';
|
|
4844
|
+
html += '<line x1="30" y1="40" x2="290" y2="40" stroke="var(--border)" stroke-width="0.5" stroke-dasharray="4"/>';
|
|
4845
|
+
html += '<line x1="30" y1="80" x2="290" y2="80" stroke="var(--border)" stroke-width="0.5" stroke-dasharray="4"/>';
|
|
4846
|
+
var bw = 30, gap = 13;
|
|
4847
|
+
bars.forEach(function(h, i) {
|
|
4848
|
+
var x = 35 + i * (bw + gap);
|
|
4849
|
+
var y = 120 - h * 1.1;
|
|
4850
|
+
html += '<rect x="' + x + '" y="' + y + '" width="' + bw + '" height="' + (h * 1.1) + '" fill="var(--primary)" rx="3" opacity="' + (0.6 + i * 0.08) + '"/>';
|
|
4851
|
+
html += '<text x="' + (x + bw / 2) + '" y="134" text-anchor="middle" fill="var(--text-muted)" font-size="9">' + labels[i] + '</text>';
|
|
4852
|
+
});
|
|
4853
|
+
html += '</svg>';
|
|
4854
|
+
return html;
|
|
4855
|
+
}
|
|
4856
|
+
|
|
4857
|
+
function renderMockLineChart() {
|
|
4858
|
+
var points = '40,90 90,65 140,75 190,40 240,50 280,25';
|
|
4859
|
+
var labels = ['Jan', 'F\u00e9v', 'Mar', 'Avr', 'Mai', 'Jun'];
|
|
4860
|
+
var xs = [40, 90, 140, 190, 240, 280];
|
|
4861
|
+
var html = '<svg viewBox="0 0 300 140" class="mock-line-svg">';
|
|
4862
|
+
html += '<line x1="30" y1="10" x2="30" y2="120" stroke="var(--border)" stroke-width="1"/>';
|
|
4863
|
+
html += '<line x1="30" y1="120" x2="290" y2="120" stroke="var(--border)" stroke-width="1"/>';
|
|
4864
|
+
html += '<line x1="30" y1="40" x2="290" y2="40" stroke="var(--border)" stroke-width="0.5" stroke-dasharray="4"/>';
|
|
4865
|
+
html += '<line x1="30" y1="80" x2="290" y2="80" stroke="var(--border)" stroke-width="0.5" stroke-dasharray="4"/>';
|
|
4866
|
+
html += '<polygon fill="var(--primary)" opacity="0.1" points="' + points + ' 280,120 40,120"/>';
|
|
4867
|
+
html += '<polyline fill="none" stroke="var(--primary)" stroke-width="2.5" points="' + points + '" stroke-linecap="round" stroke-linejoin="round"/>';
|
|
4868
|
+
points.split(' ').forEach(function(p) {
|
|
4869
|
+
var xy = p.split(',');
|
|
4870
|
+
html += '<circle cx="' + xy[0] + '" cy="' + xy[1] + '" r="3" fill="var(--primary)" stroke="var(--bg-card)" stroke-width="1.5"/>';
|
|
4871
|
+
});
|
|
4872
|
+
xs.forEach(function(x, i) {
|
|
4873
|
+
html += '<text x="' + x + '" y="134" text-anchor="middle" fill="var(--text-muted)" font-size="9">' + labels[i] + '</text>';
|
|
4874
|
+
});
|
|
4875
|
+
html += '</svg>';
|
|
4876
|
+
return html;
|
|
4877
|
+
}
|
|
4878
|
+
|
|
4879
|
+
/* ---------- Form Tab Switching ---------- */
|
|
4880
|
+
function switchFormTab(mockupId, tabIndex) {
|
|
4881
|
+
document.querySelectorAll('.mock-form-tab-bar[data-mockup="' + mockupId + '"] .mock-form-tab').forEach(function(t) {
|
|
4882
|
+
t.classList.remove('active');
|
|
4883
|
+
});
|
|
4884
|
+
var allTabs = document.querySelectorAll('.mock-form-tab-bar[data-mockup="' + mockupId + '"] .mock-form-tab');
|
|
4885
|
+
if (allTabs[tabIndex]) allTabs[tabIndex].classList.add('active');
|
|
4886
|
+
document.querySelectorAll('.mock-form-tab-content[data-mockup="' + mockupId + '"]').forEach(function(el) {
|
|
4887
|
+
el.classList.remove('active');
|
|
4888
|
+
});
|
|
4889
|
+
var target = document.querySelector('.mock-form-tab-content[data-mockup="' + mockupId + '"][data-tab="' + tabIndex + '"]');
|
|
4890
|
+
if (target) target.classList.add('active');
|
|
4891
|
+
}
|
|
4892
|
+
|
|
4715
4893
|
/* ---------- Helpers ---------- */
|
|
4716
4894
|
function formatActionLabel(action) {
|
|
4717
4895
|
var labels = {
|
|
@@ -721,16 +721,12 @@ function renderModuleMockups(code) {
|
|
|
721
721
|
return typeof s === 'string' ? { code: s, name: s, resources: [] } : s;
|
|
722
722
|
}) : [];
|
|
723
723
|
|
|
724
|
-
// Priority 1: HTML mockups from screens[] specs
|
|
724
|
+
// Priority 1: HTML mockups from screens[] specs (wireframes NOT shown when screens exist)
|
|
725
725
|
if (screens.length > 0) {
|
|
726
726
|
var html = '';
|
|
727
727
|
if (typeof renderScreenMockups === 'function') {
|
|
728
728
|
html = renderScreenMockups(code);
|
|
729
729
|
}
|
|
730
|
-
if (wireframes.length > 0) {
|
|
731
|
-
html += '<h3 style="color:var(--text-bright);font-size:1rem;margin:2rem 0 1rem;">Wireframes</h3>';
|
|
732
|
-
html += wireframes.map(function(wf, i) { return renderWireframeMockup(code, wf, i); }).join('');
|
|
733
|
-
}
|
|
734
730
|
return html;
|
|
735
731
|
}
|
|
736
732
|
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
Generates realistic HTML mockups from screens[] specs
|
|
4
4
|
============================================ */
|
|
5
5
|
|
|
6
|
+
var _formTabCounter = 0;
|
|
7
|
+
|
|
6
8
|
function renderScreenMockups(code) {
|
|
7
9
|
var spec = data.moduleSpecs[code] || {};
|
|
8
10
|
var screens = spec.screens || [];
|
|
@@ -162,6 +164,7 @@ function renderSmartFormMockup(res) {
|
|
|
162
164
|
}
|
|
163
165
|
if (tabs.length === 0) return '<div style="padding:2rem;text-align:center;color:var(--text-muted);">Formulaire sans champs définis</div>';
|
|
164
166
|
|
|
167
|
+
var mockupId = 'form-tabs-' + (++_formTabCounter);
|
|
165
168
|
var html = '';
|
|
166
169
|
|
|
167
170
|
// Header
|
|
@@ -174,37 +177,51 @@ function renderSmartFormMockup(res) {
|
|
|
174
177
|
});
|
|
175
178
|
html += '</div></div>';
|
|
176
179
|
|
|
177
|
-
// Tabs
|
|
180
|
+
// Tabs (clickable when more than 1)
|
|
178
181
|
if (tabs.length > 1) {
|
|
179
|
-
html += '<div
|
|
182
|
+
html += '<div class="mock-form-tab-bar" data-mockup="' + mockupId + '">';
|
|
180
183
|
tabs.forEach(function(tab, i) {
|
|
181
|
-
html += '<span
|
|
184
|
+
html += '<span class="mock-form-tab' + (i === 0 ? ' active' : '') + '"';
|
|
185
|
+
html += ' onclick="switchFormTab(\'' + mockupId + '\', ' + i + ')"';
|
|
186
|
+
html += '>' + escapeHtml(tab.label) + '</span>';
|
|
182
187
|
});
|
|
183
188
|
html += '</div>';
|
|
184
189
|
}
|
|
185
190
|
|
|
186
|
-
//
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
191
|
+
// Tab contents (all rendered, only first visible)
|
|
192
|
+
tabs.forEach(function(tab, ti) {
|
|
193
|
+
var isOnly = tabs.length <= 1;
|
|
194
|
+
html += '<div class="mock-form-tab-content' + (ti === 0 || isOnly ? ' active' : '') + '"';
|
|
195
|
+
html += ' data-mockup="' + mockupId + '" data-tab="' + ti + '">';
|
|
196
|
+
|
|
197
|
+
var fields = tab.fields || [];
|
|
198
|
+
var rows = [];
|
|
199
|
+
for (var i = 0; i < fields.length; i += 2) {
|
|
200
|
+
rows.push(fields.slice(i, i + 2));
|
|
201
|
+
}
|
|
192
202
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
203
|
+
rows.forEach(function(row) {
|
|
204
|
+
if (row.length === 1 && row[0].type === 'subtable') {
|
|
205
|
+
html += renderSubtableMockup(row[0]);
|
|
206
|
+
} else {
|
|
207
|
+
html += '<div class="mock-form-row">';
|
|
208
|
+
row.forEach(function(field) {
|
|
209
|
+
html += '<div class="mock-form-group">';
|
|
210
|
+
html += '<label class="mock-label">' + escapeHtml(field.label || field.field);
|
|
211
|
+
if (field.required) html += ' <span style="color:var(--error);">*</span>';
|
|
212
|
+
html += '</label>';
|
|
213
|
+
html += renderFormFieldMockup(field);
|
|
214
|
+
html += '</div>';
|
|
215
|
+
});
|
|
204
216
|
html += '</div>';
|
|
205
|
-
}
|
|
206
|
-
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
if (fields.length === 0) {
|
|
221
|
+
html += '<div style="padding:2rem;text-align:center;color:var(--text-muted);font-style:italic;">Contenu de l\'onglet « ' + escapeHtml(tab.label) + ' »</div>';
|
|
207
222
|
}
|
|
223
|
+
|
|
224
|
+
html += '</div>';
|
|
208
225
|
});
|
|
209
226
|
|
|
210
227
|
return html;
|
|
@@ -308,10 +325,10 @@ function renderSmartDashboardMockup(res) {
|
|
|
308
325
|
});
|
|
309
326
|
html += '</div>';
|
|
310
327
|
|
|
311
|
-
//
|
|
312
|
-
var charts = res.charts || [{ label: 'Graphique' }];
|
|
328
|
+
// Charts (SVG mock previews)
|
|
329
|
+
var charts = res.charts || [{ label: 'Graphique', type: 'bar' }];
|
|
313
330
|
charts.forEach(function(chart) {
|
|
314
|
-
html +=
|
|
331
|
+
html += renderMockChart(chart);
|
|
315
332
|
});
|
|
316
333
|
|
|
317
334
|
return html;
|
|
@@ -352,6 +369,96 @@ function renderSmartFilterMockup(res) {
|
|
|
352
369
|
return html;
|
|
353
370
|
}
|
|
354
371
|
|
|
372
|
+
/* ---------- Mock Charts ---------- */
|
|
373
|
+
function renderMockChart(chart) {
|
|
374
|
+
var type = (chart.type || '').toLowerCase();
|
|
375
|
+
var label = chart.label || chart.type || 'Graphique';
|
|
376
|
+
var html = '<div class="mock-chart-container">';
|
|
377
|
+
html += '<div class="mock-chart-header">' + escapeHtml(label) + '</div>';
|
|
378
|
+
html += '<div class="mock-chart-body">';
|
|
379
|
+
if (type === 'pie' || type === 'donut') {
|
|
380
|
+
html += renderMockPieChart();
|
|
381
|
+
} else if (type === 'line' || type === 'area') {
|
|
382
|
+
html += renderMockLineChart();
|
|
383
|
+
} else {
|
|
384
|
+
html += renderMockBarChart();
|
|
385
|
+
}
|
|
386
|
+
html += '</div></div>';
|
|
387
|
+
return html;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function renderMockPieChart() {
|
|
391
|
+
var html = '<div style="display:flex;align-items:center;gap:1.5rem;">';
|
|
392
|
+
html += '<svg viewBox="0 0 42 42" class="mock-pie-svg">';
|
|
393
|
+
html += '<circle cx="21" cy="21" r="15.9" fill="transparent" stroke="var(--primary)" stroke-width="8" stroke-dasharray="35 65" stroke-dashoffset="25"/>';
|
|
394
|
+
html += '<circle cx="21" cy="21" r="15.9" fill="transparent" stroke="var(--success)" stroke-width="8" stroke-dasharray="25 75" stroke-dashoffset="-10"/>';
|
|
395
|
+
html += '<circle cx="21" cy="21" r="15.9" fill="transparent" stroke="var(--warning)" stroke-width="8" stroke-dasharray="22 78" stroke-dashoffset="-35"/>';
|
|
396
|
+
html += '<circle cx="21" cy="21" r="15.9" fill="transparent" stroke="var(--error)" stroke-width="8" stroke-dasharray="18 82" stroke-dashoffset="-57"/>';
|
|
397
|
+
html += '</svg>';
|
|
398
|
+
html += '<div class="mock-chart-legend">';
|
|
399
|
+
html += '<div><span class="mock-legend-dot" style="background:var(--primary);"></span> Cat\u00e9gorie A (35%)</div>';
|
|
400
|
+
html += '<div><span class="mock-legend-dot" style="background:var(--success);"></span> Cat\u00e9gorie B (25%)</div>';
|
|
401
|
+
html += '<div><span class="mock-legend-dot" style="background:var(--warning);"></span> Cat\u00e9gorie C (22%)</div>';
|
|
402
|
+
html += '<div><span class="mock-legend-dot" style="background:var(--error);"></span> Cat\u00e9gorie D (18%)</div>';
|
|
403
|
+
html += '</div></div>';
|
|
404
|
+
return html;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function renderMockBarChart() {
|
|
408
|
+
var bars = [85, 60, 45, 70, 55, 90];
|
|
409
|
+
var labels = ['Jan', 'F\u00e9v', 'Mar', 'Avr', 'Mai', 'Jun'];
|
|
410
|
+
var html = '<svg viewBox="0 0 300 140" class="mock-bar-svg">';
|
|
411
|
+
html += '<line x1="30" y1="10" x2="30" y2="120" stroke="var(--border)" stroke-width="1"/>';
|
|
412
|
+
html += '<line x1="30" y1="120" x2="290" y2="120" stroke="var(--border)" stroke-width="1"/>';
|
|
413
|
+
html += '<line x1="30" y1="40" x2="290" y2="40" stroke="var(--border)" stroke-width="0.5" stroke-dasharray="4"/>';
|
|
414
|
+
html += '<line x1="30" y1="80" x2="290" y2="80" stroke="var(--border)" stroke-width="0.5" stroke-dasharray="4"/>';
|
|
415
|
+
var bw = 30, gap = 13;
|
|
416
|
+
bars.forEach(function(h, i) {
|
|
417
|
+
var x = 35 + i * (bw + gap);
|
|
418
|
+
var y = 120 - h * 1.1;
|
|
419
|
+
html += '<rect x="' + x + '" y="' + y + '" width="' + bw + '" height="' + (h * 1.1) + '" fill="var(--primary)" rx="3" opacity="' + (0.6 + i * 0.08) + '"/>';
|
|
420
|
+
html += '<text x="' + (x + bw / 2) + '" y="134" text-anchor="middle" fill="var(--text-muted)" font-size="9">' + labels[i] + '</text>';
|
|
421
|
+
});
|
|
422
|
+
html += '</svg>';
|
|
423
|
+
return html;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function renderMockLineChart() {
|
|
427
|
+
var points = '40,90 90,65 140,75 190,40 240,50 280,25';
|
|
428
|
+
var labels = ['Jan', 'F\u00e9v', 'Mar', 'Avr', 'Mai', 'Jun'];
|
|
429
|
+
var xs = [40, 90, 140, 190, 240, 280];
|
|
430
|
+
var html = '<svg viewBox="0 0 300 140" class="mock-line-svg">';
|
|
431
|
+
html += '<line x1="30" y1="10" x2="30" y2="120" stroke="var(--border)" stroke-width="1"/>';
|
|
432
|
+
html += '<line x1="30" y1="120" x2="290" y2="120" stroke="var(--border)" stroke-width="1"/>';
|
|
433
|
+
html += '<line x1="30" y1="40" x2="290" y2="40" stroke="var(--border)" stroke-width="0.5" stroke-dasharray="4"/>';
|
|
434
|
+
html += '<line x1="30" y1="80" x2="290" y2="80" stroke="var(--border)" stroke-width="0.5" stroke-dasharray="4"/>';
|
|
435
|
+
html += '<polygon fill="var(--primary)" opacity="0.1" points="' + points + ' 280,120 40,120"/>';
|
|
436
|
+
html += '<polyline fill="none" stroke="var(--primary)" stroke-width="2.5" points="' + points + '" stroke-linecap="round" stroke-linejoin="round"/>';
|
|
437
|
+
points.split(' ').forEach(function(p) {
|
|
438
|
+
var xy = p.split(',');
|
|
439
|
+
html += '<circle cx="' + xy[0] + '" cy="' + xy[1] + '" r="3" fill="var(--primary)" stroke="var(--bg-card)" stroke-width="1.5"/>';
|
|
440
|
+
});
|
|
441
|
+
xs.forEach(function(x, i) {
|
|
442
|
+
html += '<text x="' + x + '" y="134" text-anchor="middle" fill="var(--text-muted)" font-size="9">' + labels[i] + '</text>';
|
|
443
|
+
});
|
|
444
|
+
html += '</svg>';
|
|
445
|
+
return html;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/* ---------- Form Tab Switching ---------- */
|
|
449
|
+
function switchFormTab(mockupId, tabIndex) {
|
|
450
|
+
document.querySelectorAll('.mock-form-tab-bar[data-mockup="' + mockupId + '"] .mock-form-tab').forEach(function(t) {
|
|
451
|
+
t.classList.remove('active');
|
|
452
|
+
});
|
|
453
|
+
var allTabs = document.querySelectorAll('.mock-form-tab-bar[data-mockup="' + mockupId + '"] .mock-form-tab');
|
|
454
|
+
if (allTabs[tabIndex]) allTabs[tabIndex].classList.add('active');
|
|
455
|
+
document.querySelectorAll('.mock-form-tab-content[data-mockup="' + mockupId + '"]').forEach(function(el) {
|
|
456
|
+
el.classList.remove('active');
|
|
457
|
+
});
|
|
458
|
+
var target = document.querySelector('.mock-form-tab-content[data-mockup="' + mockupId + '"][data-tab="' + tabIndex + '"]');
|
|
459
|
+
if (target) target.classList.add('active');
|
|
460
|
+
}
|
|
461
|
+
|
|
355
462
|
/* ---------- Helpers ---------- */
|
|
356
463
|
function formatActionLabel(action) {
|
|
357
464
|
var labels = {
|
|
@@ -258,15 +258,6 @@
|
|
|
258
258
|
}
|
|
259
259
|
.mock-kpi-value { font-size: 1.3rem; font-weight: 700; color: var(--text-bright); }
|
|
260
260
|
.mock-kpi-label { font-size: 0.7rem; color: var(--text-muted); margin-top: 0.2rem; }
|
|
261
|
-
.mock-chart-placeholder {
|
|
262
|
-
height: 150px;
|
|
263
|
-
background: linear-gradient(135deg, rgba(99,102,241,0.05), rgba(6,182,212,0.05));
|
|
264
|
-
border: 1px dashed var(--border);
|
|
265
|
-
border-radius: 8px;
|
|
266
|
-
display: flex; align-items: center; justify-content: center;
|
|
267
|
-
color: var(--text-muted); font-size: 0.85rem;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
261
|
@media (max-width: 768px) {
|
|
271
262
|
.mock-kpi-grid { grid-template-columns: repeat(2, 1fr); }
|
|
272
263
|
}
|
|
@@ -134,3 +134,87 @@
|
|
|
134
134
|
padding: 0.15rem 0.5rem;
|
|
135
135
|
border-radius: 4px;
|
|
136
136
|
}
|
|
137
|
+
|
|
138
|
+
/* ---- Mock Charts (SVG previews) ---- */
|
|
139
|
+
.mock-chart-container {
|
|
140
|
+
margin: 0.75rem 0;
|
|
141
|
+
border: 1px solid var(--border);
|
|
142
|
+
border-radius: 8px;
|
|
143
|
+
overflow: hidden;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.mock-chart-header {
|
|
147
|
+
padding: 0.5rem 0.75rem;
|
|
148
|
+
font-size: 0.85rem;
|
|
149
|
+
font-weight: 500;
|
|
150
|
+
color: var(--text-bright);
|
|
151
|
+
background: var(--bg-hover);
|
|
152
|
+
border-bottom: 1px solid var(--border);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.mock-chart-body {
|
|
156
|
+
padding: 1rem;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.mock-pie-svg {
|
|
160
|
+
width: 120px;
|
|
161
|
+
height: 120px;
|
|
162
|
+
flex-shrink: 0;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.mock-bar-svg,
|
|
166
|
+
.mock-line-svg {
|
|
167
|
+
width: 100%;
|
|
168
|
+
height: 140px;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.mock-chart-legend {
|
|
172
|
+
font-size: 0.8rem;
|
|
173
|
+
color: var(--text);
|
|
174
|
+
display: flex;
|
|
175
|
+
flex-direction: column;
|
|
176
|
+
gap: 0.4rem;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.mock-legend-dot {
|
|
180
|
+
display: inline-block;
|
|
181
|
+
width: 10px;
|
|
182
|
+
height: 10px;
|
|
183
|
+
border-radius: 2px;
|
|
184
|
+
margin-right: 0.4rem;
|
|
185
|
+
vertical-align: middle;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/* ---- Form Tab Switching ---- */
|
|
189
|
+
.mock-form-tab-bar {
|
|
190
|
+
display: flex;
|
|
191
|
+
gap: 0;
|
|
192
|
+
border-bottom: 1px solid var(--border);
|
|
193
|
+
margin-bottom: 1.5rem;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.mock-form-tab {
|
|
197
|
+
padding: 0.5rem 1rem;
|
|
198
|
+
font-size: 0.85rem;
|
|
199
|
+
cursor: pointer;
|
|
200
|
+
border-bottom: 2px solid transparent;
|
|
201
|
+
color: var(--text-muted);
|
|
202
|
+
transition: all var(--transition-fast);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.mock-form-tab:hover {
|
|
206
|
+
color: var(--text);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.mock-form-tab.active {
|
|
210
|
+
border-bottom-color: var(--primary);
|
|
211
|
+
color: var(--primary-light);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.mock-form-tab-content {
|
|
215
|
+
display: none;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.mock-form-tab-content.active {
|
|
219
|
+
display: block;
|
|
220
|
+
}
|