Menüänderung

This commit is contained in:
2026-03-30 21:28:39 +02:00
parent f21a0599fc
commit 1290382aa0
22 changed files with 429 additions and 186 deletions
+17
View File
@@ -0,0 +1,17 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "PHP installieren",
"type": "shell",
"command": "sudo apt update && sudo apt install php php-mysql -y",
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}
+1 -1
View File
@@ -395,7 +395,7 @@ function app_tenant_navigation_items(?array $auth, array $license = []): array
$hasExports = !empty($features['pdf_export']) || !empty($features['paper_strike_entry']) || !empty($features['basic_exports']);
$items = [
['key' => 'dashboard', 'label' => 'Uebersicht', 'href' => '/dashboard/'],
['key' => 'dashboard', 'label' => 'Übersicht', 'href' => '/dashboard/'],
['key' => 'content', 'label' => 'Hinweise', 'href' => '/content/'],
['key' => 'support', 'label' => 'Support', 'href' => '/support/'],
['key' => 'surveys', 'label' => 'Umfragen', 'href' => '/surveys/'],
+58 -36
View File
@@ -230,33 +230,61 @@ $themeCss = app_tenant_theme_root_css($tenantSettings);
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Kaffeeliste SaaS</title>
<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,.links,.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))}.links a,.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}.links a{background:#fff;color:var(--brand);border-color:rgba(var(--brand-rgb),.12)}.links a.active{background:var(--brand);color:#fff}.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}.table table{min-width:0}}
*{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}}
</style>
</head>
<body>
<main class="shell">
<header class="bar">
<div class="brand">
<strong>Kaffeeliste SaaS</strong>
<strong>Die Kaffeeliste</strong>
<span>Kaffeeliste, Hinweise und Support in einem Menü.</span>
</div>
<nav class="links" aria-label="Navigation">
<?php if ($auth === null): ?>
<a href="/" class="<?= $page === 'home' ? 'active' : '' ?>">Start</a>
<a href="/login/" class="<?= $page === 'login' ? 'active' : '' ?>">Anmeldung</a>
<a href="/admin/login/" class="<?= $page === 'tenants' ? 'active' : '' ?>">Verwaltung</a>
<?php else: ?>
<?php foreach ($tenantNavItems as $item): ?>
<a href="<?= h((string) ($item['href'] ?? '/')) ?>" class="<?= $page === (string) ($item['key'] ?? '') ? 'active' : '' ?>"><?= h((string) ($item['label'] ?? 'Link')) ?></a>
<?php endforeach; ?>
<form method="post" action="/logout/"><button type="submit" class="button secondary">Abmelden</button></form>
<?php endif; ?>
</nav>
</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): ?>
<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>
<?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>
<?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>
</div>
</section>
<?php if ($auth !== null): ?>
<section class="card">
<div class="context">
@@ -432,7 +460,7 @@ $themeCss = app_tenant_theme_root_css($tenantSettings);
<?php if (app_is_platform_admin($auth) && app_admin_user() !== null): ?>
<div class="actions" style="margin-top:18px">
<a class="button secondary" href="/admin/">Zur zentralen Verwaltung</a>
<a class="button" href="/members/">Personen und Admins verwalten</a>
<span class="badge">Mitglieder im Menü</span>
</div>
<?php endif; ?>
</section>
@@ -448,24 +476,20 @@ $themeCss = app_tenant_theme_root_css($tenantSettings);
<?php endif; ?>
</section>
<section class="grid grid-3" style="margin-top:18px">
<section class="grid grid-2" style="margin-top:18px">
<article class="card">
<div class="eyebrow">Content</div>
<h2>Hinweise und FAQ</h2>
<p>Tenant-Admins pflegen Meldungen, Standardvorlagen und FAQ direkt im Mandantenkontext.</p>
<div class="actions" style="margin-top:14px"><a class="button secondary" href="/content/">Öffnen</a></div>
<div class="eyebrow">Bereiche im Menü</div>
<h2>Funktionen werden über das Menü geöffnet</h2>
<ul class="list">
<li>Hinweise, Support und Umfragen erscheinen als feste Hauptbereiche im Tenant-Menü.</li>
<li>Mitglieder, Rollen, Buchungen und Zahlungen werden nur für passende Rollen eingeblendet.</li>
<li>Einstellungen und Exporte erscheinen nur, wenn das Lizenzmodell diese Funktionen freischaltet.</li>
</ul>
</article>
<article class="card">
<div class="eyebrow">Support</div>
<h2>Vorgänge mit Status</h2>
<p>Benutzer und Verantwortliche sehen Supportanfragen mit Routing, Verlauf und Bearbeitungsstand.</p>
<div class="actions" style="margin-top:14px"><a class="button secondary" href="/support/">Öffnen</a></div>
</article>
<article class="card">
<div class="eyebrow">Surveys</div>
<h2>Snapshots und Freigaben</h2>
<p>Umfragen werden live bearbeitet, Mitglieder sehen freigegebene Snapshots statt Entwürfe.</p>
<div class="actions" style="margin-top:14px"><a class="button secondary" href="/surveys/">Öffnen</a></div>
<div class="eyebrow">Lizenzlogik</div>
<h2>Nur passende Funktionen sichtbar</h2>
<p>Die Oberfläche blendet Bereiche nicht als tote Kacheln ein. Was im Menü erscheint, hängt von Rolle und Lizenz des Tenants ab.</p>
</article>
</section>
@@ -509,9 +533,7 @@ $themeCss = app_tenant_theme_root_css($tenantSettings);
<article class="card">
<div class="eyebrow">Nächster Schritt</div>
<h2>Womit du direkt weiterkommst</h2>
<div class="actions">
<a class="button" href="/content/">Hinweise ansehen</a>
</div>
<p>Weitere Bereiche wechselst du direkt über das Menü. Sichtbar wird dort nur, was für deine Rolle freigegeben ist.</p>
</article>
<?php endif; ?>
</section>
@@ -533,7 +555,7 @@ $themeCss = app_tenant_theme_root_css($tenantSettings);
<?php if (app_is_platform_admin($auth) && app_admin_user() !== null): ?>
<div class="actions" style="margin-top:18px">
<a class="button secondary" href="/admin/">Zur zentralen Verwaltung</a>
<a class="button secondary" href="/dashboard/">Mandanten-Dashboard</a>
<span class="badge">Übersicht im Menü</span>
</div>
<?php endif; ?>
</section>
@@ -787,7 +809,7 @@ $themeCss = app_tenant_theme_root_css($tenantSettings);
<section class="alert alert-warning">Die angeforderte Seite konnte nicht gefunden werden.</section>
<?php endif; ?>
<p class="footer">Kaffeeliste SaaS | zentrale Anmeldung, Tenant-Verwaltung und alle Kernprozesse der modernen Kaffeeliste</p>
<p class="footer">Die Kaffeeliste | zentrale Anmeldung, Tenant-Verwaltung und alle Kernprozesse der modernen Kaffeeliste</p>
</main>
</body>
</html>
+1 -1
View File
@@ -21,7 +21,7 @@ $auth = app_auth_user();
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Kaffeeliste SaaS - Abmelden</title>
<title>Die Kaffeeliste - Abmelden</title>
<style>
:root{--bg:#f4efe6;--card:#fffaf3;--ink:#24160e;--muted:#675546;--brand:#0c6b66;--line:rgba(36,22,14,.12);--shadow:0 24px 54px rgba(57,35,22,.11);--radius:24px}
*{box-sizing:border-box}
+61 -28
View File
@@ -59,7 +59,7 @@ $themeCss = app_tenant_theme_root_css($tenantSettings);
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Kaffeeliste - Umfragen</title>
<title>Die Kaffeeliste - Umfragen</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}
*{box-sizing:border-box}
@@ -73,10 +73,23 @@ $themeCss = app_tenant_theme_root_css($tenantSettings);
.brand__title{font-size:1.18rem;font-weight:700}
.brand__subtitle{color:var(--muted)}
.toolbar,.meta,.stack{display:flex;flex-wrap:wrap;gap:10px}
.links,.meta,.stack{display:flex;flex-wrap:wrap;gap:10px}
.links a,.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}
.links a{background:#fff;color:var(--brand);border-color:rgba(var(--brand-rgb),.12)}
.links a.active{background:var(--brand);color:#fff}
.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%)}
@@ -110,24 +123,43 @@ $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}}
@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}}
</style>
</head>
<body>
<main class="shell">
<header class="bar">
<div class="brand">
<strong class="brand__title">Kaffeeliste SaaS</strong>
<strong class="brand__title">Die Kaffeeliste</strong>
<span class="brand__subtitle">Umfragen und Freigaben im Tenant.</span>
</div>
<nav class="links">
<?php foreach ($tenantNavItems as $item): ?>
<a class="<?= (($item['key'] ?? '') === 'surveys') ? 'active' : '' ?>" href="<?= h((string) ($item['href'] ?? '/')) ?>"><?= h((string) ($item['label'] ?? 'Link')) ?></a>
<?php endforeach; ?>
<form method="post" action="/logout/"><button type="submit" class="button button--ghost">Abmelden</button></form>
</nav>
</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 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>
<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>
</details>
</div>
</section>
<section class="hero">
<div>
<p class="hero__kicker">Umfragen</p>
@@ -135,9 +167,10 @@ $themeCss = app_tenant_theme_root_css($tenantSettings);
<p class="hero__lead">
Entwürfe bleiben intern bearbeitbar. Mitglieder sehen nur veröffentlichte Stände.
</p>
<div class="hero__actions">
<a class="button" href="#admin-surveys">Admin Uebersicht</a>
<a class="button button--ghost" href="#member-surveys">Mitgliederansicht</a>
<div class="meta" style="margin-top:16px;">
<span class="badge">Administration</span>
<span class="badge">Mitglieder-Sicht</span>
<span class="badge badge--solid">Snapshot</span>
</div>
</div>
<div class="stack">
@@ -151,12 +184,12 @@ $themeCss = app_tenant_theme_root_css($tenantSettings);
</div>
</section>
<section id="admin-surveys" class="grid grid--2">
<section class="grid grid--2">
<article class="table-card">
<div class="table-card__header">
<div>
<p class="hero__kicker">Verwaltung</p>
<h2>Entwuerfe und Freigaben</h2>
<h2>Entwürfe und Freigaben</h2>
</div>
<span class="pill">Draft -> Review -> Snapshot</span>
</div>
@@ -181,20 +214,20 @@ $themeCss = app_tenant_theme_root_css($tenantSettings);
<?php if ($survey->isDraft()): ?>
<span class="status">Entwurf</span>
<?php elseif ($survey->status() === 'in_review'): ?>
<span class="status status--warning">Pruefung</span>
<span class="status status--warning">Prüfung</span>
<?php else: ?>
<span class="status status--success">Freigegeben</span>
<?php endif; ?>
</td>
<td class="muted">
<?= h($survey->isDraft() ? 'Live bearbeitbar, noch nicht freigegeben.' : 'Bereit fuer Freigabe oder Snapshot.') ?>
<?= h($survey->isDraft() ? 'Live bearbeitbar, noch nicht freigegeben.' : 'Bereit für Freigabe oder Snapshot.') ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<div class="note">
Die Fachseite pflegt Entwuerfe live. Mitglieder sehen spaeter nur den veroeffentlichten Snapshot.
Die Fachseite pflegt Entwürfe live. Mitglieder sehen später nur den veröffentlichten Snapshot.
</div>
</article>
@@ -209,17 +242,17 @@ $themeCss = app_tenant_theme_root_css($tenantSettings);
<?php endforeach; ?>
</div>
<div class="note">
Freigaben koennen tenantweit konfigurierbar bleiben. Snapshot und Entwurf sind sauber getrennt.
Freigaben können tenantweit konfigurierbar bleiben. Snapshot und Entwurf sind sauber getrennt.
</div>
</article>
</section>
<section id="member-surveys" class="grid grid--2" style="margin-top:18px;">
<section class="grid grid--2" style="margin-top:18px;">
<article class="table-card">
<div class="table-card__header">
<div>
<p class="hero__kicker">Veroeffentlicht</p>
<h2>Snapshots fuer Mitglieder</h2>
<p class="hero__kicker">Veröffentlicht</p>
<h2>Snapshots für Mitglieder</h2>
</div>
<span class="pill">Read only</span>
</div>
@@ -270,14 +303,14 @@ $themeCss = app_tenant_theme_root_css($tenantSettings);
<?php endforeach; ?>
</div>
<div class="note">
Mitglieder sehen nur freigegebene Snapshots. Entwuerfe bleiben fuer Tenant-Admins und Survey-Manager editierbar.
Mitglieder sehen nur freigegebene Snapshots. Entwürfe bleiben für Tenant-Admins und Survey-Manager editierbar.
</div>
</article>
</section>
<section class="grid grid--2" style="margin-top:18px;">
<article class="panel">
<h2>Rollen Im Modul</h2>
<h2>Rollen im Modul</h2>
<div class="stack">
<?php foreach (($board['roles'] ?? []) as $role): ?>
<div class="feature-list__item">
@@ -299,7 +332,7 @@ $themeCss = app_tenant_theme_root_css($tenantSettings);
<?php endforeach; ?>
</ul>
<div class="note">
Tenant-Verantwortliche koennen Freigaben steuern und bei Bedarf den Vier-Augen-Mechanismus tenantweit deaktivieren.
Tenant-Verantwortliche können Freigaben steuern und bei Bedarf den Vier-Augen-Mechanismus tenantweit deaktivieren.
</div>
</article>
</section>
@@ -1,6 +1,6 @@
@extends('layouts.app')
@section('page_title', 'Kaffeeliste SaaS - Passwort Reset')
@section('page_title', 'Die Kaffeeliste - Passwort Reset')
@section('content')
<section class="hero">
@@ -1,6 +1,6 @@
@extends('layouts.app')
@section('page_title', 'Kaffeeliste SaaS - Zentrale Anmeldung')
@section('page_title', 'Die Kaffeeliste - Zentrale Anmeldung')
@php
$preview = $loginPreview ?? [
@@ -1,6 +1,6 @@
@extends('layouts.app')
@section('page_title', 'Kaffeeliste SaaS - Redaktion')
@section('page_title', 'Die Kaffeeliste - Redaktion')
@php
$editorial = $editorial ?? [
@@ -1,6 +1,6 @@
@extends('layouts.app')
@section('page_title', 'Kaffeeliste SaaS - Hinweise')
@section('page_title', 'Die Kaffeeliste - Hinweise')
@php
$data = $content ?? [
@@ -1,15 +1,15 @@
@extends('layouts.app')
@section('page_title', 'Kaffeeliste SaaS - Uebersicht')
@section('page_title', 'Die Kaffeeliste - Übersicht')
@section('content')
<section class="hero">
<div>
<p class="hero__kicker">Uebersicht</p>
<p class="hero__kicker">Übersicht</p>
<h2 class="hero__title">Dein Bereich</h2>
<p class="hero__lead">
Das Dashboard zeigt den aktuellen Kontostand, die Nutzung im Monat und die letzten Buchungen.
Die Werte sind hier bewusst als fachliche Anker platziert und koennen spaeter direkt aus dem Ledger gespeist werden.
Die Werte sind hier bewusst als fachliche Anker platziert und können später direkt aus dem Ledger gespeist werden.
</p>
</div>
<div class="toolbar">
@@ -23,7 +23,7 @@
<article class="card metric">
<p class="metric__label">Kontostand</p>
<div class="metric__value">7,50 EUR</div>
<p class="muted">Positiver Puffer fuer den laufenden Monat.</p>
<p class="muted">Positiver Puffer für den laufenden Monat.</p>
</article>
<article class="card metric">
<p class="metric__label">Striche diesen Monat</p>
@@ -38,7 +38,7 @@
<article class="card metric">
<p class="metric__label">Letzte Buchung</p>
<div class="metric__value">Heute</div>
<p class="muted">Aktuelle Aktivitaet im Mandantenbereich.</p>
<p class="muted">Aktuelle Aktivität im Mandantenbereich.</p>
</article>
</section>
@@ -46,7 +46,7 @@
<article class="table-card">
<div class="table-card__header">
<div>
<p class="card__eyebrow">Aktivitaet</p>
<p class="card__eyebrow">Aktivität</p>
<h3>Letzte Buchungen</h3>
</div>
<span class="pill">Live feed</span>
@@ -87,12 +87,20 @@
<article class="panel">
<h3>Arbeitsbereich</h3>
<p class="muted">Schnellzugriffe fuer die wichtigsten Arbeitsschritte im Tagesbetrieb.</p>
<div class="stack">
<a class="button" href="/members">Mitglieder verwalten</a>
<a class="button button--ghost" href="/ledger">Ledger pruefen</a>
<a class="button button--ghost" href="/payments">Zahlungen ansehen</a>
<a class="button button--ghost" href="/imports">CSV-Import starten</a>
<p class="muted">Die Navigation läuft vollständig über das Menü. Im Inhalt bleiben nur die fachlichen Schwerpunkte.</p>
<div class="timeline timeline--tight">
<div class="timeline__item">
<p class="timeline__title">Mitglieder und Rollen</p>
<p class="timeline__meta">Im Menü sichtbar, wenn die angemeldete Rolle den Bereich verwalten darf.</p>
</div>
<div class="timeline__item">
<p class="timeline__title">Buchungen und Zahlungen</p>
<p class="timeline__meta">Nur für Finanzrollen freigeschaltet und dadurch nicht für jede Anmeldung sichtbar.</p>
</div>
<div class="timeline__item">
<p class="timeline__title">Exporte und Zusatzfunktionen</p>
<p class="timeline__meta">Werden im Menü nur angezeigt, wenn das Lizenzmodell sie für den Tenant enthält.</p>
</div>
</div>
<div class="note" style="margin-top: 18px;">
Der alte Funktionskern bleibt sichtbar: Saldo, Striche, Einzahlungen und letzte Aktionen bilden die produktive Achse.
@@ -1,6 +1,6 @@
@extends('layouts.app')
@section('page_title', 'Kaffeeliste SaaS - Exporte')
@section('page_title', 'Die Kaffeeliste - Exporte')
@section('content')
<section class="hero">
@@ -1,6 +1,6 @@
@extends('layouts.app')
@section('page_title', 'Kaffeeliste SaaS - Importe')
@section('page_title', 'Die Kaffeeliste - Importe')
@section('content')
<section class="hero">
+136 -29
View File
@@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light">
<title>@yield('page_title', $title ?? 'Kaffeeliste SaaS')</title>
<title>@yield('page_title', $title ?? 'Die Kaffeeliste')</title>
@php
$layoutAuth = $auth ?? (function_exists('app_auth_user') ? app_auth_user() : null);
$layoutLicense = $tenantLicense ?? ['features' => []];
@@ -113,31 +113,96 @@
color: var(--brand-strong);
}
.badge--solid { background: var(--brand); color: #fff; border-color: transparent; }
.app-nav {
.tenant-nav-wrap {
width: min(var(--content-width), calc(100% - 32px));
margin: 0 auto 18px;
display: flex;
flex-wrap: wrap;
gap: 10px;
margin: 16px auto 0;
}
.app-nav a {
padding: 0.65rem 0.95rem;
.tenant-nav-shell {
display: grid;
gap: 12px;
padding: 14px 16px;
border: 1px solid var(--line);
border-radius: 20px;
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;
}
.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__mobile {
display: none;
position: relative;
}
.tenant-nav__toggle {
display: inline-flex;
align-items: center;
gap: 10px;
list-style: none;
cursor: pointer;
padding: 0.72rem 1rem;
border-radius: 999px;
border: 1px solid rgba(44, 32, 23, 0.08);
background: rgba(255, 251, 244, 0.88);
background: rgba(255, 251, 244, 0.94);
color: var(--text);
font-size: 0.92rem;
font-weight: 600;
font-weight: 700;
}
.app-nav a.is-primary {
.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: rgba(255, 252, 246, 0.98);
box-shadow: var(--shadow);
}
.tenant-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;
}
.app-nav a:hover {
.tenant-nav__link:hover {
text-decoration: none;
border-color: rgba(var(--brand-rgb), 0.2);
background: #fffdf8;
}
.tenant-nav__logout {
display: flex;
justify-content: flex-end;
flex: 0 0 auto;
}
.app-main { padding: 34px 0 56px; }
.hero {
display: grid;
@@ -429,11 +494,17 @@
.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; }
}
</style>
</head>
@@ -444,7 +515,7 @@
<div class="brand">
<div class="brand__mark">K</div>
<div class="brand__text">
<h1 class="brand__title">Kaffeeliste SaaS</h1>
<h1 class="brand__title">Die Kaffeeliste</h1>
<p class="brand__subtitle">Kaffeekasse, Mitglieder und Verwaltung pro Tenant</p>
</div>
</div>
@@ -457,33 +528,69 @@
<span class="badge badge--solid">Mitglied</span>
@endif
@else
<span class="badge">Klassische Oberflaeche</span>
<span class="badge badge--solid">Mandantenfaehig</span>
<span class="badge">Klassische Oberfläche</span>
<span class="badge badge--solid">Mandantenfähig</span>
@endif
</div>
</div>
<nav class="app-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="{{ $isActive ? 'is-primary' : '' }}">{{ $item['label'] ?? 'Link' }}</a>
@empty
<a class="is-primary" href="/">Start</a>
<a href="/login/">Anmeldung</a>
@endforelse
</nav>
</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)
@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>
@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 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>
@endif
</div>
</details>
</div>
</section>
<main class="app-main">
@yield('content')
</main>
<footer class="app-footer">
<div class="app-footer__inner">
<span>Kaffeeliste SaaS</span>
<span>Die Kaffeeliste</span>
<span>Mitglieder, Striche, Einzahlungen, Hinweise und Exporte in einem System</span>
</div>
</footer>
@@ -1,6 +1,6 @@
@extends('layouts.app')
@section('page_title', 'Kaffeeliste SaaS - Buchungen')
@section('page_title', 'Die Kaffeeliste - Buchungen')
@php
$overview = $ledgerOverview ?? [];
@@ -1,6 +1,6 @@
@extends('layouts.app')
@section('page_title', 'Kaffeeliste SaaS - Mitglieder')
@section('page_title', 'Die Kaffeeliste - Mitglieder')
@section('content')
<section class="hero">
@@ -1,6 +1,6 @@
@extends('layouts.app')
@section('page_title', 'Kaffeeliste SaaS - Benachrichtigungen')
@section('page_title', 'Die Kaffeeliste - Benachrichtigungen')
@section('content')
<section class="hero">
@@ -1,6 +1,6 @@
@extends('layouts.app')
@section('page_title', 'Kaffeeliste SaaS - Zahlungen')
@section('page_title', 'Die Kaffeeliste - Zahlungen')
@php
$overview = $paymentOverview ?? [];
@@ -7,7 +7,7 @@
<?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>Kaffeeliste - Support</title>
<title>Die Kaffeeliste - Support</title>
<style>
:root{
<?= $themeCss ?>
@@ -29,10 +29,23 @@
.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)}
.links,.actions,.context{display:flex;flex-wrap:wrap;gap:10px;align-items:center}
.links a,.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}
.links a{background:#fff;color:var(--brand);border-color:rgba(var(--brand-rgb),.12)}
.links a.active{background:var(--brand);color:#fff}
.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%)}
@@ -81,30 +94,51 @@
.footer{margin-top:18px;text-align:center;color:var(--muted);font-size:.92rem}
@media(max-width:1040px){
.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}}
</style>
</head>
<body>
<main class="shell">
<header class="bar">
<div class="brand">
<strong>Kaffeeliste SaaS</strong>
<span>Support, Vorgaenge und Rueckmeldungen im Tenant.</span>
<strong>Die Kaffeeliste</strong>
<span>Support, Vorgänge und Rückmeldungen im Tenant.</span>
</div>
<nav class="links">
<?php foreach ($tenantNavItems as $item): ?>
<a href="<?= support_h((string) ($item['href'] ?? '/')) ?>" class="<?= (($item['key'] ?? '') === 'support') ? 'active' : '' ?>"><?= support_h((string) ($item['label'] ?? 'Link')) ?></a>
<?php endforeach; ?>
<form method="post" action="/logout/"><button type="submit" class="button secondary">Abmelden</button></form>
</nav>
</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 schliessen sie im selben Verlauf.
Mitglieder legen hier Vorgänge an. Verantwortliche verfolgen, beantworten und schließen sie im selben Verlauf.
</p>
<div class="context">
<?= support_badge($isManager ? 'Verantwortlichen-Sicht' : 'Mitgliedersicht', 'success') ?>
@@ -124,7 +158,7 @@
<?php elseif (!$supportTablesReady): ?>
<section class="alert alert-warning">
Das Support-Modul ist strukturell vorbereitet, aber die Support-Tabellen sind noch nicht angelegt.
Bitte fuehre die Migrationen aus, bevor du das Modul produktiv nutzt.
Bitte führe die Migrationen aus, bevor du das Modul produktiv nutzt.
</section>
<?php endif; ?>
@@ -132,7 +166,7 @@
<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 Rueckfrage an Mitglieder.</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>
@@ -362,7 +396,7 @@
</section>
<?php endif; ?>
<p class="footer">Kaffeeliste | Support, Hinweise und Betriebsprozesse im Tenant-Menü</p>
<p class="footer">Die Kaffeeliste | Support, Hinweise und Betriebsprozesse im Tenant-Menü</p>
</main>
</body>
</html>
@@ -1,6 +1,6 @@
@extends('layouts.app')
@section('page_title', $title ?? 'Kaffeeliste SaaS - Umfragen')
@section('page_title', $title ?? 'Die Kaffeeliste - Umfragen')
@php
$board = $board ?? [
@@ -22,17 +22,14 @@
<h2 class="hero__title">Umfragen</h2>
<p class="hero__lead">
Das Survey-Modul ist jetzt nicht mehr nur eine Zusatzfunktion, sondern ein klarer
Verwaltungsbereich fuer Tenant-Admins und Survey-Manager. Entwuerfe werden live gepflegt,
Verwaltungsbereich für Tenant-Admins und Survey-Manager. Entwürfe werden live gepflegt,
Mitglieder sehen nach Freigabe einen stabilen Snapshot.
</p>
</div>
<div class="hero__actions">
<a class="button" href="#survey-board">Admin-Uebersicht</a>
<a class="button button--ghost" href="#member-surveys">Mitglieder-Sicht</a>
</div>
<div class="hero__meta">
<span class="badge">Tenant scoped</span>
<span class="badge">Snapshot publishing</span>
<span class="badge">Administration</span>
<span class="badge">Mitglieder-Sicht</span>
<span class="badge">Tenant-weit</span>
<span class="badge badge--solid">Freigabe gesteuert</span>
</div>
</div>
@@ -48,12 +45,12 @@
</aside>
</section>
<section id="survey-board" class="grid grid--2">
<section class="grid grid--2">
<article class="table-card">
<div class="table-card__header">
<div>
<p class="card__eyebrow">Administration</p>
<h3>Aktive Entwuerfe und Freigabestufen</h3>
<h3>Aktive Entwürfe und Freigabestufen</h3>
</div>
<span class="pill">Draft -> Review -> Snapshot</span>
</div>
@@ -79,13 +76,13 @@
@if ($survey->isDraft())
<span class="status">Entwurf</span>
@elseif ($survey->status() === 'in_review')
<span class="status status--warning">Pruefung</span>
<span class="status status--warning">Prüfung</span>
@else
<span class="status status--success">Freigegeben</span>
@endif
</td>
<td class="muted">
{{ $survey->isDraft() ? 'Kann live bearbeitet werden.' : 'Bereit fuer Snapshot und Freigabe.' }}
{{ $survey->isDraft() ? 'Kann live bearbeitet werden.' : 'Bereit für Snapshot und Freigabe.' }}
</td>
</tr>
@empty
@@ -109,17 +106,17 @@
@endforeach
</div>
<div class="note" style="margin-top: 18px;">
Freigaben koennen tenantweit deaktivierbar bleiben, der veroeffentlichte Stand bleibt trotzdem ein eigener Snapshot.
Freigaben können tenantweit deaktivierbar bleiben, der veröffentlichte Stand bleibt trotzdem ein eigener Snapshot.
</div>
</article>
</section>
<section id="member-surveys" class="grid grid--2" style="margin-top: 18px;">
<section class="grid grid--2" style="margin-top: 18px;">
<article class="table-card">
<div class="table-card__header">
<div>
<p class="card__eyebrow">Veroeffentlicht</p>
<h3>Snapshots fuer Mitglieder</h3>
<p class="card__eyebrow">Veröffentlicht</p>
<h3>Snapshots für Mitglieder</h3>
</div>
<span class="pill">Read only</span>
</div>
@@ -155,7 +152,7 @@
</tr>
@empty
<tr>
<td colspan="4" class="muted">Noch kein Snapshot veroeffentlicht.</td>
<td colspan="4" class="muted">Noch kein Snapshot veröffentlicht.</td>
</tr>
@endforelse
</tbody>
@@ -176,14 +173,14 @@
@endforeach
</div>
<div class="note" style="margin-top: 18px;">
Mitglieder sehen nur freigegebene Snapshots. Entwuerfe bleiben fuer die Fachseite bearbeitbar.
Mitglieder sehen nur freigegebene Snapshots. Entwürfe bleiben für die Fachseite bearbeitbar.
</div>
</article>
</section>
<section class="grid grid--2" style="margin-top: 18px;">
<article class="panel">
<h3>Rollen Im Modul</h3>
<h3>Rollen im Modul</h3>
<div class="stack">
@foreach ($board['roles'] ?? [] as $role)
<div class="feature-list__item">
@@ -198,7 +195,7 @@
</article>
<article class="panel">
<h3>Regeln Fuer Das Publishing</h3>
<h3>Regeln für das Publishing</h3>
<ul class="list-reset">
@foreach ($board['publishing_rules'] ?? [] as $rule)
<li style="margin-bottom: 12px;">
@@ -1,6 +1,6 @@
@extends('layouts.app')
@section('page_title', 'Kaffeeliste SaaS - Verwaltung')
@section('page_title', 'Die Kaffeeliste - Verwaltung')
@php
$data = $overview ?? [
@@ -32,19 +32,16 @@
<p class="hero__kicker">Verwaltung</p>
<h2 class="hero__title">Mandanten</h2>
<p class="hero__lead">
Diese Konsole ist die zentrale Schaltstelle fuer Tenant-Rollout, Domains, zentrale Anmeldung und den Umgang
Diese Konsole ist die zentrale Schaltstelle für Tenant-Rollout, Domains, zentrale Anmeldung und den Umgang
mit Mitarbeitenden, die in mehreren Tenants hinterlegt sind. Statt verteilter Einzelansichten entsteht eine
gemeinsame Portfolio-Sicht fuer Betrieb und Weiterentwicklung.
gemeinsame Portfolio-Sicht für Betrieb und Weiterentwicklung.
</p>
</div>
<div class="hero__actions">
<a class="button" href="/login">Mitglieder-Login testen</a>
<a class="button button--ghost" href="/">Landingpage ansehen</a>
</div>
<div class="hero__meta">
<span class="badge badge--solid">Tenant Portfolio</span>
<span class="badge badge--solid">Zentrale Steuerung</span>
<span class="badge">Anmeldung</span>
<span class="badge">Identity & Routing</span>
<span class="badge">Mehrfachzuordnungen</span>
<span class="badge">Admin-Konsole</span>
</div>
</div>
@@ -107,7 +104,7 @@
<section class="split" style="margin-top: 18px;">
<article class="panel">
<p class="card__eyebrow">Mehrfachzuordnungen</p>
<h3>Wie die Plattform E-Mail-Adressen ueber mehrere Tenants hinweg behandelt.</h3>
<h3>Wie die Plattform E-Mail-Adressen über mehrere Tenants hinweg behandelt.</h3>
<div class="timeline" style="margin-top: 18px;">
@foreach ($data['shared_access'] as $entry)
<div class="timeline__item">
@@ -125,7 +122,7 @@
@elseif ($entry['status'] === 'mehrfach')
<span class="status status--warning">Tenant-Auswahl</span>
@else
<span class="status status--danger">Einladung noetig</span>
<span class="status status--danger">Einladung nötig</span>
@endif
</div>
<p class="muted">{{ $entry['next_step'] }}</p>
@@ -136,7 +133,7 @@
<article class="panel">
<p class="card__eyebrow">Zentrale Aufgaben</p>
<h3>Prioritaeten fuer den Plattformbetrieb.</h3>
<h3>Prioritäten für den Plattformbetrieb.</h3>
<ul class="list-reset" style="margin-top: 18px;">
@foreach ($data['operations'] as $operation)
<li>
@@ -148,7 +145,7 @@
</ul>
<div class="note" style="margin-top: 18px;">
Die Tenant Console ist bewusst nicht nur eine Tabelle: Sie verbindet Rollout, Login-Logik, Mehrfachzuordnungen
und zentrale Operations-Signale zu einer konsistenten Admin-Oberflaeche.
und zentrale Operations-Signale zu einer konsistenten Admin-Oberfläche.
</div>
</article>
</section>
@@ -159,7 +156,7 @@
<p class="card__eyebrow">Rollenmodell</p>
<h3>Tenant-Admin Vollzugriff plus Spezialrollen mit klarer Delegation.</h3>
</div>
<a class="button button--ghost" href="/tenants/roles/">Rollenansicht öffnen</a>
<span class="pill">Im Menü unter Rollen</span>
</div>
<div class="grid grid--4" style="margin-bottom: 18px;">
@foreach ($roleOverview['metrics'] as $metric)
@@ -7,7 +7,7 @@
<?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>Kaffeeliste - Rollen</title>
<title>Die Kaffeeliste - Rollen</title>
<style>
:root{
<?= $themeCss ?>
@@ -31,9 +31,22 @@
.brand{display:grid;gap:4px}
.brand strong{font-size:1.18rem}
.brand span,.muted{color:var(--muted)}
.links,.actions,.context,.meta{display:flex;flex-wrap:wrap;gap:10px;align-items:center}
.links a,.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)}
.links a.active{background:var(--brand);color:#fff}
.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%)}
@@ -64,24 +77,43 @@
.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}}
@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}}
</style>
</head>
<body>
<main class="shell">
<header class="bar">
<div class="brand">
<strong>Kaffeeliste</strong>
<strong>Die Kaffeeliste</strong>
<span>Rollen und Rechte im Tenant.</span>
</div>
<nav class="links">
<?php foreach ($tenantNavItems as $item): ?>
<a class="<?= (($item['key'] ?? '') === 'roles') ? 'active' : '' ?>" href="<?= tenant_roles_h((string) ($item['href'] ?? '/')) ?>"><?= tenant_roles_h((string) ($item['label'] ?? 'Link')) ?></a>
<?php endforeach; ?>
<form method="post" action="/logout/"><button type="submit" class="button button--ghost">Abmelden</button></form>
</nav>
</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 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>
<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>
</details>
</div>
</section>
<section class="hero">
<div>
<p class="hero__kicker">Rollen</p>
@@ -89,10 +121,6 @@
<p class="hero__lead">
Tenant-Admins behalten den Gesamtzugriff. Fachrollen für Finanzen, Support und Umfragen werden gezielt delegiert.
</p>
<div class="hero__actions">
<a class="button" href="/dashboard/">Zum Dashboard</a>
<a class="button button--ghost" href="/members/">Mitglieder</a>
</div>
<div class="meta" style="margin-top:16px;">
<span class="badge">lokal + ADFS/OIDC</span>
<span class="badge">Rollenmatrix</span>
@@ -118,7 +146,7 @@
<p class="hero__kicker">Rollenmatrix</p>
<h2>Vollzugriff, Spezialrollen und Leserechte</h2>
</div>
<span class="pill">tenant scoped</span>
<span class="pill">Tenant-weit</span>
</div>
<table>
<thead>
+1 -1
View File
@@ -1,6 +1,6 @@
@extends('layouts.app')
@section('page_title', 'Kaffeeliste SaaS - Zentrale Plattform')
@section('page_title', 'Die Kaffeeliste - Zentrale Plattform')
@php
$overview = $tenantOverview ?? [