From 92d8a1aabfbf3ac22283d98c973ba00b7ca09218 Mon Sep 17 00:00:00 2001 From: CarterPerez-dev Date: Sun, 3 May 2026 07:00:27 -0400 Subject: [PATCH] feat(monitor/frontend): bottom ticker (world events marquee, 60s loop) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 32px strip in the bottomticker grid-area that renders the ticker Zustand store as a horizontally scrolling marquee — one of the few places motion is allowed because new entries pushing older ones along IS data changing. Source label uppercase + letter-spaced --fg-3, headline body --fg-2, relative timestamp mono tabular --fg-3. Empty state is a bare strip (no "no events" message, no illustration) — operator UI doesn't onboard. The track only renders when items.length > 0 so the marquee animation isn't running over an empty list. formatRel uses module-scope SECONDS_PER_MINUTE / SECONDS_PER_HOUR / MS_PER_SECOND constants (no inline magic numbers). Animation uses kebab keyframe name (stylelint keyframes-name-pattern). --- .../pages/dashboard/BottomTicker.module.scss | 48 +++++++++++++++++++ .../src/pages/dashboard/BottomTicker.tsx | 41 ++++++++++++++++ .../src/pages/dashboard/Dashboard.tsx | 2 + 3 files changed, 91 insertions(+) create mode 100644 PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/BottomTicker.module.scss create mode 100644 PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/BottomTicker.tsx diff --git a/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/BottomTicker.module.scss b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/BottomTicker.module.scss new file mode 100644 index 00000000..496358b8 --- /dev/null +++ b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/BottomTicker.module.scss @@ -0,0 +1,48 @@ +// ©AngelaMos | 2026 +// BottomTicker.module.scss + +.ticker { + grid-area: bottomticker; + height: 32px; + border-top: 1px solid var(--fg-4); + background: var(--bg); + overflow: hidden; + position: relative; +} + +.track { + display: flex; + align-items: center; + height: 100%; + gap: 24px; + padding-left: 100%; + white-space: nowrap; + animation: ticker-scroll 60s linear infinite; +} + +@keyframes ticker-scroll { + to { + transform: translateX(-100%); + } +} + +.item { + display: inline-flex; + align-items: center; + gap: 8px; + font-size: var(--type-body); + color: var(--fg-2); +} + +.source { + font-size: var(--type-label); + letter-spacing: var(--letter-spacing-label); + color: var(--fg-3); + text-transform: uppercase; +} + +.ts { + font-family: var(--font-mono); + color: var(--fg-3); + font-variant-numeric: tabular-nums; +} diff --git a/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/BottomTicker.tsx b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/BottomTicker.tsx new file mode 100644 index 00000000..7d914a7e --- /dev/null +++ b/PROJECTS/advanced/monitor-the-situation-dashboard/frontend/src/pages/dashboard/BottomTicker.tsx @@ -0,0 +1,41 @@ +// ©AngelaMos | 2026 +// BottomTicker.tsx + +import { useTicker } from '@/stores/ticker' +import styles from './BottomTicker.module.scss' + +const MS_PER_SECOND = 1000 +const SECONDS_PER_MINUTE = 60 +const SECONDS_PER_HOUR = 3600 + +export function BottomTicker(): React.ReactElement { + const items = useTicker((s) => s.items) + return ( +
+ {items.length > 0 && ( +
+ {items.map((item) => ( + + {item.source} + {item.headline} + {formatRel(item.ts)} + + ))} +
+ )} +
+ ) +} + +BottomTicker.displayName = 'BottomTicker' + +function formatRel(ts: number): string { + const diff = (Date.now() - ts) / MS_PER_SECOND + if (diff < SECONDS_PER_MINUTE) { + return `${Math.floor(diff)}s ago` + } + if (diff < SECONDS_PER_HOUR) { + return `${Math.floor(diff / SECONDS_PER_MINUTE)}m ago` + } + return `${Math.floor(diff / SECONDS_PER_HOUR)}h ago` +} 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 5caec687..5a28da01 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 @@ -2,6 +2,7 @@ // Dashboard.tsx import { AlertBanner } from './AlertBanner' +import { BottomTicker } from './BottomTicker' import styles from './Dashboard.module.scss' import { TopStrip } from './TopStrip' @@ -15,6 +16,7 @@ export function Dashboard(): React.ReactElement {