// WattIQ Product Detail Page — single component, content-driven // Alle 9 secties renderen vanuit window.WATTIQ_PRODUCTS data const { useState, useMemo, useEffect } = React; // ============== ICONS (inline SVG) ============== const Icon = ({ name, size = 24, stroke = 1.6 }) => { const props = { width: size, height: size, fill: "none", stroke: "currentColor", strokeWidth: stroke, strokeLinecap: "round", strokeLinejoin: "round", }; switch (name) { // problem icons (used inside .icon) case "queue": return ; case "scale": return ; case "isolated": return ; case "report": return ; case "hours": return ; case "noise": return ; case "cost": return ; case "wait": return ; case "fixed": return ; case "spike": return ; case "solar": return ; case "fuel": return ; case "ban": return ; case "refuel": return ; case "noroof": return ; case "time": return ; case "loss": return ; // flow icons case "grid": return ; case "core": return ; case "ems": return ; case "balance": return ; case "load": return ; case "battery": return ; case "genset": return ; case "bridge": return ; case "charger": return ; case "ev": return ; case "fleet": return ; case "container": return ; case "deploy": return ; case "led": return ; case "auto": return ; case "dc": return ; case "rental": return ; case "stage": return ; // feature icons case "peak": return ; case "modular": return ; case "plug": return ; case "remote": return ; case "hybrid": return ; default: return ; } }; // ============== HERO STAGE (product image + meta overlay) ============== const HeroStage = ({ product }) => { return (
{product.stageMeta.map((m, i) => {m})}
{product.stageStatus}
{product.name}
{product.stageCorner[0]} {product.stageCorner[1]}
); }; // ============== HERO (Section 1) ============== const Hero = ({ product }) => (
Product · {product.family} — {product.category}

{product.headline}

{product.sublead}

Plan een gesprek Bekijk hoe het werkt
{product.heroMeta.map((m, i) => (
{m.k}
))}
); // ============== PROBLEM ICONS (mini diagrams) ============== const ProblemIcon = ({ kind }) => (
{/* grid */} {kind === "queue" && (<> 12 24 36 mnd project )} {kind === "hours" && (<> 24/7 DRAAIUREN )} {kind === "noise" && (<> {[1,2,3,4,5,6].map((i) => ( ))} 65+ dB(A) )} {kind === "cost" && (<> €/dag +340% )} {kind === "report" && (<> CSRD ? )} {kind === "scale" && (<> VAST )} {kind === "isolated" && (<> BATT )} {kind === "wait" && (<> {[0,1,2,3,4,5].map((i) => ( ))} 12 / 24 / 36 MND )} {kind === "fixed" && (<> FIXED €280k CapEx )} {kind === "spike" && (<> MAX )} {kind === "solar" && (<> EXPORT 0,02€ )} {kind === "fuel" && (<> 1L/uur VOOR 1.2 kW LED )} {kind === "ban" && (<> STAGE IIIA · BANNED )} {kind === "refuel" && (<> REFUEL 02:00 · OPERATOR )} {kind === "noroof" && (<> NO ROOF )} {kind === "time" && (<> {[0,1,2,3,4].map((i) => ( {i+1}d ))} )} {kind === "loss" && (<> AC DC AC −18% LOSS )}
); // ============== PROBLEM (Section 2) — Diagonal Hero (V6) ============== const Problem = ({ product }) => (
02 — Probleem

Vier blokkades.
Eén oorzaak:
starre infrastructuur.

{product.problemLead} Hieronder de patronen die we op elke locatie tegenkomen — en waarom geen losse oplossing volstaat.

{product.problems.map((p, i) => (
{p.num}

{p.title}

{p.body}

))}
); // ============== FLOW (Section 3) ============== const Flow = ({ product }) => (
03 — Hoe het werkt

{product.flowHead}

{product.flowLead}

{product.flow.map((n, i) => (
{n.t}
))}
{product.flowCaption.map((c, i) => (
{c.k}
))}
); // ============== FEATURES (Section 4) — Icon Strip Compact ============== const Features = ({ product }) => (
04 — Eigenschappen

{product.featuresHead}

Iedere feature toont operationeel voordeel — niet alleen wat het is.

