spb/datacenterindex
Public
HTML 53.9%
TypeScript 44.5%
JavaScript 0.6%
SQL 0.5%
1"use client";23import type { ExploreResponse, FacilityStatus } from "@dci/core";4import { BarList, ColumnChart } from "@/components/ui/charts";5import { HonestEmpty } from "@/components/intelligence/shared";6import { fmtInt, fmtMw } from "@/lib/format";7import { STATUS_SHORT } from "@/lib/labels";89/** byStatus / byCountry / byYear charts over the whole filtered set (not the current page). */10export function ExploreCharts({ charts, entity, countryHrefBase }: { charts: ExploreResponse["charts"]; entity: "facilities" | "projects"; /** href prefix the iso2 is appended to (function props cannot cross the RSC boundary) */ countryHrefBase: string }) {11 const countryHref = (iso2: string) => `${countryHrefBase}${encodeURIComponent(iso2)}`;12 const byStatus = charts.byStatus.map((s) => ({ label: STATUS_SHORT[s.key as FacilityStatus] ?? s.key, value: s.count, secondary: s.mw }));13 const byYear = charts.byYear.filter((y) => y.year >= 1990 && y.year <= 2100).map((y) => ({ label: String(y.year), value: y.count, secondary: y.mw }));14 const byCountry = charts.byCountry.slice(0, 15);15 const mwTotal = charts.byStatus.reduce((a, s) => a + (s.mw ?? 0), 0);16 return (17 <div className="grid gap-8 lg:grid-cols-2">18 <div>19 <div className="mb-2 flex items-baseline justify-between">20 <h2 className="text-[13.5px] font-semibold text-ink">By status</h2>21 <span className="figure text-[11.5px] text-ink-3">records{mwTotal > 0 ? ` · ${fmtMw(mwTotal)} published` : ""}</span>22 </div>23 {byStatus.length ? (24 <div className="pt-8">25 <ColumnChart data={byStatus} format={(v) => fmtInt(v)} height={160} ariaLabel="Records by lifecycle status" />26 </div>27 ) : (28 <HonestEmpty title="No status distribution" />29 )}30 {byStatus.length > 0 && (31 <ul className="mt-2 grid grid-cols-2 gap-x-4 gap-y-0.5 text-[11.5px] text-ink-3 sm:grid-cols-3">32 {charts.byStatus.map((s) => (33 <li key={s.key} className="flex justify-between gap-2">34 <span>{STATUS_SHORT[s.key as FacilityStatus] ?? s.key}</span>35 <span className="figure">36 {fmtInt(s.count)} · {s.mw === null ? "MW —" : fmtMw(s.mw)}37 </span>38 </li>39 ))}40 </ul>41 )}42 </div>43 <div>44 <div className="mb-2 flex items-baseline justify-between">45 <h2 className="text-[13.5px] font-semibold text-ink">{entity === "projects" ? "By expected opening year" : "By opening year"}</h2>46 <span className="figure text-[11.5px] text-ink-3">records</span>47 </div>48 {byYear.length ? (49 <div className="pt-8">50 <ColumnChart data={byYear} format={(v) => fmtInt(v)} height={160} color="var(--ink-2)" ariaLabel="Records by year" />51 </div>52 ) : (53 <HonestEmpty title="No dated records" detail={entity === "projects" ? "no expected opening published" : "no opening date published in this set"} />54 )}55 <p className="mt-1.5 text-[11px] text-ink-4">Records without a published date are not charted.</p>56 </div>57 <div className="lg:col-span-2">58 <div className="mb-2 flex items-baseline justify-between">59 <h2 className="text-[13.5px] font-semibold text-ink">By country</h2>60 <span className="figure text-[11.5px] text-ink-3">top {byCountry.length} · records · published MW</span>61 </div>62 {byCountry.length ? <BarList items={byCountry.map((c) => ({ label: c.name, value: c.count, href: countryHref(c.key), secondary: c.mw === null ? "MW —" : fmtMw(c.mw) }))} format={(v) => fmtInt(v)} /> : <HonestEmpty title="No country distribution" />}63 </div>64 </div>65 );66}67