viberadar 0.3.3 → 0.3.4
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/ui/dashboard.html +139 -3
- package/package.json +1 -1
package/dist/ui/dashboard.html
CHANGED
|
@@ -339,6 +339,64 @@
|
|
|
339
339
|
font-family: monospace;
|
|
340
340
|
}
|
|
341
341
|
|
|
342
|
+
/* ── Feature drill-down ──────────────────────────────────────────────────── */
|
|
343
|
+
.drill-header {
|
|
344
|
+
display: flex;
|
|
345
|
+
align-items: center;
|
|
346
|
+
gap: 14px;
|
|
347
|
+
padding-bottom: 14px;
|
|
348
|
+
border-bottom: 1px solid var(--border);
|
|
349
|
+
margin-bottom: 16px;
|
|
350
|
+
flex-wrap: wrap;
|
|
351
|
+
}
|
|
352
|
+
.back-btn {
|
|
353
|
+
background: none;
|
|
354
|
+
border: 1px solid var(--border);
|
|
355
|
+
border-radius: 6px;
|
|
356
|
+
color: var(--muted);
|
|
357
|
+
cursor: pointer;
|
|
358
|
+
padding: 5px 12px;
|
|
359
|
+
font-size: 12px;
|
|
360
|
+
flex-shrink: 0;
|
|
361
|
+
transition: background 0.1s, color 0.1s;
|
|
362
|
+
}
|
|
363
|
+
.back-btn:hover { background: var(--border); color: var(--text); }
|
|
364
|
+
.drill-title {
|
|
365
|
+
display: flex; align-items: center; gap: 8px;
|
|
366
|
+
font-size: 17px; font-weight: 700;
|
|
367
|
+
}
|
|
368
|
+
.drill-stats {
|
|
369
|
+
display: flex; gap: 16px;
|
|
370
|
+
font-size: 12px; color: var(--muted);
|
|
371
|
+
margin-left: auto;
|
|
372
|
+
}
|
|
373
|
+
.drill-desc {
|
|
374
|
+
font-size: 13px; color: var(--muted);
|
|
375
|
+
margin-bottom: 14px; line-height: 1.5;
|
|
376
|
+
}
|
|
377
|
+
.drill-section-label {
|
|
378
|
+
font-size: 10px; text-transform: uppercase;
|
|
379
|
+
letter-spacing: 0.5px; color: var(--muted);
|
|
380
|
+
margin: 14px 0 6px;
|
|
381
|
+
}
|
|
382
|
+
.file-rows { display: flex; flex-direction: column; gap: 2px; }
|
|
383
|
+
.file-row {
|
|
384
|
+
display: grid;
|
|
385
|
+
grid-template-columns: 22px 1fr auto;
|
|
386
|
+
align-items: center;
|
|
387
|
+
gap: 8px;
|
|
388
|
+
padding: 7px 10px;
|
|
389
|
+
border-radius: 6px;
|
|
390
|
+
cursor: pointer;
|
|
391
|
+
font-size: 13px;
|
|
392
|
+
transition: background 0.1s;
|
|
393
|
+
}
|
|
394
|
+
.file-row:hover { background: var(--bg-card); }
|
|
395
|
+
.file-row.active { background: var(--bg-hover); border-left: 2px solid var(--blue); padding-left: 8px; }
|
|
396
|
+
.file-row-icon { font-size: 12px; }
|
|
397
|
+
.file-row-name { font-weight: 500; word-break: break-all; }
|
|
398
|
+
.file-row-dir { font-size: 11px; color: var(--dim); text-align: right; word-break: break-word; }
|
|
399
|
+
|
|
342
400
|
/* ── Misc ────────────────────────────────────────────────────────────────── */
|
|
343
401
|
.loading { display: flex; align-items: center; justify-content: center; height: 200px; color: var(--muted); font-size: 14px; }
|
|
344
402
|
.empty { text-align: center; padding: 40px 20px; color: var(--muted); font-size: 14px; }
|
|
@@ -388,6 +446,7 @@ let view = 'features';
|
|
|
388
446
|
let searchQuery = '';
|
|
389
447
|
let activeTypes = new Set();
|
|
390
448
|
let activePanelKey = null;
|
|
449
|
+
let drillFeatureKey = null; // null = grid, string = inside a feature
|
|
391
450
|
|
|
392
451
|
// ─── Color helpers ────────────────────────────────────────────────────────────
|
|
393
452
|
const TYPE_COLORS = {
|
|
@@ -519,7 +578,18 @@ function renderSidebar() {
|
|
|
519
578
|
// ─── Content ──────────────────────────────────────────────────────────────────
|
|
520
579
|
function renderContent() {
|
|
521
580
|
const c = document.getElementById('content');
|
|
522
|
-
view === 'features'
|
|
581
|
+
if (view === 'features') {
|
|
582
|
+
drillFeatureKey ? renderFeatureDetail(c) : renderFeatureCards(c);
|
|
583
|
+
} else {
|
|
584
|
+
renderModuleGrid(c);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function backToFeatures() {
|
|
589
|
+
drillFeatureKey = null;
|
|
590
|
+
activePanelKey = null;
|
|
591
|
+
document.getElementById('panel').classList.remove('open');
|
|
592
|
+
renderContent();
|
|
523
593
|
}
|
|
524
594
|
|
|
525
595
|
function renderFeatureCards(c) {
|
|
@@ -566,7 +636,7 @@ function renderFeatureCards(c) {
|
|
|
566
636
|
<span class="feature-progress-label" style="color:${covColor(pct)}">${f.testedCount}/${f.fileCount} ✓</span>
|
|
567
637
|
</div>
|
|
568
638
|
</div>`;
|
|
569
|
-
card.onclick = () =>
|
|
639
|
+
card.onclick = () => { drillFeatureKey = f.key; activePanelKey = null; document.getElementById('panel').classList.remove('open'); renderContent(); };
|
|
570
640
|
grid.appendChild(card);
|
|
571
641
|
});
|
|
572
642
|
|
|
@@ -602,6 +672,71 @@ function renderFeatureCards(c) {
|
|
|
602
672
|
}
|
|
603
673
|
}
|
|
604
674
|
|
|
675
|
+
function renderFeatureDetail(c) {
|
|
676
|
+
const feat = D.features.find(f => f.key === drillFeatureKey);
|
|
677
|
+
if (!feat) { backToFeatures(); return; }
|
|
678
|
+
|
|
679
|
+
const mods = D.modules.filter(m => m.featureKeys && m.featureKeys.includes(drillFeatureKey));
|
|
680
|
+
const src = mods.filter(m => m.type !== 'test');
|
|
681
|
+
const tst = mods.filter(m => m.type === 'test');
|
|
682
|
+
const testedCount = src.filter(m => m.hasTests).length;
|
|
683
|
+
const pct = src.length > 0 ? Math.round(testedCount / src.length * 100) : 0;
|
|
684
|
+
|
|
685
|
+
const q = searchQuery.toLowerCase();
|
|
686
|
+
const filteredSrc = q ? src.filter(m =>
|
|
687
|
+
m.name.toLowerCase().includes(q) || m.relativePath.toLowerCase().includes(q)
|
|
688
|
+
) : src;
|
|
689
|
+
const filteredTst = q ? tst.filter(m =>
|
|
690
|
+
m.name.toLowerCase().includes(q) || m.relativePath.toLowerCase().includes(q)
|
|
691
|
+
) : tst;
|
|
692
|
+
|
|
693
|
+
c.innerHTML = `
|
|
694
|
+
<div class="drill-header">
|
|
695
|
+
<button class="back-btn" onclick="backToFeatures()">← Все фичи</button>
|
|
696
|
+
<div class="drill-title">
|
|
697
|
+
<div style="width:10px;height:10px;border-radius:50%;background:${feat.color};flex-shrink:0"></div>
|
|
698
|
+
<span>${feat.label}</span>
|
|
699
|
+
</div>
|
|
700
|
+
<div class="drill-stats">
|
|
701
|
+
<span>${src.length} файлов</span>
|
|
702
|
+
<span style="color:${covColor(pct)}">${pct}% с тестами</span>
|
|
703
|
+
<span>${tst.length} тест-файлов</span>
|
|
704
|
+
</div>
|
|
705
|
+
</div>
|
|
706
|
+
${feat.description ? `<div class="drill-desc">${feat.description}</div>` : ''}
|
|
707
|
+
<div class="file-rows" id="fileRows">
|
|
708
|
+
${filteredSrc.length === 0 && !q
|
|
709
|
+
? '<div style="font-size:13px;color:var(--dim)">Нет файлов — возможно паттерны в конфиге не совпадают</div>'
|
|
710
|
+
: filteredSrc.map(m => fileRow(m)).join('')
|
|
711
|
+
}
|
|
712
|
+
${filteredTst.length > 0 ? `
|
|
713
|
+
<div class="drill-section-label">Тест-файлы (${filteredTst.length})</div>
|
|
714
|
+
${filteredTst.map(m => fileRow(m, true)).join('')}
|
|
715
|
+
` : ''}
|
|
716
|
+
</div>`;
|
|
717
|
+
|
|
718
|
+
c.querySelectorAll('.file-row[data-id]').forEach(row => {
|
|
719
|
+
row.onclick = () => {
|
|
720
|
+
const m = D.modules.find(m => m.id === row.dataset.id);
|
|
721
|
+
if (m) openModulePanel(m);
|
|
722
|
+
};
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
function fileRow(m, isTest = false) {
|
|
727
|
+
const parts = m.relativePath.replace(/\\/g, '/').split('/');
|
|
728
|
+
const name = parts[parts.length - 1];
|
|
729
|
+
const dir = parts.slice(0, -1).join('/');
|
|
730
|
+
const icon = isTest ? '🧪' : (m.hasTests ? '✅' : '⬜');
|
|
731
|
+
const isActive = activePanelKey === m.id;
|
|
732
|
+
return `
|
|
733
|
+
<div class="file-row${isActive ? ' active' : ''}" data-id="${m.id}">
|
|
734
|
+
<span class="file-row-icon">${icon}</span>
|
|
735
|
+
<span class="file-row-name">${name}</span>
|
|
736
|
+
<span class="file-row-dir">${dir}</span>
|
|
737
|
+
</div>`;
|
|
738
|
+
}
|
|
739
|
+
|
|
605
740
|
function renderModuleGrid(c) {
|
|
606
741
|
const q = searchQuery.toLowerCase();
|
|
607
742
|
const list = D.modules.filter(m => {
|
|
@@ -833,6 +968,7 @@ document.querySelectorAll('.view-tab').forEach(tab => {
|
|
|
833
968
|
tab.onclick = () => {
|
|
834
969
|
if (tab.classList.contains('disabled')) return;
|
|
835
970
|
view = tab.dataset.view;
|
|
971
|
+
drillFeatureKey = null;
|
|
836
972
|
activePanelKey = null;
|
|
837
973
|
searchQuery = '';
|
|
838
974
|
activeTypes.clear();
|
|
@@ -871,7 +1007,7 @@ async function refreshData() {
|
|
|
871
1007
|
renderSidebar();
|
|
872
1008
|
renderContent();
|
|
873
1009
|
|
|
874
|
-
// Re-
|
|
1010
|
+
// Re-render drill-down or re-open panel
|
|
875
1011
|
const panelOpen = document.getElementById('panel').classList.contains('open');
|
|
876
1012
|
if (panelOpen && activePanelKey) {
|
|
877
1013
|
if (activePanelKey === '__unmapped__') {
|