Create cohesive control panel redesign #30

Merged
parkel merged 3 commits from issue-28-control-panel-redesign into master 2026-05-21 11:44:29 +02:00
11 changed files with 112 additions and 56 deletions
Showing only changes of commit b67ae7ff8b - Show all commits

View File

@@ -3,11 +3,16 @@
{% block title %}Changelog - pobsync{% endblock %}
{% block content %}
<h1>Changelog</h1>
<section class="actions" aria-label="Changelog actions">
<a class="button-link secondary" href="{% url 'dashboard' %}">Back to dashboard</a>
</section>
<header class="page-header">
<div class="page-title">
<div class="page-kicker">Release notes</div>
<h1>Changelog</h1>
<div class="page-subtitle">Installed release notes rendered from the repository changelog.</div>
</div>
<section class="actions" aria-label="Changelog actions">
<a class="button-link secondary" href="{% url 'dashboard' %}">Back to dashboard</a>
</section>
</header>
<section class="panel">
<div class="stack spaced">

View File

@@ -3,11 +3,16 @@
{% block title %}Global Config{% endblock %}
{% block content %}
<h1>{% if global_config %}Global Config{% else %}Create Global Config{% endif %}</h1>
<section class="actions" aria-label="Global config actions">
<a class="button-link" href="{% url 'dashboard' %}">Back to dashboard</a>
</section>
<header class="page-header">
<div class="page-title">
<div class="page-kicker">Configuration</div>
<h1>{% if global_config %}Global Config{% else %}Create Global Config{% endif %}</h1>
<div class="page-subtitle">Defaults used by hosts unless a host overrides them explicitly.</div>
</div>
<section class="actions" aria-label="Global config actions">
<a class="button-link" href="{% url 'dashboard' %}">Back to dashboard</a>
</section>
</header>
<section class="panel">
<h2>{% if global_config %}Edit Global Config{% else %}Create Global Config{% endif %}</h2>

View File

@@ -3,15 +3,20 @@
{% block title %}{% if host %}Config | {{ host.host }}{% else %}New Host{% endif %}{% endblock %}
{% block content %}
<h1>{% if host %}Config: {{ host.host }}{% else %}New Host{% endif %}</h1>
<section class="actions" aria-label="Config actions">
{% if host %}
<a class="button-link" href="{% url 'host_detail' host.host %}">Back to host</a>
{% else %}
<a class="button-link" href="{% url 'dashboard' %}">Back to dashboard</a>
{% endif %}
</section>
<header class="page-header">
<div class="page-title">
<div class="page-kicker">Configuration</div>
<h1>{% if host %}Config: {{ host.host }}{% else %}New Host{% endif %}</h1>
<div class="page-subtitle">Host-specific backup, retention, SSH, include, and exclude settings.</div>
</div>
<section class="actions" aria-label="Config actions">
{% if host %}
<a class="button-link" href="{% url 'host_detail' host.host %}">Back to host</a>
{% else %}
<a class="button-link" href="{% url 'dashboard' %}">Back to dashboard</a>
{% endif %}
</section>
</header>
<section class="panel">
<h2>{% if host %}Edit Host Config{% else %}Create Host Config{% endif %}</h2>

View File

@@ -3,11 +3,16 @@
{% block title %}Logs | pobsync{% endblock %}
{% block content %}
<h1>Logs</h1>
<section class="actions" aria-label="Log actions">
<a class="button-link secondary" href="{% url 'dashboard' %}">Back to dashboard</a>
</section>
<header class="page-header">
<div class="page-title">
<div class="page-kicker">Operations</div>
<h1>Logs</h1>
<div class="page-subtitle">Filter pobsync service logs by unit, priority, host, run, or message content.</div>
</div>
<section class="actions" aria-label="Log actions">
<a class="button-link secondary" href="{% url 'dashboard' %}">Back to dashboard</a>
</section>
</header>
<section class="panel">
<h2>Filter</h2>

View File

@@ -3,11 +3,16 @@
{% block title %}Purged Snapshots | pobsync{% endblock %}
{% block content %}
<h1>Purged Snapshots</h1>
<section class="actions" aria-label="Purged snapshot actions">
<a class="button-link secondary" href="{% url 'dashboard' %}">Back to dashboard</a>
</section>
<header class="page-header">
<div class="page-title">
<div class="page-kicker">Retention</div>
<h1>Purged Snapshots</h1>
<div class="page-subtitle">Audit trail for snapshots removed by retention or manual purge actions.</div>
</div>
<section class="actions" aria-label="Purged snapshot actions">
<a class="button-link secondary" href="{% url 'dashboard' %}">Back to dashboard</a>
</section>
</header>
<section class="panel">
<h2>Filters</h2>

View File

@@ -3,11 +3,16 @@
{% block title %}Schedule | {{ host.host }}{% endblock %}
{% block content %}
<h1>Schedule: {{ host.host }}</h1>
<section class="actions" aria-label="Schedule actions">
<a class="button-link" href="{% url 'host_detail' host.host %}">Back to host</a>
</section>
<header class="page-header">
<div class="page-title">
<div class="page-kicker">Schedule</div>
<h1>{{ host.host }}</h1>
<div class="page-subtitle">Automatic backup timing and scheduled prune behavior for this host.</div>
</div>
<section class="actions" aria-label="Schedule actions">
<a class="button-link" href="{% url 'host_detail' host.host %}">Back to host</a>
</section>
</header>
<section class="panel">
<h2>{% if schedule %}Edit Schedule{% else %}Create Schedule{% endif %}</h2>

View File

