Move reload/restart/shutdown into an Acties menu in the top bar
The actions are now on every page in the same place instead of a button row that differed per page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7e07428a81
commit
efc581e0ae
8 files changed
+51
-21
No files matched your search
@@ -42,6 +42,16 @@ h2 { font-size: 1.1rem; margin: 0 0 .9rem; }
|
||||
.mainnav a:hover, .mainnav button:hover { background: var(--navy-2); color: #fff; }
|
||||
.mainnav a[aria-current="page"] { background: var(--navy-2); color: #fff; }
|
||||
.nav-logout { margin: 0; }
|
||||
.nav-menu { position: relative; }
|
||||
.nav-menu summary { list-style: none; color: #d6e2f3; padding: .45rem .7rem; border-radius: 6px; font-size: .95rem; cursor: pointer; user-select: none; }
|
||||
.nav-menu summary::-webkit-details-marker { display: none; }
|
||||
.nav-menu summary:hover, .nav-menu[open] summary { background: var(--navy-2); color: #fff; }
|
||||
.nav-menu-list { position: absolute; right: 0; top: calc(100% + 6px); min-width: 230px; padding: .3rem 0; background: #fff; border: 1px solid var(--border); border-radius: 6px; box-shadow: 0 8px 24px rgba(11, 37, 69, .15); z-index: 20; }
|
||||
.nav-menu-list form { margin: 0; }
|
||||
.mainnav .nav-menu-list button { display: block; width: 100%; text-align: left; padding: .55rem .9rem; border-radius: 0; color: var(--text); }
|
||||
.mainnav .nav-menu-list button:hover { background: #f1f4f8; color: var(--text); }
|
||||
.mainnav .nav-menu-list button.danger { color: #b03030; }
|
||||
.nav-menu-list hr { border: 0; border-top: 1px solid var(--border); margin: .3rem 0; }
|
||||
.nav-toggle { display: none; background: none; border: 1px solid rgba(255, 255, 255, .3); border-radius: 8px; width: 44px; height: 40px; cursor: pointer; }
|
||||
.nav-toggle .bars, .nav-toggle .bars::before, .nav-toggle .bars::after { display: block; width: 20px; height: 2px; background: #fff; margin: 0 auto; position: relative; content: ""; }
|
||||
.nav-toggle .bars::before { position: absolute; top: -6px; }
|
||||
@@ -51,7 +61,12 @@ h2 { font-size: 1.1rem; margin: 0 0 .9rem; }
|
||||
.nav-toggle { display: block; }
|
||||
.mainnav { display: none; width: 100%; flex-direction: column; align-items: stretch; padding-bottom: .75rem; }
|
||||
.mainnav.open { display: flex; }
|
||||
.mainnav a, .mainnav button { padding: .75rem; text-align: left; width: 100%; }
|
||||
.mainnav a, .mainnav button, .nav-menu summary { padding: .75rem; text-align: left; width: 100%; }
|
||||
.nav-menu-list { position: static; min-width: 0; margin: .25rem 0 0 .75rem; background: none; border: 0; border-left: 1px solid rgba(255, 255, 255, .2); border-radius: 0; box-shadow: none; }
|
||||
.mainnav .nav-menu-list button { color: #d6e2f3; }
|
||||
.mainnav .nav-menu-list button:hover { background: var(--navy-2); color: #fff; }
|
||||
.mainnav .nav-menu-list button.danger { color: #ffb4b4; }
|
||||
.nav-menu-list hr { border-top-color: rgba(255, 255, 255, .2); }
|
||||
}
|
||||
|
||||
/* --- layout ------------------------------------------------------------------ */
|
||||
|
||||
@@ -16,6 +16,17 @@
|
||||
});
|
||||
}
|
||||
|
||||
// Actions menu: close after choosing an item, on a click outside it or on Escape
|
||||
const menu = $("[data-nav-menu]");
|
||||
if (menu) {
|
||||
document.addEventListener("click", (event) => {
|
||||
if (menu.open && (!menu.contains(event.target) || event.target.closest(".nav-menu-list button"))) menu.open = false;
|
||||
});
|
||||
document.addEventListener("keydown", (event) => {
|
||||
if (event.key === "Escape" && menu.open) menu.open = false;
|
||||
});
|
||||
}
|
||||
|
||||
// Confirmation dialog for dangerous actions (forms with data-confirm)
|
||||
const dialog = $("#confirm-dialog");
|
||||
$$("form[data-confirm]").forEach((form) => {
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
<span class="badge badge-{{ status }}" {% if field %}data-badge="{{ field }}"{% endif %}><span class="dot" aria-hidden="true"></span><span data-badge-label>{{ label }}</span></span>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro action(url, label, cls="btn-primary", confirm=None, confirm_title=None, big=False) -%}
|
||||
<form method="post" action="{{ url }}" class="inline-form" {% if confirm %}data-confirm="{{ confirm }}" data-confirm-title="{{ confirm_title or label }}"{% endif %}>
|
||||
{% macro menu_action(url, label, confirm=None, confirm_title=None, danger=False) -%}
|
||||
<form method="post" action="{{ url }}" {% if confirm %}data-confirm="{{ confirm }}" data-confirm-title="{{ confirm_title or label }}"{% endif %}>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<button type="submit" class="btn {{ cls }}{% if big %} btn-big{% endif %}">{{ label }}</button>
|
||||
<button type="submit"{% if danger %} class="danger"{% endif %}>{{ label }}</button>
|
||||
</form>
|
||||
{%- endmacro %}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{% from "_macros.html" import menu_action with context %}
|
||||
<!doctype html>
|
||||
<html lang="nl">
|
||||
<head>
|
||||
@@ -27,6 +28,16 @@
|
||||
<a href="{{ url_for('ui.network') }}" {% if active == 'network' %}aria-current="page"{% endif %}>Netwerk</a>
|
||||
<a href="{{ url_for('ui.logs') }}" {% if active == 'logs' %}aria-current="page"{% endif %}>Logboek</a>
|
||||
<a href="{{ url_for('ui.change_password') }}" {% if active == 'account' %}aria-current="page"{% endif %}>Wachtwoord</a>
|
||||
<details class="nav-menu" data-nav-menu>
|
||||
<summary>Acties <span aria-hidden="true">▾</span></summary>
|
||||
<div class="nav-menu-list">
|
||||
{{ menu_action(url_for('ui.kiosk_reload'), "Pagina herladen") }}
|
||||
{{ menu_action(url_for('ui.kiosk_restart'), "Chromium herstarten", confirm="Chromium wordt afgesloten en opnieuw gestart. Het scherm is enkele seconden zwart.") }}
|
||||
<hr>
|
||||
{{ menu_action(url_for('ui.system_reboot'), "Raspberry Pi herstarten", confirm="De Raspberry Pi herstart. Het scherm en de beheerinterface zijn ongeveer een minuut niet beschikbaar.", danger=True) }}
|
||||
{{ menu_action(url_for('ui.system_poweroff'), "Veilig uitschakelen", confirm="De Raspberry Pi wordt uitgeschakeld. Daarna kan hij alleen weer aan door de stroom los en weer vast te maken.", confirm_title="Raspberry Pi uitschakelen", danger=True) }}
|
||||
</div>
|
||||
</details>
|
||||
<form method="post" action="{{ url_for('ui.logout') }}" class="nav-logout">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<button type="submit">Uitloggen</button>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
{% from "_macros.html" import badge, action, meter with context %}
|
||||
{% from "_macros.html" import badge, meter with context %}
|
||||
{% set title = "Dashboard" %}
|
||||
{% block content %}
|
||||
<div class="page-head">
|
||||
@@ -7,11 +7,6 @@
|
||||
<p class="muted">Ververst automatisch elke 5 seconden.</p>
|
||||
</div>
|
||||
|
||||
<section class="actions-row" aria-label="Snelle acties">
|
||||
{{ action(url_for('ui.kiosk_reload'), "Pagina herladen", "btn-primary", big=True) }}
|
||||
{{ action(url_for('ui.kiosk_restart'), "Chromium herstarten", "btn-secondary", confirm="Chromium wordt afgesloten en opnieuw gestart. Het scherm is enkele seconden zwart.", big=True) }}
|
||||
{{ action(url_for('ui.system_reboot'), "Raspberry Pi herstarten", "btn-danger", confirm="De Raspberry Pi herstart. Het scherm en de beheerinterface zijn ongeveer een minuut niet beschikbaar.", big=True) }}
|
||||
</section>
|
||||
|
||||
<div class="grid" data-live-status="{{ url_for('ui.api_status') }}">
|
||||
<section class="card">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
{% from "_macros.html" import badge, action, csrf with context %}
|
||||
{% from "_macros.html" import badge, csrf with context %}
|
||||
{% set title = "Kiosk" %}
|
||||
{% block content %}
|
||||
<div class="page-head">
|
||||
@@ -7,10 +7,6 @@
|
||||
{{ badge(s.browser.status, s.browser.label) }}
|
||||
</div>
|
||||
|
||||
<section class="actions-row">
|
||||
{{ action(url_for('ui.kiosk_reload'), "Pagina herladen", "btn-primary", big=True) }}
|
||||
{{ action(url_for('ui.kiosk_restart'), "Chromium herstarten", "btn-secondary", confirm="Chromium wordt afgesloten en opnieuw gestart.", big=True) }}
|
||||
</section>
|
||||
|
||||
<div class="grid grid-2">
|
||||
<section class="card">
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
{% extends "base.html" %}
|
||||
{% from "_macros.html" import action, csrf with context %}
|
||||
{% from "_macros.html" import csrf with context %}
|
||||
{% set title = "Systeem" %}
|
||||
{% block content %}
|
||||
<div class="page-head"><h1>Systeembeheer</h1></div>
|
||||
|
||||
<section class="actions-row">
|
||||
{{ action(url_for('ui.kiosk_restart'), "Chromium herstarten", "btn-secondary", confirm="Chromium wordt afgesloten en opnieuw gestart.", big=True) }}
|
||||
{{ action(url_for('ui.system_reboot'), "Raspberry Pi herstarten", "btn-danger", confirm="De Raspberry Pi herstart. Het scherm en de beheerinterface zijn ongeveer een minuut niet beschikbaar.", big=True) }}
|
||||
{{ action(url_for('ui.system_poweroff'), "Veilig uitschakelen", "btn-secondary", confirm="De Raspberry Pi wordt uitgeschakeld. Daarna kan hij alleen weer aan door de stroom los en weer vast te maken.", confirm_title="Raspberry Pi uitschakelen", big=True) }}
|
||||
</section>
|
||||
|
||||
<div class="grid grid-2">
|
||||
<section class="card">
|
||||
|
||||
@@ -298,3 +298,10 @@ def test_admin_port_change_restarts_manager(logged_in, fake_helper, paths):
|
||||
assert resp.status_code == 200 and ":8443/" in resp.get_data(as_text=True)
|
||||
assert fake_helper.calls[-1][0] == "restart-manager"
|
||||
assert load_config(paths.config_file)["admin_port"] == 8443
|
||||
|
||||
|
||||
@pytest.mark.parametrize("path", ["/", "/kiosk", "/system", "/network", "/logs", "/account/password"])
|
||||
def test_actions_menu_on_every_page(logged_in, path):
|
||||
html = logged_in.get(path).get_data(as_text=True)
|
||||
for action in ("/kiosk/reload", "/kiosk/restart", "/system/reboot", "/system/poweroff"):
|
||||
assert f'action="{action}"' in html
|
||||
Reference in new issue
Block a user