flwo - denni sankey graf
All checks were successful
deploy / deploy (push) Successful in 1m19s
test / smoke-test (push) Successful in 3s

This commit is contained in:
Dusan Vojacek
2026-04-10 22:49:43 +02:00
parent 74ffa5c3e7
commit a65d134682
2 changed files with 56 additions and 5 deletions

View File

@@ -1,5 +1,5 @@
import { ChevronDown, ChevronLeft, ChevronRight, ChevronUp } from 'lucide-react'
import { Fragment, useMemo, useState } from 'react'
import { Fragment, useEffect, useMemo, useState } from 'react'
import { EnergyFlowSankey, type FlowTotals } from '../components/EnergyFlowSankey'
import { useEnergyFlowsDaily, useEnergyFlowsIntervals } from '../hooks/useEnergyFlows'
@@ -132,10 +132,23 @@ export default function EnergyFlows() {
const [month, setMonth] = useState(currentMonth)
const [expandedDay, setExpandedDay] = useState<string | null>(null)
/** null = součet za celý měsíc; jinak ISO den pro Sankey + perspektivní karty */
const [scopeDay, setScopeDay] = useState<string | null>(null)
const { days, loading, error, reload } = useEnergyFlowsDaily(siteId, month)
const totals = useMemo(() => (days.length > 0 ? aggregateFlows(days) : null), [days])
useEffect(() => {
setScopeDay(null)
}, [month])
const totals = useMemo(() => {
if (days.length === 0) return null
if (scopeDay) {
const row = days.find((d) => d.day === scopeDay)
if (row) return aggregateFlows([row])
}
return aggregateFlows(days)
}, [days, scopeDay])
const flowOnly: FlowTotals | null = totals
? {
@@ -201,6 +214,30 @@ export default function EnergyFlows() {
</div>
) : (
<>
{days.length > 0 && (
<div className="flex flex-wrap items-center gap-3 rounded-lg border border-slate-800 bg-slate-900/60 px-3 py-2">
<label htmlFor="energy-flow-scope" className="text-sm text-slate-400">
Graf a karty:
</label>
<select
id="energy-flow-scope"
className="max-w-[min(100%,20rem)] rounded-lg border border-slate-700 bg-slate-900 px-3 py-1.5 text-sm text-slate-200"
value={scopeDay ?? ''}
onChange={(e) => {
const v = e.target.value
setScopeDay(v === '' ? null : v)
}}
>
<option value="">Celý měsíc (součet)</option>
{days.map((d) => (
<option key={d.day} value={d.day}>
{fmtDay(d.day)}
</option>
))}
</select>
</div>
)}
{totals && (
<div className="grid gap-3 sm:grid-cols-3">
<div className="rounded-xl border border-slate-800 bg-slate-900 p-4">
@@ -243,7 +280,9 @@ export default function EnergyFlows() {
)}
<div className="rounded-xl border border-slate-800 bg-slate-900 p-4">
<h2 className="mb-2 text-sm font-medium text-slate-300">Sankey součet za měsíc</h2>
<h2 className="mb-2 text-sm font-medium text-slate-300">
Sankey {scopeDay ? fmtDay(scopeDay) : 'celý měsíc (součet)'}
</h2>
<EnergyFlowSankey totals={flowOnly} />
</div>
@@ -281,7 +320,19 @@ export default function EnergyFlows() {
<span className="mr-1 inline-block w-4">
{expandedDay === row.day ? <ChevronUp size={14} /> : <ChevronDown size={14} />}
</span>
{fmtDay(row.day)}
<button
type="button"
className={`rounded px-1 text-left underline-offset-2 hover:underline ${
scopeDay === row.day ? 'text-amber-300' : ''
}`}
title="Zobrazit tento den v Sankey a v kartách"
onClick={(e) => {
e.stopPropagation()
setScopeDay(row.day)
}}
>
{fmtDay(row.day)}
</button>
</td>
<td className="px-3 py-2 text-right text-sm">{row.pv_production_kwh.toFixed(1)}</td>
<td className="px-3 py-2 text-right text-sm">{row.grid_import_kwh.toFixed(2)}</td>