TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1import type { Metadata } from 'next';2import { Suspense } from 'react';3import { PageHeader } from '@/components/ui/page-header';4import { Segmented } from '@/components/ui/tabs';5import { FilterBar } from '@/components/ui/filter-bar';6import { Card, EmptyState, Badge } from '@/components/ui/primitives';7import { listAuctionHouses, listAuctions } from '@/lib/queries/market-lists';8import { fmtDate, fmtRelative } from '@/lib/format';9import { catName } from '@/lib/taxonomy';10import { FAMILIES } from '@rareindex/taxonomy';11import { sp1, spEnum, withParams, type SP } from '@/lib/search-params';1213export const metadata: Metadata = { title: 'Auction calendar', description: 'Upcoming collectible auctions today, this week and this month, filterable by category, house and region.' };1415export default async function CalendarPage({ searchParams }: { searchParams: Promise<SP> }) {16 const sp = await searchParams;17 const range = spEnum(sp, 'range', ['today', 'week', 'month'] as const, 'week');18 const category = sp1(sp, 'category') ?? null;19 const house = sp1(sp, 'house') ?? null;20 const now = new Date();21 const to = new Date(now.getTime() + (range === 'today' ? 1 : range === 'week' ? 7 : 31) * 86_400_000);22 const [auctions, houses] = await Promise.all([listAuctions({ from: now, to, category, house, limit: 200 }), listAuctionHouses()]);23 const byDay = new Map<string, typeof auctions>();24 for (const a of auctions) {25 const d = (a.endsAt ?? a.startsAt ?? now).toISOString().slice(0, 10);26 (byDay.get(d) ?? byDay.set(d, []).get(d)!).push(a);27 }28 const params = { category: category ?? undefined, house: house ?? undefined };29 return (30 <div>31 <PageHeader kicker="Auction houses" crumbs={[{ label: 'Auctions', href: '/auctions' }, { label: 'Calendar' }]} title="Auction calendar" description="Auctions closing in the selected window, grouped by day (UTC)." compact />32 <div className="mb-4 flex flex-wrap items-end justify-between gap-3">33 <Segmented active={range} options={[{ id: 'today', label: 'Today', href: withParams('/auctions/calendar', { ...params, range: 'today' }) }, { id: 'week', label: 'This week', href: withParams('/auctions/calendar', { ...params, range: 'week' }) }, { id: 'month', label: 'This month', href: withParams('/auctions/calendar', { ...params, range: 'month' }) }]} />34 <Suspense>35 <FilterBar fields={[{ name: 'category', label: 'Category', type: 'select', options: FAMILIES.map((f) => ({ value: f.slug, label: f.name })), width: 'w-44' }, { name: 'house', label: 'House', type: 'select', options: houses.map((h) => ({ value: h.house, label: h.house })), width: 'w-44' }]} />36 </Suspense>37 </div>38 {byDay.size ? (39 <div className="space-y-4">40 {[...byDay.entries()].sort(([a], [b]) => (a < b ? -1 : 1)).map(([day, items]) => (41 <Card key={day}>42 <div className="border-b border-border px-4 py-2 text-xs font-semibold">{fmtDate(day, { month: 'long' })}</div>43 <ul className="divide-y divide-border text-[12px]">44 {items.map((a) => (45 <li key={a.id} className="flex flex-wrap items-center justify-between gap-2 px-4 py-2">46 <div className="min-w-0">47 <a href={a.url} target="_blank" rel="noopener nofollow" className="font-medium text-fg hover:underline">48 {a.name}49 </a>50 <div className="text-[11px] text-muted">51 {a.auctionHouse}52 {a.location ? ` · ${a.location}` : ''}53 {a.categorySlugs.length ? ` · ${a.categorySlugs.slice(0, 3).map(catName).join(', ')}` : ''}54 </div>55 </div>56 <div className="flex items-center gap-2 text-[11px] text-muted">57 <Badge tone={a.status === 'live' ? 'gain' : 'index'}>{a.status}</Badge>58 <span>{a.endsAt ? `closes ${fmtDate(a.endsAt, { time: true })} (${fmtRelative(a.endsAt)})` : a.startsAt ? `starts ${fmtDate(a.startsAt, { time: true })}` : ''}</span>59 <span>· {a.lotsTracked} lots</span>60 </div>61 </li>62 ))}63 </ul>64 </Card>65 ))}66 </div>67 ) : (68 <Card>69 <EmptyState title="No auctions in this window" description="Auctions appear once auction-house connectors publish catalogues. Try a wider range." />70 </Card>71 )}72 </div>73 );74}75