nova stranka flow a obsluha
All checks were successful
deploy / deploy (push) Successful in 8m52s
test / smoke-test (push) Successful in 5s

This commit is contained in:
Dusan Vojacek
2026-04-10 22:13:58 +02:00
parent 64221f701a
commit f714cab0ab
14 changed files with 1670 additions and 4 deletions

View File

@@ -0,0 +1,101 @@
import { ResponsiveSankey } from '@nivo/sankey'
export type FlowTotals = {
pv_to_load_kwh: number
pv_to_batt_kwh: number
pv_to_grid_kwh: number
batt_to_load_kwh: number
batt_to_grid_kwh: number
grid_to_load_kwh: number
grid_to_batt_kwh: number
}
const NODES = [
{ id: 'FVE' },
{ id: 'Síť' },
{ id: 'Baterie' },
{ id: 'Spotřeba' },
] as const
function buildLinks(t: FlowTotals): { source: string; target: string; value: number }[] {
const out: { source: string; target: string; value: number }[] = []
const add = (source: string, target: string, v: number) => {
if (v > 0.0005) out.push({ source, target, value: v })
}
add('FVE', 'Spotřeba', t.pv_to_load_kwh)
add('FVE', 'Baterie', t.pv_to_batt_kwh)
add('FVE', 'Síť', t.pv_to_grid_kwh)
add('Baterie', 'Spotřeba', t.batt_to_load_kwh)
add('Baterie', 'Síť', t.batt_to_grid_kwh)
add('Síť', 'Spotřeba', t.grid_to_load_kwh)
add('Síť', 'Baterie', t.grid_to_batt_kwh)
return out
}
type Props = {
totals: FlowTotals | null
}
export function EnergyFlowSankey({ totals }: Props) {
if (!totals) {
return (
<div className="flex h-[440px] items-center justify-center text-sm text-slate-500">
Žádná data
</div>
)
}
const links = buildLinks(totals)
if (links.length === 0) {
return (
<div className="flex h-[440px] items-center justify-center text-sm text-slate-500">
V tomto měsíci nejsou žádné modelované toky (chybí audit / telemetrie).
</div>
)
}
return (
<div className="h-[440px] w-full min-h-[320px]">
<ResponsiveSankey
data={{ nodes: [...NODES], links }}
margin={{ top: 24, right: 180, bottom: 24, left: 24 }}
align="justify"
sort="input"
colors={{ scheme: 'set2' }}
nodeOpacity={1}
nodeHoverOpacity={1}
nodeThickness={20}
nodeSpacing={28}
nodeBorderWidth={0}
linkOpacity={0.45}
linkHoverOpacity={0.75}
linkContract={2}
enableLinkGradient
labelPosition="outside"
labelOrientation="horizontal"
labelPadding={12}
labelTextColor={{ from: 'color', modifiers: [['darker', 1.2]] }}
theme={{
background: 'transparent',
labels: {
text: {
fill: '#e2e8f0',
fontSize: 12,
fontWeight: 500,
},
},
tooltip: {
container: {
background: '#1e293b',
color: '#f8fafc',
fontSize: 12,
borderRadius: 8,
border: '1px solid #334155',
},
},
}}
valueFormat={(v) => `${Number(v).toFixed(2)} kWh`}
/>
</div>
)
}