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%
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.