(ui) Harden dashboard responsive layout

Rework the dashboard priority grid to avoid cramped four-column layouts,
prevent stretched empty panels, and make long host and snapshot text wrap
safely across dashboard cards.

Refs #38
This commit is contained in:
2026-05-21 14:43:24 +02:00
parent 4c76ae9f52
commit 025cd0336c
3 changed files with 44 additions and 7 deletions

View File

@@ -304,17 +304,22 @@
} }
.inline-form { margin: 0; } .inline-form { margin: 0; }
.dashboard-priority-grid { .dashboard-priority-grid {
align-items: start;
display: grid; display: grid;
gap: 14px; gap: 14px;
grid-template-columns: minmax(280px, 1.25fr) repeat(3, minmax(220px, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
margin-bottom: 20px; margin-bottom: 20px;
} }
.priority-panel { .priority-panel {
display: grid; display: grid;
gap: 12px; gap: 12px;
margin-bottom: 0; margin-bottom: 0;
min-width: 0;
}
.priority-panel > h2:first-child {
flex-wrap: wrap;
margin-bottom: 0;
} }
.priority-panel > h2:first-child { margin-bottom: 0; }
.action-list, .action-list,
.activity-list, .activity-list,
.schedule-list { .schedule-list {
@@ -404,6 +409,14 @@
gap: 2px; gap: 2px;
min-width: 0; min-width: 0;
} }
.action-row strong,
.action-row .muted,
.activity-row strong,
.activity-row .muted,
.schedule-row strong,
.schedule-row .muted {
overflow-wrap: anywhere;
}
.schedule-time { .schedule-time {
justify-items: end; justify-items: end;
text-align: right; text-align: right;
@@ -436,6 +449,10 @@
justify-content: space-between; justify-content: space-between;
padding-top: 8px; padding-top: 8px;
} }
.storage-priority-facts strong {
text-align: right;
overflow-wrap: anywhere;
}
.host-control-grid { .host-control-grid {
display: grid; display: grid;
gap: 14px; gap: 14px;
@@ -623,7 +640,7 @@
.host-card-timeline { .host-card-timeline {
display: grid; display: grid;
gap: 16px 22px; gap: 16px 22px;
grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
} }
.host-card-stats { .host-card-stats {
align-content: start; align-content: start;
@@ -649,6 +666,10 @@
.host-card-item .value { .host-card-item .value {
overflow-wrap: anywhere; overflow-wrap: anywhere;
} }
.host-card-item .value a {
overflow-wrap: anywhere;
word-break: break-word;
}
.host-card-stat { .host-card-stat {
display: grid; display: grid;
gap: 3px; gap: 3px;
@@ -778,6 +799,18 @@
.host-card-stats { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); } .host-card-stats { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
.insight-grid { grid-template-columns: 1fr; } .insight-grid { grid-template-columns: 1fr; }
} }
@media (max-width: 1100px) {
.dashboard-priority-grid {
grid-template-columns: 1fr;
}
.schedule-row {
grid-template-columns: 1fr;
}
.schedule-time {
justify-items: start;
text-align: left;
}
}
</style> </style>
</head> </head>
<body> <body>

View File

@@ -33,7 +33,7 @@
{% endif %} {% endif %}
<section class="dashboard-priority-grid" aria-label="Operator priorities"> <section class="dashboard-priority-grid" aria-label="Operator priorities">
<article class="panel priority-panel"> <article class="panel priority-panel dashboard-panel-required">
<h2>Required Action</h2> <h2>Required Action</h2>
{% if action_items %} {% if action_items %}
<div class="action-list"> <div class="action-list">
@@ -70,7 +70,7 @@
{% endif %} {% endif %}
</article> </article>
<article class="panel priority-panel"> <article class="panel priority-panel dashboard-panel-schedules">
<h2>Next Scheduled Work <a class="button-link secondary compact" href="{% url 'schedules_list' %}">View all</a></h2> <h2>Next Scheduled Work <a class="button-link secondary compact" href="{% url 'schedules_list' %}">View all</a></h2>
{% if next_schedule_rows %} {% if next_schedule_rows %}
<div class="schedule-list"> <div class="schedule-list">
@@ -96,7 +96,7 @@
{% endif %} {% endif %}
</article> </article>
<article class="panel priority-panel"> <article class="panel priority-panel dashboard-panel-activity">
<h2>Recent Activity <a class="button-link secondary compact" href="{% url 'runs_list' %}">View all</a></h2> <h2>Recent Activity <a class="button-link secondary compact" href="{% url 'runs_list' %}">View all</a></h2>
{% if recent_runs %} {% if recent_runs %}
<div class="activity-list"> <div class="activity-list">
@@ -115,7 +115,7 @@
{% endif %} {% endif %}
</article> </article>
<article class="panel priority-panel"> <article class="panel priority-panel dashboard-panel-storage">
<h2>Storage Pressure</h2> <h2>Storage Pressure</h2>
{% if stats_summary.runs_sampled %} {% if stats_summary.runs_sampled %}
<div class="storage-priority"> <div class="storage-priority">

View File

@@ -103,6 +103,10 @@ class ViewTests(TestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertContains(response, "Control panel") self.assertContains(response, "Control panel")
self.assertContains(response, "Backup health, required action, storage pressure, and recent activity in one place.") self.assertContains(response, "Backup health, required action, storage pressure, and recent activity in one place.")
self.assertContains(response, "dashboard-panel-required")
self.assertContains(response, "dashboard-panel-schedules")
self.assertContains(response, "dashboard-panel-activity")
self.assertContains(response, "dashboard-panel-storage")
self.assertContains(response, "Dashboard") self.assertContains(response, "Dashboard")
self.assertContains(response, "web-01") self.assertContains(response, "web-01")
self.assertContains(response, "20260519-021500Z__ABCDEFGH") self.assertContains(response, "20260519-021500Z__ABCDEFGH")