quarkshologridledger/reference/FRONTEND.md

765 lines
48 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Frontend Architecture: Quark's Holo-Grid Ledger
> **Status**: Design Analysis — Pre-Implementation
> **Dependencies**: `reference/ENGINE.md`, `data-source/taxonomy.yaml`, `AGENTS.md`
> **Framework**: TanStack Start + TanStack Router (file-based routing)
---
## 1. Layout Topology
The application uses a fixed-viewport, three-column terminal layout defined in `__root.tsx`. The root shell is persistent — only the center column swaps content via `<Outlet />`.
```
┌────────────────────────────────────────────────────────────────────────────┐
│ [=] QUARK'S HOLO-GRID LEDGER // SUB-NET RECEPTOR STARDATE: 54201.3 │
│ BALANCE: 4,250.00 GPL │
├──────────────┬────────────────────────────────┬────────────────────────────┤
│ │ │ │
│ LEFT PANEL │ CENTER PANEL │ RIGHT PANEL │
│ col-span-3 │ col-span-6 │ col-span-3 │
│ │ │ │
│ Navigation │ <Outlet /> │ Betslip │
│ Sidebar │ (route content) │ Sidecar │
│ │ │ │
│ 240px-ish │ flex-1 │ 280px-ish │
│ │ │ │
├──────────────┴────────────────────────────────┴────────────────────────────┤
│ SYSTEM RUNTIME: OK // CORE MODULES ACTIVE TANSTACK + VITE │
└────────────────────────────────────────────────────────────────────────────┘
```
### Responsive Breakpoints
```
DESKTOP (≥1280px): [col-3 Sidebar] [col-6 Main] [col-3 Betslip]
TABLET (768-1279): [col-2 Sidebar] [col-7 Main] [col-3 Betslip] (sidebar compressed)
MOBILE (<768): [Full-width Main] + bottom tab bar + slide-up betslip
```
On mobile, the sidebar collapses into a **bottom navigation bar** with 5 terminal command buttons, and the betslip becomes a pull-up sheet triggered from the bar.
```
┌────────────────────────────────────────┐
│ QUARK'S HOLO-GRID LEDGER 4,250 GPL │
├────────────────────────────────────────┤
│ │
│ MAIN CONTENT │
│ (full-width) │
│ │
│ │
├────────────────────────────────────────┤
│ GRID │ ALPHA │ BETA │ SLIP │ LEDGER │
│ ◈ │ α │ β │ ₲ │ ▤ │
└────────────────────────────────────────┘
```
---
## 2. Navigation Sidebar (Left Panel)
The sidebar is the primary navigation interface. It must feel like a hardware command console — numbered entries, hierarchical quadrant trees, and live status indicators.
### 2.1 Sidebar Sections
```
╔══════════════════════════════════╗
║ [ SYSTEM ARRAY ] Quadrants ║
╠══════════════════════════════════╣
║ ║
║ > GRID OVERVIEW [00] ║ ← Link to /
║ ║
║ ═══════════════════════════════ ║
║ > LIVE ENGAGEMENTS ║ ← Section header
║ ║
║ ▾ ALPHA QUADRANT [α] ║ ← Collapsible quadrant group
║ ├─ ◆SFC vs █BRG ●LIVE ║ ← Active match (link to /quadrants/{id})
║ │ 62% ████░░ 71% ║ ← Inline shield bars
║ ├─ ▼CRD vs ☼BAJ ●LIVE ║
║ │ 48% ███░░░ 55% ║
║ └─ ⚔KLG vs ◈ROM ○PRE ║ ← Pre-match (different indicator)
║ ║
║ ▸ BETA QUADRANT [β] ║ ← Collapsed quadrant
║ 2 active ║
║ ║
║ ▸ GAMMA QUADRANT [γ] ║
║ 1 active ║
║ ║
║ ▸ DELTA QUADRANT [δ] ║
║ 3 active ║
║ ║
║ ═══════════════════════════════ ║
║ > STATION ARCHIVES ║ ← Section header
║ ║
║ [01] Wager Ledger ║ ← Link to /ledger
║ [02] Faction Registry ║ ← Link to /registry/factions
║ [03] Venue Database ║ ← Link to /registry/venues
║ [04] Transmission Schedule ║ ← Link to /schedule
║ ║
╚══════════════════════════════════╝
```
### 2.2 Live Match Items
Each match in the sidebar should show at a glance:
- Faction glyphs and abbreviations (e.g., `◆SFC vs █BRG`)
- Match status indicator: `●LIVE` (pulsing green) / `○PRE` (amber) / `◉RES` (red)
- Compact shield capacity bars (optional, space permitting)
- Clicking navigates to `/quadrants/{sectorId}?quadrant=Alpha&currency=Latinum`
### 2.3 Sidebar Data Source
The sidebar reads from the `sector:index:active` Valkey set via a **server function** that returns all active match summaries. It polls at the same 2s interval as match telemetry.
```tsx
// Server function: returns lightweight match summaries for sidebar
const getActiveMatchIndex = createServerFn({ method: 'GET' })
.handler(async () => {
const matchIds = await valkey.smembers('sector:index:active')
// For each match, read just the fields needed for sidebar rendering
// (id, name, quadrant, status, factions.home.abbreviation, factions.away.abbreviation,
// factions.home.shieldCapacity, factions.away.shieldCapacity)
return summaries
})
```
---
## 3. Route Map
### 3.1 File-Based Route Structure
```
src/routes/
├── __root.tsx # Shell layout (header, sidebar, betslip, footer)
├── index.tsx # Grid Overview / Dashboard
├── quadrants/
│ ├── index.tsx # Quadrant listing (all matches, filterable)
│ └── $sectorId.tsx # Live match detail (the main event page)
├── ledger/
│ ├── index.tsx # Wager Ledger overview (active + recent settled)
│ ├── active.tsx # Active/open wagers only
│ └── settled.tsx # Settled/graded wagers history
├── registry/
│ ├── factions.tsx # Faction directory (all teams)
│ ├── factions.$factionId.tsx # Faction detail (stats, form, history)
│ ├── venues.tsx # Venue directory (all sectors)
│ └── venues.$venueId.tsx # Venue detail (modifiers, match history)
└── schedule.tsx # Upcoming match schedule
```
### 3.2 Route Definitions with Validated Search Params
Every route enforces typed search params via Zod validators per AGENTS.md Rule A.
```tsx
// ─── / (Grid Overview Dashboard) ───
export const Route = createFileRoute('/')({
validateSearch: z.object({
quadrant: z.enum(['alpha', 'beta', 'gamma', 'delta']).optional(),
currency: z.enum(['Latinum', 'Credits']).default('Latinum'),
}),
})
// ─── /quadrants (Quadrant Match Listing) ───
export const Route = createFileRoute('/quadrants/')({
validateSearch: z.object({
quadrant: z.enum(['alpha', 'beta', 'gamma', 'delta']).optional(),
status: z.enum(['LIVE', 'PRE_MATCH', 'RESOLVING', 'all']).default('all'),
currency: z.enum(['Latinum', 'Credits']).default('Latinum'),
sort: z.enum(['shields', 'yield', 'newest', 'ending']).default('newest'),
}),
})
// ─── /quadrants/$sectorId (Live Match Detail) ───
export const Route = createFileRoute('/quadrants/$sectorId')({
validateSearch: z.object({
quadrant: z.enum(['alpha', 'beta', 'gamma', 'delta']),
currency: z.enum(['Latinum', 'Credits']).default('Latinum'),
marketCategory: z.enum(['primary', 'secondary', 'exotic', 'all']).default('all'),
}),
})
// ─── /ledger (Wager Ledger) ───
export const Route = createFileRoute('/ledger/')({
validateSearch: z.object({
status: z.enum(['active', 'settled', 'all']).default('all'),
quadrant: z.enum(['alpha', 'beta', 'gamma', 'delta']).optional(),
currency: z.enum(['Latinum', 'Credits']).default('Latinum'),
page: z.number().default(1),
}),
})
// ─── /registry/factions (Faction Directory) ───
export const Route = createFileRoute('/registry/factions')({
validateSearch: z.object({
quadrant: z.enum(['alpha', 'beta', 'gamma', 'delta']).optional(),
type: z.enum(['aggressive', 'defensive', 'tactical', 'infiltrator', 'swarm']).optional(),
sort: z.enum(['name', 'attack', 'shield', 'evasion', 'winrate']).default('name'),
}),
})
// ─── /registry/venues (Venue Directory) ───
export const Route = createFileRoute('/registry/venues')({
validateSearch: z.object({
quadrant: z.enum(['alpha', 'beta', 'gamma', 'delta']).optional(),
sort: z.enum(['name', 'shield_mod', 'damage_mod', 'chaos']).default('name'),
}),
})
// ─── /schedule (Upcoming Matches) ───
export const Route = createFileRoute('/schedule')({
validateSearch: z.object({
quadrant: z.enum(['alpha', 'beta', 'gamma', 'delta']).optional(),
currency: z.enum(['Latinum', 'Credits']).default('Latinum'),
}),
})
```
---
## 4. Page Designs
### 4.1 Grid Overview Dashboard (`/`)
The landing page — a high-density terminal dashboard showing all active engagements at a glance.
```
┌──────────────────────────────────────────────────────────────────┐
│ [ CONSOLE TERMINAL ] Active Feeds │
├──────────────────────────────────────────────────────────────────┤
│ │
│ > SUBSPACE BROADCAST — LIVE ENGAGEMENTS ACROSS ALL QUADRANTS │
│ ════════════════════════════════════════════════════════════ │
│ │
│ ┌─── FILTER ─────────────────────────────────────────────────┐ │
│ │ [ALL] [α ALPHA] [β BETA] [γ GAMMA] [δ DELTA] │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ SECTOR 001 — EARTH ORBITAL ARRAY ●LIVE α │ │
│ │ ◆ Starfleet Command 62% ████████░░░ VS │ │
│ │ █ Borg Collective 71% █████████░░ │ │
│ │ ────────────────────────────────────────────────── │ │
│ │ KY: 14.7 TW TICK: 42/90 ODDS: SFC 2.85 | BRG 1.48 │
│ │ ▸ 6 MARKETS OPEN [VIEW] → │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ THE BADLANDS — PLASMA STORM ZONE ●LIVE α │ │
│ │ ▼ Cardassian Union 48% ██████░░░░ VS │ │
│ │ ☼ Bajoran Militia 55% ███████░░░ │ │
│ │ ────────────────────────────────────────────────── │ │
│ │ KY: 8.2 TW TICK: 31/75 ODDS: CRD 2.10 | BAJ 1.72 │
│ │ ▸ 8 MARKETS OPEN [VIEW] → │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ NARENDRA III — HONOR'S EDGE ○PRE β │ │
│ │ ⚔ Klingon Empire VS │ │
│ │ ◈ Romulan Star Empire │ │
│ │ ────────────────────────────────────────────────── │ │
│ │ COMMENCING IN: 00:47 PRE-MATCH ODDS AVAILABLE │ │
│ │ ▸ 4 MARKETS OPEN [VIEW] → │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ LIVE TICKER ──────────────────────────────────────────┐ │
│ │ 14:32 Borg Adaptation triggered — Damage reduced 50% │ │
│ │ 14:30 Critical Hit! Starfleet deals 3× damage │ │
│ │ 14:28 Bajor: Prophet Intervention — Markets SUSPENDED │ │
│ │ 14:25 New match: Klingons vs Romulans @ Narendra III │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────┘
```
**Key features:**
- Match summary cards with inline shield bars and headline odds
- Quadrant filter tabs (search param `?quadrant=alpha`)
- Live event ticker scrolling recent events from ALL active matches
- Pre-match cards show countdown timer instead of combat data
- Each card links to `/quadrants/$sectorId`
### 4.2 Match Detail Page (`/quadrants/$sectorId`)
The most important page — full live match view with all markets.
```
┌──────────────────────────────────────────────────────────────────┐
│ [ ENGAGEMENT MATRIX ] Wolf 359 — Memorial Perimeter │
├──────────────────────────────────────────────────────────────────┤
│ │
│ ┌─ VERSUS ARENA ────────────────────────────────────────────┐ │
│ │ │ │
│ │ ◆ STARFLEET COMMAND █ BORG COLLECTIVE │ │
│ │ [SFC] [BRG] │ │
│ │ │ │
│ │ SHIELDS 62% ████████░░░ 71% █████████░░ SHIELDS │ │
│ │ ATTACK 58 99 ATTACK │ │
│ │ EVASION 40 12 EVASION │ │
│ │ │ │
│ │ ─────────── ✦ VS ✦ ────────── │ │
│ │ │ │
│ │ KINETIC YIELD: 14.7 TW TICK: 42 / 90 │ │
│ │ VENUE: Wolf 359 [debris-field, high-anomaly] │ │
│ │ SHIELD MOD: 0.95 DMG MOD: 1.10 EVA MOD: 0.80 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ ACTIVE EFFECTS ──────────────────────────────────────────┐ │
│ │ ⚡ BORG ADAPTATION (BRG) — Incoming damage ×0.5 [3/5] │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ FILTER ───────────────────────────────────────────────────┐ │
│ │ [ALL] [PRIMARY] [SECONDARY] [EXOTIC] │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ MARKETS ─────────────────────────────────────────────────┐ │
│ │ │ │
│ │ COMBAT RESOLUTION — MONEYLINE [primary] │ │
│ │ ┌──────────────────────────┬───────────────────────────┐ │ │
│ │ │ ◆ Starfleet Victory │ █ Borg Assimilation │ │ │
│ │ │ [2.85] ▲ │ [1.48] ▼ │ │ │
│ │ └──────────────────────────┴───────────────────────────┘ │ │
│ │ │ │
│ │ FIRST SHIELD BREACH [primary] │ │
│ │ ┌──────────────────────────┬───────────────────────────┐ │ │
│ │ │ ◆ SFC Shields First │ █ BRG Shields First │ │ │
│ │ │ [1.65] ▼ │ [2.20] ▲ │ │ │
│ │ └──────────────────────────┴───────────────────────────┘ │ │
│ │ │ │
│ │ TOTAL KINETIC YIELD [primary] │ │
│ │ ┌──────────────────────────┬───────────────────────────┐ │ │
│ │ │ Over 8.5 TW │ Under 8.5 TW │ │ │
│ │ │ [1.35] ▼ │ [3.10] ▲ │ │ │
│ │ └──────────────────────────┴───────────────────────────┘ │ │
│ │ │ │
│ │ COMEBACK VICTORY [exotic] │ │
│ │ ┌──────────────────────────┬───────────────────────────┐ │ │
│ │ │ ◆ SFC 30+ Comeback │ █ BRG 30+ Comeback │ │ │
│ │ │ [5.50] │ [8.20] ▲ │ │ │
│ │ └──────────────────────────┴───────────────────────────┘ │ │
│ │ │ │
│ │ MUTUAL DESTRUCTION [exotic] │ │
│ │ ┌──────────────────────────┬───────────────────────────┐ │ │
│ │ │ Both Shields 0% │ Survivor │ │ │
│ │ │ [12.00] │ [1.05] │ │ │
│ │ └──────────────────────────┴───────────────────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ ENGAGEMENT LOG ──────────────────────────────────────────┐ │
│ │ [T42] ◆ Starfleet fires — HIT — 4.2% shield damage │ │
│ │ [T42] █ Borg fires — MISS — Evasion successful │ │
│ │ [T38] ⚡ EVENT: Borg Adaptation — Incoming dmg ×0.5 │ │
│ │ [T35] ◆ Starfleet fires — CRIT — 12.6% shield damage │ │
│ │ [T31] 🌀 EVENT: Debris Impact — BRG shields -12% │ │
│ │ [T28] █ Borg fires — HIT — 6.1% shield damage │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────┘
```
**Key features:**
- Versus arena header with full faction stats, shield bars, and active effects
- Venue modifier display (so bettors understand the environment)
- Market category filter tabs (search param `?marketCategory=primary`)
- Each market selection is a clickable button that adds to betslip
- Price trend indicators (▲ up / ▼ down) with color coding
- Engagement log showing tick-by-tick combat events
- Market suspension overlay when `SUBSPACE INTERRUPTION` event fires
### 4.3 Betslip Sidecar (Right Panel — always visible)
The betslip is NOT a route — it's a persistent panel in the root layout, managed via React context or a lightweight client store.
```
╔══════════════════════════════════╗
║ [ WAGER SLIP ] Secure Link ║
╠══════════════════════════════════╣
║ ║
║ SELECTIONS (2) ║
║ ════════════════════════════ ║
║ ║
║ ┌────────────────────────────┐ ║
║ │ Wolf 359 — SFC vs BRG │ ║
║ │ ◆ Starfleet Victory │ ║
║ │ [2.85] [×] │ ║
║ │ STAKE: [___25.00___] GPL │ ║
║ │ RETURN: 71.25 GPL │ ║
║ └────────────────────────────┘ ║
║ ║
║ ┌────────────────────────────┐ ║
║ │ Badlands — CRD vs BAJ │ ║
║ │ Over 8.5 Terawatts │ ║
║ │ [1.85] [×] │ ║
║ │ STAKE: [___10.00___] GPL │ ║
║ │ RETURN: 18.50 GPL │ ║
║ └────────────────────────────┘ ║
║ ║
║ ═══════════════════════════════ ║
║ SINGLES TOTAL STAKE: 35.00 ║
║ POTENTIAL RETURN: 89.75 ║
║ ║
║ ─── ACCUMULATOR ────────────── ║
║ COMBINED ODDS: [5.27] ║
║ ACCA STAKE: [________] GPL ║
║ ACCA RETURN: 0.00 GPL ║
║ ║
║ ═══════════════════════════════ ║
║ ║
║ ┌────────────────────────────┐ ║
║ │ ▶ TRANSMIT WAGER ◀ │ ║
║ │ Rule of Acquisition #62 │ ║
║ └────────────────────────────┘ ║
║ ║
║ ─── RECENT TRANSMISSIONS ───── ║
║ ✓ SFC Victory @ 2.40 +36.00 ║
║ ✗ Over 8.5 TW @ 1.85 -10.00 ║
║ ○ BRG Victory @ 1.48 PENDING ║
║ ║
╚══════════════════════════════════╝
```
**Key features:**
- Selections added by clicking market buttons on any match page
- Individual stake inputs per selection (singles)
- Accumulator option with combined odds
- Live price updates — if odds change while in betslip, show old→new with flash
- Odds lock warning if price moves significantly
- Recent bet results at the bottom
- Persistent across route changes (React context, not URL state)
### 4.4 Wager Ledger (`/ledger`)
```
┌──────────────────────────────────────────────────────────────────┐
│ [ WAGER LEDGER ] Transaction Archive │
├──────────────────────────────────────────────────────────────────┤
│ │
│ ┌─ FILTER ───────────────────────────────────────────────────┐ │
│ │ [ALL] [ACTIVE] [SETTLED] Quadrant: [ALL ▾] │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ ACCOUNT SUMMARY ─────────────────────────────────────────┐ │
│ │ BALANCE: 4,250.00 GPL ACTIVE RISK: 285.00 GPL │ │
│ │ SESSION P/L: +127.50 GPL ▲ │ │
│ │ WIN RATE: 58% (14/24) AVG ODDS: 2.15 │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ─── ACTIVE WAGERS ──────────────────────────────────────────── │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ #W-0042 Wolf 359 — SFC vs BRG ●LIVE α │ │
│ │ Selection: ◆ Starfleet Victory │ │
│ │ Odds Taken: 2.85 Stake: 25.00 GPL │ │
│ │ Current Odds: 3.10 Potential: 71.25 GPL │ │
│ │ Status: OPEN — Shields: SFC 62% | BRG 71% │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ─── SETTLED WAGERS ─────────────────────────────────────────── │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ #W-0039 Qo'noS Arena — KLG vs GRN ✓ WON β │ │
│ │ Selection: ⚔ Klingon Victory │ │
│ │ Odds: 1.65 Stake: 30.00 Return: 49.50 GPL +19.50 │ │
│ └────────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ #W-0037 Badlands — CRD vs BAJ ✗ LOST α │ │
│ │ Selection: Over 8.5 TW │ │
│ │ Odds: 1.85 Stake: 10.00 Return: 0.00 GPL -10.00 │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────┘
```
### 4.5 Faction Registry (`/registry/factions`)
```
┌──────────────────────────────────────────────────────────────────┐
│ [ FACTION REGISTRY ] Combatant Database │
├──────────────────────────────────────────────────────────────────┤
│ │
│ ┌─ FILTER ───────────────────────────────────────────────────┐ │
│ │ Quadrant: [ALL ▾] Type: [ALL ▾] Sort: [NAME ▾] │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ GLYPH FACTION QDR TYPE SHD ATK EVA │
│ ═════ ═══════════════════ ═══ ═══════════ ═══ ═══ ═══ │
│ ◆ Starfleet Command α defensive 92 65 50 │
│ █ Borg Collective δ swarm 95 90 15 │
│ ⚔ Klingon Empire β aggressive 60 95 35 │
│ ◈ Romulan Star Empire β infiltrator 72 70 85 │
│ ◉ The Dominion γ swarm 75 88 40 │
│ ∞ Species 8472 δ aggressive 99 99 70 │
│ ... │
│ │
│ Click a faction row to view detailed profile → │
│ │
└──────────────────────────────────────────────────────────────────┘
```
Clicking a faction row navigates to `/registry/factions/$factionId`, which shows:
- Full tactical profile text
- Rivalry modifiers table
- Active matches this faction is in
- Recent match history (W/L record)
- Type advantage breakdown
### 4.6 Venue Database (`/registry/venues`)
```
┌──────────────────────────────────────────────────────────────────┐
│ [ VENUE DATABASE ] Sector Pool Registry │
├──────────────────────────────────────────────────────────────────┤
│ │
│ VENUE QDR SHD× DMG× EVA× ANOMALY │
│ ═══════════════════════ ════ ═════ ═════ ═════ ════════ │
│ Fluidic Space Rift δ 0.60 1.50 0.80 35% !!! │
│ The Badlands α 0.70 1.30 0.60 35% !!! │
│ Founders' Approach γ 0.85 1.10 0.60 40% !!! │
│ Qo'noS Arena β 0.80 1.25 0.95 8% │
│ Vulcan Defense Grid α 1.20 0.85 1.00 3% │
│ Nekrit Expanse δ 1.00 1.00 1.00 1% │
│ ... │
│ │
└──────────────────────────────────────────────────────────────────┘
```
### 4.7 Match Schedule (`/schedule`)
```
┌──────────────────────────────────────────────────────────────────┐
│ [ TRANSMISSION SCHEDULE ] Upcoming Engagements │
├──────────────────────────────────────────────────────────────────┤
│ │
│ ─── SCHEDULED (starting soon) ──────────────────────────────── │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ T-47s Narendra III — Honor's Edge [β] │ │
│ │ ⚔ Klingon Empire vs ◈ Romulan Star Empire │ │
│ │ Rivalry: 1.30 — MARQUEE MATCHUP │ │
│ │ Pre-match odds available at T-60s │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ T-2m Gornar Orbit — Hegemony Arena [β] │ │
│ │ ▲ Gorn Hegemony vs ⊘ Orion Syndicate │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ─── RECENTLY SETTLED ───────────────────────────────────────── │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ SETTLED Vulcan Grid — T'Khasi Defense Grid [α] │ │
│ │ ◆ Starfleet Command 68% vs ₲ Ferengi 0% │ │
│ │ RESULT: Starfleet Victory — 72 ticks │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────┘
```
---
## 5. Component Architecture
Per AGENTS.md, route files contain only configuration. All visual markup lives in components.
### 5.1 Component Map
```
src/components/
├── ascii/ # Low-level visual primitives
│ ├── AsciiPanel.tsx # Bordered panel container (EXISTS)
│ ├── ShieldBar.tsx # Horizontal shield capacity bar
│ ├── PriceTag.tsx # Odds display with trend indicator
│ ├── StatusBadge.tsx # LIVE/PRE/RESOLVING/SETTLED indicator
│ ├── TerminalLine.tsx # Decorative separator line
│ ├── BlinkingCursor.tsx # Animated cursor for loading states
│ ├── TickProgress.tsx # Match progress bar (tick X / maxTicks)
│ └── GlyphIcon.tsx # Faction glyph renderer with color
├── panels/ # Domain feature views (large composites)
│ ├── NavigationMenu.tsx # Left sidebar navigation (EXISTS)
│ ├── BetslipSidecar.tsx # Right sidebar betslip
│ ├── MatchCard.tsx # Summary card for dashboard grid
│ ├── VersusArena.tsx # Full faction-vs-faction header
│ ├── MarketGrid.tsx # All markets for a match
│ ├── MarketSelection.tsx # Single clickable odds button
│ ├── EngagementLog.tsx # Scrolling event log for a match
│ ├── LiveTicker.tsx # Global event feed (dashboard)
│ ├── FactionTable.tsx # Sortable faction directory table
│ ├── VenueTable.tsx # Sortable venue directory table
│ ├── FactionProfile.tsx # Detailed faction stats view
│ ├── WagerCard.tsx # Single bet display (active or settled)
│ ├── AccountSummary.tsx # Balance, P/L, win rate stats
│ ├── ScheduleCard.tsx # Upcoming/settled match card
│ └── QuadrantFilterBar.tsx # Reusable quadrant filter tabs
└── providers/ # Client state providers
├── BetslipProvider.tsx # Betslip selection state (React Context)
└── CurrencyProvider.tsx # Active currency preference
```
### 5.2 Route → Component Mapping
Routes stay thin (AGENTS.md compliance). Heavy rendering delegates to panel components.
```tsx
// src/routes/quadrants/$sectorId.tsx — THIN ROUTE FILE
export const Route = createFileRoute('/quadrants/$sectorId')({
validateSearch: z.object({
quadrant: z.enum(['alpha', 'beta', 'gamma', 'delta']),
currency: z.enum(['Latinum', 'Credits']).default('Latinum'),
marketCategory: z.enum(['primary', 'secondary', 'exotic', 'all']).default('all'),
}),
loader: async ({ params }) => {
const snapshot = await getCachedSectorData({ input: params.sectorId })
return { snapshot }
},
component: MatchDetailView, // ← delegates to panel
})
// The actual MatchDetailView component lives in src/components/panels/
// It composes: VersusArena + MarketGrid + EngagementLog
```
---
## 6. Data Flow & Server Functions
### 6.1 Server Function Registry
```
src/server/
├── db/
│ └── valkeyConnector.ts # Valkey client instance
└── functions/
├── getActiveMatchIndex.ts # All active match summaries (sidebar)
├── getMatchTelemetry.ts # Full match state for detail page
├── getUpcomingSchedule.ts # Scheduled matches
├── getSettledMatches.ts # Recently settled results
├── getFactionRegistry.ts # Static faction data from taxonomy
├── getVenueRegistry.ts # Static venue data from taxonomy
└── submitWager.ts # Place a bet (write to Valkey)
```
### 6.2 Polling Strategy
| Data | Interval | Source | Route |
|------|----------|--------|-------|
| Active match index (sidebar) | 2s | `sector:index:active` | `__root.tsx` (always) |
| Match telemetry (detail page) | 2s | `sector:telemetry:{id}` | `/quadrants/$sectorId` |
| Match events (detail page) | 2s | `sector:events:{id}` | `/quadrants/$sectorId` |
| Dashboard match cards | 2s | All active telemetry | `/` |
| Schedule | 10s | `sector:schedule:upcoming` | `/schedule` |
| Faction/venue registry | Static | `taxonomy.yaml` (cached) | `/registry/*` |
| Betslip price updates | 2s | Individual market keys | `BetslipSidecar` |
### 6.3 SSR + Client Hydration Pattern
```tsx
// Pattern for all live data routes:
// 1. Loader runs on server — fetches initial snapshot from Valkey
loader: async ({ params }) => {
return await getCachedSectorData({ input: params.sectorId })
},
// 2. Component hydrates with loader data, then starts client polling
function MatchDetailView() {
const initialData = Route.useLoaderData()
const { data } = useQuery({
queryKey: ['telemetry', sectorId],
queryFn: () => getCachedSectorData({ input: sectorId }),
initialData, // SSR data used immediately
refetchInterval: 2000, // Client polls every 2s after hydration
})
}
```
---
## 7. Client State Management
Only two pieces of state live on the client (not URL, not server):
### 7.1 Betslip State (React Context)
```tsx
interface BetslipSelection {
matchId: string
matchName: string
marketId: string
selectionName: string
price: number // Price when added
currentPrice: number // Live price (updated by polling)
stake: number // User-entered stake
factionGlyph: string
}
interface BetslipState {
selections: BetslipSelection[]
addSelection: (sel: BetslipSelection) => void
removeSelection: (marketId: string) => void
updateStake: (marketId: string, stake: number) => void
clearAll: () => void
accumulatorStake: number
setAccumulatorStake: (stake: number) => void
}
```
### 7.2 Currency Preference
Currency is tracked in URL search params (`?currency=Latinum`) per AGENTS.md deterministic URL state rule — NOT in client state. The `CurrencyProvider` simply reads from the URL.
---
## 8. Route Transition Map
```mermaid
graph TD
ROOT["__root.tsx<br/>(always rendered)"]
DASH["/ Dashboard<br/>Grid Overview"]
QUAD_LIST["/quadrants<br/>Filtered Match List"]
MATCH["/quadrants/$sectorId<br/>Live Match Detail"]
LEDGER["/ledger<br/>Wager Overview"]
ACTIVE["/ledger/active<br/>Open Wagers"]
SETTLED["/ledger/settled<br/>Settled History"]
FACTIONS["/registry/factions<br/>All Factions"]
FACTION_D["/registry/factions/$id<br/>Faction Profile"]
VENUES["/registry/venues<br/>All Venues"]
VENUE_D["/registry/venues/$id<br/>Venue Profile"]
SCHED["/schedule<br/>Upcoming Matches"]
ROOT --> DASH
ROOT --> QUAD_LIST
ROOT --> MATCH
ROOT --> LEDGER
ROOT --> SCHED
ROOT --> FACTIONS
ROOT --> VENUES
DASH -->|"click match card"| MATCH
QUAD_LIST -->|"click match"| MATCH
SCHED -->|"click scheduled"| MATCH
LEDGER --> ACTIVE
LEDGER --> SETTLED
ACTIVE -->|"click match ref"| MATCH
FACTIONS --> FACTION_D
FACTION_D -->|"click active match"| MATCH
VENUES --> VENUE_D
VENUE_D -->|"click active match"| MATCH
MATCH -->|"click market selection"| BETSLIP["Betslip (context, not route)"]
style MATCH fill:#34d399,color:#000
style BETSLIP fill:#fbbf24,color:#000
style DASH fill:#38bdf8,color:#000
```