{product.features.map((f, i) => (
{String(i + 1).padStart(2, "0")}

{f.title}

{f.body}

))}
); // ============== SCENARIO STAGE (mini diagram per type) ============== const ScenarioStage = ({ kind }) => (
SIM_001REAL-WORLD
LIVE
{/* y-axis labels */} 100% 50% 0% {kind === "site-load" && (<> {/* battery SoC area */} {/* genset bursts */} SoC GENSET 06:00 12:00 22:00 14-DAY · ROTTERDAM SITE )} {kind === "fuel-curve" && (<> {/* stacked curves */} BATTERY GENSET DAG 1 DAG 11 DAG 21 21-DAY OFF-GRID PROJECT )} {kind === "peak-shave" && (<> CAP {/* shaved peak */} SHAVED AANSLUITINGSLAST 07:00 12:00 18:00 )} {kind === "charge-curve" && (<> {/* multiple charging trucks */} {[0,1,2,3].map(i => ( ))} 8 TRUCKS · DC SESSIONS 18:00 00:00 06:00 2.4 MWh DELIVERED · 80kW GRID )} {kind === "backup-curve" && (<> FORGE 22 MIN BATTERY SoC SCHALEN VAN PIEK · 4-UURS WINDOW )} {kind === "light-cycle" && (<> {/* solar/battery cycle */} {[0,1,2].map(d => ( DAG{d+1} ))} SOLAR HARVEST + LED RUNTIME 14 NACHTEN · 0L DIESEL )} {kind === "harvest-curve" && (<> 30 kWp PIEK SOLAR HARVEST 06:00 13:00 20:00 )}
); // ============== SCENARIO (Section 5) — Three-Phase Cards + Sparklines ============== const SCN_LINES = [ "M0,40 L40,38 L80,18 L120,8 L160,12 L200,30 L240,42", "M0,30 L40,22 L80,16 L120,18 L160,28 L200,38 L240,42", "M0,40 L40,42 L80,32 L120,18 L160,10 L200,12 L240,22", ]; const Scenario = ({ product }) => { // pak eerste 3 stappen — als minder, vul aan const phases = product.scenarioSteps.slice(0, 3); return (
05 — Scenario

{product.scenarioHead}

{product.scenarioLead}

{phases.map((p, i) => (
PHASE 0{i + 1}
{p.t}

{p.title}

{p.body}

SAMPLE / 5 MIN LIVE
))}
); }; // ============== VARIANTS (Section 6a) — Sub-tab strip + Hero variant in focus ============== // Parses a model name into a series label + short variant identifier // "WattIQ Core 260" → { series: "CORE", variant: "260" } // "WattIQ Core Lite 40" → { series: "LITE", variant: "40" } // "WattIQ Forge D60" → { series: "FORGE", variant: "D60" } // "WattIQ Beacon H" → { series: "BEACON", variant: "H" } const parseModelName = (fullName, productName) => { let n = (fullName || "").replace(/^WattIQ\s+/i, ""); const base = (productName || "").replace(/^WattIQ\s+/i, ""); if (n.toLowerCase().startsWith(base.toLowerCase() + " ")) n = n.slice(base.length + 1); const parts = n.split(/\s+/).filter(Boolean); if (parts.length === 0) return { series: base.toUpperCase(), variant: "" }; const looksLikeSeries = /^[A-Za-z]+$/.test(parts[0]) && parts.length > 1; if (looksLikeSeries) { return { series: parts[0].toUpperCase(), variant: parts.slice(1).join(" ") }; } return { series: base.toUpperCase(), variant: parts.join(" ") }; }; // Per-variant photos — slug = lowercased model name with spaces → hyphens, "WattIQ " prefix stripped. // Fallback is the product's main image when no specific photo exists. const VARIANT_IMAGES = { // CORE "core-125": "assets/products/core-125.png", "core-260": "assets/products/core-260.png", "core-1000": "assets/products/core-2000.png", "core-2000": "assets/products/core-2000.png", "core-lite-40": "assets/products/core-lite-100.png", "core-lite-100": "assets/products/core-lite-100.png", "core-flex-50": "assets/products/core-flex-100-1.png", "core-flex-100": "assets/products/core-flex-100-1.png", // FUSION "fusion-gb30": "assets/products/fusion.png", "fusion-gsb20": "assets/products/fusion-2.png", "fusion-gsb60": "assets/products/fusion-3.png", // BRIDGE "bridge-30": "assets/products/bridge-1.png", "bridge-60": "assets/products/bridge-2.png", // GRID "grid-80": "assets/products/grid-80-1.png", "grid-200": "assets/products/grid-200-1.png", "grid-600": "assets/products/grid-600-1.png", "grid-800": "assets/products/grid-1000-1.png", "grid-1000": "assets/products/grid-1000-1.png", // FORGE "forge-d30": "assets/products/forge-d60.png", "forge-d60": "assets/products/forge-d60.png", "forge-d120": "assets/products/forge-d120.png", // BEACON "beacon-h": "assets/products/beacon-h-1.png", "beacon-d-hydra": "assets/products/beacon-d-hydra-1.png", "beacon-d-manual": "assets/products/beacon-d-manual.png", // RAY "ray-fold-10": "assets/products/ray-fold-1.png", "ray-fold-30": "assets/products/ray-fold-2.png", "ray-flex-10": "assets/products/ray-flex-1.png", "ray-flex-30": "assets/products/ray-flex-2.png", }; const getVariantImage = (fullName, fallback) => { if (!fullName) return fallback; const slug = fullName.replace(/^WattIQ\s+/i, "").replace(/\s+/g, "-").toLowerCase(); return VARIANT_IMAGES[slug] || fallback; }; const Variants = ({ product }) => { const models = product.models || []; if (models.length === 0) return null; // Reset selection back to first variant whenever the product family changes const [pickIdx, setPickIdx] = useState(0); useEffect(() => { setPickIdx(0); }, [product.id]); const m = models[pickIdx] || models[0]; const [name, useCase, capacity, power, specific, housing] = m; const code = name.replace(/^WattIQ\s+/i, "").replace(/\s+/g, "-").toUpperCase(); const photo = getVariantImage(name, product.image); return (
06 — Varianten

