feat: PWA auction assistant — mobile-first progressive web app
FastAPI backend: - /api/players — search/filter 505 Serie A players with calibrated prices - /api/player/name — full detail with ML predictions (quantile P10/P50/P90, starter probability, bandit bid recommendation, opponent bid estimate) - /api/roster — CRUD for live auction roster with budget tracking - /api/stats — league-wide price distribution - All ML models loaded on startup (quantile, survival, bandit, opponent, budget) PWA frontend: - Installable on iOS/Android via manifest.json + service worker - Dark theme matching Fantabeto design system - Player search with role filter, quick-scan list - Detail sheet with ML intelligence cards - One-tap bid buttons (market, ML rec, max) - Live budget bar, roster management - Works offline for cached assets - Mobile-first (480px max-width) Launch: uvicorn pwa.api:app --port 8601 Live at: http://localhost:8601
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||
<meta name="theme-color" content="#00D084">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚽</text></svg>">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/styles.css">
|
||||
<title>Fantabeto — Auction</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app">
|
||||
<!-- Header -->
|
||||
<header>
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;">
|
||||
<h1>⚽ <span>Fantabeto</span> Auction</h1>
|
||||
<div id="ping" class="ping">● connected</div>
|
||||
</div>
|
||||
<div class="budget-bar">
|
||||
<div class="label">SPENT</div>
|
||||
<div class="value" id="budgetSpent">0</div>
|
||||
<div class="progress">
|
||||
<div class="progress-fill" id="budgetFill" style="width:0%;"></div>
|
||||
</div>
|
||||
<div class="label">LEFT</div>
|
||||
<div class="value" id="budgetRemaining" style="color:var(--green);">500</div>
|
||||
<div class="label">cr</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Tabs -->
|
||||
<div class="tabs">
|
||||
<button class="tab active" data-tab="auction" onclick="switchTab('auction')">🔍 Players</button>
|
||||
<button class="tab" data-tab="roster" onclick="switchTab('roster')">📋 Roster <span id="rosterCount" style="font-size:10px;color:var(--muted);"></span></button>
|
||||
<button class="tab" data-tab="stats" onclick="switchTab('stats')">📊 Stats</button>
|
||||
</div>
|
||||
|
||||
<!-- Auction View -->
|
||||
<div id="auctionView">
|
||||
<div class="search-bar">
|
||||
<input type="text" placeholder="Search players..." id="searchInput"
|
||||
oninput="state.search=this.value;searchPlayers()">
|
||||
<select onchange="state.roleFilter=this.value;searchPlayers()">
|
||||
<option value="">All</option>
|
||||
<option value="P">GK</option>
|
||||
<option value="D">DEF</option>
|
||||
<option value="C">MID</option>
|
||||
<option value="A">FWD</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="player-list" id="playerList">
|
||||
<div class="empty">Loading players...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Roster View -->
|
||||
<div id="rosterView" style="display:none;">
|
||||
<div style="padding:12px 16px;">
|
||||
<button onclick="resetRoster()" style="background:var(--card);border:1px solid var(--red);color:var(--red);padding:8px 16px;border-radius:8px;cursor:pointer;font-size:12px;">
|
||||
Reset Roster
|
||||
</button>
|
||||
<span style="font-size:11px;color:var(--muted);margin-left:8px;">
|
||||
Quotas: P3 D8 C8 A6
|
||||
</span>
|
||||
</div>
|
||||
<div class="player-list" id="rosterList">
|
||||
<div class="empty">No players drafted yet.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stats View -->
|
||||
<div id="statsView" style="display:none;">
|
||||
<div class="empty">Loading stats...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Detail overlay -->
|
||||
<div class="detail-overlay" id="detailOverlay" style="display:none;" onclick="closeDetail()">
|
||||
</div>
|
||||
|
||||
<script src="/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user