implementace Ekonomiky
All checks were successful
test / smoke-test (push) Successful in 5s
deploy / deploy (push) Successful in 11s

This commit is contained in:
Dusan Vojacek
2026-04-05 20:10:43 +02:00
parent caf3f522e2
commit 5fcc47bce2
13 changed files with 1310 additions and 31 deletions

View File

@@ -0,0 +1,123 @@
import {
Bar,
CartesianGrid,
Cell,
ComposedChart,
Line,
ReferenceLine,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from 'recharts'
import type { ChartDayPoint } from '../../types/economics'
type Props = {
points: ChartDayPoint[]
}
const GREEN = '#22c55e'
const RED = '#ef4444'
const BLUE = '#3b82f6'
function formatDay(iso: string): string {
const d = new Date(iso + 'T00:00:00')
return `${d.getDate()}.`
}
type PayloadEntry = {
name?: string
value?: number
color?: string
}
function CustomTooltip({
active,
payload,
label,
}: {
active?: boolean
payload?: PayloadEntry[]
label?: string
}) {
if (!active || !payload?.length || !label) return null
const balance = payload.find((p) => p.name === 'daily_balance_czk')
const cumulative = payload.find((p) => p.name === 'cumulative_balance_czk')
return (
<div className="rounded-lg border border-slate-700 bg-slate-800 px-3 py-2 text-xs shadow-lg">
<p className="mb-1 font-medium text-slate-200">{label}</p>
{balance && (
<p style={{ color: (balance.value ?? 0) >= 0 ? GREEN : RED }}>
Den: {(balance.value ?? 0) >= 0 ? '+' : ''}
{(balance.value ?? 0).toFixed(2)}
</p>
)}
{cumulative && (
<p style={{ color: BLUE }}>
Kumulativ: {(cumulative.value ?? 0) >= 0 ? '+' : ''}
{(cumulative.value ?? 0).toFixed(2)}
</p>
)}
</div>
)
}
export function EconomicsChart({ points }: Props) {
if (points.length === 0) {
return (
<div className="flex h-64 items-center justify-center text-sm text-slate-500">
Žádná data pro tento měsíc
</div>
)
}
const data = points.map((p) => ({
...p,
label: formatDay(p.day),
}))
return (
<ResponsiveContainer width="100%" height={320}>
<ComposedChart data={data} margin={{ top: 8, right: 16, bottom: 4, left: 0 }}>
<CartesianGrid strokeDasharray="3 3" stroke="#334155" />
<XAxis dataKey="label" tick={{ fontSize: 11, fill: '#94a3b8' }} />
<YAxis
yAxisId="left"
tick={{ fontSize: 11, fill: '#94a3b8' }}
label={{
value: 'Kč/den',
angle: -90,
position: 'insideLeft',
style: { fontSize: 11, fill: '#94a3b8' },
}}
/>
<YAxis
yAxisId="right"
orientation="right"
tick={{ fontSize: 11, fill: BLUE }}
label={{
value: 'Kumulativ Kč',
angle: 90,
position: 'insideRight',
style: { fontSize: 11, fill: BLUE },
}}
/>
<Tooltip content={<CustomTooltip />} />
<ReferenceLine yAxisId="left" y={0} stroke="#475569" strokeDasharray="2 2" />
<Bar yAxisId="left" dataKey="daily_balance_czk" radius={[3, 3, 0, 0]} maxBarSize={32}>
{data.map((entry, idx) => (
<Cell key={idx} fill={entry.daily_balance_czk >= 0 ? GREEN : RED} fillOpacity={0.8} />
))}
</Bar>
<Line
yAxisId="right"
type="monotone"
dataKey="cumulative_balance_czk"
stroke={BLUE}
strokeWidth={2}
dot={{ r: 3, fill: BLUE }}
/>
</ComposedChart>
</ResponsiveContainer>
)
}