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 · 66 lines markdown
Rendered Raw Blame History
1# SatelliteIndex web — frontend guide (apps/web)23Next 16 (App Router, React 19, TypeScript strict, Tailwind v4). Server components by default; client components only for4interactivity (`'use client'`). The FastAPI backend runs on `http://127.0.0.1:8311`; the browser talks to the same origin5(`/api/v1/*` is rewritten to the API in `next.config.ts`).67## Data access89- **Server components**: `import { api, safe, ApiError } from '@/lib/api'` — typed wrappers for every endpoint (see `src/lib/api.ts`,10  types in `src/lib/types.ts`). `api.x()` throws `ApiError` (`.notFound`, `.unavailable`). Use `notFound()` from `next/navigation`11  on 404. Wrap optional panels with `safe()` and render `<Unavailable/>` when null. **Never crash a page because one panel failed.**12- **Client components**: `import { clientApi } from '@/lib/client-api'` (positions, live position, track, search, view beacon).13- Inspect any payload live: `curl -s localhost:8311/api/v1/<path> | python3 -m json.tool | head -80`. OpenAPI: `localhost:8311/api/v1/docs`.14- Postgres aggregates can arrive as **strings** (`Num = number | string | null`): always go through `num()` / `fmtInt()` etc. from `@/lib/format`.15- **No hardcoded statistics, counts, timestamps or fake data.** If something is missing, show "Unavailable" (`<Unavailable what="…"/>`).1617## Design system (dark, scientific, premium — not a crypto dashboard, not generic SaaS cards)1819Tokens 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`,20`border-rule | border-rule-strong`, `text-accent | bg-accent | text-accent-ink | bg-accent-soft`, `text-accent-2`, status21`text-active | text-warn | text-danger | text-inactive` (+ `-soft` backgrounds), orbit classes `text-leo | text-meo | text-geo | text-heo | text-other`,22chart series `series-1..8`. Utility classes: `.panel` (translucent bordered surface — use sparingly, prefer spatial composition and hairlines),23`.eyebrow` (small caps label), `.display` (big headline), `.mono` / `.tnum` (telemetry, IDs, numbers), `.container-x`,24`.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),25`.grid-bg`, `.dot` / `.pulse`, `.link`, `.scrollbar-thin`, `.no-scrollbar`.2627Shared components (do **not** modify; build local ones in your own folder if you need variants):28- `@/components/ui/section` → `Container`, `PageHeader`, `Section` (eyebrow/title/action), `Stat` (big number tile)29- `@/components/ui/badges` → `StatusBadge`, `OrbitBadge`, `TypeBadge`, `MissionLabel`, `FreshnessBadge`30- `@/components/ui/pagination` → `Pagination` (URL-driven), `@/components/ui/unavailable` → `Unavailable`31- `@/components/charts/charts` → `HBars`, `Bars`, `StackedBars`, `AreaChart`, `Donut`, `Sparkline`, `Histogram` (pure SVG, server-safe)32- `@/components/map/world-map` → `WorldMap` (SVG equirectangular; `tracks`, `markers`; `splitTrack` for antimeridian), `MAP_SIZE`33- `@/lib/site` → `routes.*`, `SITE_NAME/SITE_URL/TAGLINE`, `ORBIT_CLASS_COLORS`, `STATUS_COLORS`, `MISSION_LABELS`, `OBJECT_TYPE_LABELS`, `EVENT_TYPE_LABELS`34- `@/lib/format` → `fmtInt fmt1 fmt2 fmtCompact fmtKm fmtDeg fmtMinutes fmtPct fmtDate fmtDateTime fmtAgo fmtYear titleCase num`35- Layout (header, mobile tab bar, footer, ⌘K search dialog) is already mounted in `src/app/layout.tsx`. Pages render inside `<main>`36  **without** padding: wrap content in `<Container>` (max 1280) or `<Container wide>` (1600), or go full-bleed on purpose (globe).3738Typography: Inter (UI) + JetBrains Mono (IDs/telemetry). Big display numbers use `tnum`. Icons: `lucide-react`.3940## Mobile rules (mandatory — every feature is tested at 390 px before it is complete)4142- No horizontal overflow at 320–430 px. Tables → `.data-table.stack` or purpose-built list rows. Long IDs wrap or truncate.43- 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.44- Desktop should use wide screens (two-column "terminal" layouts on detail pages: main visualisation + right telemetry panel).45- DOM order = visual order on all breakpoints (no `order:` tricks). Heavy globe/3D features lazy-load (`next/dynamic`, `ssr: false`).4647## SEO & sharing4849Every public page exports `generateMetadata` (title, description, `alternates.canonical`, `openGraph`, `twitter`). Detail pages should50add JSON-LD where sensible (`<script type="application/ld+json">`). Titles follow `ISS (ZARYA) — Live Orbit, NORAD 25544 | SatelliteIndex`51(the layout template appends `| SatelliteIndex` automatically: pass only the first part).5253## Freshness & sources (transparency is a feature)5455Show "Orbit updated X ago · epoch age" and "Metadata updated X ago" using `fmtAgo`; expose a **Sources** section on detail pages56(`detail.sources`, `detail.provenance`, `detail.freshness`). Derived values (orbit class, mission type, constellation membership,57activity score) must be labelled *derived* with a link to `/methodology`.5859## Verification before you report60611. `cd apps/web && pnpm typecheck` must pass with zero errors (fix yours; if a shared type is wrong, say so in your report).622. The dev server already runs at `http://localhost:8310` (hot reload). Load each page you built at 390 px and 1440 px with Playwright:63   `node -e "…"` importing `/Users/simon-pierreboucher/Desktop/uqo-eval/node_modules/playwright/index.mjs` (chromium), take screenshots into64   `apps/web/qa/screens/<your-area>/`, and check `document.documentElement.scrollWidth <= innerWidth` and zero console errors.653. Do **not** run `next build` and do not start another dev server (port 8310 is taken). Do not edit files outside your ownership list.66