spb/datacenterindex
Public
HTML 53.9%
TypeScript 44.5%
JavaScript 0.6%
SQL 0.5%
1import type { ClaimDTO } from "@dci/core";2import { ExternalLink } from "lucide-react";3import { ConfidenceBadge, EstimateMark, ScopeBadge, SemanticsLabel, TierMark } from "@/components/ui/badge";4import { SourceBadge } from "@/components/ui/source-badge";5import { TBody, THead, Table, Td, Th, Tr } from "@/components/ui/table";6import { fmtDate, fmtValue } from "@/lib/format";7import { CLAIM_STATUS_LABEL, isSiteScopeLabel } from "@/lib/labels";8import { Evidence } from "./evidence-context";910/**11 * Every capacity / investment claim on a record: predicate · scope · value · source · published · confidence · tier.12 * Site-scoped claims come first; non-site claims (portfolio, company, metro…) are grouped under13 * "Figures not assigned to this site" so they are never read as the record's own MW.14 */15export function ClaimsTable({ claims, kind = "capacity", emptyNote }: { claims: ClaimDTO[]; kind?: "capacity" | "investment" | "all"; emptyNote?: string }) {16 const rows = claims.filter((c) => (kind === "all" ? true : kind === "capacity" ? /_mw$/.test(c.predicate) : /_usd$/.test(c.predicate)));17 if (!rows.length) return <p className="rounded-sm border border-dashed border-hair-2 px-3 py-2.5 text-[12.5px] text-ink-3">{emptyNote ?? `No ${kind === "all" ? "" : `${kind} `}claim has been extracted for this record yet — the figures above come from structured source fields (see Source evidence).`}</p>;18 const site = rows.filter((c) => isSiteScopeLabel(c.scope));19 const other = rows.filter((c) => !isSiteScopeLabel(c.scope));20 return (21 <div className="flex flex-col gap-4">22 {site.length > 0 && <Rows rows={site} />}23 {other.length > 0 && (24 <div>25 <div className="mb-1.5 flex flex-wrap items-baseline gap-x-2">26 <h3 className="text-[13px] font-semibold text-ink">Figures not assigned to this site</h3>27 <span className="text-[11.5px] text-ink-3">portfolio, company, metro, country or unknown scope — shown as evidence, never summed into this record</span>28 </div>29 <Rows rows={other} />30 </div>31 )}32 </div>33 );34}3536function Rows({ rows }: { rows: ClaimDTO[] }) {37 const sorted = [...rows].sort((a, b) => Number(b.isWinner) - Number(a.isWinner) || a.predicate.localeCompare(b.predicate) || ((a.publishedAt ?? a.retrievedAt) < (b.publishedAt ?? b.retrievedAt) ? 1 : -1));38 return (39 <Table dense>40 <THead>41 <tr>42 <Th>Figure</Th>43 <Th>Scope</Th>44 <Th numeric>Value</Th>45 <Th>Source</Th>46 <Th className="hidden lg:table-cell">Published</Th>47 <Th>Confidence</Th>48 <Th className="hidden md:table-cell">Tier</Th>49 <Th className="hidden md:table-cell">Status</Th>50 </tr>51 </THead>52 <TBody>53 {sorted.map((c) => (54 <Tr key={c.id} className={c.status !== "current" ? "text-ink-3" : undefined}>55 <Td primary>56 <span className="inline-flex flex-wrap items-center gap-1.5">57 <SemanticsLabel semantics={c.predicate} className="text-[12.5px] text-ink" />58 {c.isWinner && <span className="inline-flex h-[16px] items-center rounded-xs border border-accent/40 bg-accent-soft px-1 text-[9.5px] font-medium uppercase tracking-[0.06em] text-accent-ink">shown</span>}59 </span>60 </Td>61 <Td label="Scope">62 <ScopeBadge scope={c.scope} />63 </Td>64 <Td label="Value" numeric lead>65 <Evidence field={c.predicate} label={c.label}>66 <span className="figure">67 {c.isEstimate && <EstimateMark className="mr-0.5" />}68 {c.value !== null ? fmtValue(c.value, c.unit === "USD" ? "usd" : "mw") : (c.valueText ?? "—")}69 </span>70 </Evidence>71 </Td>72 <Td label="Source">73 <span className="inline-flex flex-wrap items-center gap-1.5">74 <SourceBadge kind={c.sourceKind} name={c.sourceName} url={c.url} publishedAt={c.publishedAt} retrievedAt={c.retrievedAt} documentId={c.documentId} />75 <a href={c.url} target="_blank" rel="noopener noreferrer" className="inline-flex items-center gap-1 text-[12px] text-ink-2 hover:text-accent">76 {c.sourceName} <ExternalLink className="size-3" />77 </a>78 </span>79 </Td>80 <Td label="Published" className="figure hidden text-[12px] text-ink-3 lg:table-cell">81 {c.publishedAt ? fmtDate(c.publishedAt) : "—"}82 </Td>83 <Td label="Confidence">84 <ConfidenceBadge level={c.confidence} size="xs" />85 </Td>86 <Td label="Tier" className="hidden md:table-cell">87 <TierMark tier={c.authorityTier} />88 </Td>89 <Td label="Status" className="hidden text-[12px] md:table-cell">90 {CLAIM_STATUS_LABEL[c.status]}91 </Td>92 </Tr>93 ))}94 </TBody>95 </Table>96 );97}98