Files
KantSites/templates/admin/shell.html.twig
T
kantarciandClaude Fable 5.1 160c394c3f
CMS quality / php (8.2) (push) Canceled after 0s
CMS quality / php (8.4) (push) Canceled after 0s
CMS quality / browser (push) Canceled after 0s
CMS quality / mysql (push) Canceled after 0s
Rework forms, statistics, design and the sidebar foot in 1.15.0
Forms are an editor now: one card per field with add and remove, the
technical name derived from the label, options only for a choice, and a
new "Aussehen" section with a button label and four colours (field border
and background, button and button text) that travel to the page as one
rule per form inside the hashed style block. A live preview draws the
form with the site's default button and names the text contrast.

Statistics open with six figures (views against the same span before,
average per day, busiest day, pages, referred views, missing addresses),
a chart with grid lines and a title per bar, a weekday chart and ranked
lists with share bars. The design screen shows the palette as colour
cards with seven ready-made palettes, picks the base colours from the
palette, previews every font in itself and the typography as a sample.
The sidebar ends in an account card with role and a logout icon.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 07:59:30 +02:00

102 lines
7.7 KiB
Twig

{# The frame around every management screen. One sidebar, always the same, so each area is one
click away and the current one is visible. Screens fill in heading, lead, actions and content. #}
{% extends 'admin/base.html.twig' %}
{% block body_class %}admin-shell{% endblock %}
{# Solange jemand hier arbeitet, laeuft der Aufgabenplan - auch auf einer Website ohne Besucher.
Das Skript fragt im Minutentakt an; was faellig ist, entscheidet der Server. #}
{% block worker %}
<meta name="kant-tick" content="{{ path('admin_tasks_tick') }}">
<meta name="kant-tick-token" content="{{ csrf_token('tasks-tick') }}">
<script src="/assets/task-worker.js?v={{ kant_version() }}" defer></script>
{% endblock %}
{% block body %}
<header class="shell-nav">
<a class="brand" href="{{ path('admin_pages') }}"><span class="brand-icon" aria-hidden="true">k.</span>kant<span>sites</span></a>
{# Auf dem Telefon ein Klappmenue, am Schreibtisch die gewohnte Spalte. Ein details-Element
kann beides, ohne eine Zeile JavaScript: die Zusammenfassung wird auf grossen Bildschirmen
ausgeblendet und der Inhalt dauerhaft gezeigt. Zwoelf Eintraege in einer Wischleiste zu
verstecken war die schlechtere Loesung - man sieht nie, was man nicht trifft. #}
<details class="shell-menu">
<summary>
<svg class="shell-menu-icon" viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true" focusable="false"><path d="M4 6h16M4 12h16M4 18h16"/></svg>
<span class="shell-menu-label">{{ {
'pages': 'Seiten', 'posts': 'Beiträge', 'partials': 'Bereiche', 'menus': 'Menüs', 'media': 'Mediathek',
'forms': 'Formulare', 'statistics': 'Statistik', 'theme': 'Design',
'settings': 'Einstellungen', 'system': 'System', 'tasks': 'Aufgaben', 'users': 'Benutzer',
'account': 'Mein Konto'
}[nav|default('')]|default('Verwaltung') }}</span>
</summary>
<nav aria-label="Verwaltung">
<p class="nav-label" id="nav-content">INHALT</p>
<ul class="nav-list" aria-labelledby="nav-content">
{% set entries = [
{id: 'pages', route: 'admin_pages', icon: 'pages', label: 'Seiten'},
{id: 'posts', route: 'admin_posts', icon: 'pages', label: 'Beiträge'},
{id: 'partials', route: 'admin_partials', icon: 'partials', label: 'Bereiche'},
{id: 'menus', route: 'admin_menus', icon: 'menus', label: 'Menüs'},
{id: 'media', route: 'admin_media', icon: 'media', label: 'Mediathek'},
{id: 'forms', route: 'admin_forms', icon: 'forms', label: 'Formulare'},
{id: 'statistics', route: 'admin_statistics', icon: 'statistics', label: 'Statistik'}
] %}
{% for entry in entries %}
<li><a class="nav-item" href="{{ path(entry.route) }}"{{ nav|default('') == entry.id ? ' aria-current="page"' : '' }}>{% include 'admin/icon.html.twig' with {name: entry.icon} only %}{{ entry.label }}</a></li>
{% endfor %}
</ul>
<p class="nav-label" id="nav-site">WEBSITE</p>
<ul class="nav-list" aria-labelledby="nav-site">
<li><a class="nav-item" href="{{ path('admin_theme') }}"{{ nav|default('') == 'theme' ? ' aria-current="page"' : '' }}>{% include 'admin/icon.html.twig' with {name: 'theme'} only %}Design</a></li>
<li><a class="nav-item" href="{{ path('admin_settings') }}"{{ nav|default('') == 'settings' ? ' aria-current="page"' : '' }}>{% include 'admin/icon.html.twig' with {name: 'settings'} only %}Einstellungen</a></li>
{% if is_granted('ROLE_ADMIN') %}<li><a class="nav-item" href="{{ path('admin_system') }}"{{ nav|default('') == 'system' ? ' aria-current="page"' : '' }}>{% include 'admin/icon.html.twig' with {name: 'system'} only %}System</a></li>{% endif %}
{% if is_granted('ROLE_ADMIN') %}<li><a class="nav-item" href="{{ path('admin_tasks') }}"{{ nav|default('') == 'tasks' ? ' aria-current="page"' : '' }}>{% include 'admin/icon.html.twig' with {name: 'tasks'} only %}Aufgaben</a></li>{% endif %}
{% if is_granted('ROLE_ADMIN') %}<li><a class="nav-item" href="{{ path('admin_users') }}"{{ nav|default('') == 'users' ? ' aria-current="page"' : '' }}>{% include 'admin/icon.html.twig' with {name: 'users'} only %}Benutzer</a></li>{% endif %}
<li><a class="nav-item nav-out" href="/">{% include 'admin/icon.html.twig' with {name: 'external'} only %}Website ansehen</a></li>
</ul>
</nav>
</details>
{# Der Fuss der Leiste: eine Kontokarte mit Namen und Rolle, daneben der Abmeldeknopf als
Symbol mit Beschriftung fuer Vorlesesoftware, darunter die Fassung. Vorher standen drei
Zeilen lose untereinander - Fassung, Name, ein grauer Knopf; jetzt ist es eine Einheit. #}
<div class="shell-foot">
<div class="shell-account">
<a class="shell-user" href="{{ path('admin_account') }}" aria-label="Mein Konto: {{ app.user.userIdentifier }}"{{ nav|default('') == 'account' ? ' aria-current="page"' : '' }}>
<span class="shell-avatar" aria-hidden="true">{{ app.user.userIdentifier|first|upper }}</span>
<span class="shell-user-text"><strong>{{ app.user.userIdentifier }}</strong><span>{{ is_granted('ROLE_ADMIN') ? 'Administrator' : (is_granted('ROLE_PUBLISHER') ? 'Veröffentlicher' : 'Redakteur') }}</span></span>
</a>
<form action="{{ path('admin_logout') }}" method="post">
<input type="hidden" name="_csrf_token" value="{{ csrf_token('logout') }}">
<button class="shell-logout" type="submit" title="Abmelden">{% include 'admin/icon.html.twig' with {name: 'logout'} only %}<span class="visually-hidden">Abmelden</span></button>
</form>
</div>
{% set updateVersion = is_granted('ROLE_ADMIN') ? kant_update_version() : null %}
<p class="shell-version">
{% if is_granted('ROLE_ADMIN') %}<a href="{{ path('admin_system') }}">KantSites {{ kant_version() }}</a>{% else %}<span>KantSites {{ kant_version() }}</span>{% endif %}
{% if updateVersion %}<a class="version-update" href="{{ path('admin_system', {pruefen: 1}) }}" aria-label="Update {{ updateVersion }} verfügbar. Aktualisierung öffnen.">Update {{ updateVersion }} verfügbar</a>{% endif %}
</p>
</div>
</header>
<main id="admin-main" class="shell-main" tabindex="-1">
<div class="shell-top">
<div class="shell-heading">
<h1>{% block heading %}Verwaltung{% endblock %}</h1>
<p class="muted">{% block lead %}{% endblock %}</p>
</div>
<div class="shell-actions">{% block actions %}{% endblock %}</div>
</div>
<div class="shell-body stack">
{# Offene Handlungen, auf jedem Bildschirm gleich: fehlende Schriften, eine neue Fassung.
Sie stehen hier statt nur auf ihrer eigenen Seite - einen Hinweis findet nur, wer schon
weiss, wo er steht. Jeder traegt den Knopf, der ihn erledigt. #}
{% for hinweis in kant_admin_notices() %}{% if not hinweis.admin or is_granted('ROLE_ADMIN') %}
<p class="shell-notice" role="status">
<span>{{ hinweis.text }}</span>
<a class="secondary" href="{{ path(hinweis.route) }}">{{ hinweis.action }} &rarr;</a>
</p>
{% endif %}{% endfor %}
{% for type in ['error', 'success'] %}{% for message in app.flashes(type) %}
<p class="notice {{ type == 'error' ? 'error' : 'ok' }}" role="{{ type == 'error' ? 'alert' : 'status' }}">{{ message }}</p>
{% endfor %}{% endfor %}
{% block content %}{% endblock %}
</div>
</main>
{% endblock %}