337 lines
35 KiB
PHP
337 lines
35 KiB
PHP
<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>
|