Dashboard
This commit is contained in:
@@ -373,6 +373,11 @@ export function Dashboard({athleteId}: { athleteId: number }) {
|
||||
|
||||
const loadData = buildLoadData(activities, wStart)
|
||||
|
||||
const ctlValues = loadData.map(d => d.ctl).filter((v): v is number => v !== null)
|
||||
const ctlDomain: [number, number] | ['auto', 'auto'] = ctlValues.length
|
||||
? [Math.floor(Math.min(...ctlValues) * 0.9), Math.ceil(Math.max(...ctlValues) * 1.1)]
|
||||
: ['auto', 'auto']
|
||||
|
||||
return (
|
||||
<div className="flex-1 overflow-y-auto bg-zinc-50 p-8">
|
||||
{/* Page header */}
|
||||
@@ -467,14 +472,14 @@ export function Dashboard({athleteId}: { athleteId: number }) {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Training Load chart */}
|
||||
{/* Weekly TSS chart */}
|
||||
<div className="border border-zinc-200 rounded-sm bg-white p-5">
|
||||
<p className="text-xs font-semibold tracking-widest text-zinc-400 uppercase mb-5">
|
||||
Weekly Training Load (TSS)
|
||||
</p>
|
||||
{loadData.some((d) => d.tss > 0 || d.ctl !== null) ? (
|
||||
<ResponsiveContainer width="100%" height={220}>
|
||||
<ComposedChart data={loadData} margin={{left: -20, right: 30}}>
|
||||
{loadData.some((d) => d.tss > 0) ? (
|
||||
<ResponsiveContainer width="100%" height={200}>
|
||||
<ComposedChart data={loadData} margin={{left: -20, right: 10}}>
|
||||
<defs>
|
||||
<linearGradient id="tssGrad" x1="0" y1="0" x2="1" y2="0">
|
||||
<stop offset="0%" stopColor="#ef4444"/>
|
||||
@@ -487,21 +492,11 @@ export function Dashboard({athleteId}: { athleteId: number }) {
|
||||
</defs>
|
||||
<XAxis dataKey="week" tick={{fontSize: 10, fill: '#a1a1aa'}} axisLine={false}
|
||||
tickLine={false}/>
|
||||
<YAxis yAxisId="tss" tick={{fontSize: 10, fill: '#a1a1aa'}} axisLine={false}
|
||||
tickLine={false}/>
|
||||
<YAxis yAxisId="fitness" orientation="right" tick={{fontSize: 10, fill: '#a1a1aa'}}
|
||||
axisLine={false} tickLine={false}/>
|
||||
<YAxis tick={{fontSize: 10, fill: '#a1a1aa'}} axisLine={false} tickLine={false}/>
|
||||
<Tooltip content={<LoadTooltip/>}/>
|
||||
<Area yAxisId="tss" type="monotone" dataKey="tss" fill="url(#tssAreaGrad)"
|
||||
stroke="none"/>
|
||||
<Line yAxisId="tss" type="monotone" dataKey="tss" stroke="url(#tssGrad)" strokeWidth={2}
|
||||
<Area type="monotone" dataKey="tss" fill="url(#tssAreaGrad)" stroke="none"/>
|
||||
<Line type="monotone" dataKey="tss" stroke="url(#tssGrad)" strokeWidth={2}
|
||||
dot={false} name="TSS"/>
|
||||
<Line yAxisId="fitness" type="monotone" dataKey="ctl" stroke="#ef4444" strokeWidth={1.5}
|
||||
strokeDasharray="5 3" dot={false} name="CTL" connectNulls/>
|
||||
<Line yAxisId="fitness" type="monotone" dataKey="atl" stroke="#fdba74" strokeWidth={1.5}
|
||||
strokeDasharray="5 3" dot={false} name="ATL" connectNulls/>
|
||||
<Line yAxisId="fitness" type="monotone" dataKey="tsb" stroke="#52525b" strokeWidth={1.5}
|
||||
strokeDasharray="3 3" dot={false} name="TSB" connectNulls/>
|
||||
</ComposedChart>
|
||||
</ResponsiveContainer>
|
||||
) : (
|
||||
@@ -510,6 +505,44 @@ export function Dashboard({athleteId}: { athleteId: number }) {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* CTL / ATL / TSB chart */}
|
||||
<div className="border border-zinc-200 rounded-sm bg-white p-5">
|
||||
<p className="text-xs font-semibold tracking-widest text-zinc-400 uppercase mb-5">
|
||||
Fitness, Fatigue & Form (CTL / ATL / TSB)
|
||||
</p>
|
||||
{loadData.some((d) => d.ctl !== null) ? (
|
||||
<ResponsiveContainer width="100%" height={200}>
|
||||
<ComposedChart data={loadData} margin={{left: -20, right: 0}}>
|
||||
<defs>
|
||||
<linearGradient id="tssAreaGrad2" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stopColor="#ef4444" stopOpacity={0.12}/>
|
||||
<stop offset="100%" stopColor="#f97316" stopOpacity={0}/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<XAxis dataKey="week" tick={{fontSize: 10, fill: '#a1a1aa'}} axisLine={false}
|
||||
tickLine={false} padding={{left: 20, right: 20}}/>
|
||||
<YAxis yAxisId="left" domain={ctlDomain} tick={{fontSize: 10, fill: '#a1a1aa'}}
|
||||
axisLine={false} tickLine={false}/>
|
||||
<YAxis yAxisId="right" orientation="right" tick={{fontSize: 10, fill: '#a1a1aa'}}
|
||||
axisLine={false} tickLine={false}/>
|
||||
<Tooltip content={<LoadTooltip/>}/>
|
||||
<Area yAxisId="left" type="monotone" dataKey="ctl" fill="url(#tssAreaGrad2)"
|
||||
stroke="none" connectNulls/>
|
||||
<Line yAxisId="left" type="monotone" dataKey="ctl" stroke="#ef4444"
|
||||
strokeWidth={2} dot={false} name="CTL" connectNulls/>
|
||||
<Line yAxisId="left" type="monotone" dataKey="atl" stroke="#fdba74" strokeWidth={1.5}
|
||||
strokeDasharray="5 3" dot={false} name="ATL" connectNulls/>
|
||||
<Line yAxisId="right" type="monotone" dataKey="tsb" stroke="#52525b" strokeWidth={1.5}
|
||||
strokeDasharray="3 3" dot={false} name="TSB" connectNulls/>
|
||||
</ComposedChart>
|
||||
</ResponsiveContainer>
|
||||
) : (
|
||||
<div className="h-[200px] flex items-center justify-center text-zinc-400 text-sm">
|
||||
No fitness data yet
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user