feat: add LMC Rank column alongside LMC Points
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -74,6 +74,7 @@
|
|||||||
<th class="cursor-pointer select-none" data-sort="name">Player</th>
|
<th class="cursor-pointer select-none" data-sort="name">Player</th>
|
||||||
<th class="text-right cursor-pointer select-none" data-sort="pts">ITC Points</th>
|
<th class="text-right cursor-pointer select-none" data-sort="pts">ITC Points</th>
|
||||||
<th class="text-right hidden sm:table-cell cursor-pointer select-none" data-sort="lmc">LMC Points</th>
|
<th class="text-right hidden sm:table-cell cursor-pointer select-none" data-sort="lmc">LMC Points</th>
|
||||||
|
<th class="text-right hidden sm:table-cell cursor-pointer select-none" data-sort="lmcrank">LMC Rank</th>
|
||||||
<th class="text-right hidden sm:table-cell cursor-pointer select-none" data-sort="wlt">W–L–T</th>
|
<th class="text-right hidden sm:table-cell cursor-pointer select-none" data-sort="wlt">W–L–T</th>
|
||||||
<th class="text-right hidden sm:table-cell cursor-pointer select-none" data-sort="wpct">Win %</th>
|
<th class="text-right hidden sm:table-cell cursor-pointer select-none" data-sort="wpct">Win %</th>
|
||||||
<th class="text-right hidden sm:table-cell cursor-pointer select-none" data-sort="rank">ITC Rank</th>
|
<th class="text-right hidden sm:table-cell cursor-pointer select-none" data-sort="rank">ITC Rank</th>
|
||||||
|
|||||||
+19
-2
@@ -158,6 +158,7 @@ const sortCols = {
|
|||||||
wpct: { key: r => { const g=(+r.wins||0)+(+r.losses||0)+(+r.ties||0); return g?(+r.wins||0)/g:-1; }, 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' },
|
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' },
|
lmc: { key: r => { const uid = r.userId || r.user?.id; return uid && lmcPoints[uid] != null ? lmcPoints[uid] : -Infinity; }, defaultDir: 'desc' },
|
||||||
|
lmcrank: { key: r => { const uid = r.userId || r.user?.id; return uid && lmcRanks[uid] != null ? lmcRanks[uid] : Infinity; }, defaultDir: 'asc' },
|
||||||
elo: { key: r => { const e = findElo(fullName(r.user || {}), r.userId || r.user?.id); return e?.elo ?? -Infinity; }, defaultDir: 'desc' },
|
elo: { key: r => { const e = findElo(fullName(r.user || {}), r.userId || r.user?.id); return e?.elo ?? -Infinity; }, defaultDir: 'desc' },
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -222,6 +223,7 @@ async function getMemberIds(force) {
|
|||||||
|
|
||||||
/* ---- LMC Points data ---- */
|
/* ---- LMC Points data ---- */
|
||||||
const lmcPoints = {}; // userId → number
|
const lmcPoints = {}; // userId → number
|
||||||
|
const lmcRanks = {}; // userId → number
|
||||||
let lmcLoaded = false;
|
let lmcLoaded = false;
|
||||||
|
|
||||||
async function loadLmcData(ids) {
|
async function loadLmcData(ids) {
|
||||||
@@ -230,7 +232,10 @@ async function loadLmcData(ids) {
|
|||||||
for await (const page of streamPlacings(CONFIG.lmcLeagueId)) {
|
for await (const page of streamPlacings(CONFIG.lmcLeagueId)) {
|
||||||
for (const rec of page) {
|
for (const rec of page) {
|
||||||
const uid = rec.userId || rec.user?.id;
|
const uid = rec.userId || rec.user?.id;
|
||||||
if (uid && ids.has(uid)) lmcPoints[uid] = +(rec.ITCPoints || 0);
|
if (uid && ids.has(uid)) {
|
||||||
|
lmcPoints[uid] = +(rec.ITCPoints || 0);
|
||||||
|
if (rec.placing != null) lmcRanks[uid] = rec.placing;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -348,15 +353,25 @@ function render(state) {
|
|||||||
: `<span class="block text-xs opacity-40">${fmtWpct(d.w, d.l, d.t)} career</span>`;
|
: `<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 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 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 mobileLmc = (() => {
|
||||||
|
if (!uid || !lmcLoaded || lmcPoints[uid] == null) return '';
|
||||||
|
const rankStr = lmcRanks[uid] != null ? ` · LMC #${lmcRanks[uid]}` : '';
|
||||||
|
return `<span class="block text-xs opacity-40 sm:hidden">LMC ${fmtPts(lmcPoints[uid])}${rankStr}</span>`;
|
||||||
|
})();
|
||||||
const lmcCell = (() => {
|
const lmcCell = (() => {
|
||||||
if (!lmcLoaded) return `<td class="text-right font-mono hidden sm:table-cell"><span class="opacity-20">…</span></td>`;
|
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;
|
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 hidden sm:table-cell">${pts != null ? fmtPts(pts) : '<span class="text-base-content/40">—</span>'}</td>`;
|
||||||
})();
|
})();
|
||||||
|
const lmcRankCell = (() => {
|
||||||
|
if (!lmcLoaded) return `<td class="text-right font-mono text-base-content/40 hidden sm:table-cell"><span class="opacity-20">…</span></td>`;
|
||||||
|
const rank = uid ? lmcRanks[uid] : undefined;
|
||||||
|
return `<td class="text-right font-mono text-base-content/40 hidden sm:table-cell">${rank != null ? '#' + rank : '—'}</td>`;
|
||||||
|
})();
|
||||||
return `<td class="text-right font-mono font-bold">${fmtPts(r.ITCPoints)}` +
|
return `<td class="text-right font-mono font-bold">${fmtPts(r.ITCPoints)}` +
|
||||||
mobilePct + mobileLmc + mobileElo + `</td>` +
|
mobilePct + mobileLmc + mobileElo + `</td>` +
|
||||||
lmcCell +
|
lmcCell +
|
||||||
|
lmcRankCell +
|
||||||
`<td class="text-right font-mono hidden sm:table-cell">` +
|
`<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}` +
|
`<span class="text-success">${r.wins || 0}</span>–<span class="text-error">${r.losses || 0}</span>–${r.ties || 0}` +
|
||||||
careerSub + `</td>` +
|
careerSub + `</td>` +
|
||||||
@@ -408,6 +423,7 @@ async function load(force) {
|
|||||||
|
|
||||||
// Reset and kick off LMC fetch non-blocking
|
// Reset and kick off LMC fetch non-blocking
|
||||||
Object.keys(lmcPoints).forEach(k => delete lmcPoints[k]);
|
Object.keys(lmcPoints).forEach(k => delete lmcPoints[k]);
|
||||||
|
Object.keys(lmcRanks).forEach(k => delete lmcRanks[k]);
|
||||||
loadLmcData(ids);
|
loadLmcData(ids);
|
||||||
|
|
||||||
for await (const page of streamPlacings(CONFIG.leagueId, CONFIG.regionId)) {
|
for await (const page of streamPlacings(CONFIG.leagueId, CONFIG.regionId)) {
|
||||||
@@ -438,6 +454,7 @@ function showSkeleton() {
|
|||||||
`<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-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><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-12 ml-auto"></div></td>` +
|
||||||
|
`<td class="hidden sm:table-cell"><div class="skeleton h-4 w-10 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-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-12 ml-auto"></div></td>` +
|
||||||
`<td class="hidden sm:table-cell"><div class="skeleton h-4 w-10 ml-auto"></div></td>` +
|
`<td class="hidden sm:table-cell"><div class="skeleton h-4 w-10 ml-auto"></div></td>` +
|
||||||
|
|||||||
Reference in New Issue
Block a user