SPB Git forge
3commits 1branches 0releases
417.0 KBsize
maindefault branch
10 days agolast push
TypeScript 66.5% Python 30.9% JavaScript 1.4% CSS 0.7%
3.3 KB · 73 lines tsx
Raw Blame History
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