Files
vyndr/web/src/lib/futuresMove.js
T
builtbykev 1664e1b3d5 Wave 2B: Offseason never-dark hub — NewsWire + FuturesBoard on /explore
Extend /explore (ExploreHub) into the 365-day never-dark hub. Two new
self-hiding sections feed REAL always-available data into the offseason:

- NewsWire (components/vyndr/NewsWire.tsx): real ESPN headlines from
  /api/news/:sport (newest-first, mono timestamps, type chips, player/team
  links) + real injuries from /api/schedule/:sport/injuries (OUT/GTD chips,
  token colors). Reuses the retired TerminalTemplates INJURY_WIRE layout but
  never routes its sample constants. Self-hides when both feeds are empty.
- FuturesBoard (components/vyndr/FuturesBoard.tsx): real futures from
  /api/futures/:sport — championship/win-total/award markets, mono tabular
  prices + movement colored by the contract (shortening=green / drifting=amber
  / flat=dim, NEVER red; move shown ONLY when the backend supplies one).
  Carries the honest "TRACKED · NOT GRADED" label — no fabricated grades on
  futures. Self-hides when markets:[].
- ExploreHub is offseason-aware (via emptyState OFF_SEASON month check): the
  hub LEADS with futures + wire when the board is dark, COMPLEMENTS the live
  board in-season. Sport selector kept; each section self-hides independently.
- Testable pure helpers: lib/futuresMove.js (move→color, never red) +
  lib/newsFormat.js (timeAgo mono-stamp, ESPN type labels).

Contracts consumed (Wave 2A owns the proxy/service files); code self-hides on
fetch failure if a proxy isn't present yet.

Tests: tests/unit/newsWire.test.js + futuresBoard.test.js (26 new). Full suite
259 suites / 3144 green; web build exit 0. vyndrParityQA stays green
(mono data, no glitch on data surfaces).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 23:27:19 -04:00

41 lines
1.8 KiB
JavaScript

/* ============================================================
VYNDR — FUTURES MOVEMENT color/label (Wave 2B, Offseason Hub).
The color contract for a futures selection's price movement. HONESTY RULE:
futures are TRACKED, not model-graded — a movement is only ever shown when
the backend actually supplies a `move` (never fabricated). The color space
is deliberately narrow and NEVER red:
• shortening → the market moved TOWARD the selection → green (value/steam)
• drifting → the market moved AWAY → amber (caution)
• flat / absent → no movement to report → dim (say less)
Red is reserved system-wide for settled-negative outcomes; a futures drift
is NOT a miss, so it must never render red.
CommonJS so the .tsx surface imports it (allowJs) AND Jest exercises it.
============================================================ */
// Canonical move → CSS custom-property token. No branch returns the red
// (--miss) token — a normal drift is caution (amber), never a miss.
const MOVE_COLOR = {
shortening: 'var(--g-a)', // green — toward the selection (value)
drifting: 'var(--amber)', // amber — away from the selection (caution)
flat: 'var(--text-2)', // dim — no movement
};
/** moveColor(move) — the token for a move, defaulting to dim for flat/absent. */
function moveColor(move) {
const m = String(move || '').toLowerCase();
return MOVE_COLOR[m] || MOVE_COLOR.flat;
}
/** moveLabel(move) — a short mono glyph+word, or '' when there is no move. */
function moveLabel(move) {
const m = String(move || '').toLowerCase();
if (m === 'shortening') return '▼ SHORTENING';
if (m === 'drifting') return '▲ DRIFTING';
if (m === 'flat') return '— FLAT';
return '';
}
module.exports = { MOVE_COLOR, moveColor, moveLabel };