feat: Enhance Dashboard layout with responsive greeting and metrics display
This commit is contained in:
parent
7e8422cbd7
commit
c2b2c71ccd
|
|
@ -3,7 +3,7 @@
|
|||
> **Gitea:** [#30 – Responsive UI](http://192.168.2.144:3000/Lars/mitai-jinkendo/issues/30)
|
||||
> **Spec:** `.claude/docs/functional/RESPONSIVE_UI.md`
|
||||
> **Breakpoint:** `<1024px` = Mobile (Bottom-Nav, bestehendes Verhalten), `≥1024px` = Desktop (Sidebar 220px)
|
||||
> **Letzte Plan-Aktualisierung:** 2026-04-05
|
||||
> **Letzte Plan-Aktualisierung:** 2026-04-06
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
| P0 | Vorbereitung & Baseline | ☑ erledigt | Spec `RESPONSIVE_UI.md` bereinigt |
|
||||
| P1 | App-Shell: Sidebar + Breakpoint + gemeinsame Navigation | ☑ erledigt | `DesktopSidebar`, `config/appNav.js`, Admin `/admin/*`-Highlight |
|
||||
| P2 | Globales Layout & Content-Bereich (CSS) | ☑ erledigt | Desktop: Header aus, Content max 1200px; Mobile unverändert Bottom-Nav |
|
||||
| P3 | Dashboard (Desktop-Grid) | ☐ pending | |
|
||||
| P3 | Dashboard (Desktop-Grid) | ☑ erledigt | 4-spaltige Kennzahlen; Begrüßung; Ernährung/Aktivität 2-spaltig |
|
||||
| P4 | Verlauf (Tabs links / Content rechts) | ☐ pending | |
|
||||
| P5 | Analyse (Prompts links / Ergebnis rechts) | ☐ pending | |
|
||||
| P6 | Erfassung / Capture & Formularseiten | ☐ pending | |
|
||||
|
|
|
|||
|
|
@ -344,4 +344,82 @@ body { font-family: var(--font); background: var(--bg); color: var(--text1); -we
|
|||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Dashboard (P3): Begrüßung + Kennzahlen-Zeile */
|
||||
.dashboard-greeting {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.dashboard-greeting__meta {
|
||||
margin-top: 0 !important;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Dashboard layout (Mobile baseline + Desktop im Block oben teilweise) ─ */
|
||||
|
||||
.dashboard-page {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dashboard-greeting {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.dashboard-stat-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.dashboard-stat-card {
|
||||
background: var(--surface);
|
||||
border-radius: 12px;
|
||||
padding: 12px 10px;
|
||||
border: 1px solid var(--border);
|
||||
transition: border-color 0.15s;
|
||||
flex: 1 1 140px;
|
||||
min-width: 80px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.dashboard-summary-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.dashboard-summary-row > .card {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.dashboard-stat-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.dashboard-stat-grid .dashboard-stat-card {
|
||||
flex: unset;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dashboard-summary-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.dashboard-summary-row > .card {
|
||||
flex: unset;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,11 +148,10 @@ function StatCard({ icon, label, value, unit, delta, deltaGoodWhenNeg=false, sub
|
|||
const deltaColor = delta==null ? null
|
||||
: (deltaGoodWhenNeg ? delta<0 : delta>0) ? 'var(--accent)' : 'var(--warn)'
|
||||
return (
|
||||
<div onClick={onClick} style={{
|
||||
flex:1, minWidth:80, background:'var(--surface)', borderRadius:12,
|
||||
padding:'12px 10px', cursor:onClick?'pointer':'default',
|
||||
border:'1px solid var(--border)', transition:'border-color 0.15s',
|
||||
}}
|
||||
<div
|
||||
className="dashboard-stat-card"
|
||||
onClick={onClick}
|
||||
style={{ cursor: onClick ? 'pointer' : 'default' }}
|
||||
onMouseEnter={e=>onClick&&(e.currentTarget.style.borderColor='var(--accent)')}
|
||||
onMouseLeave={e=>onClick&&(e.currentTarget.style.borderColor='var(--border)')}>
|
||||
<div style={{fontSize:18,marginBottom:4}}>{icon}</div>
|
||||
|
|
@ -321,13 +320,13 @@ export default function Dashboard() {
|
|||
console.log('[Dashboard] hasAnyData=', hasAnyData, 'latestW=', !!latestW, 'latestCal=', !!latestCal, 'nutrition.length=', nutrition.length)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="dashboard-page">
|
||||
{/* Header greeting */}
|
||||
<div style={{marginBottom:16}}>
|
||||
<div className="dashboard-greeting">
|
||||
<h1 style={{fontSize:22,fontWeight:800,margin:0,color:'var(--text1)'}}>
|
||||
Hallo, {activeProfile?.name||'Nutzer'} 👋
|
||||
</h1>
|
||||
<div style={{fontSize:12,color:'var(--text3)',marginTop:2}}>
|
||||
<div className="dashboard-greeting__meta" style={{fontSize:12,color:'var(--text3)',marginTop:2}}>
|
||||
{dayjs().format('dddd, DD. MMMM YYYY')}
|
||||
{latestW && ` · Letztes Update ${dayjs(latestW.date).format('DD.MM.')}`}
|
||||
</div>
|
||||
|
|
@ -362,8 +361,8 @@ export default function Dashboard() {
|
|||
<QuickWeight onSaved={load}/>
|
||||
</div>
|
||||
|
||||
{/* Key metrics */}
|
||||
<div style={{display:'flex',gap:8,marginBottom:16,flexWrap:'wrap'}}>
|
||||
{/* Key metrics — Mobile: flex-wrap; Desktop: 4-spaltig (RESPONSIVE_UI P3) */}
|
||||
<div className="dashboard-stat-grid">
|
||||
<StatCard icon="⚖️" label="Gewicht" value={latestW?.weight??'–'} unit="kg"
|
||||
delta={wDelta} deltaGoodWhenNeg={true}
|
||||
sub={latestW ? dayjs(latestW.date).format('DD.MM.') : '–'}
|
||||
|
|
@ -451,7 +450,7 @@ export default function Dashboard() {
|
|||
)}
|
||||
|
||||
{/* Activity + Nutrition summary row */}
|
||||
<div style={{display:'flex',gap:8,marginBottom:16}}>
|
||||
<div className="dashboard-summary-row">
|
||||
{(avgKcal||avgProtein) && (
|
||||
<div className="card" style={{flex:1,cursor:'pointer'}} onClick={()=>nav('/history',{state:{tab:'nutrition'}})}>
|
||||
<div style={{fontWeight:600,fontSize:12,marginBottom:8,color:'var(--text3)'}}>🍽️ ERNÄHRUNG (Ø 7T)</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user