feat(blocks): live_strommix widget for energy-charts data
Editor-controlled live-strommix card that surfaces what share of
electricity demand is currently covered by weather-dependent
renewables (Wind+Solar / Load), plus the conventional generation that
fills the gap, the cross-border saldo, and the day-ahead price (DE/FR
side-by-side). Status bucket + label + argument come from the CMS so
editors can iterate copy without a deploy.
- LiveStrommixBlockData type next to the other block-types.
- New `/api/strommix` aggregator route bundles four CMS external
collections (strommix_de, crossborder_de, price_de, price_fr) into a
single payload, computes the renewable + conventional sums and the
cross-border saldo (Σ flow_xx_gw, sign convention: + = export from
DE), surfaces stale-on-error flags from the CMS response headers.
Cache-control 5min so a single node only hits energy-charts once
per window.
- StrommixBlock.svelte renders the card: percentage, bucket label,
bucket argument (markdown), context row (conventional + saldo with
↑/↓ arrow), price row with optional FR comparison + negative-price
badge, footer with disclaimer + "Wie wird das berechnet?" link +
stale-data hint when the CMS served fallback. Re-polls every 5min
client-side; bucket thresholds + labels + arguments come from the
block instance so multiple placements can carry different copy.
- ContentRows.svelte dispatcher branch on `_type === "live_strommix"`.
Wired against `cms_content/_types/core/live_strommix.json5` (added in
the rustycms repo today). content_layout.row{1,2,3}Content already
allow the new block-type via the same commit on the CMS side.
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
import OrganisationsBlock from "./blocks/OrganisationsBlock.svelte";
|
||||
import OpnFormBlock from "./blocks/OpnFormBlock.svelte";
|
||||
import DeadlineBannerBlock from "./blocks/DeadlineBannerBlock.svelte";
|
||||
import StrommixBlock from "./blocks/StrommixBlock.svelte";
|
||||
import { isBlockLayoutBreakout, getSpaceBottomClass } from "$lib/block-layout";
|
||||
import type { BlockLayout } from "$lib/block-layout";
|
||||
import type { Translations } from "$lib/translations";
|
||||
@@ -41,6 +42,7 @@
|
||||
OrganisationsBlockData,
|
||||
OpnFormBlockData,
|
||||
DeadlineBannerBlockData,
|
||||
LiveStrommixBlockData,
|
||||
} from "$lib/block-types";
|
||||
|
||||
let { layout, class: className = "", translations = {} }: { layout: RowContentLayout; class?: string; translations?: Translations | null } = $props();
|
||||
@@ -140,6 +142,8 @@
|
||||
<OpnFormBlock block={item as OpnFormBlockData} />
|
||||
{:else if blockType(item) === "deadline_banner"}
|
||||
<DeadlineBannerBlock block={item as DeadlineBannerBlockData} />
|
||||
{:else if blockType(item) === "live_strommix"}
|
||||
<StrommixBlock block={item as LiveStrommixBlockData} />
|
||||
{:else}
|
||||
<div class="col-span-12 rounded border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-800">
|
||||
Unbekannter Block: <code>{blockType(item)}</code>
|
||||
|
||||
Reference in New Issue
Block a user