spb/datacenterindex
Public
HTML 53.9%
TypeScript 44.5%
JavaScript 0.6%
SQL 0.5%
1import type { ProjectSummary } from "@dci/core";2import Link from "next/link";3import { EstimateMark, StatusBadge } from "@/components/ui/badge";4import { CountryFlag } from "@/components/ui/country-flag";5import { EmptyState } from "@/components/ui/states";6import { fmtMw, fmtPartialDateSafe, fmtUsd, placeLine } from "@/lib/format-project";7import { routes } from "@/lib/routes";89/** ProjectSummary rows: name · operator · location · MW · status · expected. */10export function ProjectList({ items, emptyTitle = "No projects yet", emptyDescription = "Announced and under-construction projects will appear once ingested." }: { items: ProjectSummary[]; emptyTitle?: string; emptyDescription?: string }) {11 if (!items.length) return <EmptyState title={emptyTitle} description={emptyDescription} compact />;12 return (13 <ul className="divide-y divide-hair">14 {items.map((p) => (15 <li key={p.id} className="relative grid grid-cols-[minmax(0,1fr)_auto] gap-x-3 py-2.5">16 <div className="min-w-0">17 <div className="flex flex-wrap items-center gap-x-2 gap-y-1">18 {/* stretched link: the whole row opens the project; the operator link sits above via z-index */}19 <Link href={routes.project(p.slug)} className="truncate text-[13.5px] font-medium text-ink hover:text-accent hover:underline after:absolute after:inset-0 after:content-['']">20 {p.name}21 </Link>22 <StatusBadge status={p.status} short size="xs" />23 </div>24 <div className="mt-0.5 flex flex-wrap items-center gap-x-1.5 text-[12px] text-ink-3">25 {p.operator ? (26 <Link href={routes.operator(p.operator.slug)} className="relative z-[1] inline-flex min-h-6 items-center text-ink-2 hover:text-ink">27 {p.operator.name}28 </Link>29 ) : (30 <span>Operator unknown</span>31 )}32 <span aria-hidden>·</span>33 <CountryFlag iso2={p.countryIso2} size={12} />34 <span className="truncate">{placeLine(p)}</span>35 {p.investmentUsd !== null && (36 <>37 <span aria-hidden>·</span>38 <span className="figure">{fmtUsd(p.investmentUsd)}</span>39 </>40 )}41 </div>42 </div>43 <div className="flex flex-col items-end text-right">44 <span className="figure text-[14px] font-medium text-ink">45 {p.plannedMw === null ? <span className="text-ink-4">—</span> : (46 <>47 <EstimateMark className="mr-0.5 text-[11px]" />48 {fmtMw(p.plannedMw)}49 </>50 )}51 </span>52 <span className="figure text-[11px] text-ink-3" title="Expected opening">53 {p.expectedOpening ? fmtPartialDateSafe(p.expectedOpening) : "TBD"}54 </span>55 </div>56 </li>57 ))}58 </ul>59 );60}61