fix reload pv on dashboard
Some checks failed
CI and deploy / migration-check (push) Failing after 15s
CI and deploy / deploy (push) Has been skipped

This commit is contained in:
Dusan Vojacek
2026-04-27 18:39:13 +02:00
parent 16fc6a065e
commit e4d4fee24d
3 changed files with 45 additions and 56 deletions

View File

@@ -3,7 +3,6 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import {
getCurrentPlan,
getSiteForecastPv,
getSitePrices,
getForecastPvSlotsRangeCorrected,
type SiteEffectivePriceRowDto,
@@ -245,45 +244,17 @@ export function useDashboardData(siteId: number | null) {
})
}
const forecastBySlot = new Map<string, { a: number; b: number }>()
const forecastDays: ForecastDayTotal[] = []
const weekDates = Array.from({ length: 7 }, (_, d) => pragueAddCalendarDays(todayPrague, d))
const forecastFetchDates = [...new Set([...dates, ...weekDates])].sort()
const forecastResults = await Promise.all(
forecastFetchDates.map((ymd) =>
getSiteForecastPv(siteId, ymd)
.then((fc) => ({ ymd, fc }))
.catch(() => ({ ymd, fc: null as Awaited<ReturnType<typeof getSiteForecastPv>> | null })),
),
)
const forecastByYmd = new Map(forecastResults.map((r) => [r.ymd, r.fc]))
const addForecastToByStart = (
fc: NonNullable<Awaited<ReturnType<typeof getSiteForecastPv>>>,
byStart: Map<string, { a: number; b: number }>,
) => {
for (const x of fc.pv_a ?? []) {
const t = new Date(x.interval_start).getTime()
const p = Number(x.power_w ?? 0)
const cur = byStart.get(slotTimeKey(t)) ?? { a: 0, b: 0 }
cur.a += p
byStart.set(slotTimeKey(t), cur)
}
for (const x of fc.pv_b ?? []) {
const t = new Date(x.interval_start).getTime()
const p = Number(x.power_w ?? 0)
const cur = byStart.get(slotTimeKey(t)) ?? { a: 0, b: 0 }
cur.b += p
byStart.set(slotTimeKey(t), cur)
}
}
for (const { fc } of forecastResults) {
if (!fc) continue
addForecastToByStart(fc, forecastBySlot)
}
// Forecast pro dashboard bereme pouze z range endpointu (jedno volání místo N× /forecast/pv per den).
// Rozsah musí pokrýt (a) okno slotů pro tabulku a (b) 7denní sumáře.
const windowFromIso = new Date(windowStart).toISOString()
const windowToIso = new Date(windowStart + TOTAL_SLOTS * SLOT_MS).toISOString()
const correctedSlots = await getForecastPvSlotsRangeCorrected(siteId, windowFromIso, windowToIso).catch(
const weekToIso = new Date(floorSlotUtcMs(Date.now()) + 7 * 24 * 60 * 60 * 1000).toISOString()
const forecastToIso = weekToIso > windowToIso ? weekToIso : windowToIso
const correctedSlots = await getForecastPvSlotsRangeCorrected(siteId, windowFromIso, forecastToIso).catch(
() => [] as Awaited<ReturnType<typeof getForecastPvSlotsRangeCorrected>>,
)
const correctedBySlot = new Map<string, number>()
@@ -295,16 +266,14 @@ export function useDashboardData(siteId: number | null) {
correctedBySlot.set(slotTimeKey(t), Number(v))
}
for (const ymd of weekDates) {
const fc = forecastByYmd.get(ymd) ?? null
if (!fc) {
forecastDays.push({ date: ymd, label: ymd, kwh: 0 })
continue
}
const byStart = new Map<string, { a: number; b: number }>()
addForecastToByStart(fc, byStart)
let kwh = 0
for (const [, v] of byStart) {
kwh += ((v.a + v.b) * 0.25) / 1000
// Křivka je po 15 min, takže energie = W * 0.25h
// Použijeme correctedBySlot v časovém okně daného prague dne.
const dayStart = new Date(ymd + 'T00:00:00').getTime()
const dayEnd = new Date(pragueAddCalendarDays(ymd, 1) + 'T00:00:00').getTime()
for (let t = dayStart; t < dayEnd; t += SLOT_MS) {
const w = correctedBySlot.get(slotTimeKey(t)) ?? 0
kwh += (w * 0.25) / 1000
}
const label = new Date(ymd + 'T12:00:00Z').toLocaleDateString('cs-CZ', {
weekday: 'short',
@@ -374,14 +343,12 @@ export function useDashboardData(siteId: number | null) {
base.sell_price = pr.sell
}
const fc = forecastBySlot.get(k)
if (fc) {
base.pv_a_forecast_w = fc.a
base.pv_b_forecast_w = fc.b
}
const corr = correctedBySlot.get(k)
if (corr != null) {
base.pv_forecast_corrected_w = corr
// Dashboard neřeší přesné rozdělení po polích; pro UI rozpad použijeme stabilní poměr.
base.pv_a_forecast_w = Math.round(corr * 0.6)
base.pv_b_forecast_w = Math.round(corr * 0.4)
}
const pi = planBySlot.get(k)