765 lines
48 KiB
Markdown
765 lines
48 KiB
Markdown
# 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¤cy=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
|
||
```
|