--- import Base from '../../layouts/Base.astro'; import MintCard from '../../components/MintCard.astro'; import ReviewByUrl from '../../components/ReviewByUrl.astro'; import { readCapabilities } from '@cashumints/shared'; import { fetchMint, fetchMints } from '../../lib/api'; import { localePath, useI18n, type Locale } from '../../i18n'; import { itemListNode } from '../../lib/schema'; import { isIndexableMint } from '../../lib/seo'; import { localePaths } from '../../i18n/paths'; /** One page per locale: `/mints`, `/es/mints`, `/nl/mints`. */ export const getStaticPaths = localePaths; interface Props { locale: Locale; } const { locale } = Astro.props; // `t` formats the numbers inside its own strings, so no separate formatter is needed. const t = useI18n(locale); const mints = await fetchMints(); /* The list payload carries no NUT information, so the "melt only" and "frozen" chips need each mint's cached info. One detail fetch per mint, at build time, against the API on the same machine: the mint pages already do exactly this in getStaticPaths. A mint whose detail cannot be read simply gets no chip. */ const capabilities = await Promise.all( mints.map((mint) => fetchMint(mint.host).then((detail) => readCapabilities(detail.info?.nuts)).catch(() => null), ), ); const online = mints.filter((m) => m.status === 'online').length; const offline = mints.filter((m) => m.status === 'offline').length; const description = t('mints.description', { total: mints.length, online, offline }); /* * The index, as a list a crawler can read in order. * * Only the mints whose own pages are indexable: listing a page here while telling a * crawler not to index it is a contradiction. The order is the server-rendered one, * which is the score order; the sort control rewrites the DOM but not this markup, * so `ItemListOrderDescending` describes what shipped. */ const canonical = new URL(localePath('/mints', locale), Astro.site).href; const schema = [ itemListNode({ canonical, fragment: 'mints', name: t('mints.heading'), items: mints.filter(isIndexableMint).map((mint) => ({ name: mint.name ?? mint.host, url: new URL(localePath(`/mint/${mint.host}`, locale), Astro.site).href, })), }), ]; --- {/* The same .page container the home page and the mint pages use, so the heading, the controls, the count line and the grid all share one left edge and one width. */}

{t('mints.heading')}

{t('mints.lede')}

{/* The way in for something this site has never seen. Every other route to writing a review starts from a page that already exists, which is exactly the case a reader with a brand new mint does not have. */}
{/* A pressed pill rather than a native checkbox: it is the same control the review filters use, and it is a real button, so it keeps the focus ring. */}

{t('mints.showing', { total: mints.length, online, offline })}

{mints.map((mint, i) => )}
{/* Two steps in one dialog: identify the mint, then the ordinary review form. */}