feat: Refactor settings navigation and enhance profile reference values display
All checks were successful
Deploy Development / deploy (push) Successful in 47s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 15s

- Introduced a new SettingsShell layout for improved navigation within the settings section.
- Updated ProfileReferenceValuesPage to display the latest entry with enhanced styling and information.
- Removed the direct link to settings from ProfileReferenceValuesPage for a cleaner UI.
- Adjusted SettingsPage to streamline the layout and focus on essential settings options.
This commit is contained in:
Lars 2026-04-07 06:24:47 +02:00
parent 296e79c3b3
commit c152721fe8
6 changed files with 95 additions and 29 deletions

View File

@ -22,6 +22,7 @@ import NutritionPage from './pages/NutritionPage'
import ActivityPage from './pages/ActivityPage'
import Analysis from './pages/Analysis'
import SettingsPage from './pages/SettingsPage'
import SettingsShell from './layouts/SettingsShell'
import ProfileReferenceValuesPage from './pages/ProfileReferenceValuesPage'
import GuidePage from './pages/GuidePage'
import AdminTierLimitsPage from './pages/AdminTierLimitsPage'
@ -226,8 +227,10 @@ function AppShell() {
<Route path="/history" element={<History/>}/>
<Route path="/goals" element={<GoalsPage/>}/>
<Route path="/analysis" element={<Analysis/>}/>
<Route path="/settings" element={<SettingsPage/>}/>
<Route path="/settings/reference-values" element={<ProfileReferenceValuesPage/>}/>
<Route path="/settings" element={<SettingsShell />}>
<Route index element={<SettingsPage />} />
<Route path="reference-values" element={<ProfileReferenceValuesPage />} />
</Route>
<Route element={<RequireAdmin />}>
<Route path="admin" element={<AdminShell />}>
<Route index element={<AdminHomePage />} />

View File

@ -552,6 +552,11 @@ a.analysis-split__nav-item {
}
}
/* Einstellungen: gleiche Split-Struktur wie Analyse/Admin */
.settings-shell {
width: 100%;
}
/* Admin: Split-Layout wie .analysis-split (nur Gruppen in der Nav) */
.admin-shell {
width: 100%;

View File

@ -0,0 +1,9 @@
/**
* Einstellungen: Sub-Navigation (Mobil = Chip-Leiste, Desktop = linke Spalte).
* Pfade müssen mit den Routes unter SettingsShell in App.jsx übereinstimmen.
*/
export const SETTINGS_SHELL_NAV_ITEMS = [
{ id: 'general', label: 'Allgemein', to: '/settings', end: true },
{ id: 'reference-values', label: 'Referenzwerte', to: '/settings/reference-values' },
]

View File

@ -0,0 +1,33 @@
import { Outlet, NavLink } from 'react-router-dom'
import { SETTINGS_SHELL_NAV_ITEMS } from '../config/settingsNav'
/**
* Wie Admin / KI-Analyse: Chips horizontal (mobil) bzw. Seitenleiste (Desktop).
*/
export default function SettingsShell() {
return (
<div className="settings-shell">
<div className="analysis-split">
<div className="analysis-split__nav-wrap">
<nav className="analysis-split__nav" aria-label="Einstellungen">
{SETTINGS_SHELL_NAV_ITEMS.map((item) => (
<NavLink
key={item.id}
to={item.to}
end={!!item.end}
className={({ isActive }) =>
'analysis-split__nav-item' + (isActive ? ' analysis-split__nav-item--active' : '')
}
>
{item.label}
</NavLink>
))}
</nav>
</div>
<div className="analysis-split__main">
<Outlet />
</div>
</div>
</div>
)
}

View File

@ -1,6 +1,5 @@
import { useState, useEffect, useCallback } from 'react'
import { Link } from 'react-router-dom'
import { ArrowLeft, Gauge, Pencil, Trash2, Plus } from 'lucide-react'
import { Gauge, Pencil, Trash2, Plus } from 'lucide-react'
import { api } from '../utils/api'
import {
labelSource,
@ -126,6 +125,9 @@ export default function ProfileReferenceValuesPage() {
})
}
const latestEntry =
!listLoading && entries.length > 0 ? entries[0] : null
const rules = selectedType?.validation_rules && typeof selectedType.validation_rules === 'object'
? selectedType.validation_rules
: {}
@ -299,13 +301,6 @@ export default function ProfileReferenceValuesPage() {
return (
<div style={{ paddingBottom: 88, textAlign: 'left' }}>
<div style={{ marginBottom: 16 }}>
<Link
to="/settings"
className="btn btn-secondary"
style={{ display: 'inline-flex', alignItems: 'center', gap: 8, textDecoration: 'none', marginBottom: 12 }}
>
<ArrowLeft size={16} /> Zurück zu Einstellungen
</Link>
<h1 className="page-title" style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<Gauge size={26} color="var(--accent)" />
Referenzwerte
@ -378,6 +373,44 @@ export default function ProfileReferenceValuesPage() {
)}
</div>
{latestEntry && selectedType && (
<div
className="card section-gap"
style={{
borderLeft: '4px solid var(--accent)',
background: 'var(--surface)',
}}
>
<div className="card-title" style={{ marginBottom: 8 }}>
Aktueller Wert
</div>
<p style={{ fontSize: 12, color: 'var(--text3)', margin: '0 0 10px', lineHeight: 1.4 }}>
Stand {String(latestEntry.effective_date || '').slice(0, 10)} · zuletzt erfasst für{' '}
<strong>{selectedType.label}</strong>
</p>
<div
style={{
fontSize: 28,
fontWeight: 700,
color: 'var(--text1)',
lineHeight: 1.2,
letterSpacing: '-0.02em',
}}
>
{formatEntryValue(latestEntry)}
{latestEntry.unit ? (
<span style={{ fontSize: 16, fontWeight: 600, color: 'var(--text2)', marginLeft: 8 }}>
{latestEntry.unit}
</span>
) : null}
</div>
<p style={{ fontSize: 13, color: 'var(--text2)', margin: '10px 0 0', lineHeight: 1.5 }}>
{labelSource(latestEntry.source)} · {labelMethod(latestEntry.method)} ·{' '}
{labelConfidence(latestEntry.confidence)}
</p>
</div>
)}
<div className="card section-gap">
<div className="card-title" style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<Plus size={16} />

View File

@ -1,5 +1,5 @@
import { useState, useEffect } from 'react'
import { Save, Download, Upload, Check, LogOut, Key, BarChart3, Target, Gauge } from 'lucide-react'
import { Save, Download, Upload, Check, LogOut, Key, BarChart3, Target } from 'lucide-react'
import { Link } from 'react-router-dom'
import { useProfile } from '../context/ProfileContext'
import { useAuth } from '../context/AuthContext'
@ -428,23 +428,6 @@ export default function SettingsPage() {
</button>
</div>
<div className="card section-gap">
<div className="card-title" style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<Gauge size={15} color="var(--accent)" /> Referenzwerte
</div>
<p style={{ fontSize: 13, color: 'var(--text2)', marginBottom: 12, lineHeight: 1.6 }}>
Persönliche Kennwerte (z. B. HF-Schwellen, Trainingshäufigkeit) historisch zum Profil gespeichert,
keine Admin-Konfiguration.
</p>
<Link
to="/settings/reference-values"
className="btn btn-secondary btn-full"
style={{ textAlign: 'center', textDecoration: 'none', boxSizing: 'border-box' }}
>
Referenzwerte verwalten
</Link>
</div>
<div className="card section-gap">
<div className="card-title" style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<Target size={15} color="var(--accent)" /> Strategische Ziele