258 lines
25 KiB
PHP
258 lines
25 KiB
PHP
<section class="options-shell">
|
||
<article class="glass-panel options-menu-panel">
|
||
<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="score">
|
||
<strong>Score anpassen</strong>
|
||
<span>Multiplikatoren und Tageslogik</span>
|
||
</button>
|
||
<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="reminders">
|
||
<strong>Erinnerungen setzen</strong>
|
||
<span>Push und tägliche Erinnerung</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>
|
||
</article>
|
||
|
||
<div class="options-overlay" data-options-overlay<?= !empty($optionsOpenPanel) ? '' : ' hidden' ?> 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-panel" data-options-panel="score" hidden>
|
||
<h2>Score 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">
|
||
<h4>Multiplikatoren</h4>
|
||
<div class="field-grid field-grid--four">
|
||
<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>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>
|
||
<div class="settings-section">
|
||
<h4>Tracking-Felder</h4>
|
||
<div class="field-grid field-grid--two">
|
||
<label class="checkbox-row checkbox-row--panel">
|
||
<input type="checkbox" name="settings[tracking][pain_enabled]" value="1" <?= !empty($settings['tracking']['pain_enabled']) ? 'checked' : '' ?>>
|
||
<span><strong>Schmerzen aktivieren</strong><small>Schmerzen werden weiter in den Score einbezogen.</small></span>
|
||
</label>
|
||
<label><span>Schmerzfaktor</span><input type="number" name="settings[scoring][pain_multiplier]" value="<?= e((string) $settings['scoring']['pain_multiplier']) ?>" min="0" max="10"></label>
|
||
</div>
|
||
</div>
|
||
<div class="settings-section">
|
||
<h4>Schlafdauerpunkte</h4>
|
||
<div class="field-grid field-grid--four">
|
||
<?php foreach ($settings['scoring']['sleep_duration_points'] as $key => $value): ?>
|
||
<label><span><?= e($key) ?></span><input type="number" name="settings[scoring][sleep_duration_points][<?= e($key) ?>]" value="<?= e((string) $value) ?>" min="0" max="20"></label>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
</div>
|
||
<button class="primary-button" type="submit">Score speichern</button>
|
||
</form>
|
||
</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 anpassen</h2>
|
||
<form method="post" action="/options" class="stack-form">
|
||
<?= csrf_field() ?>
|
||
<input type="hidden" name="form_name" value="settings">
|
||
<label><span>Spaziergang auswerten 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="band-grid"><?php foreach ($settings['scoring']['walk_bands'] as $index => $band): ?><div class="band-card"><label><span>Min</span><input type="number" name="settings[scoring][walk_bands][<?= e((string) $index) ?>][min]" value="<?= e((string) $band['min']) ?>"></label><label><span>Max</span><input type="number" name="settings[scoring][walk_bands][<?= e((string) $index) ?>][max]" value="<?= e((string) $band['max']) ?>"></label><label><span>Punkte</span><input type="number" name="settings[scoring][walk_bands][<?= e((string) $index) ?>][points]" value="<?= e((string) $band['points']) ?>"></label></div><?php endforeach; ?></div>
|
||
<button class="primary-button" type="submit">Spaziergang 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="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>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>
|
||
<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>Ø 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>Tagesstimmung</h3></div></div><div class="line-chart" data-chart-type="line" data-series="mood" 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>
|