Files

337 lines
35 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<section class="options-shell">
<div class="options-overlay" data-options-overlay data-options-standalone="1" data-open-panel="<?= e((string) ($optionsOpenPanel ?? '')) ?>">
<div class="options-overlay__backdrop" data-options-close></div>
<section class="options-modal glass-panel" role="dialog" aria-modal="true">
<div class="options-modal__controls">
<button class="dashboard-modal__round" type="button" data-options-back></button>
<button class="dashboard-modal__round" type="button" data-options-close>×</button>
</div>
<div class="options-menu-panel" data-options-menu>
<div class="section-head">
<div>
<p class="eyebrow">Optionen</p>
<h3>Einstellungen und Bereiche</h3>
</div>
</div>
<div class="options-menu-grid">
<button class="options-menu-card" type="button" data-options-open="sports"><strong>Sportarten anpassen</strong><span>Eigene Sportarten und Bonuspunkte</span></button>
<button class="options-menu-card" type="button" data-options-open="walk"><strong>Spaziergang anpassen</strong><span>Zeit oder Schritte auswerten</span></button>
<button class="options-menu-card" type="button" data-options-open="sleep"><strong>Schlaf anpassen</strong><span>Optimale Schlafmenge markieren</span></button>
<button class="options-menu-card" type="button" data-options-open="reminders"><strong>Erinnerungen setzen</strong><span>Push und tägliche Erinnerung</span></button>
<button class="options-menu-card" type="button" data-options-open="health"><strong>Health Import</strong><span>Apple Health automatisch übernehmen</span></button>
<button class="options-menu-card" type="button" data-options-open="ratings"><strong>Bewertungsskala ändern</strong><span>Labels und Schutzregeln</span></button>
<button class="options-menu-card" type="button" data-options-open="stats"><strong>Statistik</strong><span>Verlauf und Aktivität</span></button>
<?php if (!empty($authUser['is_admin'])): ?>
<button class="options-menu-card" type="button" data-options-open="users"><strong>Neue Nutzer anlegen</strong><span>Accounts und Adminrechte</span></button>
<?php endif; ?>
<button class="options-menu-card" type="button" data-options-open="security"><strong>Sicherheit</strong><span>Passwort und Backup</span></button>
<?php if (!empty($authUser['is_admin'])): ?>
<button class="options-menu-card" type="button" data-options-open="ai"><strong>KI</strong><span>OpenAI und Zusammenfassungen</span></button>
<?php endif; ?>
<form method="post" action="/logout" class="options-logout-form">
<?= csrf_field() ?>
<button class="options-menu-card options-menu-card--danger" type="submit"><strong>Abmelden</strong><span>Sitzung sicher beenden</span></button>
</form>
</div>
</div>
<div class="options-panel" data-options-panel="sports" hidden>
<h2>Sportarten anpassen</h2>
<form method="post" action="/options" class="stack-form stack-form--spacious">
<?= csrf_field() ?>
<input type="hidden" name="form_name" value="settings">
<div class="settings-section">
<div class="section-head section-head--compact">
<div>
<h4>Sportarten und Bonuspunkte</h4>
<p class="helper-text">Diese Sportarten stehen in deinen Momenten zur Auswahl.</p>
</div>
<button class="ghost-button" type="button" data-add-sport-type>Sportart hinzufügen</button>
</div>
<input type="hidden" name="settings[sport_types_present]" value="1">
<?php if (!empty($sportTypePresets)): ?>
<div class="preset-list">
<?php foreach ($sportTypePresets as $preset): ?>
<button class="preset-pill" type="button" data-sport-preset data-id="<?= e($preset['id']) ?>" data-label="<?= e($preset['label']) ?>" data-icon="<?= e($preset['icon']) ?>" data-location="<?= e($preset['location'] ?? '') ?>" data-recovery-group="<?= e($preset['recovery_group']) ?>" data-bonus-points="<?= e((string) $preset['bonus_points']) ?>" data-allow-consecutive="<?= !empty($preset['allow_consecutive']) ? '1' : '0' ?>">
<img src="<?= e(sport_icon_path($preset['icon'])) ?>" alt=""><span><?= e($preset['label']) ?></span>
</button>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div class="sport-type-list" data-sport-type-list>
<?php foreach ($settings['sport_types'] as $index => $sportType): ?>
<div class="sport-type-card band-card" data-sport-type-row>
<input type="hidden" name="settings[sport_types][<?= e((string) $index) ?>][id]" value="<?= e($sportType['id']) ?>" data-name-template="settings[sport_types][__INDEX__][id]">
<div class="field-grid field-grid--four">
<label><span>Bezeichnung</span><input type="text" name="settings[sport_types][<?= e((string) $index) ?>][label]" value="<?= e($sportType['label']) ?>" data-name-template="settings[sport_types][__INDEX__][label]"></label>
<label><span>Icon</span><select name="settings[sport_types][<?= e((string) $index) ?>][icon]" data-name-template="settings[sport_types][__INDEX__][icon]"><?php foreach (sport_icon_options() as $iconValue => $iconLabel): ?><option value="<?= e($iconValue) ?>" <?= $sportType['icon'] === $iconValue ? 'selected' : '' ?>><?= e($iconLabel) ?></option><?php endforeach; ?></select></label>
<label><span>Ort</span><select name="settings[sport_types][<?= e((string) $index) ?>][location]" data-name-template="settings[sport_types][__INDEX__][location]"><?php foreach ($sportLocationOptions as $locationValue => $locationLabel): ?><option value="<?= e($locationValue) ?>" <?= ($sportType['location'] ?? '') === $locationValue ? 'selected' : '' ?>><?= e($locationLabel) ?></option><?php endforeach; ?></select></label>
<label><span>Erholungsgruppe</span><input type="text" name="settings[sport_types][<?= e((string) $index) ?>][recovery_group]" value="<?= e($sportType['recovery_group']) ?>" data-name-template="settings[sport_types][__INDEX__][recovery_group]"></label>
</div>
<div class="field-grid field-grid--four"><label><span>Bonuspunkte</span><input type="number" name="settings[sport_types][<?= e((string) $index) ?>][bonus_points]" value="<?= e((string) $sportType['bonus_points']) ?>" min="0" max="20" data-name-template="settings[sport_types][__INDEX__][bonus_points]"></label></div>
<label class="checkbox-row"><input type="checkbox" name="settings[sport_types][<?= e((string) $index) ?>][allow_consecutive]" value="1" <?= !empty($sportType['allow_consecutive']) ? 'checked' : '' ?> data-name-template="settings[sport_types][__INDEX__][allow_consecutive]"><span>Darf an Folgetagen Bonus geben</span></label>
<div class="sport-type-card__actions"><button class="ghost-button ghost-button--small" type="button" data-remove-sport-type>Entfernen</button></div>
</div>
<?php endforeach; ?>
</div>
<template id="sport-type-row-template">
<div class="sport-type-card band-card" data-sport-type-row>
<input type="hidden" value="" data-name-template="settings[sport_types][__INDEX__][id]">
<div class="field-grid field-grid--four">
<label><span>Bezeichnung</span><input type="text" value="" data-name-template="settings[sport_types][__INDEX__][label]"></label>
<label><span>Icon</span><select data-name-template="settings[sport_types][__INDEX__][icon]"><?php foreach (sport_icon_options() as $iconValue => $iconLabel): ?><option value="<?= e($iconValue) ?>"><?= e($iconLabel) ?></option><?php endforeach; ?></select></label>
<label><span>Ort</span><select data-name-template="settings[sport_types][__INDEX__][location]"><?php foreach ($sportLocationOptions as $locationValue => $locationLabel): ?><option value="<?= e($locationValue) ?>"><?= e($locationLabel) ?></option><?php endforeach; ?></select></label>
<label><span>Erholungsgruppe</span><input type="text" value="" data-name-template="settings[sport_types][__INDEX__][recovery_group]"></label>
</div>
<div class="field-grid field-grid--four"><label><span>Bonuspunkte</span><input type="number" value="2" min="0" max="20" data-name-template="settings[sport_types][__INDEX__][bonus_points]"></label></div>
<label class="checkbox-row"><input type="checkbox" value="1" data-name-template="settings[sport_types][__INDEX__][allow_consecutive]"><span>Darf an Folgetagen Bonus geben</span></label>
<div class="sport-type-card__actions"><button class="ghost-button ghost-button--small" type="button" data-remove-sport-type>Entfernen</button></div>
</div>
</template>
</div>
<button class="primary-button" type="submit">Sportarten speichern</button>
</form>
</div>
<div class="options-panel" data-options-panel="walk" hidden>
<h2>Spaziergang und Schritte</h2>
<form method="post" action="/options" class="stack-form">
<?= csrf_field() ?>
<input type="hidden" name="form_name" value="settings">
<p class="helper-text">Spaziergänge werden als Momente angezeigt. Punkte kommen nicht mehr aus einzelnen Spaziergängen, sondern aus der täglichen Gesamtschrittzahl.</p>
<label><span>Spaziergang anzeigen nach</span><select name="settings[walk][mode]"><?php foreach ($walkModeOptions as $modeValue => $modeLabel): ?><option value="<?= e($modeValue) ?>" <?= ($settings['walk']['mode'] ?? 'time') === $modeValue ? 'selected' : '' ?>><?= e($modeLabel) ?></option><?php endforeach; ?></select></label>
<div class="settings-section">
<h4>Schritte-Bonus</h4>
<div class="field-grid field-grid--three">
<label><span>Mehr als</span><input type="number" name="settings[scoring][step_bonus][min]" value="<?= e((string) ($settings['scoring']['step_bonus']['min'] ?? 10000)) ?>" min="0" max="100000"></label>
<label><span>Bis einschließlich</span><input type="number" name="settings[scoring][step_bonus][max]" value="<?= e((string) ($settings['scoring']['step_bonus']['max'] ?? 15000)) ?>" min="0" max="100000"></label>
<label><span>Bonuspunkte</span><input type="number" name="settings[scoring][step_bonus][points]" value="<?= e((string) ($settings['scoring']['step_bonus']['points'] ?? 1)) ?>" min="0" max="20"></label>
</div>
</div>
<div class="settings-section">
<h4>Schritte-Zielkurve</h4>
<p class="helper-text">Diese Punkte fließen als kleiner Bonus oder Malus in die Tagesbilanz ein. Positive Werte motivieren, negative Werte markieren zu wenig oder zu viel Bewegung.</p>
<div class="band-grid">
<?php foreach (($settings['scoring']['walk_step_targets'] ?? []) as $index => $target): ?>
<div class="band-card">
<label><span>Schritte</span><input type="number" name="settings[scoring][walk_step_targets][<?= e((string) $index) ?>][steps]" value="<?= e((string) ($target['steps'] ?? 0)) ?>" min="0" max="100000"></label>
<label><span>Punkte</span><input type="number" name="settings[scoring][walk_step_targets][<?= e((string) $index) ?>][points]" value="<?= e((string) ($target['points'] ?? 0)) ?>" min="-20" max="20"></label>
</div>
<?php endforeach; ?>
</div>
</div>
<button class="primary-button" type="submit">Schritte speichern</button>
</form>
</div>
<div class="options-panel" data-options-panel="sleep" hidden>
<h2>Schlaf anpassen</h2>
<form method="post" action="/options" class="stack-form">
<?= csrf_field() ?>
<input type="hidden" name="form_name" value="settings">
<p class="helper-text">Diese Zielmenge wird im importierten Schlafbalken als horizontale Markierung angezeigt und fließt in die automatische Stimmung/Energie/Stress-Einschätzung ein.</p>
<label><span>Optimale Schlafdauer</span><input type="number" name="settings[sleep][optimal_hours]" value="<?= e((string) ($settings['sleep']['optimal_hours'] ?? 7.0)) ?>" min="1" max="16" step="0.1"></label>
<button class="primary-button" type="submit">Schlaf speichern</button>
</form>
</div>
<div class="options-panel" data-options-panel="reminders" hidden>
<h2>Erinnerungen setzen</h2>
<form method="post" action="/options" class="stack-form">
<?= csrf_field() ?>
<input type="hidden" name="form_name" value="settings">
<div class="field-grid field-grid--two">
<label class="checkbox-row checkbox-row--panel"><input type="checkbox" name="settings[notifications][enabled]" value="1" <?= !empty($settings['notifications']['enabled']) ? 'checked' : '' ?>><span>Tägliche Push-Erinnerung aktivieren</span></label>
<label><span>Uhrzeit der Erinnerung</span><input type="time" name="settings[notifications][time]" value="<?= e((string) ($settings['notifications']['time'] ?? '20:30')) ?>"></label>
</div>
<div class="push-panel band-card" data-push-panel data-push-ready="<?= !empty($pushAvailable) && !empty($pushPublicKey) ? '1' : '0' ?>">
<div><h5>Push auf diesem Gerät</h5><p class="helper-text" data-push-status><?php if (!empty($pushAvailable) && !empty($pushPublicKey)): ?>Installiere die App auf Wunsch als PWA und aktiviere dann Push direkt auf diesem Gerät.<?php else: ?>Push ist auf diesem Server gerade noch nicht verfügbar.<?php endif; ?></p></div>
<div class="push-actions"><button class="ghost-button" type="button" data-push-enable <?= empty($pushAvailable) || empty($pushPublicKey) ? 'disabled' : '' ?>>Push aktivieren</button><button class="ghost-button" type="button" data-push-disable <?= empty($pushAvailable) || empty($pushPublicKey) ? 'disabled' : '' ?>>Auf diesem Gerät entfernen</button><button class="ghost-button" type="button" data-push-test <?= empty($pushAvailable) || empty($pushPublicKey) ? 'disabled' : '' ?>>Test senden</button></div>
</div>
<button class="primary-button" type="submit">Erinnerungen speichern</button>
</form>
</div>
<div class="options-panel" data-options-panel="health" hidden>
<h2>Health Import</h2>
<article class="detail-card detail-card--overlay">
<p class="eyebrow">REST-Endpunkt</p>
<div class="stack-form">
<label><span>URL in Health Auto Export</span><input type="text" value="<?= e((string) $healthImportUrl) ?>" readonly></label>
<label><span>HTTP-Header</span><input type="text" value="Authorization: Bearer <?= !empty($healthImportConfig['token_prefix']) ? e((string) $healthImportConfig['token_prefix']) . '…' : 'TOKEN' ?>" readonly></label>
</div>
<p class="helper-text">Lege in Health Auto Export zwei REST-API-Automationen an: Gesundheitsmetriken mit <code>step_count</code> und <code>sleep_analysis</code>, sowie Workouts mit JSON Version 2 und Routendaten.</p>
</article>
<article class="detail-card detail-card--overlay">
<p class="eyebrow">Putzliga</p>
<div class="stack-form">
<label><span>URL in Putzliga</span><input type="text" value="<?= e((string) $putzligaImportUrl) ?>" readonly></label>
<label><span>HTTP-Header</span><input type="text" value="Authorization: Bearer <?= !empty($putzligaImportConfig['token_prefix']) ? e((string) $putzligaImportConfig['token_prefix']) . '…' : 'TOKEN' ?>" readonly></label>
</div>
<p class="helper-text">Putzliga erstellt ab 3 erledigten Aufgaben pro Tag einen Moment mit den erledigten Aufgaben als Chips und aktualisiert ihn, wenn weitere Aufgaben dazukommen.</p>
<?php if (!empty($putzligaImportToken)): ?>
<label><span>Neuer Token, nur jetzt sichtbar</span><input type="text" value="<?= e((string) $putzligaImportToken) ?>" readonly></label>
<?php endif; ?>
<div class="user-list">
<div class="user-row"><strong>Token</strong><span><?= !empty($putzligaImportConfig['enabled']) ? 'aktiv' : 'nicht eingerichtet' ?></span></div>
<div class="user-row"><strong>Letzter Sync</strong><span><?= !empty($putzligaImportConfig['last_import_at']) ? e(format_display_datetime((string) $putzligaImportConfig['last_import_at'])) : '-' ?></span></div>
<div class="user-row"><strong>Statusmeldung</strong><span><?= !empty($putzligaImportConfig['last_message']) ? e((string) $putzligaImportConfig['last_message']) : '-' ?></span></div>
</div>
<form method="post" action="/options" class="stack-form">
<?= csrf_field() ?>
<input type="hidden" name="form_name" value="putzliga_import_token">
<button class="primary-button" type="submit"><?= !empty($putzligaImportConfig['enabled']) ? 'Putzliga-Token neu erstellen' : 'Putzliga-Token erstellen' ?></button>
</form>
<?php if (!empty($putzligaImportConfig['enabled'])): ?>
<form method="post" action="/options" class="stack-form">
<?= csrf_field() ?>
<input type="hidden" name="form_name" value="putzliga_import_revoke">
<button class="ghost-button" type="submit">Putzliga-Token deaktivieren</button>
</form>
<?php endif; ?>
</article>
<?php if (!empty($healthImportToken)): ?>
<article class="detail-card detail-card--overlay health-token-card">
<p class="eyebrow">Neuer Token</p>
<label><span>Nur jetzt sichtbar</span><input type="text" value="<?= e((string) $healthImportToken) ?>" readonly></label>
<p class="helper-text">Kopiere diesen Token als Bearer-Token in Health Auto Export. Danach wird nur noch der Anfang angezeigt.</p>
</article>
<?php endif; ?>
<article class="detail-card detail-card--overlay" data-health-import-status>
<p class="eyebrow">Status</p>
<div class="health-import-progress" data-health-progress-wrap data-progress-done="<?= e((string) ($healthImportConfig['progress_done'] ?? 0)) ?>" data-progress-total="<?= e((string) ($healthImportConfig['progress_total'] ?? 0)) ?>">
<progress class="health-import-progress__bar" data-health-progress-bar max="100" value="0">0%</progress>
<p class="helper-text" data-health-progress-text>
<?php if (($healthImportConfig['last_status'] ?? '') === 'running'): ?>
Import läuft: <?= e((string) ($healthImportConfig['progress_done'] ?? 0)) ?> von <?= e((string) ($healthImportConfig['progress_total'] ?? 0)) ?> verarbeitet.
<?php elseif (!empty($healthImportConfig['last_message'])): ?>
<?= e((string) $healthImportConfig['last_message']) ?>
<?php else: ?>
Noch kein Import gelaufen.
<?php endif; ?>
</p>
</div>
<div class="user-list">
<div class="user-row"><strong>Token</strong><span data-health-token-state><?= !empty($healthImportConfig['enabled']) ? 'aktiv' : 'nicht eingerichtet' ?></span></div>
<?php if (!empty($healthImportConfig['last_import_at'])): ?>
<div class="user-row"><strong>Letzter Import</strong><span data-health-last-import><?= e(format_display_datetime((string) $healthImportConfig['last_import_at'])) ?></span></div>
<?php else: ?>
<div class="user-row"><strong>Letzter Import</strong><span data-health-last-import>-</span></div>
<?php endif; ?>
<div class="user-row"><strong>Statusmeldung</strong><span data-health-last-message><?= !empty($healthImportConfig['last_message']) ? e((string) $healthImportConfig['last_message']) : '-' ?></span></div>
</div>
<form method="post" action="/options" class="stack-form">
<?= csrf_field() ?>
<input type="hidden" name="form_name" value="health_import_token">
<button class="primary-button" type="submit"><?= !empty($healthImportConfig['enabled']) ? 'Token neu erstellen' : 'Token erstellen' ?></button>
</form>
<?php if (!empty($healthImportConfig['enabled'])): ?>
<form method="post" action="/options" class="stack-form">
<?= csrf_field() ?>
<input type="hidden" name="form_name" value="health_import_revoke">
<button class="ghost-button" type="submit">Token deaktivieren</button>
</form>
<?php endif; ?>
</article>
</div>
<div class="options-panel" data-options-panel="ratings" hidden>
<h2>Bewertungsskala ändern</h2>
<form method="post" action="/options" class="stack-form stack-form--spacious">
<?= csrf_field() ?>
<input type="hidden" name="form_name" value="settings">
<div class="settings-section">
<h4>Tagesbilanz als Hauptmetrik</h4>
<p class="helper-text">Stimmung, Energie und Stress bilden die Basis. Schlaf, Schritte, Sport, Spaziergang und Notizen verschieben den Tag nur gedeckelt in eine positivere oder negativere Richtung.</p>
<div class="field-grid field-grid--three">
<label><span>Gewicht Stimmung</span><input type="number" name="settings[day_balance][mood_weight]" value="<?= e((string) ($settings['day_balance']['mood_weight'] ?? 3)) ?>" min="0" max="10"></label>
<label><span>Gewicht Energie</span><input type="number" name="settings[day_balance][energy_weight]" value="<?= e((string) ($settings['day_balance']['energy_weight'] ?? 2)) ?>" min="0" max="10"></label>
<label><span>Gewicht Stress</span><input type="number" name="settings[day_balance][stress_weight]" value="<?= e((string) ($settings['day_balance']['stress_weight'] ?? 2)) ?>" min="0" max="10"></label>
</div>
<div class="field-grid field-grid--two">
<label><span>Max. Bonus/Malus in Stufen</span><input type="number" name="settings[day_balance][adjustment_cap]" value="<?= e((string) ($settings['day_balance']['adjustment_cap'] ?? 1.0)) ?>" min="0" max="2" step="0.1"></label>
<label><span>Punkte pro Stufenverschiebung</span><input type="number" name="settings[day_balance][points_per_step]" value="<?= e((string) ($settings['day_balance']['points_per_step'] ?? 12)) ?>" min="1" max="50"></label>
</div>
</div>
<div class="settings-section"><h4>Bewertungsskala</h4><div class="band-grid"><?php foreach ($settings['ratings'] as $index => $rating): ?><div class="band-card"><label><span>Label</span><input type="text" name="settings[ratings][<?= e((string) $index) ?>][label]" value="<?= e($rating['label']) ?>"></label><label><span>Min</span><input type="number" name="settings[ratings][<?= e((string) $index) ?>][min]" value="<?= e((string) $rating['min']) ?>"></label><label><span>Max</span><input type="number" name="settings[ratings][<?= e((string) $index) ?>][max]" value="<?= e((string) $rating['max']) ?>"></label></div><?php endforeach; ?></div></div>
<div class="settings-section"><h4>Schutzregeln</h4><div class="band-grid"><?php foreach ($settings['guardrails'] as $index => $guardrail): ?><div class="band-card"><label><span>Stimmung max</span><input type="number" name="settings[guardrails][<?= e((string) $index) ?>][mood_max]" value="<?= e((string) $guardrail['mood_max']) ?>" min="1" max="10"></label><label><span>Energie max</span><input type="number" name="settings[guardrails][<?= e((string) $index) ?>][energy_max]" value="<?= e((string) ($guardrail['energy_max'] ?? '')) ?>" min="1" max="10"></label><label><span>Maximales Label</span><input type="text" name="settings[guardrails][<?= e((string) $index) ?>][cap_label]" value="<?= e($guardrail['cap_label']) ?>"></label></div><?php endforeach; ?></div></div>
<label><span>Tagebuchpunkte bei nicht-leerer Notiz</span><input type="number" name="settings[scoring][journal_points]" value="<?= e((string) $settings['scoring']['journal_points']) ?>" min="0" max="20"></label>
<button class="primary-button" type="submit">Bewertung speichern</button>
</form>
</div>
<div class="options-panel" data-options-panel="stats" hidden>
<h2>Statistik</h2>
<form method="post" action="/options" class="stack-form stack-form--spacious stats-settings-form">
<?= csrf_field() ?>
<input type="hidden" name="form_name" value="settings">
<div class="settings-section">
<h4>Statistik-Darstellung</h4>
<label><span>Hauptwert anzeigen als</span><select name="settings[display][score_mode]">
<?php foreach (['scale' => '5-Stufen-Bilanz', 'percent' => 'Prozentwert', 'points' => 'Punkte'] as $mode => $label): ?>
<option value="<?= e($mode) ?>" <?= ($settings['display']['score_mode'] ?? 'scale') === $mode ? 'selected' : '' ?>><?= e($label) ?></option>
<?php endforeach; ?>
</select></label>
</div>
<button class="primary-button" type="submit">Statistik speichern</button>
</form>
<section class="stats-grid">
<article class="metric-card glass-panel"><span>Getrackte Tage</span><strong><?= e((string) $statsSummary['tracked_days']) ?></strong></article>
<article class="metric-card glass-panel"><span>Ø Score</span><strong><?= e(format_points((float) $statsSummary['average_score'])) ?></strong></article>
<article class="metric-card glass-panel"><span>Ø Bilanz</span><strong><?= ((float) $statsSummary['average_balance']) > 0 ? '+' : '' ?><?= e(format_points((float) $statsSummary['average_balance'])) ?></strong></article>
<article class="metric-card glass-panel"><span>Ø Stimmung</span><strong><?= e(format_points((float) $statsSummary['average_mood'])) ?>/10</strong></article>
<article class="metric-card glass-panel"><span>Ø Stress</span><strong><?= e(format_points((float) $statsSummary['average_stress'])) ?>/10</strong></article>
<article class="metric-card glass-panel"><span>Serie</span><strong><?= e((string) $statsSummary['streak']) ?> Tage</strong></article>
</section>
<section class="dashboard-grid dashboard-grid--embedded-stats">
<article class="glass-panel chart-card chart-card--calendar"><div class="section-head"><div><p class="eyebrow">Kalender</p><h3>Gesamtstimmung pro Tag</h3></div></div><div id="calendar-heatmap" class="calendar-heatmap" data-payload="<?= e($statsChartPayload) ?>"></div></article>
<article class="glass-panel chart-card"><div class="section-head"><div><p class="eyebrow">Trend</p><h3>Errechnete Tagesbilanz</h3></div></div><div class="line-chart" data-chart-type="line" data-series="balance" data-payload="<?= e($statsChartPayload) ?>"></div></article>
<article class="glass-panel chart-card"><div class="section-head"><div><p class="eyebrow">Belastung</p><h3>Stressverlauf</h3></div></div><div class="line-chart" data-chart-type="line" data-series="stress" data-payload="<?= e($statsChartPayload) ?>"></div></article>
<article class="glass-panel chart-card chart-card--wide"><div class="section-head"><div><p class="eyebrow">Aktivität</p><h3>Sport und Spaziergang</h3></div></div><div class="bar-chart" data-chart-type="bars" data-series="sport" data-payload="<?= e($statsChartPayload) ?>"></div></article>
</section>
</div>
<?php if (!empty($authUser['is_admin'])): ?>
<div class="options-panel" data-options-panel="users" hidden>
<h2>Neue Nutzer anlegen</h2>
<form method="post" action="/options" class="stack-form">
<?= csrf_field() ?>
<input type="hidden" name="form_name" value="create_user">
<label><span>Benutzername</span><input type="text" name="username" required></label>
<label><span>Startpasswort</span><input type="password" name="password" minlength="10" required></label>
<label class="checkbox-row"><input type="checkbox" name="is_admin" value="1"><span>Als Admin anlegen</span></label>
<button class="primary-button" type="submit">Account erstellen</button>
</form>
<?php if ($users !== []): ?><div class="user-list"><?php foreach ($users as $account): ?><div class="user-row"><strong><?= e($account['username']) ?></strong><span><?= !empty($account['is_admin']) ? 'Admin' : 'Nutzer' ?></span></div><?php endforeach; ?></div><?php endif; ?>
</div>
<?php endif; ?>
<div class="options-panel" data-options-panel="security" hidden>
<h2>Sicherheit</h2>
<article class="detail-card detail-card--overlay">
<p class="eyebrow">Backup</p>
<form method="post" action="/options" class="stack-form"><?= csrf_field() ?><input type="hidden" name="form_name" value="export_backup"><button class="primary-button" type="submit" <?= empty($backupAvailable) ? 'disabled' : '' ?>>Backup herunterladen</button></form>
<form method="post" action="/options" enctype="multipart/form-data" class="stack-form"><?= csrf_field() ?><input type="hidden" name="form_name" value="import_backup"><label><span>Backup importieren</span><input type="file" name="backup_files[]" accept=".zip,.txt" multiple></label><button class="ghost-button" type="submit">Backup importieren</button></form>
</article>
<article class="detail-card detail-card--overlay">
<p class="eyebrow">Passwort</p>
<form method="post" action="/options" class="stack-form"><?= csrf_field() ?><input type="hidden" name="form_name" value="password"><label><span>Aktuelles Passwort</span><input type="password" name="current_password" required></label><label><span>Neues Passwort</span><input type="password" name="new_password" minlength="10" required></label><label><span>Neues Passwort wiederholen</span><input type="password" name="new_password_confirm" minlength="10" required></label><button class="primary-button" type="submit">Passwort aktualisieren</button></form>
</article>
</div>
<?php if (!empty($authUser['is_admin'])): ?>
<div class="options-panel" data-options-panel="ai" hidden>
<h2>KI</h2>
<?php if (!empty($aiStatus)): ?><div class="user-list"><div class="user-row"><strong>API-Key</strong><span><?= !empty($aiStatus['has_api_key']) ? 'vorhanden' : 'fehlt' ?></span></div><div class="user-row"><strong>Aktuelles Modell</strong><span><?= e((string) ($aiStatus['model'] ?? '')) ?></span></div><div class="user-row"><strong>Timeout</strong><span><?= e((string) ($aiStatus['timeout'] ?? '')) ?> s</span></div></div><?php endif; ?>
<form method="post" action="/options" class="stack-form"><?= csrf_field() ?><input type="hidden" name="form_name" value="ai_config"><label><span>OpenAI-Modell</span><input type="text" name="ai[model]" value="<?= e((string) ($aiConfig['model'] ?? 'gpt-4o-mini')) ?>" required></label><label><span>Timeout in Sekunden</span><input type="number" name="ai[timeout]" value="<?= e((string) ($aiConfig['timeout'] ?? 25)) ?>" min="5" max="120" required></label><button class="primary-button" type="submit">KI-Konfiguration speichern</button></form>
</div>
<?php endif; ?>
</section>
</div>
</section>