SPB Git
5.7 KB

# SatelliteIndex web — frontend guide (apps/web)

Next 16 (App Router, React 19, TypeScript strict, Tailwind v4). Server components by default; client components only for interactivity ('use client'). The FastAPI backend runs on http://127.0.0.1:8311; the browser talks to the same origin (/api/v1/* is rewritten to the API in next.config.ts).

# Data access

  • Server components: import { api, safe, ApiError } from '@/lib/api' — typed wrappers for every endpoint (see src/lib/api.ts, types in src/lib/types.ts). api.x() throws ApiError (.notFound, .unavailable). Use notFound() from next/navigation on 404. Wrap optional panels with safe() and render <Unavailable/> when null. Never crash a page because one panel failed.
  • Client components: import { clientApi } from '@/lib/client-api' (positions, live position, track, search, view beacon).
  • Inspect any payload live: curl -s localhost:8311/api/v1/<path> | python3 -m json.tool | head -80. OpenAPI: localhost:8311/api/v1/docs.
  • Postgres aggregates can arrive as strings (Num = number | string | null): always go through num() / fmtInt() etc. from @/lib/format.
  • No hardcoded statistics, counts, timestamps or fake data. If something is missing, show "Unavailable" (<Unavailable what="…"/>).

# Design system (dark, scientific, premium — not a crypto dashboard, not generic SaaS cards)

Tokens in src/app/globals.css → Tailwind utilities: bg-space | bg-plane | bg-plane-2 | bg-plane-3, text-ink | text-ink-2 | text-ink-3, border-rule | border-rule-strong, text-accent | bg-accent | text-accent-ink | bg-accent-soft, text-accent-2, status text-active | text-warn | text-danger | text-inactive (+ -soft backgrounds), orbit classes text-leo | text-meo | text-geo | text-heo | text-other, chart series series-1..8. Utility classes: .panel (translucent bordered surface — use sparingly, prefer spatial composition and hairlines), .eyebrow (small caps label), .display (big headline), .mono / .tnum (telemetry, IDs, numbers), .container-x, .data-table (+ .stack to transform rows into stacked cards under 768 px — put data-label="…" on each <td>, className="primary" on the name cell, .num for numbers), .grid-bg, .dot / .pulse, .link, .scrollbar-thin, .no-scrollbar.

Shared components (do not modify; build local ones in your own folder if you need variants):

  • @/components/ui/section → Container, PageHeader, Section (eyebrow/title/action), Stat (big number tile)
  • @/components/ui/badges → StatusBadge, OrbitBadge, TypeBadge, MissionLabel, FreshnessBadge
  • @/components/ui/pagination → Pagination (URL-driven), @/components/ui/unavailable → Unavailable
  • @/components/charts/charts → HBars, Bars, StackedBars, AreaChart, Donut, Sparkline, Histogram (pure SVG, server-safe)
  • @/components/map/world-map → WorldMap (SVG equirectangular; tracks, markers; splitTrack for antimeridian), MAP_SIZE
  • @/lib/site → routes.*, SITE_NAME/SITE_URL/TAGLINE, ORBIT_CLASS_COLORS, STATUS_COLORS, MISSION_LABELS, OBJECT_TYPE_LABELS, EVENT_TYPE_LABELS
  • @/lib/format → fmtInt fmt1 fmt2 fmtCompact fmtKm fmtDeg fmtMinutes fmtPct fmtDate fmtDateTime fmtAgo fmtYear titleCase num
  • Layout (header, mobile tab bar, footer, ⌘K search dialog) is already mounted in src/app/layout.tsx. Pages render inside <main> without padding: wrap content in <Container> (max 1280) or <Container wide> (1600), or go full-bleed on purpose (globe).

Typography: Inter (UI) + JetBrains Mono (IDs/telemetry). Big display numbers use tnum. Icons: lucide-react.

# Mobile rules (mandatory — every feature is tested at 390 px before it is complete)

  • No horizontal overflow at 320–430 px. Tables → .data-table.stack or purpose-built list rows. Long IDs wrap or truncate.
  • Tap targets ≥ 44 px. The fixed bottom tab bar (58 px + safe area) is reserved by <body> padding; don't add another fixed bottom bar.
  • Desktop should use wide screens (two-column "terminal" layouts on detail pages: main visualisation + right telemetry panel).
  • DOM order = visual order on all breakpoints (no order: tricks). Heavy globe/3D features lazy-load (next/dynamic, ssr: false).

# SEO & sharing

Every public page exports generateMetadata (title, description, alternates.canonical, openGraph, twitter). Detail pages should add JSON-LD where sensible (<script type="application/ld+json">). Titles follow ISS (ZARYA) — Live Orbit, NORAD 25544 | SatelliteIndex (the layout template appends | SatelliteIndex automatically: pass only the first part).

# Freshness & sources (transparency is a feature)

Show "Orbit updated X ago · epoch age" and "Metadata updated X ago" using fmtAgo; expose a Sources section on detail pages (detail.sources, detail.provenance, detail.freshness). Derived values (orbit class, mission type, constellation membership, activity score) must be labelled derived with a link to /methodology.

# Verification before you report

  1. cd apps/web && pnpm typecheck must pass with zero errors (fix yours; if a shared type is wrong, say so in your report).
  2. The dev server already runs at http://localhost:8310 (hot reload). Load each page you built at 390 px and 1440 px with Playwright: node -e "…" importing /Users/simon-pierreboucher/Desktop/uqo-eval/node_modules/playwright/index.mjs (chromium), take screenshots into apps/web/qa/screens/<your-area>/, and check document.documentElement.scrollWidth <= innerWidth and zero console errors.
  3. Do not run next build and do not start another dev server (port 8310 is taken). Do not edit files outside your ownership list.
Files · b375eb55330c778055d03ca344b7dc1be9a5ddc0
Menu

Theme