feat: add LMC Points column fetched from BCP league cY0LeCAPfyiG

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-01 09:34:34 -05:00
co-authored by Claude Sonnet 4.6
parent 27b336f035
commit f33022fe92
2 changed files with 44 additions and 8 deletions
+43 -8
View File
@@ -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}` +
`&regionId=${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 += `&regionId=${regionId}`;
let nextKey = null;
let prevNextKey = undefined;
@@ -325,8 +348,15 @@ function render(state) {
: `<span class="block text-xs opacity-40">${fmtWpct(d.w, d.l, d.t)} career</span>`;
const mobilePct = `<span class="block text-xs opacity-40 sm:hidden">${fmtWpct(r.wins, r.losses, r.ties)} win rate</span>`;
const mobileElo = eloMatch ? `<span class="block text-xs opacity-40 sm:hidden">ELO ${Math.round(eloMatch.elo)}</span>` : '';
const mobileLmc = (uid && lmcLoaded && lmcPoints[uid] != null) ? `<span class="block text-xs opacity-40 sm:hidden">LMC ${fmtPts(lmcPoints[uid])}</span>` : '';
const lmcCell = (() => {
if (!lmcLoaded) return `<td class="text-right font-mono hidden sm:table-cell"><span class="opacity-20">…</span></td>`;
const pts = uid ? lmcPoints[uid] : undefined;
return `<td class="text-right font-mono hidden sm:table-cell">${pts != null ? fmtPts(pts) : '<span class="text-base-content/40">—</span>'}</td>`;
})();
return `<td class="text-right font-mono font-bold">${fmtPts(r.ITCPoints)}` +
mobilePct + mobileElo + `</td>` +
mobilePct + mobileLmc + mobileElo + `</td>` +
lmcCell +
`<td class="text-right font-mono hidden sm:table-cell">` +
`<span class="text-success">${r.wins || 0}</span><span class="text-error">${r.losses || 0}</span>${r.ties || 0}` +
careerSub + `</td>` +
@@ -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() {
`<td><div class="skeleton h-4 w-5"></div></td>` +
`<td><div class="skeleton h-4 w-3/5"></div><div class="skeleton h-3 w-2/5 mt-1 sm:hidden"></div></td>` +
`<td><div class="skeleton h-4 w-12 ml-auto"></div><div class="skeleton h-3 w-10 mt-1 ml-auto sm:hidden"></div></td>` +
`<td class="hidden sm:table-cell"><div class="skeleton h-4 w-12 ml-auto"></div></td>` +
`<td class="hidden sm:table-cell"><div class="skeleton h-4 w-14 ml-auto"></div></td>` +
`<td class="hidden sm:table-cell"><div class="skeleton h-4 w-12 ml-auto"></div></td>` +
`<td class="hidden sm:table-cell"><div class="skeleton h-4 w-10 ml-auto"></div></td>` +