@@ -3,11 +3,16 @@
{% block title %}Self Check | pobsync{% endblock %}
{% block content %}
<h1>Self Check</h1>
<section class="actions" aria-label="Self check actions">
<a class="button-link secondary" href="{% url 'dashboard' %}">Back to dashboard</a>
</section>
<header class="page-header">
<div class="page-title">
<div class="page-kicker">Operations</div>
<h1>Self Check</h1>
<div class="page-subtitle">Runtime, filesystem, service, and configuration checks for this pobsync installation.</div>
</div>
<section class="actions" aria-label="Self check actions">
<a class="button-link secondary" href="{% url 'dashboard' %}">Back to dashboard</a>
</section>
</header>
<section class="grid" aria-label="Self check summary">
<div class="metric"><div class="label">OK</div><div class="value">{{ summary.ok }}</div></div>

View File

@@ -3,11 +3,16 @@
{% block title %}{% if credential %}SSH Key | {{ credential.name }}{% else %}New SSH Key{% endif %} | pobsync{% endblock %}
{% block content %}
<h1>{% if credential %}SSH Key: {{ credential.name }}{% else %}New SSH Key{% endif %}</h1>
<section class="actions" aria-label="SSH key form actions">
<a class="button-link" href="{% url 'ssh_credentials' %}">Back to SSH keys</a>
</section>
<header class="page-header">
<div class="page-title">
<div class="page-kicker">Access</div>
<h1>{% if credential %}SSH Key: {{ credential.name }}{% else %}New SSH Key{% endif %}</h1>
<div class="page-subtitle">{% if credential %}Review key metadata, known hosts, and deletion safety for this credential.{% else %}Register an existing private key for use by pobsync backups.{% endif %}</div>
</div>
<section class="actions" aria-label="SSH key form actions">
<a class="button-link" href="{% url 'ssh_credentials' %}">Back to SSH keys</a>
</section>
</header>
<section class="panel">
<h2>{% if credential %}Edit SSH Credential{% else %}Create SSH Credential{% endif %}</h2>

View File

@@ -3,11 +3,16 @@
{% block title %}Generate SSH Key | pobsync{% endblock %}
{% block content %}
<h1>Generate SSH Key</h1>
<section class="actions" aria-label="SSH key form actions">
<a class="button-link" href="{% url 'ssh_credentials' %}">Back to SSH keys</a>
</section>
<header class="page-header">
<div class="page-title">
<div class="page-kicker">Access</div>
<h1>Generate SSH Key</h1>
<div class="page-subtitle">Create a pobsync-managed SSH key pair for one or more backup targets.</div>
</div>
<section class="actions" aria-label="SSH key form actions">
<a class="button-link" href="{% url 'ssh_credentials' %}">Back to SSH keys</a>
</section>
</header>
<section class="panel">
<h2>Create Key Pair</h2>

View File

@@ -3,13 +3,18 @@
{% block title %}SSH Keys | pobsync{% endblock %}
{% block content %}
<h1>SSH Keys</h1>
<section class="actions" aria-label="SSH key actions">
<a class="button-link" href="{% url 'generate_ssh_credential' %}">Generate SSH key</a>
<a class="button-link secondary" href="{% url 'create_ssh_credential' %}">Add existing key</a>
<a class="button-link secondary" href="{% url 'dashboard' %}">Back to dashboard</a>
</section>
<header class="page-header">
<div class="page-title">
<div class="page-kicker">Access</div>
<h1>SSH Keys</h1>
<div class="page-subtitle">Manage the key pairs pobsync uses to reach backup targets.</div>
</div>
<section class="actions" aria-label="SSH key actions">
<a class="button-link" href="{% url 'generate_ssh_credential' %}">Generate SSH key</a>
<a class="button-link secondary" href="{% url 'create_ssh_credential' %}">Add existing key</a>
<a class="button-link secondary" href="{% url 'dashboard' %}">Back to dashboard</a>
</section>
</header>
<section class="panel">
<h2>Credentials</h2>

View File

@@ -295,6 +295,7 @@ class ViewTests(TestCase):
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Self Check")
self.assertContains(response, "Runtime, filesystem, service, and configuration checks")
self.assertContains(response, "Django debug")
self.assertContains(response, "Database connection")
self.assertContains(response, "State root")
@@ -329,6 +330,7 @@ class ViewTests(TestCase):
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Logs")
self.assertContains(response, "Filter pobsync service logs")
self.assertContains(response, "web-01 failed backup run 12")
self.assertNotContains(response, "web-02 failed backup run 12")
self.assertNotContains(response, "started")
@@ -358,6 +360,7 @@ class ViewTests(TestCase):
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Purged Snapshots")
self.assertContains(response, "Audit trail for snapshots removed")
self.assertContains(response, "20260518-021500Z__OLDSNAP")
self.assertContains(response, "outside retention policy")
self.assertContains(response, "Scheduled")
@@ -412,6 +415,7 @@ class ViewTests(TestCase):
)
self.assertRedirects(response, reverse("ssh_credentials"))
self.assertContains(response, "Manage the key pairs pobsync uses")
self.assertContains(response, "SSH credential saved for backup-key.")
self.assertContains(response, "backup-key")
credential = SshCredential.objects.get(name="backup-key")
@@ -641,6 +645,7 @@ class ViewTests(TestCase):
response = self.client.get(reverse("edit_global_config"))
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Defaults used by hosts unless a host overrides them")
self.assertContains(response, f'value="{credential.id}" selected')
self.assertContains(response, "--archive")
self.assertContains(response, "/proc/***")
@@ -2031,6 +2036,7 @@ class ViewTests(TestCase):
response = self.client.get(reverse("edit_host_schedule", args=[host.host]))
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Automatic backup timing and scheduled prune behavior")
self.assertContains(response, "Create Schedule")
self.assertContains(response, "Schedule expression")
self.assertContains(response, "evaluated by the pobsync scheduler service")