{models.length} configuraties.
Eén platform.

Kies de variant die past bij je locatie. Onder elke configuratie zit hetzelfde EMS en dezelfde Control-koppeling — alleen capaciteit, vermogen en behuizing verschillen.

{models.map((row, i) => { const p = parseModelName(row[0], product.name); return ( ); })}
WIQ-{code} SELECTED {name}
WIQ-{code}
{name}
— Capaciteit / Output
— Vermogen / Battery
— Specifiek
— Behuizing
— Use-case
Aanvraag deze variant Datasheet PDF
); }; // ============== SPECS (Section 6) — Card Grid Dense ============== // Pakt 3 representatieve modellen uit product.models — middelste = "POPULAIR" const Specs = ({ product }) => { const all = product.models || []; // pick 3: first, middle, last (or all if fewer) let picks; if (all.length >= 3) { picks = [all[0], all[Math.floor(all.length / 2)], all[all.length - 1]]; } else { picks = all.slice(0, 3); } // common spec rows pulled from product.specs (k/v pairs) const sp = product.specs || []; const findSpec = (re) => { const m = sp.find((s) => re.test(s.k)); return m ? m.v : ""; }; const ipRow = findSpec(/behuizing|housing|ip/i) || "IP54"; const tempRow = findSpec(/temp|operating/i) || "−20 / +50 °C"; const warrRow = findSpec(/garantie|warranty|inzetduur/i) || "10 jaar"; return (
06 — Specificaties

{product.specsHead}

{product.specsLead}

{picks.map((m, i) => { const isMid = i === 1 && picks.length === 3; return (
{isMid &&
POPULAIR
}
SKU 0{i + 1}

{m[0]}

{(m[1] || "").toUpperCase()}
{[ ["Capaciteit / Output", m[2]], ["Vermogen / Battery", m[3]], ["Specifiek", m[4]], ["Behuizing", m[5] || ipRow], ["Temp", tempRow], ].map(([k, v], j) => (
{k}
))}
); })}
{all.length} configuraties beschikbaar · PDF datasheet op aanvraag · Garantie: {warrRow}
); }; // ============== ECOSYSTEM (Section 7) — Constellation Map (V19) ============== const ECO_FAMILY = [ { id: "core", code: "CORE", t: "Core", role: "Storage", desc: "Modulaire BESS · 40 kWh – 2 MWh" }, { id: "fusion", code: "FUSN", t: "Fusion", role: "Hybrid", desc: "Hybrid skid · genset + battery" }, { id: "bridge", code: "BRDG", t: "Bridge", role: "Bridge", desc: "Net-tijdelijke aansluiting" }, { id: "grid", code: "GRID", t: "Grid", role: "Grid", desc: "Vaste netinfrastructuur" }, { id: "forge", code: "FRGE", t: "Forge", role: "Charge", desc: "EV-laadcluster · 50–400 kW" }, { id: "beacon", code: "BCON", t: "Beacon", role: "Light", desc: "Mobile lighting · solar-first" }, { id: "ray", code: "RAY", t: "Ray", role: "Solar", desc: "Solar harvest · direct-DC" }, ]; // 6 omringende posities (60° intervallen) — actief product zit altijd in centrum const ECO_ORBIT = [ { x: 50, y: 14 }, { x: 84, y: 30 }, { x: 86, y: 72 }, { x: 50, y: 88 }, { x: 14, y: 72 }, { x: 16, y: 30 }, ]; const Ecosystem = ({ product }) => { const others = ECO_FAMILY.filter((p) => p.id !== product.id).slice(0, 6); const hub = ECO_FAMILY.find((p) => p.id === product.id) || { code: product.family, t: product.name }; return (
07 — Ecosystem

