Menüübersicht

This commit is contained in:
2026-03-30 21:52:56 +02:00
parent 1290382aa0
commit 0b7156af47
5 changed files with 1153 additions and 495 deletions
+90 -48
View File
@@ -232,70 +232,111 @@ $themeCss = app_tenant_theme_root_css($tenantSettings);
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Die Kaffeeliste</title>
<style>
:root{<?= $themeCss ?>--line:rgba(37,24,15,.14);--radius:18px;--shadow:0 16px 36px rgba(37,24,15,.08)}
*{box-sizing:border-box}body{margin:0;font-family:"Aptos","Segoe UI",sans-serif;color:var(--ink);background:linear-gradient(180deg,#f9f6ef 0%,var(--bg) 100%)}a{color:inherit}.shell{width:min(1180px,calc(100vw - 32px));margin:20px auto 40px}.bar,.hero,.card,.alert{border:1px solid var(--line);border-radius:var(--radius);background:var(--card);box-shadow:var(--shadow)}.bar,.actions,.context{display:flex;flex-wrap:wrap;gap:10px}.bar{justify-content:space-between;align-items:center;padding:18px 22px;margin-bottom:14px}.brand strong,h1,h2,h3{font-family:Georgia,serif}.brand strong{font-size:1.18rem}.brand span,p,.muted{color:var(--muted)}.hero,.card{padding:24px}.hero{margin-bottom:18px;background:linear-gradient(180deg,#fffdf8 0%,#f9f4ea 100%)}.grid{display:grid;gap:18px}.grid-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-4{grid-template-columns:repeat(4,minmax(0,1fr))}.tenant-nav-wrap{margin-bottom:18px}.tenant-nav{display:grid;gap:12px;padding:14px 16px;border:1px solid var(--line);border-radius:20px;background:rgba(255,251,244,.92);box-shadow:var(--shadow)}.tenant-nav__desktop{display:flex;align-items:center;gap:14px;justify-content:space-between}.tenant-nav__links{display:flex;gap:10px;flex:1 1 auto;overflow-x:auto;scrollbar-width:none}.tenant-nav__links::-webkit-scrollbar{display:none}.tenant-nav__link,.button,button{display:inline-flex;align-items:center;justify-content:center;padding:10px 14px;border-radius:999px;text-decoration:none;font-weight:700;border:1px solid transparent;cursor:pointer}.tenant-nav__link{background:#fff;color:var(--brand);border-color:rgba(var(--brand-rgb),.12);white-space:nowrap}.tenant-nav__link.active{background:var(--brand);color:#fff}.tenant-nav__mobile{display:none;position:relative}.tenant-nav__mobile[open]{z-index:20}.tenant-nav__toggle{display:inline-flex;align-items:center;justify-content:center;gap:10px;padding:10px 14px;border-radius:999px;text-decoration:none;font-weight:700;border:1px solid rgba(var(--brand-rgb),.12);cursor:pointer;list-style:none;background:#fff;color:var(--brand)}.tenant-nav__toggle::-webkit-details-marker{display:none}.tenant-nav__toggle::before{content:"";width:18px;height:12px;border-top:2px solid currentColor;border-bottom:2px solid currentColor;box-shadow:inset 0 -4px 0 0 currentColor}.tenant-nav__panel{margin-top:12px;padding:14px;border-radius:18px;border:1px solid var(--line);background:#fffdf9;box-shadow:var(--shadow)}.tenant-nav__stack{display:grid;gap:10px}.tenant-nav__logout{display:flex;justify-content:flex-end}.button,button{background:var(--brand);color:#fff}.button.secondary{background:#fff;color:var(--brand);border-color:rgba(var(--brand-rgb),.18)}.eyebrow{display:inline-block;margin-bottom:12px;color:var(--accent);font-size:.82rem;font-weight:800;letter-spacing:.14em;text-transform:uppercase}h1{font-size:clamp(2rem,4vw,3.2rem);margin:0 0 12px}h2{font-size:1.35rem;margin:0 0 12px}h3{font-size:1.05rem;margin:0 0 10px}p{margin:0;line-height:1.6}.stack{display:grid;gap:12px}.metric{padding:18px;border:1px solid var(--line);border-radius:16px;background:#fff}.metric strong{display:block;font-size:1.8rem;margin-bottom:8px}.list{margin:14px 0 0;padding-left:18px;color:var(--muted);line-height:1.7}.alert{padding:16px 18px;margin-bottom:18px}.alert-success{background:rgba(var(--brand-rgb),.08)}.alert-warning{background:rgba(var(--accent-rgb),.1)}.alert-error{background:rgba(154,31,31,.1)}.alert-info{background:rgba(var(--brand-rgb),.08)}.badge{display:inline-flex;align-items:center;padding:7px 12px;border-radius:999px;font-size:.86rem;font-weight:700}.badge-neutral{background:rgba(var(--brand-rgb),.08);color:var(--brand)}.badge-success{background:rgba(var(--brand-rgb),.12);color:var(--brand)}.badge-warning{background:rgba(var(--accent-rgb),.14);color:#8c6500}.table{overflow-x:auto}.table table{width:100%;border-collapse:collapse;min-width:720px}.table th,.table td{padding:13px 10px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}.table th{font-size:.85rem;letter-spacing:.08em;text-transform:uppercase;color:var(--muted)}form.grid{grid-template-columns:repeat(2,minmax(0,1fr))}label{display:flex;flex-direction:column;gap:8px;font-weight:700}input,select,textarea{width:100%;padding:12px 14px;border-radius:14px;border:1px solid rgba(37,24,15,.15);font:inherit;background:#fff;color:var(--ink)}textarea{min-height:120px}.footer{margin-top:18px;text-align:center;color:var(--muted);font-size:.92rem}@media(max-width:960px){.grid-2,.grid-3,.grid-4,form.grid{grid-template-columns:1fr}.bar{align-items:flex-start;flex-direction:column}.tenant-nav__desktop{display:none}.tenant-nav__mobile{display:block}.table table{min-width:0}}@media(min-width:961px){.tenant-nav__mobile{display:none}}
:root{<?= $themeCss ?>--line:rgba(37,24,15,.14);--shadow:0 18px 40px rgba(37,24,15,.08);--radius:18px}
*{box-sizing:border-box}
body{margin:0;min-height:100vh;font-family:"Aptos","Segoe UI",sans-serif;color:var(--ink);background:linear-gradient(180deg,#f9f6ef 0%,var(--bg) 100%)}
a{color:inherit;text-decoration:none}
h1,h2,h3{font-family:Georgia,serif;letter-spacing:-.02em}
.button,button{display:inline-flex;align-items:center;justify-content:center;padding:10px 14px;border-radius:999px;border:1px solid transparent;background:var(--brand);color:#fff;font:inherit;font-weight:700;cursor:pointer}
.button.secondary,.button--ghost{background:#fff;color:var(--brand);border-color:rgba(var(--brand-rgb),.18)}
.page-shell{width:min(1460px,calc(100vw - 32px));margin:20px auto 40px;display:grid;grid-template-columns:minmax(280px,300px) minmax(0,1fr);gap:20px;align-items:start}
.sidebar,.hero,.card,.alert{border:1px solid var(--line);border-radius:var(--radius);background:var(--card);box-shadow:var(--shadow)}
.sidebar{position:sticky;top:20px;display:grid;gap:16px;padding:18px;background:rgba(255,251,244,.96)}
.sidebar__brand{display:grid;gap:6px;padding-bottom:14px;border-bottom:1px solid rgba(37,24,15,.1)}
.sidebar__eyebrow,.eyebrow{display:inline-block;color:var(--accent);text-transform:uppercase;letter-spacing:.14em;font-size:.8rem;font-weight:800}
.sidebar__eyebrow{margin:0}
.sidebar__title{margin:0;font-size:1.55rem;line-height:1.05}
.sidebar__subtitle,.muted,p{color:var(--muted)}
.sidebar__meta,.actions,.context{display:flex;flex-wrap:wrap;gap:10px;align-items:center}
.sidebar__section{display:inline-block;color:var(--accent);text-transform:uppercase;letter-spacing:.14em;font-size:.76rem;font-weight:800}
.sidebar__nav,.sidebar__stack,.stack{display:grid;gap:10px}
.sidebar__link{display:flex;align-items:center;justify-content:space-between;padding:12px 14px;border-radius:16px;border:1px solid rgba(var(--brand-rgb),.12);background:#fff;color:var(--brand);font-weight:700}
.sidebar__link.active{background:var(--brand);color:#fff}
.sidebar__footer{display:grid;gap:12px;padding-top:14px;border-top:1px solid rgba(37,24,15,.1)}
.sidebar__mobile{display:none}
.sidebar__mobile[open]{z-index:20}
.sidebar__toggle{display:flex;align-items:center;justify-content:space-between;gap:12px;cursor:pointer;list-style:none;padding:12px 14px;border-radius:16px;border:1px solid rgba(var(--brand-rgb),.12);background:#fff;color:var(--brand);font-weight:700}
.sidebar__toggle::-webkit-details-marker{display:none}
.sidebar__toggle::after{content:"";width:11px;height:11px;border-right:2px solid currentColor;border-bottom:2px solid currentColor;transform:rotate(45deg);transition:transform .2s ease}
.sidebar__mobile[open] .sidebar__toggle::after{transform:rotate(225deg)}
.sidebar__panel{margin-top:12px;padding:14px;border-radius:18px;border:1px solid var(--line);background:#fffdf9;box-shadow:var(--shadow)}
.content{min-width:0;display:grid;gap:18px}
.hero,.card{padding:24px}
.hero{margin-bottom:0;background:linear-gradient(180deg,#fffdf8 0%,#f9f4ea 100%)}
.grid{display:grid;gap:18px}.grid-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-4{grid-template-columns:repeat(4,minmax(0,1fr))}
h1{font-size:clamp(2rem,4vw,3.2rem);margin:0 0 12px}h2{font-size:1.35rem;margin:0 0 12px}h3{font-size:1.05rem;margin:0 0 10px}p{margin:0;line-height:1.6}
.metric{padding:18px;border:1px solid var(--line);border-radius:16px;background:#fff}.metric strong{display:block;font-size:1.8rem;margin-bottom:8px}
.list{margin:14px 0 0;padding-left:18px;color:var(--muted);line-height:1.7}
.alert{padding:16px 18px;margin-bottom:0}.alert-success{background:rgba(var(--brand-rgb),.08)}.alert-warning{background:rgba(var(--accent-rgb),.1)}.alert-error{background:rgba(154,31,31,.1)}.alert-info{background:rgba(var(--brand-rgb),.08)}
.badge{display:inline-flex;align-items:center;padding:7px 12px;border-radius:999px;font-size:.86rem;font-weight:700}
.badge-neutral{background:rgba(var(--brand-rgb),.08);color:var(--brand)}.badge-success{background:rgba(var(--brand-rgb),.12);color:var(--brand)}.badge-warning{background:rgba(var(--accent-rgb),.14);color:#8c6500}
.table{overflow-x:auto}.table table{width:100%;border-collapse:collapse;min-width:720px}.table th,.table td{padding:13px 10px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}.table th{font-size:.85rem;letter-spacing:.08em;text-transform:uppercase;color:var(--muted)}
form.grid{grid-template-columns:repeat(2,minmax(0,1fr))}label{display:flex;flex-direction:column;gap:8px;font-weight:700}input,select,textarea{width:100%;padding:12px 14px;border-radius:14px;border:1px solid rgba(37,24,15,.15);font:inherit;background:#fff;color:var(--ink)}textarea{min-height:120px}
.footer{margin-top:0;text-align:center;color:var(--muted);font-size:.92rem}
@media(max-width:960px){.page-shell{grid-template-columns:1fr;width:min(100vw - 20px,1460px)}.sidebar{position:static}.sidebar__desktop{display:none}.sidebar__mobile{display:block}.grid-2,.grid-3,.grid-4,form.grid{grid-template-columns:1fr}.table table{min-width:0}}
@media(min-width:961px){.sidebar__mobile{display:none}}
</style>
</head>
<body>
<main class="shell">
<header class="bar">
<div class="brand">
<strong>Die Kaffeeliste</strong>
<span>Kaffeeliste, Hinweise und Support in einem Menü.</span>
<main class="page-shell">
<aside class="sidebar" aria-label="Bereichsnavigation">
<div class="sidebar__brand">
<p class="sidebar__eyebrow">Die Kaffeeliste</p>
<h1 class="sidebar__title"><?= $auth === null ? 'Start' : h((string) ($auth['tenant_name'] ?? 'Tenant')) ?></h1>
<p class="sidebar__subtitle"><?= $auth === null ? 'Zentrale Anmeldung, Verwaltung und Tenant-Zugriff.' : 'Alle Bereiche des Tenants in einer gemeinsamen Navigation.' ?></p>
</div>
</header>
<section class="tenant-nav-wrap" aria-label="Bereichsnavigation">
<div class="tenant-nav">
<div class="tenant-nav__desktop">
<nav class="tenant-nav__links" aria-label="Navigation">
<?php if ($auth === null): ?>
<div class="sidebar__meta">
<?= badge('Startseite') ?>
<?= badge('Mandantenfähig', 'success') ?>
</div>
<?php endif; ?>
<div class="sidebar__desktop">
<span class="sidebar__section">Bereiche</span>
<nav class="sidebar__nav" aria-label="Navigation">
<?php if ($auth === null): ?>
<a href="/" class="sidebar__link <?= $page === 'home' ? 'active' : '' ?>" <?= $page === 'home' ? 'aria-current="page"' : '' ?>>Start</a>
<a href="/login/" class="sidebar__link <?= $page === 'login' ? 'active' : '' ?>" <?= $page === 'login' ? 'aria-current="page"' : '' ?>>Anmeldung</a>
<a href="/admin/login/" class="sidebar__link <?= $page === 'tenants' ? 'active' : '' ?>" <?= $page === 'tenants' ? 'aria-current="page"' : '' ?>>Verwaltung</a>
<?php else: ?>
<?php foreach ($tenantNavItems as $item): ?>
<a href="<?= h((string) ($item['href'] ?? '/')) ?>" class="sidebar__link <?= $page === (string) ($item['key'] ?? '') ? 'active' : '' ?>" <?= $page === (string) ($item['key'] ?? '') ? 'aria-current="page"' : '' ?>><?= h((string) ($item['label'] ?? 'Link')) ?></a>
<?php endforeach; ?>
<?php endif; ?>
</nav>
<div class="sidebar__footer">
<?php if ($auth !== null): ?>
<form method="post" action="/logout/"><button type="submit" class="button secondary">Abmelden</button></form>
<?php endif; ?>
</div>
</div>
<details class="sidebar__mobile">
<summary class="sidebar__toggle">Menü</summary>
<div class="sidebar__panel">
<nav class="sidebar__stack" aria-label="Mobile Navigation">
<?php if ($auth === null): ?>
<a href="/" class="tenant-nav__link <?= $page === 'home' ? 'active' : '' ?>" <?= $page === 'home' ? 'aria-current="page"' : '' ?>>Start</a>
<a href="/login/" class="tenant-nav__link <?= $page === 'login' ? 'active' : '' ?>" <?= $page === 'login' ? 'aria-current="page"' : '' ?>>Anmeldung</a>
<a href="/admin/login/" class="tenant-nav__link <?= $page === 'tenants' ? 'active' : '' ?>" <?= $page === 'tenants' ? 'aria-current="page"' : '' ?>>Verwaltung</a>
<a href="/" class="sidebar__link <?= $page === 'home' ? 'active' : '' ?>" <?= $page === 'home' ? 'aria-current="page"' : '' ?>>Start</a>
<a href="/login/" class="sidebar__link <?= $page === 'login' ? 'active' : '' ?>" <?= $page === 'login' ? 'aria-current="page"' : '' ?>>Anmeldung</a>
<a href="/admin/login/" class="sidebar__link <?= $page === 'tenants' ? 'active' : '' ?>" <?= $page === 'tenants' ? 'aria-current="page"' : '' ?>>Verwaltung</a>
<?php else: ?>
<?php foreach ($tenantNavItems as $item): ?>
<a href="<?= h((string) ($item['href'] ?? '/')) ?>" class="tenant-nav__link <?= $page === (string) ($item['key'] ?? '') ? 'active' : '' ?>" <?= $page === (string) ($item['key'] ?? '') ? 'aria-current="page"' : '' ?>><?= h((string) ($item['label'] ?? 'Link')) ?></a>
<a href="<?= h((string) ($item['href'] ?? '/')) ?>" class="sidebar__link <?= $page === (string) ($item['key'] ?? '') ? 'active' : '' ?>" <?= $page === (string) ($item['key'] ?? '') ? 'aria-current="page"' : '' ?>><?= h((string) ($item['label'] ?? 'Link')) ?></a>
<?php endforeach; ?>
<?php endif; ?>
</nav>
<?php if ($auth !== null): ?>
<form class="tenant-nav__logout" method="post" action="/logout/"><button type="submit" class="button secondary">Abmelden</button></form>
<?php endif; ?>
</div>
<details class="tenant-nav__mobile">
<summary class="tenant-nav__toggle">Navigation</summary>
<div class="tenant-nav__panel">
<nav class="tenant-nav__stack" aria-label="Mobile Navigation">
<?php if ($auth === null): ?>
<a href="/" class="tenant-nav__link <?= $page === 'home' ? 'active' : '' ?>" <?= $page === 'home' ? 'aria-current="page"' : '' ?>>Start</a>
<a href="/login/" class="tenant-nav__link <?= $page === 'login' ? 'active' : '' ?>" <?= $page === 'login' ? 'aria-current="page"' : '' ?>>Anmeldung</a>
<a href="/admin/login/" class="tenant-nav__link <?= $page === 'tenants' ? 'active' : '' ?>" <?= $page === 'tenants' ? 'aria-current="page"' : '' ?>>Verwaltung</a>
<?php else: ?>
<?php foreach ($tenantNavItems as $item): ?>
<a href="<?= h((string) ($item['href'] ?? '/')) ?>" class="tenant-nav__link <?= $page === (string) ($item['key'] ?? '') ? 'active' : '' ?>" <?= $page === (string) ($item['key'] ?? '') ? 'aria-current="page"' : '' ?>><?= h((string) ($item['label'] ?? 'Link')) ?></a>
<?php endforeach; ?>
<?php endif; ?>
</nav>
<div class="sidebar__footer">
<?php if ($auth !== null): ?>
<form class="tenant-nav__logout" method="post" action="/logout/"><button type="submit" class="button secondary">Abmelden</button></form>
<form method="post" action="/logout/"><button type="submit" class="button secondary">Abmelden</button></form>
<?php endif; ?>
</div>
</details>
</div>
</section>
<?php if ($auth !== null): ?>
<section class="card">
<div class="context">
<?= badge((string) $auth['tenant_name']) ?>
<span class="muted"><?= h((string) $auth['display_name']) ?> ist angemeldet als <?= h((string) $auth['email']) ?></span>
<?= app_is_platform_admin($auth) ? badge('Global-Admin', 'success') : (app_is_tenant_admin($auth) ? badge('Tenant-Admin', 'success') : badge('Mitglied')) ?>
<?= badge('Lizenz ' . (string) ($tenantLicense['plan_name'] ?? 'Free')) ?>
</div>
</section>
<?php endif; ?>
</details>
</aside>
<div class="content">
<?php if ($auth !== null && !empty($auth['acting_as_platform_admin'])): ?>
<section class="alert alert-info">
Du siehst diesen Mandanten mit erweitertem Global-Admin-Zugriff.
@@ -810,6 +851,7 @@ $themeCss = app_tenant_theme_root_css($tenantSettings);
<?php endif; ?>
<p class="footer">Die Kaffeeliste | zentrale Anmeldung, Tenant-Verwaltung und alle Kernprozesse der modernen Kaffeeliste</p>
</div>
</main>
</body>
</html>
+217 -61
View File
@@ -59,46 +59,158 @@ $themeCss = app_tenant_theme_root_css($tenantSettings);
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Die Kaffeeliste - Umfragen</title>
<title>Die Kaffeeliste</title>
<style>
:root{<?= $themeCss ?>--line:rgba(37,24,15,.14);--shadow:0 16px 36px rgba(37,24,15,.08);--radius:18px;--radius-lg:16px;--content-width:1200px}
:root{<?= $themeCss ?>--line:rgba(37,24,15,.14);--shadow:0 18px 40px rgba(37,24,15,.08);--radius:18px}
*{box-sizing:border-box}
body{margin:0;min-height:100vh;font-family:"Aptos","Segoe UI",sans-serif;color:var(--ink);background:linear-gradient(180deg,#f9f6ef 0%,var(--bg) 100%)}
body{
margin:0;
min-height:100vh;
font-family:"Aptos","Segoe UI",sans-serif;
color:var(--ink);
background:linear-gradient(180deg,#f9f6ef 0%,var(--bg) 100%);
}
a{color:inherit;text-decoration:none}
h1,h2,h3,.brand__title{font-family:Georgia,serif;letter-spacing:-.02em}
.shell{width:min(var(--content-width),calc(100vw - 32px));margin:20px auto 40px}
.bar,.hero,.panel,.table-card,.note{border:1px solid var(--line);border-radius:var(--radius);background:var(--card);box-shadow:var(--shadow)}
.bar{display:flex;justify-content:space-between;gap:16px;align-items:center;padding:18px 22px;margin-bottom:18px}
.brand{display:grid;gap:4px}
.brand__title{font-size:1.18rem;font-weight:700}
.brand__subtitle{color:var(--muted)}
.toolbar,.meta,.stack{display:flex;flex-wrap:wrap;gap:10px}
.meta,.stack{display:flex;flex-wrap:wrap;gap:10px}
.tenant-nav-wrap{margin-bottom:18px}
.tenant-nav{display:grid;gap:12px;padding:14px 16px;border:1px solid var(--line);border-radius:20px;background:rgba(255,251,244,.92);box-shadow:var(--shadow)}
.tenant-nav__desktop{display:flex;align-items:center;gap:14px;justify-content:space-between}
.tenant-nav__links{display:flex;gap:10px;flex:1 1 auto;overflow-x:auto;scrollbar-width:none}
.tenant-nav__links::-webkit-scrollbar{display:none}
.tenant-nav__link,.button,.pill,button{display:inline-flex;align-items:center;justify-content:center;padding:10px 14px;border-radius:999px;font-weight:700;border:1px solid transparent}
.tenant-nav__link{background:#fff;color:var(--brand);border-color:rgba(var(--brand-rgb),.12);white-space:nowrap}
.tenant-nav__link.active{background:var(--brand);color:#fff}
.tenant-nav__mobile{display:none;position:relative}
.tenant-nav__mobile[open]{z-index:20}
.tenant-nav__toggle{display:inline-flex;align-items:center;gap:10px;list-style:none;cursor:pointer;padding:10px 14px;border-radius:999px;background:#fff;color:var(--brand);font-weight:700;border:1px solid rgba(var(--brand-rgb),.12)}
.tenant-nav__toggle::-webkit-details-marker{display:none}
.tenant-nav__toggle::before{content:"";width:18px;height:12px;border-top:2px solid currentColor;border-bottom:2px solid currentColor;box-shadow:inset 0 -4px 0 0 currentColor}
.tenant-nav__panel{margin-top:12px;padding:14px;border-radius:18px;border:1px solid var(--line);background:#fffdf9;box-shadow:var(--shadow)}
.tenant-nav__stack{display:grid;gap:10px}
.tenant-nav__logout{display:flex;justify-content:flex-end}
.badge,.pill{display:inline-flex;align-items:center;padding:7px 12px;border-radius:999px;background:#fff;color:var(--brand);font-weight:700;font-size:.86rem;border:1px solid rgba(var(--brand-rgb),.12)}
.badge--solid{background:var(--brand);color:#fff}
.hero{display:grid;grid-template-columns:minmax(0,1.35fr) minmax(260px,.65fr);gap:20px;padding:24px;margin-bottom:18px;background:linear-gradient(180deg,#fffdf8 0%,#f9f4ea 100%)}
h1,h2,h3,.brand__title,.hero__title{font-family:Georgia,serif;letter-spacing:-.02em}
.button,button{
display:inline-flex;
align-items:center;
justify-content:center;
padding:10px 14px;
border-radius:999px;
border:1px solid transparent;
background:var(--brand);
color:#fff;
font:inherit;
font-weight:700;
cursor:pointer;
}
.button--ghost{
background:#fff;
color:var(--brand);
border-color:rgba(var(--brand-rgb),.18);
}
.page-shell{
width:min(1460px,calc(100vw - 32px));
margin:20px auto 40px;
display:grid;
grid-template-columns:minmax(280px,300px) minmax(0,1fr);
gap:20px;
align-items:start;
}
.sidebar,.hero,.panel,.table-card,.note{
border:1px solid var(--line);
border-radius:var(--radius);
background:var(--card);
box-shadow:var(--shadow);
}
.sidebar{
position:sticky;
top:20px;
display:grid;
gap:16px;
padding:18px;
background:rgba(255,251,244,.96);
}
.sidebar__brand{
display:grid;
gap:6px;
padding-bottom:14px;
border-bottom:1px solid rgba(37,24,15,.1);
}
.sidebar__eyebrow{
margin:0;
color:var(--accent);
text-transform:uppercase;
letter-spacing:.16em;
font-size:.78rem;
font-weight:800;
}
.sidebar__title,.panel h2,.table-card h2{font-family:Georgia,serif;letter-spacing:-.02em}
.sidebar__title{
margin:0;
font-size:1.55rem;
line-height:1.05;
}
.sidebar__subtitle,.muted{color:var(--muted)}
.sidebar__meta,.toolbar,.meta{display:flex;flex-wrap:wrap;gap:10px;align-items:center}
.stack{display:grid;gap:10px}
.sidebar__section{
display:inline-block;
color:var(--accent);
text-transform:uppercase;
letter-spacing:.14em;
font-size:.76rem;
font-weight:800;
}
.sidebar__nav{display:grid;gap:10px}
.sidebar__link{
display:flex;
align-items:center;
justify-content:space-between;
padding:12px 14px;
border-radius:16px;
border:1px solid rgba(var(--brand-rgb),.12);
background:#fff;
color:var(--brand);
font-weight:700;
}
.sidebar__link.active{background:var(--brand);color:#fff}
.sidebar__footer{
display:grid;
gap:12px;
padding-top:14px;
border-top:1px solid rgba(37,24,15,.1);
}
.sidebar__mobile{display:none}
.sidebar__mobile[open]{z-index:20}
.sidebar__toggle{
display:flex;
align-items:center;
justify-content:space-between;
gap:12px;
cursor:pointer;
list-style:none;
padding:12px 14px;
border-radius:16px;
border:1px solid rgba(var(--brand-rgb),.12);
background:#fff;
color:var(--brand);
font-weight:700;
}
.sidebar__toggle::-webkit-details-marker{display:none}
.sidebar__toggle::after{
content:"";
width:11px;
height:11px;
border-right:2px solid currentColor;
border-bottom:2px solid currentColor;
transform:rotate(45deg);
transition:transform .2s ease;
}
.sidebar__mobile[open] .sidebar__toggle::after{transform:rotate(225deg)}
.sidebar__panel{
margin-top:12px;
padding:14px;
border-radius:18px;
border:1px solid var(--line);
background:#fffdf9;
box-shadow:var(--shadow);
}
.sidebar__stack{display:grid;gap:10px}
.content{min-width:0;display:grid;gap:18px}
.hero{
display:grid;
grid-template-columns:minmax(0,1.35fr) minmax(260px,.65fr);
gap:20px;
padding:24px;
margin-bottom:0;
background:linear-gradient(180deg,#fffdf8 0%,#f9f4ea 100%);
}
.hero__kicker{margin:0 0 10px;color:var(--accent);text-transform:uppercase;letter-spacing:.15em;font-size:.8rem;font-weight:800}
.hero__title{margin:0 0 12px;font-size:clamp(1.9rem,4vw,3rem);line-height:1.05}
.hero__lead{margin:0;color:var(--muted);max-width:70ch;line-height:1.7}
.hero__actions{display:flex;flex-wrap:wrap;gap:12px;margin-top:16px}
.button{background:var(--brand);color:#fff}
.button--ghost{background:#fff;color:var(--brand);border-color:rgba(var(--brand-rgb),.18)}
.grid{display:grid;gap:18px}
.grid--2{grid-template-columns:repeat(2,minmax(0,1fr))}
.grid--3{grid-template-columns:repeat(3,minmax(0,1fr))}
@@ -123,47 +235,90 @@ $themeCss = app_tenant_theme_root_css($tenantSettings);
.note{padding:16px 18px;margin-top:16px;background:rgba(var(--brand-rgb),.06)}
.list-reset{margin:0;padding-left:18px}
.list-reset li+li{margin-top:10px}
@media(max-width:960px){.hero,.grid--2,.grid--3{grid-template-columns:1fr}.bar{align-items:flex-start;flex-direction:column}.tenant-nav__desktop{display:none}.tenant-nav__mobile{display:block}}
@media(min-width:961px){.tenant-nav__mobile{display:none}}
.badge{
display:inline-flex;
align-items:center;
padding:7px 12px;
border-radius:999px;
background:#fff;
color:var(--brand);
font-weight:700;
font-size:.86rem;
border:1px solid rgba(var(--brand-rgb),.12);
white-space:nowrap;
}
.badge--solid{background:var(--brand);color:#fff}
.pill{
display:inline-flex;
align-items:center;
padding:7px 12px;
border-radius:999px;
border:1px solid rgba(var(--brand-rgb),.15);
background:rgba(var(--brand-rgb),.07);
color:var(--brand);
font-size:.84rem;
font-weight:700;
white-space:nowrap;
}
@media(max-width:960px){
.page-shell{grid-template-columns:1fr;width:min(100vw - 20px,1460px)}
.sidebar{position:static}
.sidebar__desktop{display:none}
.sidebar__mobile{display:block}
.hero,.grid--2,.grid--3{grid-template-columns:1fr}
}
@media(min-width:961px){
.sidebar__mobile{display:none}
}
</style>
</head>
<body>
<main class="shell">
<header class="bar">
<div class="brand">
<strong class="brand__title">Die Kaffeeliste</strong>
<span class="brand__subtitle">Umfragen und Freigaben im Tenant.</span>
<main class="page-shell">
<aside class="sidebar" aria-label="Bereichsnavigation">
<div class="sidebar__brand">
<p class="sidebar__eyebrow">Die Kaffeeliste</p>
<h1 class="sidebar__title">Umfragen</h1>
<p class="sidebar__subtitle">Umfragen und Freigaben im Tenant.</p>
</div>
</header>
<section class="tenant-nav-wrap" aria-label="Bereichsnavigation">
<div class="tenant-nav">
<div class="tenant-nav__desktop">
<nav class="tenant-nav__links" aria-label="Tenant-Menü">
<div class="sidebar__meta">
<span class="badge"><?= h($tenantName) ?></span>
<span class="badge"><?= h((string) ($tenantLicense['plan_name'] ?? 'Free')) ?></span>
<span class="badge badge--solid">Snapshot</span>
</div>
<div class="sidebar__desktop">
<span class="sidebar__section">Bereiche</span>
<nav class="sidebar__nav" aria-label="Tenant-Menü">
<?php foreach ($tenantNavItems as $item): ?>
<a class="sidebar__link <?= (($item['key'] ?? '') === 'surveys') ? 'active' : '' ?>" href="<?= h((string) ($item['href'] ?? '/')) ?>" <?= (($item['key'] ?? '') === 'surveys') ? 'aria-current="page"' : '' ?>><?= h((string) ($item['label'] ?? 'Link')) ?></a>
<?php endforeach; ?>
</nav>
<div class="sidebar__footer">
<form method="post" action="/logout/"><button type="submit" class="button button--ghost">Abmelden</button></form>
</div>
</div>
<details class="sidebar__mobile">
<summary class="sidebar__toggle">Menü</summary>
<div class="sidebar__panel">
<nav class="sidebar__stack" aria-label="Mobiles Tenant-Menü">
<?php foreach ($tenantNavItems as $item): ?>
<a class="tenant-nav__link <?= (($item['key'] ?? '') === 'surveys') ? 'active' : '' ?>" href="<?= h((string) ($item['href'] ?? '/')) ?>" <?= (($item['key'] ?? '') === 'surveys') ? 'aria-current="page"' : '' ?>><?= h((string) ($item['label'] ?? 'Link')) ?></a>
<a class="sidebar__link <?= (($item['key'] ?? '') === 'surveys') ? 'active' : '' ?>" href="<?= h((string) ($item['href'] ?? '/')) ?>" <?= (($item['key'] ?? '') === 'surveys') ? 'aria-current="page"' : '' ?>><?= h((string) ($item['label'] ?? 'Link')) ?></a>
<?php endforeach; ?>
</nav>
<form class="tenant-nav__logout" method="post" action="/logout/"><button type="submit" class="button button--ghost">Abmelden</button></form>
</div>
<details class="tenant-nav__mobile">
<summary class="tenant-nav__toggle">Navigation</summary>
<div class="tenant-nav__panel">
<nav class="tenant-nav__stack" aria-label="Mobiles Tenant-Menü">
<?php foreach ($tenantNavItems as $item): ?>
<a class="tenant-nav__link <?= (($item['key'] ?? '') === 'surveys') ? 'active' : '' ?>" href="<?= h((string) ($item['href'] ?? '/')) ?>" <?= (($item['key'] ?? '') === 'surveys') ? 'aria-current="page"' : '' ?>><?= h((string) ($item['label'] ?? 'Link')) ?></a>
<?php endforeach; ?>
</nav>
<form class="tenant-nav__logout" method="post" action="/logout/"><button type="submit" class="button button--ghost">Abmelden</button></form>
<div class="sidebar__footer">
<form method="post" action="/logout/"><button type="submit" class="button button--ghost">Abmelden</button></form>
</div>
</details>
</div>
</section>
</div>
</details>
</aside>
<div class="content">
<section class="hero">
<div>
<p class="hero__kicker">Umfragen</p>
<h1 class="hero__title">Umfragen</h1>
<h2 class="hero__title">Umfragen</h2>
<p class="hero__lead">
Entwürfe bleiben intern bearbeitbar. Mitglieder sehen nur veröffentlichte Stände.
</p>
@@ -336,6 +491,7 @@ $themeCss = app_tenant_theme_root_css($tenantSettings);
</div>
</article>
</section>
</div>
</main>
</body>
</html>
+359 -228
View File
@@ -28,10 +28,13 @@
:root {
{!! $layoutThemeCss !!}
--line: rgba(44, 32, 23, 0.14);
--line-strong: rgba(44, 32, 23, 0.2);
--shadow: 0 18px 42px rgba(68, 48, 34, 0.08);
--shadow-soft: 0 12px 26px rgba(68, 48, 34, 0.05);
--radius-xl: 24px;
--radius-lg: 18px;
--content-width: 1220px;
--sidebar-width: 292px;
--content-width: 1260px;
}
* { box-sizing: border-box; }
@@ -41,6 +44,7 @@
min-height: 100vh;
color: var(--text);
background:
radial-gradient(circle at top left, rgba(var(--brand-rgb), 0.08), transparent 32%),
linear-gradient(180deg, #f8f1e6 0%, var(--bg) 100%);
font-family: "Trebuchet MS", "Aptos", "Segoe UI", sans-serif;
line-height: 1.55;
@@ -55,32 +59,45 @@
a:hover { text-decoration: underline; }
img { max-width: 100%; }
.app-shell { min-height: 100vh; }
.app-header {
.app-shell {
width: min(var(--content-width), calc(100% - 28px));
margin: 14px auto;
min-height: calc(100vh - 28px);
display: grid;
grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
gap: 18px;
}
.app-sidebar {
position: sticky;
top: 0;
z-index: 50;
border-bottom: 1px solid var(--line);
background: rgba(249, 242, 231, 0.96);
backdrop-filter: blur(12px);
top: 14px;
align-self: start;
min-height: calc(100vh - 28px);
}
.app-header__inner,
.app-main,
.app-footer__inner {
width: min(var(--content-width), calc(100% - 32px));
margin: 0 auto;
.app-sidebar__panel {
height: calc(100vh - 28px);
display: grid;
grid-template-rows: auto auto 1fr auto;
gap: 20px;
padding: 24px 20px;
border: 1px solid var(--line);
border-radius: 30px;
background:
linear-gradient(180deg, rgba(255, 252, 246, 0.98), rgba(249, 242, 231, 0.96));
box-shadow: var(--shadow);
}
.app-header__inner {
.brand {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
padding: 18px 0;
gap: 14px;
min-width: 0;
}
.brand { display: flex; align-items: center; gap: 14px; min-width: 0; }
.brand__mark {
width: 44px;
height: 44px;
width: 46px;
height: 46px;
border-radius: 16px;
display: grid;
place-items: center;
@@ -89,16 +106,19 @@
background: linear-gradient(135deg, var(--brand) 0%, #115e59 55%, var(--accent) 100%);
box-shadow: 0 12px 26px rgba(45, 106, 79, 0.2);
}
.brand__text { display: grid; gap: 2px; min-width: 0; }
.brand__title { margin: 0; font-size: 1.02rem; font-weight: 700; letter-spacing: 0.01em; }
.brand__text { display: grid; gap: 3px; min-width: 0; }
.brand__title { margin: 0; font-size: 1.16rem; font-weight: 700; letter-spacing: 0.01em; }
.brand__subtitle { margin: 0; color: var(--muted); font-size: 0.92rem; }
.sidebar-meta,
.header-meta {
display: flex;
align-items: center;
gap: 12px;
gap: 10px;
flex-wrap: wrap;
justify-content: flex-end;
}
.badge,
.pill {
display: inline-flex;
@@ -112,54 +132,129 @@
font-weight: 600;
color: var(--brand-strong);
}
.badge--solid { background: var(--brand); color: #fff; border-color: transparent; }
.tenant-nav-wrap {
width: min(var(--content-width), calc(100% - 32px));
margin: 16px auto 0;
.sidebar-nav {
display: grid;
gap: 8px;
align-content: start;
}
.tenant-nav-shell {
.sidebar-nav__link {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 0.88rem 1rem;
border-radius: 18px;
border: 1px solid rgba(44, 32, 23, 0.08);
background: rgba(255, 255, 255, 0.72);
color: var(--text);
font-size: 0.95rem;
font-weight: 700;
box-shadow: var(--shadow-soft);
}
.sidebar-nav__link::after {
content: "";
font-size: 1rem;
color: var(--muted);
}
.sidebar-nav__link:hover {
text-decoration: none;
border-color: rgba(var(--brand-rgb), 0.22);
background: rgba(255, 255, 255, 0.95);
}
.sidebar-nav__link.is-active {
background: linear-gradient(135deg, var(--brand) 0%, var(--brand-strong) 100%);
color: #fff;
border-color: transparent;
}
.sidebar-nav__link.is-active::after { color: rgba(255, 255, 255, 0.88); }
.sidebar-footer {
display: grid;
gap: 12px;
align-content: end;
}
.sidebar-note {
padding: 14px 16px;
border-radius: 18px;
background: rgba(var(--brand-rgb), 0.08);
border: 1px solid rgba(var(--brand-rgb), 0.12);
color: var(--brand-strong);
}
.sidebar-note strong {
display: block;
margin-bottom: 4px;
}
.app-body {
min-width: 0;
display: grid;
grid-template-rows: auto 1fr auto;
gap: 18px;
}
.context-bar { padding-top: 6px; }
.context-bar__inner {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
padding: 20px 24px;
border: 1px solid var(--line);
border-radius: 20px;
border-radius: 28px;
background: rgba(255, 251, 244, 0.92);
box-shadow: var(--shadow);
}
.tenant-nav__desktop {
display: flex;
align-items: center;
gap: 14px;
justify-content: space-between;
.context-copy { display: grid; gap: 6px; }
.context-copy__title {
margin: 0;
font-size: 1.5rem;
line-height: 1.1;
}
.tenant-nav__links {
display: flex;
gap: 10px;
flex: 1 1 auto;
overflow-x: auto;
scrollbar-width: none;
.context-copy__lead {
margin: 0;
color: var(--muted);
font-size: 0.95rem;
}
.tenant-nav__links::-webkit-scrollbar { display: none; }
.tenant-nav__mobile {
.mobile-nav {
display: none;
position: relative;
}
.tenant-nav__toggle {
.mobile-nav[open] { z-index: 40; }
.mobile-nav__toggle {
display: inline-flex;
align-items: center;
gap: 10px;
list-style: none;
cursor: pointer;
padding: 0.72rem 1rem;
padding: 0.78rem 1rem;
border-radius: 999px;
border: 1px solid rgba(44, 32, 23, 0.08);
background: rgba(255, 251, 244, 0.94);
border: 1px solid var(--line-strong);
background: rgba(255, 255, 255, 0.94);
color: var(--text);
font-size: 0.92rem;
font-size: 0.94rem;
font-weight: 700;
}
.tenant-nav__toggle::-webkit-details-marker { display: none; }
.tenant-nav__toggle::before {
.mobile-nav__toggle::-webkit-details-marker { display: none; }
.mobile-nav__toggle::before {
content: "";
width: 18px;
height: 12px;
@@ -167,89 +262,30 @@
border-bottom: 2px solid currentColor;
box-shadow: inset 0 -4px 0 0 currentColor;
}
.tenant-nav__panel {
margin-top: 12px;
padding: 14px;
border-radius: 18px;
.mobile-nav__panel {
position: absolute;
right: 0;
top: calc(100% + 12px);
width: min(320px, calc(100vw - 32px));
padding: 16px;
border-radius: 24px;
border: 1px solid var(--line);
background: rgba(255, 252, 246, 0.98);
box-shadow: var(--shadow);
}
.tenant-nav__stack {
.mobile-nav__stack {
display: grid;
gap: 10px;
}
.tenant-nav__link {
padding: 0.75rem 0.95rem;
border-radius: 16px;
border: 1px solid rgba(44, 32, 23, 0.08);
background: rgba(255, 251, 244, 0.96);
color: var(--text);
font-size: 0.94rem;
font-weight: 600;
white-space: nowrap;
}
.tenant-nav__link.is-primary {
background: linear-gradient(135deg, var(--brand) 0%, var(--brand-strong) 100%);
color: #fff;
}
.tenant-nav__link:hover {
text-decoration: none;
border-color: rgba(var(--brand-rgb), 0.2);
background: #fffdf8;
}
.tenant-nav__logout {
.mobile-nav__footer {
margin-top: 12px;
display: flex;
justify-content: flex-end;
flex: 0 0 auto;
}
.app-main { padding: 34px 0 56px; }
.hero {
display: grid;
gap: 24px;
margin-bottom: 28px;
padding: 30px;
border: 1px solid var(--line);
border-radius: var(--radius-xl);
background:
linear-gradient(135deg, rgba(255, 251, 244, 0.98), rgba(252, 247, 240, 0.95));
box-shadow: var(--shadow);
}
.hero--split {
grid-template-columns: minmax(0, 1.25fr) minmax(300px, 0.75fr);
align-items: stretch;
}
.hero__content,
.hero__aside {
display: grid;
gap: 18px;
}
.hero__kicker {
margin: 0 0 12px;
text-transform: uppercase;
letter-spacing: 0.15em;
font-size: 0.8rem;
color: var(--accent);
font-weight: 700;
}
.hero__title {
margin: 0;
font-size: clamp(2rem, 4vw, 3.6rem);
line-height: 1.05;
letter-spacing: -0.03em;
}
.hero__lead {
margin: 0;
max-width: 68ch;
color: var(--muted);
font-size: 1.02rem;
}
.hero__actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 4px; }
.hero__meta {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.button,
button,
input[type="submit"] {
@@ -264,16 +300,69 @@
cursor: pointer;
box-shadow: 0 12px 24px rgba(var(--brand-rgb), 0.18);
}
.button--ghost {
background: transparent;
color: var(--brand-strong);
border: 1px solid rgba(var(--brand-rgb), 0.18);
box-shadow: none;
}
.app-main { padding: 0 0 6px; }
.hero {
display: grid;
gap: 24px;
margin-bottom: 28px;
padding: 30px;
border: 1px solid var(--line);
border-radius: var(--radius-xl);
background:
linear-gradient(135deg, rgba(255, 251, 244, 0.98), rgba(252, 247, 240, 0.95));
box-shadow: var(--shadow);
}
.hero--split {
grid-template-columns: minmax(0, 1.25fr) minmax(300px, 0.75fr);
align-items: stretch;
}
.hero__content,
.hero__aside {
display: grid;
gap: 18px;
}
.hero__kicker {
margin: 0 0 12px;
text-transform: uppercase;
letter-spacing: 0.15em;
font-size: 0.8rem;
color: var(--accent);
font-weight: 700;
}
.hero__title {
margin: 0;
font-size: clamp(2rem, 4vw, 3.6rem);
line-height: 1.05;
letter-spacing: -0.03em;
}
.hero__lead {
margin: 0;
max-width: 68ch;
color: var(--muted);
font-size: 1.02rem;
}
.hero__actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 4px; }
.hero__meta { display: flex; flex-wrap: wrap; gap: 10px; }
.grid { display: grid; gap: 18px; }
.grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.card,
.panel,
.form-panel,
@@ -284,6 +373,7 @@
box-shadow: var(--shadow);
padding: 22px;
}
.card__eyebrow {
margin: 0 0 10px;
font-size: 0.8rem;
@@ -292,21 +382,23 @@
color: var(--accent);
font-weight: 700;
}
.metric { display: grid; gap: 8px; }
.metric__value {
font-size: clamp(1.6rem, 2.5vw, 2.4rem);
font-weight: 800;
letter-spacing: -0.03em;
}
.metric__label,
.muted { color: var(--muted); }
.list-reset { margin: 0; padding: 0; list-style: none; }
.list-reset li + li { margin-top: 12px; }
.stack { display: grid; gap: 14px; }
.feature-list {
display: grid;
gap: 12px;
}
.feature-list { display: grid; gap: 12px; }
.feature-list__item {
display: flex;
gap: 12px;
@@ -316,6 +408,7 @@
background: rgba(255, 255, 255, 0.78);
border: 1px solid rgba(31, 41, 51, 0.08);
}
.feature-list__badge {
flex: 0 0 auto;
width: 34px;
@@ -327,30 +420,33 @@
color: var(--brand-strong);
font-weight: 800;
}
.feature-list__title {
margin: 0 0 4px;
font-weight: 700;
}
.feature-list__copy {
margin: 0;
color: var(--muted);
font-size: 0.94rem;
}
.split {
display: grid;
gap: 18px;
grid-template-columns: minmax(0, 1.4fr) minmax(0, 0.9fr);
}
.auth-summary__card {
padding: 16px;
border-radius: 18px;
background: rgba(255, 255, 255, 0.75);
border: 1px solid rgba(31, 41, 51, 0.08);
}
.tenant-grid {
display: grid;
gap: 12px;
}
.tenant-grid { display: grid; gap: 12px; }
.tenant-row {
display: flex;
justify-content: space-between;
@@ -361,19 +457,11 @@
background: rgba(255, 255, 255, 0.82);
border: 1px solid rgba(31, 41, 51, 0.08);
}
.tenant-row__meta {
display: grid;
gap: 4px;
}
.tenant-row__title {
margin: 0;
font-weight: 700;
}
.tenant-row__copy {
margin: 0;
color: var(--muted);
font-size: 0.92rem;
}
.tenant-row__meta { display: grid; gap: 4px; }
.tenant-row__title { margin: 0; font-weight: 700; }
.tenant-row__copy { margin: 0; color: var(--muted); font-size: 0.92rem; }
.callout {
padding: 16px 18px;
border-radius: 18px;
@@ -381,21 +469,18 @@
border: 1px solid rgba(45, 106, 79, 0.14);
color: var(--brand-strong);
}
.callout strong {
display: block;
margin-bottom: 4px;
}
.metric--compact {
padding: 18px 20px;
}
.toolbar {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-top: 18px;
}
.metric--compact { padding: 18px 20px; }
.toolbar { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 18px; }
.toolbar .badge { background: #fff; }
.table-card { overflow: hidden; padding: 0; }
.table-card__header {
display: flex;
align-items: center;
@@ -403,15 +488,18 @@
gap: 16px;
padding: 22px 22px 12px;
}
.table-card__body {
padding: 0 22px 22px;
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
min-width: 680px;
}
th,
td {
padding: 0.95rem 0.8rem;
@@ -419,6 +507,7 @@
text-align: left;
vertical-align: top;
}
th {
font-size: 0.8rem;
text-transform: uppercase;
@@ -427,7 +516,9 @@
font-weight: 700;
background: rgba(249, 242, 231, 0.6);
}
tr:last-child td { border-bottom: 0; }
.status {
display: inline-flex;
align-items: center;
@@ -439,11 +530,14 @@
font-weight: 700;
font-size: 0.82rem;
}
.status--warning { background: rgba(180, 83, 9, 0.12); color: #92400e; }
.status--danger { background: rgba(185, 28, 28, 0.12); color: #991b1b; }
.form-grid { display: grid; gap: 16px; }
.field { display: grid; gap: 8px; }
label { font-size: 0.9rem; font-weight: 700; }
input,
select,
textarea {
@@ -455,7 +549,9 @@
font: inherit;
color: var(--text);
}
textarea { min-height: 120px; resize: vertical; }
.note {
padding: 14px 16px;
border-radius: 16px;
@@ -463,8 +559,10 @@
border: 1px solid rgba(15, 118, 110, 0.12);
color: var(--brand-strong);
}
.timeline { display: grid; gap: 14px; }
.timeline--tight { gap: 10px; }
.timeline__item {
display: grid;
gap: 6px;
@@ -473,9 +571,12 @@
background: rgba(255, 255, 255, 0.8);
border: 1px solid rgba(31, 41, 51, 0.08);
}
.timeline__title { margin: 0; font-weight: 700; }
.timeline__meta { margin: 0; color: var(--muted); font-size: 0.92rem; }
.app-footer { padding: 0 0 38px; }
.app-footer { padding: 0 0 12px; }
.app-footer__inner {
display: flex;
justify-content: space-between;
@@ -486,114 +587,144 @@
color: var(--muted);
font-size: 0.92rem;
}
@media (max-width: 960px) {
@media (max-width: 980px) {
.app-shell {
width: calc(100% - 20px);
margin: 10px auto;
min-height: calc(100vh - 20px);
grid-template-columns: 1fr;
}
.app-sidebar { display: none; }
.mobile-nav { display: block; }
.context-bar { padding-top: 0; }
.context-bar__inner { padding: 18px; }
.grid--2,
.grid--3,
.grid--4,
.split,
.hero--split { grid-template-columns: 1fr; }
.app-header__inner { flex-direction: column; align-items: flex-start; }
.header-meta { justify-content: flex-start; }
.tenant-nav__desktop { display: none; }
.tenant-nav__mobile { display: block; }
.hero,
.card,
.panel,
.form-panel,
.table-card { padding: 18px; }
.tenant-nav-wrap { width: calc(100% - 32px); }
}
@media (min-width: 961px) {
.tenant-nav__mobile { display: none; }
.form-panel { padding: 18px; }
.table-card { padding: 0; }
table { min-width: 0; }
}
</style>
</head>
<body class="@yield('body_class')">
<div class="app-shell">
<header class="app-header">
<div class="app-header__inner">
<aside class="app-sidebar" aria-label="Tenant-Navigation">
<div class="app-sidebar__panel">
<div class="brand">
<div class="brand__mark">K</div>
<div class="brand__text">
<div class="brand__text">
<h1 class="brand__title">Die Kaffeeliste</h1>
<p class="brand__subtitle">Kaffeekasse, Mitglieder und Verwaltung pro Tenant</p>
<p class="brand__subtitle">Kaffeekasse und Verwaltung im Tenant</p>
</div>
</div>
<div class="header-meta">
@if (is_array($layoutAuth))
<span class="badge">{{ $layoutAuth['tenant_name'] ?? 'Tenant' }}</span>
@if (function_exists('app_can_manage_tenant') && app_can_manage_tenant($layoutAuth))
<span class="badge badge--solid">Verwaltung</span>
@else
<span class="badge badge--solid">Mitglied</span>
@endif
@else
<span class="badge">Klassische Oberfläche</span>
<span class="badge badge--solid">Mandantenfähig</span>
@endif
</div>
</div>
</header>
<section class="tenant-nav-wrap" aria-label="Bereichsnavigation">
<div class="tenant-nav-shell">
<div class="tenant-nav__desktop">
<nav class="tenant-nav__links" aria-label="Hauptnavigation">
@forelse ($layoutNavItems as $item)
<nav class="sidebar-nav" aria-label="Hauptnavigation">
@forelse ($layoutNavItems as $item)
@php
$itemHref = rtrim((string) ($item['href'] ?? '/'), '/');
$itemHref = $itemHref === '' ? '/' : $itemHref;
$isActive = $layoutPath === $itemHref;
@endphp
<a href="{{ $item['href'] ?? '/' }}" class="sidebar-nav__link {{ $isActive ? 'is-active' : '' }}" @if ($isActive) aria-current="page" @endif>{{ $item['label'] ?? 'Link' }}</a>
@empty
@php
$guestItems = [
['href' => '/', 'label' => 'Start'],
['href' => '/login/', 'label' => 'Anmeldung'],
];
@endphp
@foreach ($guestItems as $item)
@php
$itemHref = rtrim((string) ($item['href'] ?? '/'), '/');
$itemHref = $itemHref === '' ? '/' : $itemHref;
$isActive = $layoutPath === $itemHref;
@endphp
<a href="{{ $item['href'] ?? '/' }}" class="tenant-nav__link {{ $isActive ? 'is-primary' : '' }}" @if ($isActive) aria-current="page" @endif>{{ $item['label'] ?? 'Link' }}</a>
@empty
<a class="tenant-nav__link is-primary" href="/">Start</a>
<a class="tenant-nav__link" href="/login/">Anmeldung</a>
@endforelse
</nav>
<a href="{{ $item['href'] }}" class="sidebar-nav__link {{ $isActive ? 'is-active' : '' }}" @if ($isActive) aria-current="page" @endif>{{ $item['label'] }}</a>
@endforeach
@endforelse
</nav>
<div class="sidebar-footer">
<div class="sidebar-note">
<strong>Menü statt Sprungboxen</strong>
Bereiche werden über die Navigation geöffnet und nicht mehr im Inhalt verlinkt.
</div>
@if (is_array($layoutAuth))
<form class="tenant-nav__logout" method="post" action="/logout/">
<button type="submit" class="button button--ghost">Abmelden</button>
<form method="post" action="/logout/">
<button type="submit" class="button button--ghost" style="width: 100%;">Abmelden</button>
</form>
@endif
</div>
</div>
</aside>
<details class="tenant-nav__mobile">
<summary class="tenant-nav__toggle">Navigation</summary>
<div class="tenant-nav__panel">
<nav class="tenant-nav__stack" aria-label="Mobile Hauptnavigation">
@forelse ($layoutNavItems as $item)
@php
$itemHref = rtrim((string) ($item['href'] ?? '/'), '/');
$itemHref = $itemHref === '' ? '/' : $itemHref;
$isActive = $layoutPath === $itemHref;
@endphp
<a href="{{ $item['href'] ?? '/' }}" class="tenant-nav__link {{ $isActive ? 'is-primary' : '' }}" @if ($isActive) aria-current="page" @endif>{{ $item['label'] ?? 'Link' }}</a>
@empty
<a class="tenant-nav__link is-primary" href="/">Start</a>
<a class="tenant-nav__link" href="/login/">Anmeldung</a>
@endforelse
</nav>
@if (is_array($layoutAuth))
<form class="tenant-nav__logout" method="post" action="/logout/">
<button type="submit" class="button button--ghost">Abmelden</button>
</form>
<div class="app-body">
<section class="context-bar">
<div class="context-bar__inner">
<div class="context-copy">
<h2 class="context-copy__title">Die Kaffeeliste</h2>
<p class="context-copy__lead">
@if (is_array($layoutAuth))
Ruhige Tenant-Oberfläche mit klarer Navigation und ohne doppelte Statusinfos.
@else
Anmeldung, Tenant-Zugriff und Verwaltung bleiben in einer ruhigen gemeinsamen Struktur.
@endif
</p>
@if (!is_array($layoutAuth))
<div class="header-meta">
<span class="badge">Mandantenfähig</span>
<span class="badge">Mobil tauglich</span>
</div>
@endif
</div>
</details>
</div>
</section>
<main class="app-main">
@yield('content')
</main>
<details class="mobile-nav">
<summary class="mobile-nav__toggle">Menü</summary>
<div class="mobile-nav__panel">
<nav class="mobile-nav__stack" aria-label="Mobile Hauptnavigation">
@forelse ($layoutNavItems as $item)
@php
$itemHref = rtrim((string) ($item['href'] ?? '/'), '/');
$itemHref = $itemHref === '' ? '/' : $itemHref;
$isActive = $layoutPath === $itemHref;
@endphp
<a href="{{ $item['href'] ?? '/' }}" class="sidebar-nav__link {{ $isActive ? 'is-active' : '' }}" @if ($isActive) aria-current="page" @endif>{{ $item['label'] ?? 'Link' }}</a>
@empty
<a href="/" class="sidebar-nav__link {{ $layoutPath === '/' ? 'is-active' : '' }}">Start</a>
<a href="/login/" class="sidebar-nav__link {{ $layoutPath === '/login' ? 'is-active' : '' }}">Anmeldung</a>
@endforelse
</nav>
@if (is_array($layoutAuth))
<div class="mobile-nav__footer">
<form method="post" action="/logout/">
<button type="submit" class="button button--ghost">Abmelden</button>
</form>
</div>
@endif
</div>
</details>
</div>
</section>
<footer class="app-footer">
<div class="app-footer__inner">
<span>Die Kaffeeliste</span>
<span>Mitglieder, Striche, Einzahlungen, Hinweise und Exporte in einem System</span>
</div>
</footer>
<main class="app-main">
@yield('content')
</main>
<footer class="app-footer">
<div class="app-footer__inner">
<span>Die Kaffeeliste</span>
<span>Mitglieder, Striche, Einzahlungen, Hinweise und Exporte in einem System</span>
</div>
</footer>
</div>
</div>
</body>
</html>
+272 -100
View File
@@ -7,50 +7,174 @@
<?php $themeCss = app_tenant_theme_root_css($tenantSettings ?? app_tenant_settings_defaults()); ?>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Die Kaffeeliste - Support</title>
<title>Die Kaffeeliste</title>
<style>
:root{
<?= $themeCss ?>
--line:rgba(37,24,15,.14);
--shadow:0 16px 36px rgba(37,24,15,.08);
--shadow:0 18px 40px rgba(37,24,15,.08);
--radius:18px;
}
*{box-sizing:border-box}
body{
margin:0;
min-height:100vh;
color:var(--ink);
font-family:"Aptos","Segoe UI",sans-serif;
background:linear-gradient(180deg,#f9f6ef 0%,var(--bg) 100%);
}
a{color:inherit}
.shell{width:min(1240px,calc(100vw - 32px));margin:20px auto 40px}
.bar,.hero,.card,.alert{border:1px solid var(--line);border-radius:var(--radius);background:var(--card);box-shadow:var(--shadow)}
.bar{display:flex;justify-content:space-between;align-items:center;gap:16px;padding:18px 22px;margin-bottom:18px;flex-wrap:wrap}
.brand strong,.hero h1,.card h2,.card h3{font-family:Georgia,serif}
.brand strong{font-size:1.18rem}
.brand span,p,.muted{color:var(--muted)}
.actions,.context{display:flex;flex-wrap:wrap;gap:10px;align-items:center}
.tenant-nav-wrap{margin-bottom:18px}
.tenant-nav{display:grid;gap:12px;padding:14px 16px;border:1px solid var(--line);border-radius:20px;background:rgba(255,251,244,.92);box-shadow:var(--shadow)}
.tenant-nav__desktop{display:flex;align-items:center;gap:14px;justify-content:space-between}
.tenant-nav__links{display:flex;gap:10px;flex:1 1 auto;overflow-x:auto;scrollbar-width:none}
.tenant-nav__links::-webkit-scrollbar{display:none}
.tenant-nav__link,.button,button{display:inline-flex;align-items:center;justify-content:center;padding:10px 14px;border-radius:999px;text-decoration:none;font-weight:700;border:1px solid transparent;cursor:pointer}
.tenant-nav__link{background:#fff;color:var(--brand);border-color:rgba(var(--brand-rgb),.12);white-space:nowrap}
.tenant-nav__link.active{background:var(--brand);color:#fff}
.tenant-nav__mobile{display:none;position:relative}
.tenant-nav__mobile[open]{z-index:20}
.tenant-nav__toggle{display:inline-flex;align-items:center;gap:10px;list-style:none;cursor:pointer;padding:10px 14px;border-radius:999px;background:#fff;color:var(--brand);font-weight:700;border:1px solid rgba(var(--brand-rgb),.12)}
.tenant-nav__toggle::-webkit-details-marker{display:none}
.tenant-nav__toggle::before{content:"";width:18px;height:12px;border-top:2px solid currentColor;border-bottom:2px solid currentColor;box-shadow:inset 0 -4px 0 0 currentColor}
.tenant-nav__panel{margin-top:12px;padding:14px;border-radius:18px;border:1px solid var(--line);background:#fffdf9;box-shadow:var(--shadow)}
.tenant-nav__stack{display:grid;gap:10px}
.tenant-nav__logout{display:flex;justify-content:flex-end}
.button,button{background:var(--brand);color:#fff}
.button.secondary{background:#fff;color:var(--brand);border-color:rgba(var(--brand-rgb),.18)}
.hero{padding:24px;margin-bottom:18px;display:grid;gap:18px;background:linear-gradient(180deg,#fffdf8 0%,#f9f4ea 100%)}
.hero__kicker{text-transform:uppercase;letter-spacing:.16em;color:var(--accent);font-size:.8rem;font-weight:800;margin:0}
.hero__title{margin:0;font-size:clamp(1.9rem,4vw,3rem);line-height:1.05}
a{color:inherit;text-decoration:none}
.button,button{
display:inline-flex;
align-items:center;
justify-content:center;
padding:10px 14px;
border-radius:999px;
border:1px solid transparent;
background:var(--brand);
color:#fff;
font:inherit;
font-weight:700;
cursor:pointer;
}
.button--ghost{
background:#fff;
color:var(--brand);
border-color:rgba(var(--brand-rgb),.18);
}
.page-shell{
width:min(1460px,calc(100vw - 32px));
margin:20px auto 40px;
display:grid;
grid-template-columns:minmax(280px,300px) minmax(0,1fr);
gap:20px;
align-items:start;
}
.sidebar,.hero,.card,.alert{
border:1px solid var(--line);
border-radius:var(--radius);
background:var(--card);
box-shadow:var(--shadow);
}
.sidebar{
position:sticky;
top:20px;
display:grid;
gap:16px;
padding:18px;
background:rgba(255,251,244,.96);
}
.sidebar__brand{
display:grid;
gap:6px;
padding-bottom:14px;
border-bottom:1px solid rgba(37,24,15,.1);
}
.sidebar__eyebrow{
margin:0;
color:var(--accent);
text-transform:uppercase;
letter-spacing:.16em;
font-size:.78rem;
font-weight:800;
}
.sidebar__title,.hero__title,.card h2,.card h3{
font-family:Georgia,serif;
letter-spacing:-.02em;
}
.sidebar__title{
margin:0;
font-size:1.55rem;
line-height:1.05;
}
.sidebar__subtitle,.muted,p{color:var(--muted)}
.sidebar__meta,.actions,.context{display:flex;flex-wrap:wrap;gap:10px;align-items:center}
.sidebar__section{
display:inline-block;
color:var(--accent);
text-transform:uppercase;
letter-spacing:.14em;
font-size:.76rem;
font-weight:800;
}
.sidebar__nav{display:grid;gap:10px}
.sidebar__link{
display:flex;
align-items:center;
justify-content:space-between;
padding:12px 14px;
border-radius:16px;
border:1px solid rgba(var(--brand-rgb),.12);
background:#fff;
color:var(--brand);
font-weight:700;
}
.sidebar__link.active{
background:var(--brand);
color:#fff;
}
.sidebar__footer{
display:grid;
gap:12px;
padding-top:14px;
border-top:1px solid rgba(37,24,15,.1);
}
.sidebar__mobile{display:none}
.sidebar__mobile[open]{z-index:20}
.sidebar__toggle{
display:flex;
align-items:center;
justify-content:space-between;
gap:12px;
cursor:pointer;
list-style:none;
padding:12px 14px;
border-radius:16px;
border:1px solid rgba(var(--brand-rgb),.12);
background:#fff;
color:var(--brand);
font-weight:700;
}
.sidebar__toggle::-webkit-details-marker{display:none}
.sidebar__toggle::after{
content:"";
width:11px;
height:11px;
border-right:2px solid currentColor;
border-bottom:2px solid currentColor;
transform:rotate(45deg);
transition:transform .2s ease;
}
.sidebar__mobile[open] .sidebar__toggle::after{transform:rotate(225deg)}
.sidebar__panel{
margin-top:12px;
padding:14px;
border-radius:18px;
border:1px solid var(--line);
background:#fffdf9;
box-shadow:var(--shadow);
}
.sidebar__stack{display:grid;gap:10px}
.content{min-width:0;display:grid;gap:18px}
.hero{
padding:24px;
display:grid;
gap:18px;
background:linear-gradient(180deg,#fffdf8 0%,#f9f4ea 100%);
}
.hero__kicker{
margin:0 0 10px;
color:var(--accent);
text-transform:uppercase;
letter-spacing:.16em;
font-size:.8rem;
font-weight:800;
}
.hero__title{
margin:0;
font-size:clamp(1.9rem,4vw,3rem);
line-height:1.05;
}
.hero__lead{margin:0;max-width:72ch;font-size:1.02rem;line-height:1.65}
.grid{display:grid;gap:18px}
.grid--2{grid-template-columns:repeat(2,minmax(0,1fr))}
@@ -58,18 +182,44 @@
.grid--4{grid-template-columns:repeat(4,minmax(0,1fr))}
.card{padding:22px}
.card h2,.card h3{margin:0 0 12px}
.eyebrow{display:inline-block;margin-bottom:10px;color:var(--accent);font-size:.82rem;font-weight:800;letter-spacing:.12em;text-transform:uppercase}
.metric{padding:18px;border-radius:16px;border:1px solid var(--line);background:#fff;display:grid;gap:8px}
.eyebrow{
display:inline-block;
margin-bottom:10px;
color:var(--accent);
font-size:.82rem;
font-weight:800;
letter-spacing:.12em;
text-transform:uppercase;
}
.metric{
padding:18px;
border-radius:16px;
border:1px solid var(--line);
background:#fff;
display:grid;
gap:8px;
}
.metric strong{font-size:1.8rem}
.badge{display:inline-flex;align-items:center;padding:7px 12px;border-radius:999px;font-size:.86rem;font-weight:700}
.badge{
display:inline-flex;
align-items:center;
padding:7px 12px;
border-radius:999px;
font-size:.86rem;
font-weight:700;
border:1px solid rgba(var(--brand-rgb),.12);
background:#fff;
color:var(--brand);
}
.badge--neutral{background:rgba(var(--brand-rgb),.1);color:var(--brand)}
.badge--success{background:rgba(17,98,61,.12);color:#11623d}
.badge--warning{background:rgba(163,75,18,.12);color:#98510c}
.badge--danger{background:rgba(154,31,31,.12);color:#9a1f1f}
.alert{padding:16px 18px;margin-bottom:18px}
.alert{padding:16px 18px}
.alert-success{background:rgba(17,98,61,.08)}
.alert-warning{background:rgba(163,75,18,.08)}
.alert-error{background:rgba(154,31,31,.08)}
.alert-info{background:rgba(var(--brand-rgb),.06)}
.split{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(360px,.95fr);gap:18px}
.stack{display:grid;gap:12px}
.table{overflow-x:auto}
@@ -83,7 +233,17 @@
.timeline__item{padding:14px 16px;border-radius:16px;background:#fff;border:1px solid rgba(31,41,51,.08)}
.timeline__meta{margin:0;color:var(--muted);font-size:.94rem;line-height:1.6}
.timeline__title{margin:0 0 6px;font-weight:800}
.pill{display:inline-flex;align-items:center;padding:7px 12px;border-radius:999px;border:1px solid rgba(var(--brand-rgb),.15);background:rgba(var(--brand-rgb),.07);color:var(--brand);font-size:.84rem;font-weight:700}
.pill{
display:inline-flex;
align-items:center;
padding:7px 12px;
border-radius:999px;
border:1px solid rgba(var(--brand-rgb),.15);
background:rgba(var(--brand-rgb),.07);
color:var(--brand);
font-size:.84rem;
font-weight:700;
}
.status-grid{display:grid;gap:8px}
.status{display:inline-flex;align-items:center;gap:8px}
.status::before{content:"";width:8px;height:8px;border-radius:999px;background:var(--brand)}
@@ -93,84 +253,95 @@
.mono{font-family:Consolas,monospace}
.footer{margin-top:18px;text-align:center;color:var(--muted);font-size:.92rem}
@media(max-width:1040px){
.page-shell{grid-template-columns:1fr;width:min(100vw - 20px,1460px)}
.sidebar{position:static}
.sidebar__desktop{display:none}
.sidebar__mobile{display:block}
.split,.grid--2,.grid--3,.grid--4{grid-template-columns:1fr}
.tenant-nav__desktop{display:none}
.tenant-nav__mobile{display:block}
table{min-width:0}
}
@media(min-width:1041px){.tenant-nav__mobile{display:none}}
@media(min-width:1041px){
.sidebar__mobile{display:none}
}
</style>
</head>
<body>
<main class="shell">
<header class="bar">
<div class="brand">
<strong>Die Kaffeeliste</strong>
<span>Support, Vorgänge und Rückmeldungen im Tenant.</span>
<main class="page-shell">
<aside class="sidebar" aria-label="Bereichsnavigation">
<div class="sidebar__brand">
<p class="sidebar__eyebrow">Die Kaffeeliste</p>
<h1 class="sidebar__title">Support</h1>
<p class="sidebar__subtitle">Support, Vorgänge und Rückmeldungen im Tenant.</p>
</div>
</header>
<section class="tenant-nav-wrap" aria-label="Bereichsnavigation">
<div class="tenant-nav">
<div class="tenant-nav__desktop">
<nav class="tenant-nav__links" aria-label="Tenant-Menü">
<?php foreach ($tenantNavItems as $item): ?>
<a href="<?= support_h((string) ($item['href'] ?? '/')) ?>" class="tenant-nav__link <?= (($item['key'] ?? '') === 'support') ? 'active' : '' ?>" <?= (($item['key'] ?? '') === 'support') ? 'aria-current="page"' : '' ?>><?= support_h((string) ($item['label'] ?? 'Link')) ?></a>
<?php endforeach; ?>
</nav>
<form class="tenant-nav__logout" method="post" action="/logout/"><button type="submit" class="button secondary">Abmelden</button></form>
</div>
<details class="tenant-nav__mobile">
<summary class="tenant-nav__toggle">Navigation</summary>
<div class="tenant-nav__panel">
<nav class="tenant-nav__stack" aria-label="Mobiles Tenant-Menü">
<?php foreach ($tenantNavItems as $item): ?>
<a href="<?= support_h((string) ($item['href'] ?? '/')) ?>" class="tenant-nav__link <?= (($item['key'] ?? '') === 'support') ? 'active' : '' ?>" <?= (($item['key'] ?? '') === 'support') ? 'aria-current="page"' : '' ?>><?= support_h((string) ($item['label'] ?? 'Link')) ?></a>
<?php endforeach; ?>
</nav>
<form class="tenant-nav__logout" method="post" action="/logout/"><button type="submit" class="button secondary">Abmelden</button></form>
</div>
</details>
</div>
</section>
<section class="hero">
<p class="hero__kicker">Support</p>
<h1 class="hero__title">Support</h1>
<p class="hero__lead">
Mitglieder legen hier Vorgänge an. Verantwortliche verfolgen, beantworten und schließen sie im selben Verlauf.
</p>
<div class="context">
<div class="sidebar__meta">
<?= support_badge($isManager ? 'Verantwortlichen-Sicht' : 'Mitgliedersicht', 'success') ?>
<?= support_badge('Tenant-weit') ?>
<?= support_badge('Status und Routing', 'warning') ?>
</div>
</section>
<?php if ($flash !== null): ?>
<section class="alert alert-<?= support_h((string) ($flash['type'] ?? 'success')) ?>">
<?= support_h((string) ($flash['message'] ?? '')) ?>
</section>
<?php endif; ?>
<div class="sidebar__desktop">
<span class="sidebar__section">Bereiche</span>
<nav class="sidebar__nav" aria-label="Tenant-Menü">
<?php foreach ($tenantNavItems as $item): ?>
<a href="<?= support_h((string) ($item['href'] ?? '/')) ?>" class="sidebar__link <?= (($item['key'] ?? '') === 'support') ? 'active' : '' ?>" <?= (($item['key'] ?? '') === 'support') ? 'aria-current="page"' : '' ?>><?= support_h((string) ($item['label'] ?? 'Link')) ?></a>
<?php endforeach; ?>
</nav>
<div class="sidebar__footer">
<form method="post" action="/logout/"><button type="submit" class="button button--ghost">Abmelden</button></form>
</div>
</div>
<?php if ($dbError !== null): ?>
<section class="alert alert-warning"><?= support_h($dbError) ?></section>
<?php elseif (!$supportTablesReady): ?>
<section class="alert alert-warning">
Das Support-Modul ist strukturell vorbereitet, aber die Support-Tabellen sind noch nicht angelegt.
Bitte führe die Migrationen aus, bevor du das Modul produktiv nutzt.
</section>
<?php endif; ?>
<details class="sidebar__mobile">
<summary class="sidebar__toggle">Menü</summary>
<div class="sidebar__panel">
<nav class="sidebar__stack" aria-label="Mobiles Tenant-Menü">
<?php foreach ($tenantNavItems as $item): ?>
<a href="<?= support_h((string) ($item['href'] ?? '/')) ?>" class="sidebar__link <?= (($item['key'] ?? '') === 'support') ? 'active' : '' ?>" <?= (($item['key'] ?? '') === 'support') ? 'aria-current="page"' : '' ?>><?= support_h((string) ($item['label'] ?? 'Link')) ?></a>
<?php endforeach; ?>
</nav>
<div class="sidebar__footer">
<form method="post" action="/logout/"><button type="submit" class="button button--ghost">Abmelden</button></form>
</div>
</div>
</details>
</aside>
<?php if ($supportTablesReady): ?>
<section class="grid grid--4">
<article class="metric"><strong><?= support_h((string) ($summary['all'] ?? 0)) ?></strong><h3>Alle Vorgänge</h3><p>Support-Anfragen im aktuellen Tenant.</p></article>
<article class="metric"><strong><?= support_h((string) ($summary['new'] ?? 0)) ?></strong><h3>Neu</h3><p>Neue Vorgänge ohne Bearbeitung.</p></article>
<article class="metric"><strong><?= support_h((string) ($summary['waiting_on_user'] ?? 0)) ?></strong><h3>Warten auf Antwort</h3><p>Vorgänge mit Rückfrage an Mitglieder.</p></article>
<article class="metric"><strong><?= support_h((string) ($summary['resolved'] ?? 0)) ?></strong><h3>Erledigt</h3><p>Abgeschlossene oder gelöste Anfragen.</p></article>
<div class="content">
<section class="hero">
<div>
<p class="hero__kicker">Support</p>
<h2 class="hero__title">Support</h2>
<p class="hero__lead">
Mitglieder legen hier Vorgänge an. Verantwortliche verfolgen, beantworten und schließen sie im selben Verlauf.
</p>
</div>
</section>
<section class="split" style="margin-top:18px">
<?php if ($flash !== null): ?>
<section class="alert alert-<?= support_h((string) ($flash['type'] ?? 'success')) ?>">
<?= support_h((string) ($flash['message'] ?? '')) ?>
</section>
<?php endif; ?>
<?php if ($dbError !== null): ?>
<section class="alert alert-warning"><?= support_h($dbError) ?></section>
<?php elseif (!$supportTablesReady): ?>
<section class="alert alert-warning">
Das Support-Modul ist strukturell vorbereitet, aber die Support-Tabellen sind noch nicht angelegt.
Bitte führe die Migrationen aus, bevor du das Modul produktiv nutzt.
</section>
<?php endif; ?>
<?php if ($supportTablesReady): ?>
<section class="grid grid--4">
<article class="metric"><strong><?= support_h((string) ($summary['all'] ?? 0)) ?></strong><h3>Alle Vorgänge</h3><p>Support-Anfragen im aktuellen Tenant.</p></article>
<article class="metric"><strong><?= support_h((string) ($summary['new'] ?? 0)) ?></strong><h3>Neu</h3><p>Neue Vorgänge ohne Bearbeitung.</p></article>
<article class="metric"><strong><?= support_h((string) ($summary['waiting_on_user'] ?? 0)) ?></strong><h3>Warten auf Antwort</h3><p>Vorgänge mit Rückfrage an Mitglieder.</p></article>
<article class="metric"><strong><?= support_h((string) ($summary['resolved'] ?? 0)) ?></strong><h3>Erledigt</h3><p>Abgeschlossene oder gelöste Anfragen.</p></article>
</section>
<section class="split">
<article class="card">
<p class="eyebrow"><?= $isManager ? 'Neuen Vorgang anlegen' : 'Anfrage anlegen' ?></p>
<h2>Support-Vorgang erstellen</h2>
@@ -280,7 +451,7 @@
</div>
</section>
<section class="grid grid--2" style="margin-top:18px">
<section class="grid grid--2">
<article class="card">
<p class="eyebrow">Detailansicht</p>
<h2>Ausgewählter Vorgang</h2>
@@ -394,9 +565,10 @@
<?php endif; ?>
</article>
</section>
<?php endif; ?>
<?php endif; ?>
<p class="footer">Die Kaffeeliste | Support, Hinweise und Betriebsprozesse im Tenant-Menü</p>
<p class="footer">Die Kaffeeliste | Support, Hinweise und Betriebsprozesse im Tenant-Menü</p>
</div>
</main>
</body>
</html>
+215 -58
View File
@@ -7,12 +7,12 @@
<?php $themeCss = app_tenant_theme_root_css($tenantSettings ?? app_tenant_settings_defaults()); ?>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Die Kaffeeliste - Rollen</title>
<title>Die Kaffeeliste</title>
<style>
:root{
<?= $themeCss ?>
--line:rgba(37,24,15,.14);
--shadow:0 16px 36px rgba(37,24,15,.08);
--shadow:0 18px 40px rgba(37,24,15,.08);
--radius:18px;
}
*{box-sizing:border-box}
@@ -25,37 +25,150 @@
}
a{color:inherit;text-decoration:none}
h1,h2,h3{font-family:Georgia,serif;letter-spacing:-.02em}
.shell{width:min(1240px,calc(100vw - 32px));margin:20px auto 40px}
.bar,.hero,.card,.table-card,.note{border:1px solid var(--line);border-radius:var(--radius);background:var(--card);box-shadow:var(--shadow)}
.bar{display:flex;justify-content:space-between;align-items:center;gap:16px;padding:18px 22px;margin-bottom:18px;flex-wrap:wrap}
.brand{display:grid;gap:4px}
.brand strong{font-size:1.18rem}
.brand span,.muted{color:var(--muted)}
.actions,.context,.meta{display:flex;flex-wrap:wrap;gap:10px;align-items:center}
.tenant-nav-wrap{margin-bottom:18px}
.tenant-nav{display:grid;gap:12px;padding:14px 16px;border:1px solid var(--line);border-radius:20px;background:rgba(255,251,244,.92);box-shadow:var(--shadow)}
.tenant-nav__desktop{display:flex;align-items:center;gap:14px;justify-content:space-between}
.tenant-nav__links{display:flex;gap:10px;flex:1 1 auto;overflow-x:auto;scrollbar-width:none}
.tenant-nav__links::-webkit-scrollbar{display:none}
.tenant-nav__link,.badge,.pill{display:inline-flex;align-items:center;justify-content:center;padding:10px 14px;border-radius:999px;background:#fff;color:var(--brand);font-weight:700;border:1px solid rgba(var(--brand-rgb),.12);white-space:nowrap}
.tenant-nav__link.active{background:var(--brand);color:#fff}
.tenant-nav__mobile{display:none;position:relative}
.tenant-nav__mobile[open]{z-index:20}
.tenant-nav__toggle{display:inline-flex;align-items:center;gap:10px;list-style:none;cursor:pointer;padding:10px 14px;border-radius:999px;background:#fff;color:var(--brand);font-weight:700;border:1px solid rgba(var(--brand-rgb),.12)}
.tenant-nav__toggle::-webkit-details-marker{display:none}
.tenant-nav__toggle::before{content:"";width:18px;height:12px;border-top:2px solid currentColor;border-bottom:2px solid currentColor;box-shadow:inset 0 -4px 0 0 currentColor}
.tenant-nav__panel{margin-top:12px;padding:14px;border-radius:18px;border:1px solid var(--line);background:#fffdf9;box-shadow:var(--shadow)}
.tenant-nav__stack{display:grid;gap:10px}
.tenant-nav__logout{display:flex;justify-content:flex-end}
.badge,.pill{padding:7px 12px;font-size:.86rem}
.badge--solid{background:var(--brand);color:#fff}
.hero{display:grid;grid-template-columns:minmax(0,1.4fr) minmax(260px,.6fr);gap:20px;padding:24px;margin-bottom:18px;background:linear-gradient(180deg,#fffdf8 0%,#f9f4ea 100%)}
.hero__kicker{margin:0 0 10px;color:var(--accent);text-transform:uppercase;letter-spacing:.15em;font-size:.8rem;font-weight:800}
.button,button{
display:inline-flex;
align-items:center;
justify-content:center;
padding:10px 14px;
border-radius:999px;
border:1px solid transparent;
background:var(--brand);
color:#fff;
font:inherit;
font-weight:700;
cursor:pointer;
}
.button--ghost{
background:#fff;
color:var(--brand);
border-color:rgba(var(--brand-rgb),.18);
}
.page-shell{
width:min(1460px,calc(100vw - 32px));
margin:20px auto 40px;
display:grid;
grid-template-columns:minmax(280px,300px) minmax(0,1fr);
gap:20px;
align-items:start;
}
.sidebar,.hero,.card,.table-card,.note{
border:1px solid var(--line);
border-radius:var(--radius);
background:var(--card);
box-shadow:var(--shadow);
}
.sidebar{
position:sticky;
top:20px;
display:grid;
gap:16px;
padding:18px;
background:rgba(255,251,244,.96);
}
.sidebar__brand{
display:grid;
gap:6px;
padding-bottom:14px;
border-bottom:1px solid rgba(37,24,15,.1);
}
.sidebar__eyebrow{
margin:0;
color:var(--accent);
text-transform:uppercase;
letter-spacing:.16em;
font-size:.78rem;
font-weight:800;
}
.sidebar__title,.hero__title,.card h2,.card h3{font-family:Georgia,serif;letter-spacing:-.02em}
.sidebar__title{
margin:0;
font-size:1.55rem;
line-height:1.05;
}
.sidebar__subtitle,.muted{color:var(--muted)}
.sidebar__meta,.actions,.context,.meta{display:flex;flex-wrap:wrap;gap:10px;align-items:center}
.sidebar__section{
display:inline-block;
color:var(--accent);
text-transform:uppercase;
letter-spacing:.14em;
font-size:.76rem;
font-weight:800;
}
.sidebar__nav{display:grid;gap:10px}
.sidebar__link{
display:flex;
align-items:center;
justify-content:space-between;
padding:12px 14px;
border-radius:16px;
border:1px solid rgba(var(--brand-rgb),.12);
background:#fff;
color:var(--brand);
font-weight:700;
}
.sidebar__link.active{background:var(--brand);color:#fff}
.sidebar__footer{
display:grid;
gap:12px;
padding-top:14px;
border-top:1px solid rgba(37,24,15,.1);
}
.sidebar__mobile{display:none}
.sidebar__mobile[open]{z-index:20}
.sidebar__toggle{
display:flex;
align-items:center;
justify-content:space-between;
gap:12px;
cursor:pointer;
list-style:none;
padding:12px 14px;
border-radius:16px;
border:1px solid rgba(var(--brand-rgb),.12);
background:#fff;
color:var(--brand);
font-weight:700;
}
.sidebar__toggle::-webkit-details-marker{display:none}
.sidebar__toggle::after{
content:"";
width:11px;
height:11px;
border-right:2px solid currentColor;
border-bottom:2px solid currentColor;
transform:rotate(45deg);
transition:transform .2s ease;
}
.sidebar__mobile[open] .sidebar__toggle::after{transform:rotate(225deg)}
.sidebar__panel{
margin-top:12px;
padding:14px;
border-radius:18px;
border:1px solid var(--line);
background:#fffdf9;
box-shadow:var(--shadow);
}
.sidebar__stack{display:grid;gap:10px}
.content{min-width:0;display:grid;gap:18px}
.hero{
display:grid;
grid-template-columns:minmax(0,1.4fr) minmax(260px,.6fr);
gap:20px;
padding:24px;
background:linear-gradient(180deg,#fffdf8 0%,#f9f4ea 100%);
}
.hero__kicker{
margin:0 0 10px;
color:var(--accent);
text-transform:uppercase;
letter-spacing:.15em;
font-size:.8rem;
font-weight:800;
}
.hero__title{margin:0 0 12px;font-size:clamp(1.9rem,4vw,3rem);line-height:1.05}
.hero__lead{margin:0;color:var(--muted);max-width:70ch;line-height:1.7}
.hero__actions{display:flex;flex-wrap:wrap;gap:12px;margin-top:16px}
.button{display:inline-flex;align-items:center;justify-content:center;padding:10px 14px;border-radius:999px;border:1px solid transparent;background:var(--brand);color:#fff;font-weight:700}
.button--ghost{background:#fff;color:var(--brand);border:1px solid rgba(var(--brand-rgb),.18)}
.grid{display:grid;gap:18px}
.grid--2{grid-template-columns:repeat(2,minmax(0,1fr))}
.grid--3{grid-template-columns:repeat(3,minmax(0,1fr))}
@@ -77,47 +190,90 @@
.stack{display:grid;gap:12px}
.chip-list{display:flex;flex-wrap:wrap;gap:8px}
.chip{display:inline-flex;align-items:center;padding:5px 10px;border-radius:999px;background:rgba(var(--brand-rgb),.08);color:var(--brand);font-size:.84rem;font-weight:700}
@media(max-width:960px){.hero,.grid--2,.grid--3,.grid--4{grid-template-columns:1fr}.bar{align-items:flex-start;flex-direction:column}.tenant-nav__desktop{display:none}.tenant-nav__mobile{display:block}}
@media(min-width:961px){.tenant-nav__mobile{display:none}}
.badge{
display:inline-flex;
align-items:center;
padding:7px 12px;
border-radius:999px;
background:#fff;
color:var(--brand);
font-weight:700;
font-size:.86rem;
border:1px solid rgba(var(--brand-rgb),.12);
white-space:nowrap;
}
.badge--solid{background:var(--brand);color:#fff}
.pill{
display:inline-flex;
align-items:center;
padding:7px 12px;
border-radius:999px;
border:1px solid rgba(var(--brand-rgb),.15);
background:rgba(var(--brand-rgb),.07);
color:var(--brand);
font-size:.84rem;
font-weight:700;
white-space:nowrap;
}
@media(max-width:960px){
.page-shell{grid-template-columns:1fr;width:min(100vw - 20px,1460px)}
.sidebar{position:static}
.sidebar__desktop{display:none}
.sidebar__mobile{display:block}
.hero,.grid--2,.grid--3,.grid--4{grid-template-columns:1fr}
}
@media(min-width:961px){
.sidebar__mobile{display:none}
}
</style>
</head>
<body>
<main class="shell">
<header class="bar">
<div class="brand">
<strong>Die Kaffeeliste</strong>
<span>Rollen und Rechte im Tenant.</span>
<main class="page-shell">
<aside class="sidebar" aria-label="Bereichsnavigation">
<div class="sidebar__brand">
<p class="sidebar__eyebrow">Die Kaffeeliste</p>
<h1 class="sidebar__title">Rollen</h1>
<p class="sidebar__subtitle">Rollen und Rechte im Tenant.</p>
</div>
</header>
<section class="tenant-nav-wrap" aria-label="Bereichsnavigation">
<div class="tenant-nav">
<div class="tenant-nav__desktop">
<nav class="tenant-nav__links" aria-label="Tenant-Menü">
<div class="sidebar__meta">
<span class="badge">lokal + ADFS/OIDC</span>
<span class="badge">Rollenmatrix</span>
<span class="badge">Delegation</span>
</div>
<div class="sidebar__desktop">
<span class="sidebar__section">Bereiche</span>
<nav class="sidebar__nav" aria-label="Tenant-Menü">
<?php foreach ($tenantNavItems as $item): ?>
<a class="sidebar__link <?= (($item['key'] ?? '') === 'roles') ? 'active' : '' ?>" href="<?= tenant_roles_h((string) ($item['href'] ?? '/')) ?>" <?= (($item['key'] ?? '') === 'roles') ? 'aria-current="page"' : '' ?>><?= tenant_roles_h((string) ($item['label'] ?? 'Link')) ?></a>
<?php endforeach; ?>
</nav>
<div class="sidebar__footer">
<form method="post" action="/logout/"><button type="submit" class="button button--ghost">Abmelden</button></form>
</div>
</div>
<details class="sidebar__mobile">
<summary class="sidebar__toggle">Menü</summary>
<div class="sidebar__panel">
<nav class="sidebar__stack" aria-label="Mobiles Tenant-Menü">
<?php foreach ($tenantNavItems as $item): ?>
<a class="tenant-nav__link <?= (($item['key'] ?? '') === 'roles') ? 'active' : '' ?>" href="<?= tenant_roles_h((string) ($item['href'] ?? '/')) ?>" <?= (($item['key'] ?? '') === 'roles') ? 'aria-current="page"' : '' ?>><?= tenant_roles_h((string) ($item['label'] ?? 'Link')) ?></a>
<a class="sidebar__link <?= (($item['key'] ?? '') === 'roles') ? 'active' : '' ?>" href="<?= tenant_roles_h((string) ($item['href'] ?? '/')) ?>" <?= (($item['key'] ?? '') === 'roles') ? 'aria-current="page"' : '' ?>><?= tenant_roles_h((string) ($item['label'] ?? 'Link')) ?></a>
<?php endforeach; ?>
</nav>
<form class="tenant-nav__logout" method="post" action="/logout/"><button type="submit" class="button button--ghost">Abmelden</button></form>
</div>
<details class="tenant-nav__mobile">
<summary class="tenant-nav__toggle">Navigation</summary>
<div class="tenant-nav__panel">
<nav class="tenant-nav__stack" aria-label="Mobiles Tenant-Menü">
<?php foreach ($tenantNavItems as $item): ?>
<a class="tenant-nav__link <?= (($item['key'] ?? '') === 'roles') ? 'active' : '' ?>" href="<?= tenant_roles_h((string) ($item['href'] ?? '/')) ?>" <?= (($item['key'] ?? '') === 'roles') ? 'aria-current="page"' : '' ?>><?= tenant_roles_h((string) ($item['label'] ?? 'Link')) ?></a>
<?php endforeach; ?>
</nav>
<form class="tenant-nav__logout" method="post" action="/logout/"><button type="submit" class="button button--ghost">Abmelden</button></form>
<div class="sidebar__footer">
<form method="post" action="/logout/"><button type="submit" class="button button--ghost">Abmelden</button></form>
</div>
</details>
</div>
</section>
</div>
</details>
</aside>
<div class="content">
<section class="hero">
<div>
<p class="hero__kicker">Rollen</p>
<h1 class="hero__title">Rollen und Rechte</h1>
<h2 class="hero__title">Rollen und Rechte</h2>
<p class="hero__lead">
Tenant-Admins behalten den Gesamtzugriff. Fachrollen für Finanzen, Support und Umfragen werden gezielt delegiert.
</p>
@@ -264,6 +420,7 @@
</ul>
</article>
</section>
</div>
</main>
</body>
</html>