second commit

This commit is contained in:
2026-04-11 19:13:40 +02:00
parent 58bcc8f0f3
commit 87f7859017
25 changed files with 488 additions and 87 deletions
+29 -10
View File
@@ -5,9 +5,9 @@ declare(strict_types=1);
$brandSubtitle = match ($page) {
'dashboard' => 'Statistiken und Verlauf',
'track' => 'Tag erfassen und bewerten',
'archive' => 'Rueckblick auf vergangene Tage',
'archive' => 'Rückblick auf vergangene Tage',
'options' => 'Logik, Sicherheit und Accounts',
'login' => 'Geschuetzter Zugang',
'login' => 'Geschützter Zugang',
'setup' => 'Erstkonfiguration',
default => 'Stimmungstracker',
};
@@ -21,7 +21,7 @@ $brandSubtitle = match ($page) {
<link rel="stylesheet" href="/assets/css/app.css">
<script defer src="/assets/js/app.js"></script>
</head>
<body class="app-body page-<?= e($page) ?>">
<body class="app-body page-<?= e($page) ?>"<?= isset($trackMood) ? ' data-track-mood="' . e($trackMood) . '"' : '' ?>>
<div class="aurora aurora-one"></div>
<div class="aurora aurora-two"></div>
<div class="shell">
@@ -30,16 +30,28 @@ $brandSubtitle = match ($page) {
<div class="brand-block">
<div class="brand-mark">M</div>
<div>
<p class="eyebrow">mood.hnz.io</p>
<p class="eyebrow">mood.heinz.media</p>
<h1>Mood</h1>
</div>
</div>
<nav class="main-nav" aria-label="Hauptnavigation">
<a class="<?= is_active_path('/') ? 'active' : '' ?>" href="/">Dashboard</a>
<a class="<?= is_active_path('/track') ? 'active' : '' ?>" href="/track">Tracken</a>
<a class="<?= is_active_path('/archive') ? 'active' : '' ?>" href="/archive">Archiv</a>
<a class="<?= is_active_path('/options') ? 'active' : '' ?>" href="/options">Optionen</a>
<a class="<?= is_active_path('/') ? 'active' : '' ?>" href="/">
<img class="nav-icon" src="<?= e(icon_path('dashboard')) ?>" alt="">
<span>Dashboard</span>
</a>
<a class="<?= is_active_path('/track') ? 'active' : '' ?>" href="/track">
<img class="nav-icon" src="<?= e(icon_path('track')) ?>" alt="">
<span>Tracken</span>
</a>
<a class="<?= is_active_path('/archive') ? 'active' : '' ?>" href="/archive">
<img class="nav-icon" src="<?= e(icon_path('archive')) ?>" alt="">
<span>Archiv</span>
</a>
<a class="<?= is_active_path('/options') ? 'active' : '' ?>" href="/options">
<img class="nav-icon" src="<?= e(icon_path('options')) ?>" alt="">
<span>Optionen</span>
</a>
</nav>
<div class="sidebar-footer">
@@ -63,7 +75,15 @@ $brandSubtitle = match ($page) {
<h2><?= e($pageTitle) ?></h2>
</div>
<div class="topbar__meta">
<span class="meta-pill"><?= e(date('d.m.Y')) ?></span>
<?php if ($page === 'track' && isset($topbarDate)): ?>
<a class="meta-pill meta-pill--button" href="/track?date=<?= e(rawurlencode(shift_date($topbarDate, -1))) ?>">Vorheriger Tag</a>
<form method="get" action="/track" class="topbar-date-form">
<input class="topbar-date-input" type="date" name="date" value="<?= e($topbarDate) ?>">
</form>
<a class="meta-pill meta-pill--button" href="/track?date=<?= e(rawurlencode(shift_date($topbarDate, 1))) ?>">Nächster Tag</a>
<?php else: ?>
<span class="meta-pill"><?= e(date('d.m.Y')) ?></span>
<?php endif; ?>
<span class="meta-pill"><?= e($authUser['username']) ?></span>
</div>
</header>
@@ -78,4 +98,3 @@ $brandSubtitle = match ($page) {
</div>
</body>
</html>
+14 -10
View File
@@ -5,24 +5,28 @@
<p class="eyebrow">Archiv</p>
<h3>Alle gespeicherten Tage</h3>
</div>
<span class="chart-chip"><?= e((string) count($entries)) ?> Eintraege</span>
<span class="chart-chip"><?= e((string) count($entries)) ?> Einträge</span>
</div>
<?php if ($entries === []): ?>
<p class="empty-state">Noch keine Eintraege vorhanden. Auf der Tracking-Seite kannst du den ersten Tag anlegen.</p>
<p class="empty-state">Noch keine Einträge vorhanden. Auf der Tracking-Seite kannst du den ersten Tag anlegen.</p>
<?php else: ?>
<div class="archive-items">
<?php foreach ($entries as $entry): ?>
<a class="archive-item <?= $selectedEntry !== null && $selectedEntry['date'] === $entry['date'] ? 'active' : '' ?>" href="/archive?date=<?= e(rawurlencode($entry['date'])) ?>">
<article class="archive-item <?= $selectedEntry !== null && $selectedEntry['date'] === $entry['date'] ? 'active' : '' ?>">
<div>
<strong><?= e($entry['date']) ?></strong>
<strong><?= e(format_display_date($entry['date'], false)) ?></strong>
<span><?= e($entry['evaluation']['label']) ?></span>
</div>
<div class="archive-item__meta">
<span><?= e(format_points((float) $entry['evaluation']['total'])) ?></span>
<span>Stimmung <?= e((string) $entry['mood']) ?>/10</span>
</div>
</a>
<div class="archive-item__actions">
<a class="ghost-link archive-action" href="/archive?date=<?= e(rawurlencode($entry['date'])) ?>">Ansehen</a>
<a class="ghost-link archive-action" href="/track?date=<?= e(rawurlencode($entry['date'])) ?>">Bearbeiten</a>
</div>
</article>
<?php endforeach; ?>
</div>
<?php endif; ?>
@@ -31,16 +35,17 @@
<aside class="stack-column">
<?php if ($selectedEntry !== null): ?>
<article class="glass-panel detail-card">
<p class="eyebrow">Ausgewaehlt</p>
<h3><?= e($selectedEntry['date']) ?></h3>
<p class="eyebrow">Ausgewählt</p>
<h3><?= e(format_display_date($selectedEntry['date'])) ?></h3>
<p class="hero-label"><?= e($selectedEntry['evaluation']['label']) ?> · <?= e(format_points((float) $selectedEntry['evaluation']['total'])) ?> Punkte</p>
<a class="primary-button button-link" href="/track?date=<?= e(rawurlencode($selectedEntry['date'])) ?>">Diesen Tag bearbeiten</a>
<dl class="detail-grid">
<div><dt>Stimmung</dt><dd><?= e((string) $selectedEntry['mood']) ?>/10</dd></div>
<div><dt>Energie</dt><dd><?= e((string) $selectedEntry['energy']) ?>/10</dd></div>
<div><dt>Stress</dt><dd><?= e((string) $selectedEntry['stress']) ?>/10</dd></div>
<div><dt>Schlaf</dt><dd><?= e((string) $selectedEntry['sleep_hours']) ?> h</dd></div>
<div><dt>Schlafgefuehl</dt><dd><?= e((string) $selectedEntry['sleep_feeling']) ?>/5</dd></div>
<div><dt>Schlafgefühl</dt><dd><?= e((string) $selectedEntry['sleep_feeling']) ?>/5</dd></div>
<div><dt>Sport</dt><dd><?= e((string) $selectedEntry['sport_minutes']) ?> min</dd></div>
<div><dt>Spaziergang</dt><dd><?= e((string) $selectedEntry['walk_minutes']) ?> min</dd></div>
</dl>
@@ -54,9 +59,8 @@
<article class="glass-panel detail-card">
<p class="eyebrow">Details</p>
<h3>Archivansicht</h3>
<p>Waehle links einen Tag aus, um alle Werte und die Tagebuchnotiz anzuzeigen.</p>
<p>Wähle links einen Tag aus, um alle Werte anzuzeigen oder direkt in die Bearbeitung zu springen.</p>
</article>
<?php endif; ?>
</aside>
</section>
+6 -7
View File
@@ -1,8 +1,8 @@
<section class="hero-grid">
<article class="hero-card hero-card--wide glass-panel">
<p class="eyebrow">Stimmung im Blick</p>
<h3>Dein Dashboard verbindet Verlauf, Score und Bewegungsdaten in einer schnellen Uebersicht.</h3>
<p class="hero-copy">Die Bewertung basiert auf deiner konfigurierbaren Logik. Sobald du die Regeln in den Optionen aenderst, spiegeln Archiv und Statistiken die neue Gewichtung wider.</p>
<h3>Dein Dashboard verbindet Verlauf, Score und Bewegungsdaten in einer schnellen Übersicht.</h3>
<p class="hero-copy">Die Bewertung basiert auf deiner konfigurierbaren Logik. Sobald du die Regeln in den Optionen änderst, spiegeln Archiv und Statistiken die neue Gewichtung wider.</p>
</article>
<article class="hero-card glass-panel">
@@ -12,7 +12,7 @@
<p class="hero-label"><?= e($summary['today']['evaluation']['label']) ?></p>
<?php else: ?>
<div class="hero-score">-</div>
<p class="hero-label">Noch kein Eintrag fuer heute</p>
<p class="hero-label">Noch kein Eintrag für heute</p>
<?php endif; ?>
</article>
</section>
@@ -57,7 +57,7 @@
<p class="eyebrow">Trend</p>
<h3>Tagesstimmung</h3>
</div>
<span class="chart-chip">letzte 30 Eintraege</span>
<span class="chart-chip">letzte 30 Einträge</span>
</div>
<div class="line-chart" data-chart-type="line" data-series="mood" data-payload="<?= e($chartPayload) ?>"></div>
</article>
@@ -68,7 +68,7 @@
<p class="eyebrow">Belastung</p>
<h3>Stressverlauf</h3>
</div>
<span class="chart-chip chart-chip--warm">letzte 30 Eintraege</span>
<span class="chart-chip chart-chip--warm">letzte 30 Einträge</span>
</div>
<div class="line-chart" data-chart-type="line" data-series="stress" data-payload="<?= e($chartPayload) ?>"></div>
</article>
@@ -76,7 +76,7 @@
<article class="glass-panel chart-card chart-card--wide">
<div class="section-head">
<div>
<p class="eyebrow">Aktivitaet</p>
<p class="eyebrow">Aktivität</p>
<h3>Sport und Spaziergang</h3>
</div>
<span class="chart-chip chart-chip--cool">Minuten pro Tag</span>
@@ -84,4 +84,3 @@
<div class="bar-chart" data-chart-type="bars" data-series="sport" data-payload="<?= e($chartPayload) ?>"></div>
</article>
</section>
+2 -3
View File
@@ -1,8 +1,8 @@
<section class="auth-shell">
<div class="auth-card glass-panel">
<p class="eyebrow">Geschuetzt und dateibasiert</p>
<p class="eyebrow">Geschützt und dateibasiert</p>
<h1>Einloggen</h1>
<p class="auth-copy">Die Eintraege liegen als Markdown-TXT-Dateien im geschuetzten Speicher und sind nur nach Login sichtbar.</p>
<p class="auth-copy">Die Einträge liegen als Markdown-TXT-Dateien im geschützten Speicher und sind nur nach Login sichtbar.</p>
<form method="post" action="/login" class="stack-form">
<?= csrf_field() ?>
@@ -20,4 +20,3 @@
</form>
</div>
</section>
-1
View File
@@ -6,4 +6,3 @@
<a class="primary-button button-link" href="/">Zur Startseite</a>
</div>
</section>
+4 -5
View File
@@ -18,7 +18,7 @@
<label><span>Stimmung</span><input type="number" name="settings[scoring][mood_multiplier]" value="<?= e((string) $settings['scoring']['mood_multiplier']) ?>" min="0" max="10"></label>
<label><span>Energie</span><input type="number" name="settings[scoring][energy_multiplier]" value="<?= e((string) $settings['scoring']['energy_multiplier']) ?>" min="0" max="10"></label>
<label><span>Stress</span><input type="number" name="settings[scoring][stress_multiplier]" value="<?= e((string) $settings['scoring']['stress_multiplier']) ?>" min="0" max="10"></label>
<label><span>Schlafgefuehl</span><input type="number" name="settings[scoring][sleep_feeling_multiplier]" value="<?= e((string) $settings['scoring']['sleep_feeling_multiplier']) ?>" min="0" max="10"></label>
<label><span>Schlafgefühl</span><input type="number" name="settings[scoring][sleep_feeling_multiplier]" value="<?= e((string) $settings['scoring']['sleep_feeling_multiplier']) ?>" min="0" max="10"></label>
</div>
</div>
@@ -35,7 +35,7 @@
</div>
<div class="settings-section">
<h4>Sport-Baender</h4>
<h4>Sport-Bänder</h4>
<div class="band-grid">
<?php foreach ($settings['scoring']['sport_bands'] as $index => $band): ?>
<div class="band-card">
@@ -48,7 +48,7 @@
</div>
<div class="settings-section">
<h4>Spaziergang-Baender</h4>
<h4>Spaziergang-Bänder</h4>
<div class="band-grid">
<?php foreach ($settings['scoring']['walk_bands'] as $index => $band): ?>
<div class="band-card">
@@ -98,7 +98,7 @@
<aside class="stack-column">
<article class="glass-panel detail-card">
<p class="eyebrow">Sicherheit</p>
<h3>Passwort aendern</h3>
<h3>Passwort ändern</h3>
<form method="post" action="/options" class="stack-form">
<?= csrf_field() ?>
<input type="hidden" name="form_name" value="password">
@@ -136,4 +136,3 @@
<?php endif; ?>
</aside>
</section>
+2 -3
View File
@@ -2,7 +2,7 @@
<div class="auth-card glass-panel">
<p class="eyebrow">Erste Einrichtung</p>
<h1>Mood initialisieren</h1>
<p class="auth-copy">Lege den ersten Admin-Account an. Danach ist die Anwendung sofort geschuetzt und weitere Accounts koennen spaeter in den Optionen erstellt werden.</p>
<p class="auth-copy">Lege den ersten Admin-Account an. Danach ist die Anwendung sofort geschützt und weitere Accounts können später in den Optionen erstellt werden.</p>
<form method="post" action="/setup" class="stack-form">
<?= csrf_field() ?>
@@ -21,8 +21,7 @@
<input type="password" name="password_confirm" autocomplete="new-password" minlength="10" required>
</label>
<button class="primary-button" type="submit">Setup abschliessen</button>
<button class="primary-button" type="submit">Setup abschließen</button>
</form>
</div>
</section>
+33 -23
View File
@@ -3,20 +3,17 @@
<div class="section-head">
<div>
<p class="eyebrow">Tag erfassen</p>
<h3>Eintrag fuer <?= e($entry['date']) ?></h3>
<h3><?= e(format_display_date($entry['date'])) ?></h3>
</div>
<div class="section-head__actions">
<a class="ghost-link" href="/archive?date=<?= e(rawurlencode($entry['date'])) ?>">Im Archiv ansehen</a>
<a class="ghost-link" href="/track?date=<?= e(today()) ?>">Heute</a>
</div>
<a class="ghost-link" href="/archive?date=<?= e(rawurlencode($entry['date'])) ?>">Im Archiv ansehen</a>
</div>
<form method="post" action="/track" class="tracker-form" id="tracker-form">
<?= csrf_field() ?>
<div class="field-grid field-grid--single">
<label>
<span>Datum</span>
<input type="date" name="date" value="<?= e($entry['date']) ?>" required>
</label>
</div>
<input type="hidden" name="date" value="<?= e($entry['date']) ?>">
<div class="field-grid field-grid--three">
<label class="range-card">
@@ -45,7 +42,7 @@
</label>
<label>
<span>Schlafgefuehl</span>
<span>Schlafgefühl</span>
<select name="sleep_feeling">
<?php foreach ($settings['labels']['sleep_feeling'] as $value => $label): ?>
<option value="<?= e((string) $value) ?>" <?= (int) $entry['sleep_feeling'] === (int) $value ? 'selected' : '' ?>>
@@ -70,36 +67,49 @@
<label>
<span>Tagebuchnotiz</span>
<textarea name="note" rows="8" placeholder="Was war heute wichtig, schwer oder schoen?"><?= e($entry['note']) ?></textarea>
<textarea name="note" rows="8" placeholder="Was war heute wichtig, schwer oder schön?"><?= e($entry['note']) ?></textarea>
</label>
<div class="form-actions">
<a class="ghost-link" href="/track?date=<?= e(today()) ?>">Heute laden</a>
<span class="helper-text">Werte ändern, speichern und bei Bedarf vergangene Tage bequem nachtragen.</span>
<button class="primary-button" type="submit">Tag speichern</button>
</div>
</form>
</article>
<aside class="stack-column">
<article class="glass-panel preview-card" id="live-score-card" data-payload="<?= e($trackPayload) ?>">
<article class="glass-panel preview-card" id="live-score-card" data-payload="<?= e($trackPayload) ?>" data-sentiment="<?= e($evaluation['sentiment']) ?>">
<p class="eyebrow">Live-Bewertung</p>
<div class="hero-score" data-preview-total><?= e(format_points((float) $evaluation['total'])) ?></div>
<p class="hero-label" data-preview-label><?= e($evaluation['label']) ?></p>
<p class="helper-text">Die Einschaetzung passt sich beim Aendern der Werte sofort an.</p>
<div class="preview-status">
<div class="preview-status__icon">
<img data-preview-icon src="<?= e(mood_icon_path($evaluation['sentiment'])) ?>" alt="">
</div>
<div>
<p class="preview-status__label" data-preview-label><?= e($evaluation['label']) ?></p>
<p class="helper-text">Die Einschätzung passt sich beim Ändern der Werte sofort an.</p>
</div>
</div>
<p class="preview-scoreline"><span data-preview-total><?= e(format_points((float) $evaluation['total'])) ?></span> Punkte</p>
<dl class="component-list" data-preview-components>
<?php
$componentLabels = [
'mood' => 'Stimmung',
'energy' => 'Energie',
'stress' => 'Stress',
'sleep_hours' => 'Schlafdauer',
'sleep_feeling' => 'Schlafgefühl',
'sport_minutes' => 'Sport',
'walk_minutes' => 'Spaziergang',
'note' => 'Notiz',
];
?>
<?php foreach ($evaluation['components'] as $name => $value): ?>
<div>
<dt><?= e($name) ?></dt>
<dt><?= e($componentLabels[$name] ?? $name) ?></dt>
<dd><?= e(format_points((float) $value)) ?></dd>
</div>
<?php endforeach; ?>
</dl>
</article>
<article class="glass-panel info-card">
<p class="eyebrow">Dateiformat</p>
<h3>Markdown in `YYYY-MM-DD.txt`</h3>
<p>Jeder Tag wird als menschenlesbare Datei gespeichert. Das macht Backups, Portabilitaet und manuelle Kontrolle einfach.</p>
</article>
</aside>
</section>