diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 2c64ef6..a9dc62e 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -852,7 +852,7 @@ function DistributionPanel({ data, language, theme, locale }: { data?: Distribut function HistoricalPanel({ status, language, locale, compact = false }: { status?: HistoricalStatus; language: Language; locale: string; compact?: boolean }) { if (!status) return
{t(language, "noDataDescription")}
; return

{language === "en" ? "Data warehouse" : "Hurtownia danych"}

{t(language, "importArchiveSubtitle")}
{!compact ? <>
{t(language, "activeChunk")}{status.active_chunk_index}/{status.total_chunks}
{status.recent_chunks.map((chunk) => )}
{t(language, "recentChunks")}{t(language, "status")}kWh
#{chunk.chunk_index}
{chunk.start_date} → {chunk.end_date}
{chunk.state}{formatValue(chunk.energy_kwh, "kWh", 2, locale)}
{status.recent_events.map((event, index) =>
{event.title}
{event.message}
{formatShortTime(event.timestamp, locale)}
)}
: null}
; } function ImportControls({ status, language, onStart, onSyncNow, onCancel }: { status?: HistoricalStatus; language: Language; onStart: (payload: { start_date?: string; end_date?: string; chunk_days?: number; force?: boolean }) => void; onSyncNow: () => void; onCancel: () => void; }) { const [startDate, setStartDate] = useState(status?.available_start_date ?? ""); const [endDate, setEndDate] = useState(status?.available_end_date ?? ""); const [chunkDays, setChunkDays] = useState(String(status?.default_chunk_days ?? 7)); useEffect(() => { if (!status) return; setStartDate((current) => current || status.available_start_date || ""); setEndDate((current) => current || status.available_end_date || ""); setChunkDays((current) => current || String(status.default_chunk_days || 7)); }, [status]); return

{language === "en" ? "Import controls" : "Sterowanie importem"}

setStartDate(event.target.value)} />
setEndDate(event.target.value)} />
setChunkDays(event.target.value)} />
; } function KioskModeCard({ active, title, subtitle, onClick }: { active: boolean; title: string; subtitle: string; onClick: () => void; }) { return ; } -function KioskLayoutPanel({ language, widgets, onChange, labels }: { language: Language; widgets: WidgetId[]; onChange: (value: WidgetId[]) => void; labels: Map; }) { const available = widgetOrder.map((item) => item.id); const selected = widgets; const unselected = available.filter((item) => !selected.includes(item)); const move = (id: WidgetId, direction: -1 | 1) => { const index = selected.indexOf(id); if (index === -1) return; const target = index + direction; if (target < 0 || target >= selected.length) return; const next = [...selected]; [next[index], next[target]] = [next[target], next[index]]; onChange(next); }; const toggle = (id: WidgetId) => { if (selected.includes(id)) { const next = selected.filter((item) => item !== id); onChange(next.length ? next : selected); return; } onChange([...selected, id]); }; return

{language === "en" ? "3. Section order" : "3. Kolejność sekcji"}

{language === "en" ? "Top list is shown in kiosk from left to right, top to bottom." : "Lista u góry jest wyświetlana w kiosku dokładnie w tej kolejności."}
{language === "en" ? "Tip: keep the most important sections first: hero, chart, strings/status." : "Wskazówka: na początku trzymaj najważniejsze sekcje: hero, wykres, stringi/status."}
{language === "en" ? "Visible in kiosk" : "Widoczne w kiosku"}
{selected.map((id, index) =>
{index + 1}. {labels.get(id)}
{widgetOrder.find((item) => item.id === id)?.tab}
)}
{language === "en" ? "Available sections" : "Dostępne sekcje"}
{unselected.map((id) => )}
; } +function KioskLayoutPanel({ language, widgets, onChange, labels }: { language: Language; widgets: WidgetId[]; onChange: (value: WidgetId[]) => void; labels: Map; }) { const available = widgetOrder.map((item) => item.id); const selected = widgets; const unselected = available.filter((item) => !selected.includes(item)); const move = (id: WidgetId, direction: -1 | 1) => { const index = selected.indexOf(id); if (index === -1) return; const target = index + direction; if (target < 0 || target >= selected.length) return; const next = [...selected]; [next[index], next[target]] = [next[target], next[index]]; onChange(next); }; const toggle = (id: WidgetId) => { if (selected.includes(id)) { const next = selected.filter((item) => item !== id); onChange(next.length ? next : selected); return; } onChange([...selected, id]); }; return

{language === "en" ? "2. Section order" : "2. Kolejność sekcji"}

{language === "en" ? "Top list is shown in kiosk from left to right, top to bottom." : "Lista u góry jest wyświetlana w kiosku dokładnie w tej kolejności."}
{language === "en" ? "Tip: keep the most important sections first: hero, chart, strings/status." : "Wskazówka: na początku trzymaj najważniejsze sekcje: hero, wykres, stringi/status."}
{language === "en" ? "Visible in kiosk" : "Widoczne w kiosku"}
{selected.map((id, index) =>
{index + 1}. {labels.get(id)}
{widgetOrder.find((item) => item.id === id)?.tab}
)}
{language === "en" ? "Available sections" : "Dostępne sekcje"}
{unselected.map((id) => )}
; } function KioskSettingsEditorPanel({ language, value, onChange, onSave, onReset, selectedMode, onModeChange, labels, buckets, compareModes, saving, dirty, canSave, saveNotice, allowPublicMode, chartItems, heroItems }: { language: Language; value: KioskSettingsPayload; onChange: (value: KioskSettingsPayload) => void; onSave: () => void; onReset: () => void; selectedMode: "public" | "private"; onModeChange: (value: "public" | "private") => void; labels: Map; buckets: Array<{ key: string; label: string }>; compareModes: string[]; saving: boolean; dirty: boolean; canSave: boolean; saveNotice: string | null; allowPublicMode: boolean; chartItems: Array<{ metric_id: string; label: string; unit: string }>; heroItems: Array<{ metric_id: string; label: string; unit: string }>; }) { const widgets = toWidgetIds(value.widgets); const chartGroups = sanitizeKioskChartGroups(value.chart_groups, chartItems);