Werkt zelfstandig.
Schaalt als systeem.

{product.ecoNote} {product.name} klikt in op WattIQ Control en werkt samen met andere modules.

{/* tiny background stars */} {/* connecting lines from hub to others */} {/* hub (active product, big orange) */}
{hub.code}
{hub.role || "Hub"}
{/* orbiting nodes */} {others.map((p, i) => { const o = ECO_ORBIT[i]; return (
{p.code}
{p.role}
); })} {/* footer caption */}
● MAP · 7 PRODUCTS · ONE PLATFORM {hub.code} ★ ACTIVE
{/* legend grid below */}
{others.map((p) => (
{p.code}
WattIQ {p.t}
{p.desc}
))}
); }; // ============== RESULTS (Section 8) — Live Dashboard ============== const Results = ({ product }) => { // pak 4 meest indrukwekkende uit 6 — eerste 4 voldoet const metrics = product.results.slice(0, 4); const sites = ["ALMERE-DC1 99.98%", "SCHIPHOL-B 99.91%", "R'DAM-FEST 100%", "UTRECHT-S2 99.87%", "ANTWERPEN-P3 99.94%"]; return (
08 — Resultaten

{product.resultsHead}

Operationele cijfers per product. Gemeten op real-world deployments.

LIVE · ALL {product.name.replace("WattIQ ", "").toUpperCase()} DEPLOYMENTS
UPDATED 14:32 · NL 21 · BE 7
{metrics.map((m, i) => (
{m.title.toUpperCase()}
▲ TRENDING UP · 12W
{m.body}
))}
{sites.map((s, i) => ● {s})}
); }; // ============== CTA (Section 9) ============== const CTA = ({ product }) => (
09 — Twee paden

Wat past bij nu?

Twee manieren om verder te gaan met {product.name}. Eén voor mensen die concreet zoeken; één voor mensen die eerst willen begrijpen.

● PAD A · CONCREET

Ik heb een
locatie in beeld.

Stuur de basisgegevens — type project, vermogen, duur. Wij komen binnen 24u terug met een eerste configuratie en richtprijs.

  • Quick-scan binnen 24u
  • Eerste configuratie + richtprijs
  • Inplannen technisch bezoek
  • Vrijblijvend, geen verplichting
START QUICK-SCAN
● PAD B · ORIËNTEREND

Ik wil eerst
begrijpen.

30 minuten met een van onze engineers. Geen sales-praat — een werksessie waarin we je situatie doorlopen en de mogelijkheden uitleggen.

  • 30 minuten · video of locatie
  • Met engineer, niet account-mgr
  • Doorlopen van vergelijkbare projecten
  • Geen verplichting tot vervolg
PLAN 30-MIN GESPREK
); // ============== PICKER ============== const Picker = ({ products, current, onPick }) => (
Productfamilies →
{products.map((p) => (
onPick(p.id)} > {p.family} WattIQ {p.name.replace("WattIQ ", "")}
))}
); // ============== TICKER ============== const Ticker = ({ product }) => (
WIQ-PRODUCT-DOC v3.4 · {product.family} · {product.name.toUpperCase()} · {product.category} · EMS-INTEGRATED · NL-ROTTERDAM
); // ============== APP ============== const ProductDetailApp = () => { const products = window.WATTIQ_PRODUCTS; const [currentId, setCurrentId] = useState(products[0].id); // Allow URL hash to drive product useEffect(() => { const fromHash = window.location.hash.replace("#", ""); if (fromHash && products.find((p) => p.id === fromHash)) { setCurrentId(fromHash); } const handler = () => { const h = window.location.hash.replace("#", ""); if (h && products.find((p) => p.id === h)) setCurrentId(h); }; window.addEventListener("hashchange", handler); return () => window.removeEventListener("hashchange", handler); }, []); const product = useMemo( () => products.find((p) => p.id === currentId) || products[0], [currentId] ); const handlePick = (id) => { setCurrentId(id); window.history.replaceState(null, "", `#${id}`); window.scrollTo({ top: 0, behavior: "smooth" }); }; return (
); }; window.ProductDetailApp = ProductDetailApp;