// ============================================================ // Cyber Runner — real data binding + Logo + shared primitives // Reads window.CR_DATA (injected server-side from Postgres) and // exposes the same globals the section components expect. // ============================================================ const CR = window.CR_DATA || {}; // Feature flag: badges are hidden for now while the core journey is clarified. // Flip to true to bring the Badges section, nav/footer links, and profile badges back. const SHOW_BADGES = false; // ---- Two-tone vector logo (recolorable via CSS vars) ------- function CyberRunnerLogo({ className = "", style = {} }) { return ( ); } // ---- Real data from the server ------------------------------- const MEMBERS = CR.members || []; const BADGES = CR.badges || []; const TEAM = CR.team || { distance: 0, runs: 0, elevation: 0, members: 0, hours: 0 }; const FUN_STATS = CR.funStats || []; const VALUES = CR.values || []; const CHALLENGES = CR.challenges || []; const GOAL = CR.goal || { targetKm: 5000, label: "5,000 km this month", month: "" }; const FEED_DATA = CR.feed || []; const CLUB_NAME = CR.clubName || "Cyber Runner"; const MONTHNAV = CR.month || { label: CR.monthLabel || "", prev: null, next: null, isCurrent: true }; const ATTENDANCE = CR.attendance || { labels: [], rows: [] }; const BADGE_MAP = Object.fromEntries(BADGES.map(b => [b.id, b])); // ---- Leaderboard config (operates on real MEMBERS) ----------- function paceLabel(sec) { if (!sec) return "—"; const mm = Math.floor(sec / 60), ss = String(sec % 60).padStart(2, "0"); return `${mm}:${ss}`; } const LEADERBOARDS = { distance: { label: "Most Distance", unit: "km", get: m => m.km, fmt: v => v.toFixed(1) }, sessions: { label: "Most Sessions", unit: "runs", get: m => m.sessions, fmt: v => String(v) }, longest: { label: "Longest Run", unit: "km", get: m => m.longest, fmt: v => v.toFixed(1) }, }; function rankBy(key) { const cfg = LEADERBOARDS[key]; return [...MEMBERS].sort((a, b) => cfg.get(b) - cfg.get(a)); } // ---- Live refresh from the JSON endpoint --------------------- function fetchClubData() { return fetch("/api/club-data", { headers: { "Accept": "application/json" } }).then(r => r.json()); } Object.assign(window, { CyberRunnerLogo, MEMBERS, BADGES, BADGE_MAP, TEAM, LEADERBOARDS, rankBy, FUN_STATS, VALUES, CHALLENGES, GOAL, FEED_DATA, CLUB_NAME, MONTHNAV, ATTENDANCE, paceLabel, fetchClubData, SHOW_BADGES, });