feat: Refactor settings navigation and enhance profile reference values display
- 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:
parent
296e79c3b3
commit
c152721fe8
|
|
@ -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 />} />
|
||||
|
|
|
|||
|
|
@ -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%;
|
||||
|
|
|
|||
9
frontend/src/config/settingsNav.js
Normal file
9
frontend/src/config/settingsNav.js
Normal 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' },
|
||||
]
|
||||
33
frontend/src/layouts/SettingsShell.jsx
Normal file
33
frontend/src/layouts/SettingsShell.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
|
|
@ -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} />
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user