SPB Git forge
38commits 1branches 0releases
338.7 MBsize
maindefault branch
6 h agolast push
HTML 53.9% TypeScript 44.5% JavaScript 0.6% SQL 0.5%
2.9 KB · 59 lines tsx
Raw Blame History
1import type { Metadata } from "next";2import { Suspense } from "react";3import { Map as MapIcon } from "lucide-react";4import { FacilityFilterBar } from "@/components/facility-filters";5import { FacilityTable } from "@/components/facility-table";6import { Button } from "@/components/ui/button";7import { Page, PageHeader } from "@/components/ui/card";8import { Pagination } from "@/components/ui/pagination";9import { OfflineNotice } from "@/components/ui/states";10import { api } from "@/lib/api";11import { fmtInt } from "@/lib/format";12import { filtersToQuery, parseFilters } from "@/lib/query";13import { routes } from "@/lib/routes";1415export const revalidate = 120;1617export const metadata: Metadata = {18  title: "Data centers",19  description: "Filterable index of data center facilities worldwide: operator, market, country, status, capacity, type and confidence.",20  alternates: { canonical: "/datacenters" },21};2223const PER_PAGE = 50;2425export default async function DatacentersPage({ searchParams }: { searchParams: Promise<Record<string, string | string[] | undefined>> }) {26  const sp = await searchParams;27  const filters = parseFilters(sp);28  const page = filters.page ?? 1;29  const query = { ...filters, page, per_page: filters.per_page ?? PER_PAGE, sort: filters.sort ?? "mw", order: filters.order ?? (filters.sort === "name" ? "asc" : "desc") };30  const res = await api.facilities(query);31  const hrefFor = (p: number) => routes.datacenters(filtersToQuery({ ...filters, page: p }));32  const mapQs = filtersToQuery({ ...filters, page: undefined, per_page: undefined, sort: undefined, order: undefined });3334  return (35    <Page>36      <PageHeader37        eyebrow="Index"38        title="Data centers"39        description={res.ok ? `${fmtInt(res.total)} facilities indexed. Figures are sourced; capacity shows the best known figure (IT, total or ≈ planned).` : "Every indexed facility with operator, market, status, capacity and confidence."}40        aside={41          <Button href={routes.map(mapQs)} size="sm">42            <MapIcon className="size-4" strokeWidth={1.75} /> Open in map43          </Button>44        }45      />46      {!res.ok && <OfflineNotice detail={res.error} className="mb-3" />}47      <div className="sticky top-12 z-[20] -mx-3 border-b border-hair bg-paper/95 px-3 py-2 backdrop-blur sm:top-14 sm:mx-0 sm:px-0">48        <Suspense>49          <FacilityFilterBar basePath={routes.datacenters()} total={res.ok ? res.total : null} />50        </Suspense>51      </div>52      <div className="pt-1">53        <FacilityTable items={res.items} filters={query} basePath={routes.datacenters()} emptyTitle={res.ok ? "No facilities match these filters" : "Facilities unavailable"} emptyDescription={res.ok ? "Loosen a filter or clear them all." : "The index API is not reachable right now."} />54      </div>55      <Pagination page={page} perPage={query.per_page} total={res.total} hrefFor={hrefFor} label="facilities" />56    </Page>57  );58}59