spb/satelliteindex
Public
TypeScript 66.5%
Python 30.9%
JavaScript 1.4%
CSS 0.7%
1import { ArrowRight, Building2, Globe2, MapPin, Orbit, Rocket, Satellite } from 'lucide-react';2import Link from 'next/link';3import { fmtInt } from '@/lib/format';4import type { SearchPayload, SearchResult } from '@/lib/types';56type EntityType = SearchResult['entity_type'];78const GROUPS: { type: EntityType; label: string; Icon: typeof Satellite }[] = [9 { type: 'satellite', label: 'Satellites', Icon: Satellite },10 { type: 'constellation', label: 'Constellations', Icon: Orbit },11 { type: 'operator', label: 'Operators', Icon: Building2 },12 { type: 'country', label: 'Countries', Icon: Globe2 },13 { type: 'launch', label: 'Launches', Icon: Rocket },14 { type: 'launch_site', label: 'Launch sites', Icon: MapPin },15];1617export function Shortcuts({ items }: { items: SearchPayload['shortcuts'] }) {18 if (!items.length) return null;19 return (20 <div className="mb-6">21 <p className="eyebrow mb-2">Filters matching your query</p>22 <ul className="flex flex-wrap gap-2">23 {items.map((s) => (24 <li key={s.href}>25 <Link href={s.href} className="inline-flex min-h-[40px] items-center gap-1.5 rounded-full border border-rule bg-plane-2 px-3 text-sm text-ink-2 hover:border-rule-strong hover:text-ink">26 <ArrowRight className="size-3.5 text-accent" aria-hidden /> {s.label}27 </Link>28 </li>29 ))}30 </ul>31 </div>32 );33}3435/** Results grouped by entity type in a fixed order; every row links to `result.href` from the API. */36export function GroupedResults({ results }: { results: SearchResult[] }) {37 const byType = new Map<EntityType, SearchResult[]>();38 for (const r of results) byType.set(r.entity_type, [...(byType.get(r.entity_type) ?? []), r]);39 const groups = GROUPS.filter((g) => byType.has(g.type));40 return (41 <div className="space-y-10">42 {groups.map(({ type, label, Icon }) => {43 const rows = byType.get(type) ?? [];44 return (45 <section key={type} aria-labelledby={`grp-${type}`}>46 <div className="mb-2 flex items-baseline justify-between gap-3">47 <h2 id={`grp-${type}`} className="flex items-center gap-2 text-base font-semibold text-ink">48 <Icon className="size-4 text-ink-3" aria-hidden /> {label}49 </h2>50 <span className="tnum text-xs text-ink-3">{fmtInt(rows.length)}</span>51 </div>52 <ul className="divide-y divide-rule border-t border-rule">53 {rows.map((r) => (54 <li key={`${r.entity_type}-${r.entity_id}`}>55 <Link href={r.href} className="group flex min-h-[52px] items-center gap-3 py-2.5">56 <span className="min-w-0 flex-1">57 <span className="block truncate text-[15px] text-ink group-hover:text-accent">{r.title}</span>58 {r.subtitle && <span className="block truncate text-xs text-ink-3">{r.subtitle}</span>}59 </span>60 <ArrowRight className="size-4 shrink-0 text-ink-3 group-hover:text-accent" aria-hidden />61 </Link>62 </li>63 ))}64 </ul>65 </section>66 );67 })}68 </div>69 );70}7172export const EXAMPLE_QUERIES = ['ISS', 'Starlink', '25544', '1998-067A', 'SpaceX', 'Canada', 'GPS', 'Falcon 9'];73