From f33022fe92a909f3854578f0f5191ca848624345 Mon Sep 17 00:00:00 2001 From: mandalore Date: Wed, 1 Jul 2026 09:34:17 -0500 Subject: [PATCH] feat: add LMC Points column fetched from BCP league cY0LeCAPfyiG Co-Authored-By: Claude Sonnet 4.6 --- index.html | 1 + src/main.js | 51 +++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/index.html b/index.html index 6a4f757..4078bf7 100644 --- a/index.html +++ b/index.html @@ -73,6 +73,7 @@ # Player ITC Points + LMC Points W–L–T Win % ITC Rank diff --git a/src/main.js b/src/main.js index 15824fd..510cf90 100644 --- a/src/main.js +++ b/src/main.js @@ -40,9 +40,10 @@ loadHTML('/footer.html', 'footer'); CONFIG — the only block you edit ============================================================ */ const CONFIG = { - teamId: "2iGDVMgX0a", - leagueId: "BYaaUfKum7z0", - regionId: "VgQKgqmTPU", + teamId: "2iGDVMgX0a", + leagueId: "BYaaUfKum7z0", + regionId: "VgQKgqmTPU", + lmcLeagueId: "cY0LeCAPfyiG", // Fallback roster if the team endpoint requires auth. memberIds: [], @@ -156,6 +157,7 @@ const sortCols = { wlt: { key: r => (+r.wins||0) * 10000 - (+r.losses||0) * 100 - (+r.ties||0), defaultDir: 'desc' }, wpct: { key: r => { const g=(+r.wins||0)+(+r.losses||0)+(+r.ties||0); return g?(+r.wins||0)/g:-1; }, defaultDir: 'desc' }, rank: { key: r => +(r.placing || Infinity), defaultDir: 'asc' }, + lmc: { key: r => { const uid = r.userId || r.user?.id; return uid && lmcPoints[uid] != null ? lmcPoints[uid] : -Infinity; }, defaultDir: 'desc' }, elo: { key: r => { const e = findElo(fullName(r.user || {}), r.userId || r.user?.id); return e?.elo ?? -Infinity; }, defaultDir: 'desc' }, }; @@ -218,10 +220,31 @@ async function getMemberIds(force) { } } +/* ---- LMC Points data ---- */ +const lmcPoints = {}; // userId → number +let lmcLoaded = false; + +async function loadLmcData(ids) { + lmcLoaded = false; + try { + for await (const page of streamPlacings(CONFIG.lmcLeagueId)) { + for (const rec of page) { + const uid = rec.userId || rec.user?.id; + if (uid && ids.has(uid)) lmcPoints[uid] = +(rec.ITCPoints || 0); + } + } + } catch (e) { + console.warn('LMC data load failed:', e); + } finally { + lmcLoaded = true; + if (lastState) render(lastState); + } +} + /* ---- Standings (cursor-paginated, circular-cursor safe) ---- */ -async function* streamPlacings() { - const base = `${API}/placings?placingsType=player&leagueId=${CONFIG.leagueId}` + - `®ionId=${CONFIG.regionId}&sortAscending=false&limit=1000`; +async function* streamPlacings(leagueId, regionId) { + let base = `${API}/placings?placingsType=player&leagueId=${leagueId}&sortAscending=false&limit=1000`; + if (regionId) base += `®ionId=${regionId}`; let nextKey = null; let prevNextKey = undefined; @@ -325,8 +348,15 @@ function render(state) { : `${fmtWpct(d.w, d.l, d.t)} career`; const mobilePct = `${fmtWpct(r.wins, r.losses, r.ties)} win rate`; const mobileElo = eloMatch ? `ELO ${Math.round(eloMatch.elo)}` : ''; + const mobileLmc = (uid && lmcLoaded && lmcPoints[uid] != null) ? `LMC ${fmtPts(lmcPoints[uid])}` : ''; + const lmcCell = (() => { + if (!lmcLoaded) return ``; + const pts = uid ? lmcPoints[uid] : undefined; + return `${pts != null ? fmtPts(pts) : ''}`; + })(); return `${fmtPts(r.ITCPoints)}` + - mobilePct + mobileElo + `` + + mobilePct + mobileLmc + mobileElo + `` + + lmcCell + `` + `${r.wins || 0}${r.losses || 0}–${r.ties || 0}` + careerSub + `` + @@ -376,7 +406,11 @@ async function load(force) { const { ids, live } = await getMemberIds(force); const allRows = []; - for await (const page of streamPlacings()) { + // Reset and kick off LMC fetch non-blocking + Object.keys(lmcPoints).forEach(k => delete lmcPoints[k]); + loadLmcData(ids); + + for await (const page of streamPlacings(CONFIG.leagueId, CONFIG.regionId)) { allRows.push(...page); // Render incrementally so results appear as pages arrive const kept = build(allRows, ids); @@ -403,6 +437,7 @@ function showSkeleton() { `
` + `
` + `
` + + `
` + `
` + `
` + `
` +