From 43df53f84569c8a9611ff4982acb9d1725ed59ca Mon Sep 17 00:00:00 2001 From: CarterPerez-dev Date: Sun, 3 May 2026 09:40:02 -0400 Subject: [PATCH] feat(monitor/frontend): ISS panel (live position + next-pass placeholder for phase 6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four-row layout: Lat/Lon (degree symbol, mono), Alt (km, mono tabular), Vel (km/h with thousands separator, mono tabular), Next Pass (— stub). Reads snapshot.iss_position directly (no per-panel store — current position is the only thing the panel cares about, and the wheretheiss.at collector pushes a complete state every 10s, so latest-event semantics match what the panel needs). Real shape verified: latitude/longitude/ altitude/velocity/timestamp/fetched_at — all numeric except fetched_at ISO string. Stale threshold 30s (3x the 10s poll cadence). Next Pass row stays "—" muted --fg-3 — Phase 6 wires the SGP4-driven user-observer-location pass prediction per spec §10.4. No "sign in to add observer" CTA: operator UI doesn't onboard. Plan 5 Task 21 Design QA: degree symbol + mono coords, alt/vel mono tabular, no decorative ISS-tracing widget (the centerpiece globe owns that), next-pass row visible-but-muted (never hidden). --- .../src/pages/dashboard/Dashboard.tsx | 2 + .../src/pages/panels/ISSPanel.module.scss | 29 ++++++++ .../frontend/src/pages/panels/ISSPanel.tsx | 70 +++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/panels/ISSPanel.module.scss create mode 100644 PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/panels/ISSPanel.tsx diff --git a/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/Dashboard.tsx b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/Dashboard.tsx index 6e76f1d2..d191ab81 100644 --- a/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/Dashboard.tsx +++ b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/Dashboard.tsx @@ -6,6 +6,7 @@ import { BTCPanel } from '@/pages/panels/BTCPanel' import { CVEVelocityPanel } from '@/pages/panels/CVEVelocityPanel' import { DShieldPanel } from '@/pages/panels/DShieldPanel' import { ETHPanel } from '@/pages/panels/ETHPanel' +import { ISSPanel } from '@/pages/panels/ISSPanel' import { KEVPanel } from '@/pages/panels/KEVPanel' import { RansomwarePanel } from '@/pages/panels/RansomwarePanel' import { SpaceWeatherPanel } from '@/pages/panels/SpaceWeatherPanel' @@ -33,6 +34,7 @@ export function Dashboard(): React.ReactElement { +
diff --git a/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/panels/ISSPanel.module.scss b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/panels/ISSPanel.module.scss new file mode 100644 index 00000000..afede6b4 --- /dev/null +++ b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/panels/ISSPanel.module.scss @@ -0,0 +1,29 @@ +// ©AngelaMos | 2026 +// ISSPanel.module.scss + +.row { + display: grid; + grid-template-columns: 90px 1fr; + gap: 8px; + padding: 4px 8px; + align-items: center; + font-family: var(--font-mono); + font-size: var(--type-body); + font-variant-numeric: tabular-nums; +} + +.label { + font-family: var(--font-sans); + font-size: var(--type-label); + letter-spacing: var(--letter-spacing-label); + text-transform: uppercase; + color: var(--fg-3); +} + +.value { + color: var(--fg-1); +} + +.muted { + color: var(--fg-3); +} diff --git a/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/panels/ISSPanel.tsx b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/panels/ISSPanel.tsx new file mode 100644 index 00000000..37d691df --- /dev/null +++ b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/panels/ISSPanel.tsx @@ -0,0 +1,70 @@ +// ©AngelaMos | 2026 +// ISSPanel.tsx + +import { useSnapshot } from '@/api/snapshot' +import styles from './ISSPanel.module.scss' +import { Panel } from './Panel' + +const STALE_AFTER_MS = 30_000 +const COORDS_DECIMALS = 2 +const ALT_DECIMALS = 0 + +interface IssPositionData { + latitude: number + longitude: number + altitude: number + velocity: number + timestamp: number + fetched_at?: string +} + +export function ISSPanel(): React.ReactElement { + const { data } = useSnapshot() + const iss = data?.iss_position as IssPositionData | undefined + + const lastTickAt = iss?.fetched_at + ? new Date(iss.fetched_at).getTime() + : undefined + const isStale = + lastTickAt !== undefined + ? Date.now() - lastTickAt > STALE_AFTER_MS + : undefined + + return ( + +
+ Lat, Lon + + {iss + ? `${iss.latitude.toFixed(COORDS_DECIMALS)}°, ${iss.longitude.toFixed(COORDS_DECIMALS)}°` + : '—'} + +
+
+ Alt + + {iss ? `${iss.altitude.toFixed(ALT_DECIMALS)} km` : '—'} + +
+
+ Vel + + {iss ? `${Math.round(iss.velocity).toLocaleString()} km/h` : '—'} + +
+
+ Next Pass + +
+
+ ) +} + +ISSPanel.displayName = 'ISSPanel'