Redesign: gemeinsames Design-System für Frontend und Backend
Some checks failed
CI / PHP Lint (push) Waiting to run
CI / PHP Lint-1 (push) Waiting to run
CI / Unit Tests & Static Analysis (push) Waiting to run
CI / PHP Lint (pull_request) Has been cancelled
CI / PHP Lint-1 (pull_request) Has been cancelled
CI / Unit Tests & Static Analysis (pull_request) Has been cancelled
Some checks failed
CI / PHP Lint (push) Waiting to run
CI / PHP Lint-1 (push) Waiting to run
CI / Unit Tests & Static Analysis (push) Waiting to run
CI / PHP Lint (pull_request) Has been cancelled
CI / PHP Lint-1 (pull_request) Has been cancelled
CI / Unit Tests & Static Analysis (pull_request) Has been cancelled
Frontend, Login/Installer/Updater und der komplette Admin-Bereich nutzen jetzt ein einziges Stylesheet (assets/global.css) statt pro Seite dupliziertem Inline-CSS. Design-System - Tokens für Flächen, Text, Linien, Marke, Status, Radien, Schatten und Layout-Maße; Dark Mode ausschließlich über Tokens (keine !important- Overrides mehr) - Komponenten: Buttons, Formularfelder, Cards, Tabellen, Badges, Alerts, Tabs, Pagination, Modals, Toasts, Statistik-Kacheln, Empty States - Schrift Inter mit System-Fallback Oberfläche - Admin-Shell neu: durchgehende Sidebar mit Marke, gruppierter Navigation und Benutzerbereich; schlanke Topbar mit Breadcrumb - Dashboard: ruhige KPI-Kacheln mit Icon-Chips, Charts an Theme-Farben gekoppelt - Öffentliche Voucher-Seite: App-Topbar, klare Formularstruktur und Ticket-Darstellung des erstellten Codes inkl. QR-Code - Login/Passwort/2FA: zweispaltiges Auth-Layout bzw. Fokus-Karten - Updater und Wartungsmodus im gleichen Look (Wartungsseite bleibt bewusst eigenständig ohne externe Abhängigkeiten) - Emoji-Icons in der UI durch Font-Awesome-Icons ersetzt Nebenbei behoben - Falscher SRI-Hash blockierte qrcode.min.js – QR-Codes wurden auf der Voucher-Seite und bei der 2FA-Einrichtung nie gerendert - assets/global.css wurde in mehreren Admin-Seiten über einen falschen Pfad eingebunden ($adminBase = '' statt '../') - TinyMCE lädt ohne API-Key jetzt die GPL-Variante von cdnjs – kein "valid API key required"-Banner mehr im Einstellungs-Editor - Tabellen in Cards scrollen horizontal statt zu überlaufen Screenshots in docs/screenshots neu erstellt, README aktualisiert (neuer Abschnitt "Design-System", Version 2.5.0). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
bad70a631f
commit
498c7e28e0
43 changed files with 2165 additions and 1740 deletions
|
|
@ -93,37 +93,24 @@ $activeSessions = $dbSessions ? $auth->activeSessionCount() : 0;
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Zwei-Faktor-Authentifizierung – <?= htmlspecialchars($appTitle) ?></title>
|
||||
<?php if (!$totpEnabled && $hasPassword): ?>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js" integrity="sha512-CNgIRecGo7nphbeZ04Sc13ka07paqdeTu0WR1IM4kNcpmBAUSHSe2keRB6Q5pBUtIxCY7bQMsVB0ANBpd6JDg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js" integrity="sha512-CNgIRecGo7nphbeZ04Sc13ka07paqdeTu0WR1IM4kNcpmBAUSHSQX0FslNhTDadL4O5SAGapGt4FodqL8My0mA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<?php endif; ?>
|
||||
<style>
|
||||
* { margin:0; padding:0; box-sizing:border-box; font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif; }
|
||||
body { background:linear-gradient(135deg,#667eea 0%,#764ba2 100%); min-height:100vh; display:flex; align-items:center; justify-content:center; padding:20px; }
|
||||
.card { background:#fff; border-radius:18px; box-shadow:0 20px 60px rgba(0,0,0,.3); max-width:480px; width:100%; padding:36px; }
|
||||
h1 { font-size:22px; color:#333; margin-bottom:6px; }
|
||||
.sub { color:#777; font-size:14px; margin-bottom:24px; }
|
||||
.alert { padding:12px 14px; border-radius:9px; font-size:14px; margin-bottom:18px; }
|
||||
.alert-error { background:#fee; border:1px solid #fcc; color:#c33; }
|
||||
.alert-ok { background:#efe; border:1px solid #cfc; color:#2a7; }
|
||||
.status { display:inline-flex; align-items:center; gap:8px; padding:6px 12px; border-radius:8px; font-size:13px; font-weight:600; margin-bottom:20px; }
|
||||
.on { background:#e3f6ea; color:#2a7; } .off { background:#fdeaea; color:#c33; }
|
||||
.qr { display:flex; justify-content:center; margin:18px 0; }
|
||||
.secret { font-family:monospace; background:#f5f6fa; padding:10px; border-radius:8px; text-align:center; letter-spacing:2px; word-break:break-all; font-size:14px; margin-bottom:18px; }
|
||||
ol { margin:0 0 18px 18px; color:#555; font-size:14px; line-height:1.7; }
|
||||
label { display:block; font-size:14px; color:#555; margin-bottom:8px; font-weight:500; }
|
||||
input[type=text] { width:100%; padding:13px; border:2px solid #e0e0e0; border-radius:10px; font-size:18px; letter-spacing:6px; text-align:center; }
|
||||
.btn { width:100%; padding:14px; border:none; border-radius:10px; font-size:15px; font-weight:600; cursor:pointer; margin-top:14px; }
|
||||
.btn-primary { background:#667eea; color:#fff; } .btn-danger { background:#e25555; color:#fff; }
|
||||
.back { display:block; text-align:center; margin-top:20px; color:#667eea; text-decoration:none; font-size:14px; }
|
||||
.codes-box { background:#fff8e6; border:1px solid #ffe3a3; border-radius:10px; padding:16px; margin-bottom:18px; }
|
||||
.codes-box strong { font-size:15px; } .codes-box p { color:#8a6d2f; font-size:13px; margin:6px 0 12px; }
|
||||
.codes { display:grid; grid-template-columns:1fr 1fr; gap:8px; }
|
||||
.codes span { font-family:monospace; background:#fff; border:1px solid #ffe3a3; border-radius:6px; padding:8px; text-align:center; letter-spacing:2px; font-size:14px; }
|
||||
</style>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="../assets/global.css">
|
||||
<script>(function(){ const t=localStorage.getItem('theme')||'light'; document.documentElement.setAttribute('data-theme',t); })();</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<h1>🔐 Zwei-Faktor-Authentifizierung</h1>
|
||||
<p class="sub">Konto: <?= htmlspecialchars($user['email']) ?></p>
|
||||
<body class="app-body focus-page">
|
||||
<div class="focus-card card">
|
||||
<div class="focus-head">
|
||||
<span class="focus-icon"><i class="fas fa-shield-halved"></i></span>
|
||||
<div>
|
||||
<h1>Zwei-Faktor-Authentifizierung</h1>
|
||||
<p class="sub">Konto: <?= htmlspecialchars($user['email']) ?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($setupRequired && !$totpEnabled): ?>
|
||||
<div class="alert alert-error">Aus Sicherheitsgründen ist 2FA für Administratoren verpflichtend. Bitte jetzt einrichten.</div>
|
||||
|
|
@ -133,7 +120,7 @@ input[type=text] { width:100%; padding:13px; border:2px solid #e0e0e0; border-ra
|
|||
|
||||
<?php if (!empty($backupCodes)): ?>
|
||||
<div class="codes-box">
|
||||
<strong>🔑 Recovery-Codes</strong>
|
||||
<strong><i class="fas fa-key"></i> Recovery-Codes</strong>
|
||||
<p>Bewahren Sie diese sicher auf. Jeder Code funktioniert <em>einmal</em>, falls Sie keinen Zugriff auf Ihre App haben.</p>
|
||||
<div class="codes">
|
||||
<?php foreach ($backupCodes as $c): ?><span><?= htmlspecialchars($c) ?></span><?php endforeach; ?>
|
||||
|
|
@ -142,22 +129,22 @@ input[type=text] { width:100%; padding:13px; border:2px solid #e0e0e0; border-ra
|
|||
<?php endif; ?>
|
||||
|
||||
<?php if (!$hasPassword): ?>
|
||||
<div class="status off">● Nicht verfügbar</div>
|
||||
<div class="status off"><i class="fas fa-circle-minus"></i> Nicht verfügbar</div>
|
||||
<p class="sub">Ihr Konto meldet sich über Microsoft 365 an. 2FA wird dort in Ihrem Microsoft-Konto verwaltet.</p>
|
||||
<?php elseif ($totpEnabled): ?>
|
||||
<div class="status on">● Aktiv</div>
|
||||
<div class="status on"><i class="fas fa-circle-check"></i> Aktiv</div>
|
||||
<p class="sub">Bei jeder Anmeldung wird zusätzlich ein Code aus Ihrer Authenticator-App abgefragt.<br>
|
||||
Verbleibende Recovery-Codes: <strong><?= (int)$auth->backupCodesRemaining($user) ?></strong></p>
|
||||
<form method="post" style="margin-bottom:10px;">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrf) ?>">
|
||||
<button type="submit" name="regen_codes" class="btn btn-secondary" style="background:#eef0ff;color:#5a63d6;width:100%;">Recovery-Codes neu erzeugen</button>
|
||||
<button type="submit" name="regen_codes" class="btn btn-secondary btn-lg btn-block">Recovery-Codes neu erzeugen</button>
|
||||
</form>
|
||||
<form method="post" onsubmit="return confirm('2FA wirklich deaktivieren?');">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrf) ?>">
|
||||
<button type="submit" name="disable_totp" class="btn btn-danger">2FA deaktivieren</button>
|
||||
<button type="submit" name="disable_totp" class="btn btn-danger btn-lg btn-block">2FA deaktivieren</button>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<div class="status off">● Inaktiv</div>
|
||||
<div class="status off"><i class="fas fa-circle-minus"></i> Inaktiv</div>
|
||||
<ol>
|
||||
<li>Authenticator-App öffnen (Google Authenticator, Authy, Microsoft Authenticator …)</li>
|
||||
<li>QR-Code scannen <em>oder</em> Secret manuell eingeben</li>
|
||||
|
|
@ -168,27 +155,28 @@ input[type=text] { width:100%; padding:13px; border:2px solid #e0e0e0; border-ra
|
|||
<form method="post">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrf) ?>">
|
||||
<label for="code">6-stelliger Code</label>
|
||||
<input type="text" id="code" name="code" inputmode="numeric" pattern="[0-9]*" maxlength="6" autocomplete="one-time-code" required>
|
||||
<button type="submit" name="enable_totp" class="btn btn-primary">2FA aktivieren</button>
|
||||
<input type="text" id="code" name="code" class="code-input" inputmode="numeric" pattern="[0-9]*" maxlength="6" autocomplete="one-time-code" required placeholder="123456">
|
||||
<button type="submit" name="enable_totp" class="btn btn-primary btn-lg btn-block" style="margin-top:14px;">2FA aktivieren</button>
|
||||
</form>
|
||||
<script>
|
||||
new QRCode(document.getElementById('qrcode'), {
|
||||
text: <?= json_encode($otpUri) ?>, width: 180, height: 180,
|
||||
text: <?= json_encode($otpUri) ?>, width: 168, height: 168,
|
||||
colorDark: '#101625', colorLight: '#ffffff',
|
||||
correctLevel: QRCode.CorrectLevel.M
|
||||
});
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($dbSessions): ?>
|
||||
<hr style="margin:20px 0;border:none;border-top:1px solid #eee;">
|
||||
<hr>
|
||||
<p class="sub">Aktive Sitzungen: <strong><?= (int)$activeSessions ?></strong></p>
|
||||
<form method="post" onsubmit="return confirm('Alle anderen Sitzungen abmelden?');">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrf) ?>">
|
||||
<button type="submit" name="logout_others" class="btn" style="background:#eef0ff;color:#5a63d6;width:100%;">Auf allen anderen Geräten abmelden</button>
|
||||
<button type="submit" name="logout_others" class="btn btn-secondary btn-lg btn-block">Auf allen anderen Geräten abmelden</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
|
||||
<a class="back" href="../index.php">← Zurück</a>
|
||||
<div class="auth-links"><a class="back-link" href="../index.php"><i class="fas fa-arrow-left"></i> Zurück</a></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue