Group dashboard targets into a collapsible Targets section with Running/Stopped subsections

Wrap running and stopped target lists under a parent Targets group.
Fix narrow-screen layout by keeping action buttons inline and hiding
metrics below 768px.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-19 03:08:00 +03:00
parent f83cd81937
commit df52a197d9
4 changed files with 37 additions and 18 deletions

View File

@@ -306,12 +306,15 @@ export async function loadDashboard() {
</div>`; </div>`;
} }
if (targets.length > 0) {
let targetsInner = '';
if (running.length > 0) { if (running.length > 0) {
runningIds = running.map(t => t.id); runningIds = running.map(t => t.id);
const stopAllBtn = `<button class="btn btn-sm btn-danger dashboard-stop-all" onclick="event.stopPropagation(); dashboardStopAll()" title="${t('dashboard.stop_all')}">⏹️ ${t('dashboard.stop_all')}</button>`; const stopAllBtn = `<button class="btn btn-sm btn-danger dashboard-stop-all" onclick="event.stopPropagation(); dashboardStopAll()" title="${t('dashboard.stop_all')}">⏹️ ${t('dashboard.stop_all')}</button>`;
const runningItems = running.map(target => renderDashboardTarget(target, true, devicesMap)).join(''); const runningItems = running.map(target => renderDashboardTarget(target, true, devicesMap)).join('');
dynamicHtml += `<div class="dashboard-section"> targetsInner += `<div class="dashboard-subsection">
${_sectionHeader('running', t('dashboard.section.running'), running.length, stopAllBtn)} ${_sectionHeader('running', t('dashboard.section.running'), running.length, stopAllBtn)}
${_sectionContent('running', runningItems)} ${_sectionContent('running', runningItems)}
</div>`; </div>`;
@@ -320,11 +323,17 @@ export async function loadDashboard() {
if (stopped.length > 0) { if (stopped.length > 0) {
const stoppedItems = stopped.map(target => renderDashboardTarget(target, false, devicesMap)).join(''); const stoppedItems = stopped.map(target => renderDashboardTarget(target, false, devicesMap)).join('');
dynamicHtml += `<div class="dashboard-section"> targetsInner += `<div class="dashboard-subsection">
${_sectionHeader('stopped', t('dashboard.section.stopped'), stopped.length)} ${_sectionHeader('stopped', t('dashboard.section.stopped'), stopped.length)}
${_sectionContent('stopped', stoppedItems)} ${_sectionContent('stopped', stoppedItems)}
</div>`; </div>`;
} }
dynamicHtml += `<div class="dashboard-section">
${_sectionHeader('targets', t('dashboard.section.targets'), targets.length)}
${_sectionContent('targets', targetsInner)}
</div>`;
}
} }
// First load: build everything in one innerHTML to avoid flicker // First load: build everything in one innerHTML to avoid flicker

View File

@@ -457,6 +457,7 @@
"overlay.error.start": "Failed to start overlay", "overlay.error.start": "Failed to start overlay",
"overlay.error.stop": "Failed to stop overlay", "overlay.error.stop": "Failed to stop overlay",
"dashboard.title": "📊 Dashboard", "dashboard.title": "📊 Dashboard",
"dashboard.section.targets": "Targets",
"dashboard.section.running": "Running", "dashboard.section.running": "Running",
"dashboard.section.stopped": "Stopped", "dashboard.section.stopped": "Stopped",
"dashboard.no_targets": "No targets configured", "dashboard.no_targets": "No targets configured",

View File

@@ -457,6 +457,7 @@
"overlay.error.start": "Не удалось запустить наложение", "overlay.error.start": "Не удалось запустить наложение",
"overlay.error.stop": "Не удалось остановить наложение", "overlay.error.stop": "Не удалось остановить наложение",
"dashboard.title": "📊 Обзор", "dashboard.title": "📊 Обзор",
"dashboard.section.targets": "Цели",
"dashboard.section.running": "Запущенные", "dashboard.section.running": "Запущенные",
"dashboard.section.stopped": "Остановленные", "dashboard.section.stopped": "Остановленные",
"dashboard.no_targets": "Нет настроенных целей", "dashboard.no_targets": "Нет настроенных целей",

View File

@@ -3276,6 +3276,14 @@ input:-webkit-autofill:focus {
font-weight: 600; font-weight: 600;
} }
.dashboard-subsection {
margin-bottom: 10px;
padding-left: 16px;
}
.dashboard-subsection .dashboard-section-header {
font-size: 0.72rem;
}
.dashboard-stop-all { .dashboard-stop-all {
margin-left: auto; margin-left: auto;
padding: 2px 8px; padding: 2px 8px;
@@ -3454,12 +3462,12 @@ input:-webkit-autofill:focus {
@media (max-width: 768px) { @media (max-width: 768px) {
.dashboard-target { .dashboard-target {
grid-template-columns: 1fr; grid-template-columns: 1fr auto;
gap: 6px; gap: 6px;
} }
.dashboard-target-actions { .dashboard-target-metrics {
justify-content: flex-end; display: none;
} }
} }