feat(blocks): live_strommix widget for energy-charts data
Deploy / verify (push) Successful in 47s
Deploy / deploy (push) Successful in 53s

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:
Peter Meier
2026-05-04 22:57:40 +02:00
parent a2ca0db9ca
commit 8ddfd026bc
5 changed files with 461 additions and 0 deletions
+26
View File
@@ -331,6 +331,32 @@ export interface OrganisationsBlockData {
layout?: BlockLayout;
}
/** Live-Strommix-Widget (_type: "live_strommix"). Editorial-Konfig + Platzierungs-Instanz.
* Fetched live data from /api/strommix (aggregator) and renders status bucket
* derived from `(wind_onshore + wind_offshore + solar) / load * 100`. */
export interface LiveStrommixBlockData {
_type?: "live_strommix";
_slug?: string;
intro_headline?: string;
intro_text?: string;
threshold_dunkelflaute?: number;
threshold_niedrig?: number;
threshold_mittel?: number;
label_dunkelflaute?: string;
label_niedrig?: string;
label_mittel?: string;
label_hoch?: string;
argument_dunkelflaute?: string;
argument_niedrig?: string;
argument_mittel?: string;
argument_hoch?: string;
enable_france_comparison?: boolean;
disclaimer?: string;
/** Reference (resolved: { _slug, _type, ...page-fields } or just slug string). */
explanation_page?: string | { _slug?: string; title?: string };
layout?: BlockLayout;
}
/** Kalender-Item (calendar_item) aus OpenAPI; terminZeit = ISO date-time. */
export type CalendarItemData = components["schemas"]["calendar_item"];