Jedes Zahlenfeld (inputmode="decimal") zeigt rechts ein Rechnersymbol. Klick darauf oder Alt+R öffnet einen Taschenrechner mit Grundrechenarten und Klammern, komplett per Tastatur bedienbar: Ziffern, + - * / ( ), Backspace, Entf, Pfeiltasten, C löscht alles, = setzt das Ergebnis ein, Enter überträgt ins Feld, Escape schließt und fokussiert das Feld. Das Ergebnis steht nach jedem Tastendruck unter der Rechnung. Das Symbol ist ein Hintergrundbild (kein Wrapper-Element, kein Eingriff in die Layouts); gerechnet wird mit eigenem Parser statt eval. In modalen Dialogen wandert der Rechner mit hinein. Stück/Kurs übernehmen 6, Zinssätze 3 Nachkommastellen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
103 lines
5.2 KiB
PHP
103 lines
5.2 KiB
PHP
<?php
|
||
|
||
use Finanzmanager\View;
|
||
|
||
/** @var string $titel */
|
||
/** @var array $flash */
|
||
/** @var string|null $aktuellerPfad */
|
||
/** @var array|null $benutzer */
|
||
|
||
$navigation = [
|
||
['pfad' => '/', 'name' => 'Cockpit', 'icon' => '⌂'],
|
||
['pfad' => '/konten', 'name' => 'Kontenübersicht', 'icon' => '▤'],
|
||
['pfad' => '/banking', 'name' => 'Online-Banking', 'icon' => '⇊'],
|
||
['pfad' => '/zahlungsverkehr', 'name' => 'Zahlungsverkehr', 'icon' => '⇄'],
|
||
['pfad' => '/auswertungen', 'name' => 'Auswertungen', 'icon' => '◱'],
|
||
['pfad' => '/planung', 'name' => 'Planung', 'icon' => '◷'],
|
||
['pfad' => '/wertpapiere', 'name' => 'Wertpapiere', 'icon' => '↗'],
|
||
// Termine und Verträge haben keinen eigenen Menüpunkt mehr, sind aber weiter
|
||
// erreichbar – deshalb leuchtet für sie die Verwaltung, unter der sie hängen.
|
||
['pfad' => '/verwaltung', 'name' => 'Verwaltung', 'icon' => '⚙', 'auch' => ['/termine', '/vertraege']],
|
||
];
|
||
|
||
// Ob die Seitenleiste eingeklappt ist, merkt sich ein Cookie. Das Skript
|
||
// setzt es beim Umschalten; hier entscheidet es schon beim Rendern, damit die
|
||
// breite Leiste nicht bei jedem Seitenwechsel kurz aufblitzt.
|
||
$eingeklappt = ($_COOKIE['seitenleiste'] ?? '') === 'eingeklappt';
|
||
|
||
/** @param list<string> $auch */
|
||
$istAktiv = static function (string $pfad, array $auch = []) use ($aktuellerPfad): bool {
|
||
if (!is_string($aktuellerPfad)) {
|
||
return false;
|
||
}
|
||
if ($pfad === '/') {
|
||
return $aktuellerPfad === '/' || $aktuellerPfad === '';
|
||
}
|
||
foreach ([$pfad, ...$auch] as $kandidat) {
|
||
if (str_starts_with($aktuellerPfad, $kandidat)) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
};
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="de"<?= ($thema = View::thema()) !== '' ? ' data-thema="' . View::h($thema) . '"' : '' ?>>
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title><?= View::h($titel ?? 'Finanzmanager') ?> – Finanzmanager</title>
|
||
<link rel="stylesheet" href="/static/app.css">
|
||
<!-- Nur hier eingebunden: Ein zweites Mal im Fuß ließe jeden Handler doppelt
|
||
laufen – Rückfragen kämen zweimal, Menüs entstünden zweimal. -->
|
||
<script src="/static/app.js" defer></script>
|
||
<!-- Der Taschenrechner hängt an jedem Zahlenfeld jeder Seite; betrag.js
|
||
liefert ihm das Lesen deutscher Beträge. -->
|
||
<script src="/static/betrag.js" defer></script>
|
||
<script src="/static/rechner.js" defer></script>
|
||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect width='16' height='16' rx='3' fill='%231d6fb8'/><text x='8' y='12.5' font-size='12' font-weight='bold' text-anchor='middle' fill='white' font-family='sans-serif'>€</text></svg>">
|
||
</head>
|
||
<body>
|
||
<?php include __DIR__ . '/meldungen.php'; ?>
|
||
<div class="rahmen<?= $eingeklappt ? ' eingeklappt' : '' ?>">
|
||
<nav class="seitenleiste" id="seitenleiste" aria-label="Hauptnavigation">
|
||
<!-- Die Marke ist zugleich der Schalter: Ein Klick schiebt die
|
||
Seitenleiste auf Symbolbreite zusammen und wieder auf. -->
|
||
<button type="button" class="seitenleisten-marke" data-seitenleiste-schalter
|
||
aria-controls="seitenleiste" aria-expanded="<?= $eingeklappt ? 'false' : 'true' ?>"
|
||
title="<?= $eingeklappt ? 'Seitenleiste ausklappen' : 'Seitenleiste einklappen' ?>">
|
||
<img src="/static/coinstack.png" alt="" class="marken-symbol">
|
||
<span class="marken-text">Finanz<span>manager</span></span>
|
||
</button>
|
||
<ul>
|
||
<?php foreach ($navigation as $eintrag): ?>
|
||
<li>
|
||
<?php /* title bleibt hinten: Der Navigationstest prüft href und
|
||
class in dieser Reihenfolge unmittelbar hintereinander. */ ?>
|
||
<a href="<?= View::h($eintrag['pfad']) ?>"
|
||
class="<?= $istAktiv($eintrag['pfad'], $eintrag['auch'] ?? []) ? 'aktiv' : '' ?>"
|
||
title="<?= View::h($eintrag['name']) ?>">
|
||
<span class="icon" aria-hidden="true"><?= $eintrag['icon'] ?></span>
|
||
<span class="beschriftung"><?= View::h($eintrag['name']) ?></span>
|
||
</a>
|
||
</li>
|
||
<?php endforeach; ?>
|
||
</ul>
|
||
</nav>
|
||
|
||
<div class="hauptbereich">
|
||
<header class="werkzeugleiste">
|
||
<h1><?= View::h($titel ?? 'Finanzmanager') ?></h1>
|
||
<div class="werkzeuge">
|
||
<a href="/einstellungen" class="werkzeug" title="Einstellungen">⚙ Einstellungen</a>
|
||
<?php if (!empty($benutzer)): ?>
|
||
<span class="benutzer"><?= View::h($benutzer['anzeigename'] ?: $benutzer['benutzername']) ?></span>
|
||
<form method="post" action="/logout" class="inline">
|
||
<input type="hidden" name="csrf_token" value="<?= View::h($csrfToken ?? '') ?>">
|
||
<button type="submit" class="werkzeug">Abmelden</button>
|
||
</form>
|
||
<?php endif; ?>
|
||
</div>
|
||
</header>
|
||
|
||
<main class="inhalt">
|