diff --git a/src/pobsync_backend/templates/pobsync_backend/base.html b/src/pobsync_backend/templates/pobsync_backend/base.html
index 070bf2b..0d321f5 100644
--- a/src/pobsync_backend/templates/pobsync_backend/base.html
+++ b/src/pobsync_backend/templates/pobsync_backend/base.html
@@ -43,7 +43,7 @@
outline: 3px solid rgba(7, 95, 174, 0.24);
outline-offset: 2px;
}
- header {
+ body > header {
background: var(--panel);
border-bottom: 1px solid var(--border);
box-shadow: var(--shadow-sm);
@@ -105,6 +105,35 @@
}
p { margin: 0 0 12px; }
p:last-child { margin-bottom: 0; }
+ .page-header {
+ align-items: end;
+ display: flex;
+ gap: 18px;
+ justify-content: space-between;
+ margin-bottom: 20px;
+ }
+ .page-title {
+ display: grid;
+ gap: 5px;
+ min-width: 0;
+ }
+ .page-title h1 { margin-bottom: 0; overflow-wrap: anywhere; }
+ .page-kicker {
+ color: var(--muted);
+ font-size: 12px;
+ font-weight: 750;
+ letter-spacing: 0.05em;
+ text-transform: uppercase;
+ }
+ .page-subtitle {
+ color: var(--muted);
+ max-width: 760px;
+ }
+ .page-header .actions {
+ flex: 0 0 auto;
+ justify-content: flex-end;
+ margin-bottom: 0;
+ }
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
@@ -483,7 +512,7 @@
padding: 0;
}
@media (max-width: 800px) {
- header { padding: 0 14px; position: static; }
+ body > header { padding: 0 14px; position: static; }
main { padding: 18px 14px 32px; }
nav {
align-items: flex-start;
@@ -493,6 +522,11 @@
}
nav strong { flex-basis: 100%; margin-right: 0; }
nav .spacer { display: none; }
+ .page-header {
+ align-items: stretch;
+ display: grid;
+ }
+ .page-header .actions { justify-content: flex-start; }
.two-col { grid-template-columns: 1fr; }
.host-card-header { display: grid; }
.host-card-status { justify-content: flex-start; max-width: none; }
diff --git a/src/pobsync_backend/templates/pobsync_backend/dashboard.html b/src/pobsync_backend/templates/pobsync_backend/dashboard.html
index b6067fb..358fd72 100644
--- a/src/pobsync_backend/templates/pobsync_backend/dashboard.html
+++ b/src/pobsync_backend/templates/pobsync_backend/dashboard.html
@@ -3,12 +3,17 @@
{% block title %}pobsync dashboard{% endblock %}
{% block content %}
-
Dashboard
-
-
+
{% if not global_config or not counts.hosts %}
diff --git a/src/pobsync_backend/templates/pobsync_backend/host_detail.html b/src/pobsync_backend/templates/pobsync_backend/host_detail.html
index ae530e2..60bbae8 100644
--- a/src/pobsync_backend/templates/pobsync_backend/host_detail.html
+++ b/src/pobsync_backend/templates/pobsync_backend/host_detail.html
@@ -3,29 +3,34 @@
{% block title %}{{ host.host }} | pobsync{% endblock %}
{% block content %}
- {{ host.host }}
-
-
+
Snapshots
{{ counts.snapshots }}
diff --git a/src/pobsync_backend/templates/pobsync_backend/retention_plan.html b/src/pobsync_backend/templates/pobsync_backend/retention_plan.html
index ba88c9c..c1547b8 100644
--- a/src/pobsync_backend/templates/pobsync_backend/retention_plan.html
+++ b/src/pobsync_backend/templates/pobsync_backend/retention_plan.html
@@ -3,15 +3,20 @@
{% block title %}Retention plan | {{ host.host }}{% endblock %}
{% block content %}
- Retention Plan: {{ host.host }}
-
-
+
diff --git a/src/pobsync_backend/templates/pobsync_backend/run_detail.html b/src/pobsync_backend/templates/pobsync_backend/run_detail.html
index 4fbf6aa..8f042f9 100644
--- a/src/pobsync_backend/templates/pobsync_backend/run_detail.html
+++ b/src/pobsync_backend/templates/pobsync_backend/run_detail.html
@@ -3,25 +3,30 @@
{% block title %}Run {{ run.id }} | {{ run.host.host }}{% endblock %}
{% block content %}
- Run {{ run.id }}
-
-
- Back to host
- {% if can_cancel %}
-
- {% endif %}
- {% if run.status == "failed" or run.status == "warning" %}
- {% if not run.reviewed_at %}
-
+
diff --git a/src/pobsync_backend/templates/pobsync_backend/snapshot_detail.html b/src/pobsync_backend/templates/pobsync_backend/snapshot_detail.html
index c37b5ee..9520853 100644
--- a/src/pobsync_backend/templates/pobsync_backend/snapshot_detail.html
+++ b/src/pobsync_backend/templates/pobsync_backend/snapshot_detail.html
@@ -3,11 +3,16 @@
{% block title %}Snapshot {{ snapshot.dirname }} | {{ snapshot.host.host }}{% endblock %}
{% block content %}
- {{ snapshot.dirname }}
-
-
+
Host
{{ snapshot.host.host }}
diff --git a/src/pobsync_backend/tests/test_views.py b/src/pobsync_backend/tests/test_views.py
index a731ba5..cb42255 100644
--- a/src/pobsync_backend/tests/test_views.py
+++ b/src/pobsync_backend/tests/test_views.py
@@ -101,6 +101,8 @@ class ViewTests(TestCase):
response = self.client.get(reverse("dashboard"))
self.assertEqual(response.status_code, 200)
+ self.assertContains(response, "Control panel")
+ self.assertContains(response, "Backup health, required action, storage pressure, and recent activity in one place.")
self.assertContains(response, "Dashboard")
self.assertContains(response, "web-01")
self.assertContains(response, "20260519-021500Z__ABCDEFGH")
@@ -869,6 +871,8 @@ class ViewTests(TestCase):
response = self.client.get(reverse("host_detail", args=[host.host]))
self.assertEqual(response.status_code, 200)
+ self.assertContains(response, "Host")
+ self.assertContains(response, "web-01.example.test")
self.assertContains(response, "Effective Config")
self.assertContains(response, "Backup source:")
self.assertNotContains(response, "Source root:")
@@ -1425,6 +1429,8 @@ class ViewTests(TestCase):
response = self.client.get(reverse("run_detail", args=[run.id]))
self.assertEqual(response.status_code, 200)
+ self.assertContains(response, "Backup run")
+ self.assertContains(response, "web-01")
self.assertContains(response, "Failure")
self.assertContains(response, "transport")
self.assertContains(response, "Check network connectivity.")
@@ -1615,6 +1621,7 @@ class ViewTests(TestCase):
response = self.client.get(reverse("snapshot_detail", args=[base.id]))
self.assertEqual(response.status_code, 200)
+ self.assertContains(response, "Snapshot")
self.assertContains(response, base.dirname)
self.assertContains(response, "BASESNAP")
self.assertContains(response, "Stats")
@@ -1697,7 +1704,9 @@ class ViewTests(TestCase):
response = self.client.get(reverse("host_retention_plan", args=[host.host]))
self.assertEqual(response.status_code, 200)
- self.assertContains(response, "Retention Plan: web-01")
+ self.assertContains(response, "Retention")
+ self.assertContains(response, "Preview which snapshots stay")
+ self.assertContains(response, "web-01")
self.assertContains(response, old_snapshot.dirname)
self.assertContains(response, new_snapshot.dirname)
self.assertContains(response, "newest")