Spaces:
Running
Running
| <script lang="ts"> | |
| import type { Match } from '$lib/types'; | |
| import { prettyMap } from '$lib/utils/format'; | |
| import { mapColorClasses } from '$lib/utils/map-colors'; | |
| import { cn } from '$lib/utils'; | |
| interface Props { | |
| matches: Match[]; | |
| } | |
| let { matches }: Props = $props(); | |
| const mapsDist = $derived.by(() => { | |
| const counts = new Map<string, number>(); | |
| for (const m of matches) counts.set(m.map_name, (counts.get(m.map_name) ?? 0) + 1); | |
| return Array.from(counts.entries()) | |
| .map(([name, count]) => ({ name, count })) | |
| .sort((a, b) => b.count - a.count); | |
| }); | |
| const totalMaps = $derived(mapsDist.reduce((s, m) => s + m.count, 0)); | |
| const maxCount = $derived(Math.max(1, ...mapsDist.map((m) => m.count))); | |
| // Histogram of rounds-per-match — buckets of 4 rounds. | |
| const roundsHist = $derived.by(() => { | |
| const buckets = new Map<string, number>(); | |
| for (const m of matches) { | |
| const r = m.rounds_played; | |
| if (!r) continue; | |
| // pinch into [start, start+3] buckets; e.g. 13-16, 17-20, etc. | |
| const start = Math.floor((r - 1) / 4) * 4 + 1; | |
| const key = `${start}-${start + 3}`; | |
| buckets.set(key, (buckets.get(key) ?? 0) + 1); | |
| } | |
| return Array.from(buckets.entries()) | |
| .map(([range, count]) => ({ range, count, sortKey: Number(range.split('-')[0]) })) | |
| .sort((a, b) => a.sortKey - b.sortKey); | |
| }); | |
| const roundsMax = $derived(Math.max(1, ...roundsHist.map((b) => b.count))); | |
| </script> | |
| <section class="mx-auto mt-12 max-w-5xl"> | |
| <h2 | |
| class="text-center font-heading text-xs font-semibold tracking-[0.2em] text-muted-foreground uppercase" | |
| > | |
| Statistics | |
| </h2> | |
| <div class="mt-6 grid gap-4 lg:grid-cols-2"> | |
| <!-- Maps distribution --> | |
| <div class="rounded-md border bg-card p-4"> | |
| <div class="mb-3 flex items-baseline justify-between gap-2"> | |
| <div> | |
| <div class="font-heading text-sm font-semibold">Maps distribution</div> | |
| <p class="text-[11px] text-muted-foreground"> | |
| Maps rendered per CS2 map ({totalMaps} total) | |
| </p> | |
| </div> | |
| </div> | |
| <div class="space-y-1"> | |
| {#each mapsDist as m (m.name)} | |
| {@const pct = (m.count / maxCount) * 100} | |
| {@const sharePct = totalMaps ? (m.count / totalMaps) * 100 : 0} | |
| <div class="grid grid-cols-[5.5rem_1fr_3.5rem] items-center gap-2 text-xs"> | |
| <span | |
| class={cn( | |
| 'truncate rounded-sm border px-1.5 py-0.5 text-center text-[10px] font-medium capitalize', | |
| mapColorClasses(m.name) | |
| )} | |
| title={m.name}>{prettyMap(m.name)}</span | |
| > | |
| <div class="relative h-3 overflow-hidden rounded-sm bg-muted/40"> | |
| <div | |
| class={cn('absolute inset-y-0 left-0 rounded-sm border-r', mapColorClasses(m.name))} | |
| style="width: {pct}%" | |
| ></div> | |
| </div> | |
| <span class="text-right font-mono text-muted-foreground tabular-nums"> | |
| {m.count}<span class="ml-1 text-[10px] text-muted-foreground/60" | |
| >{sharePct.toFixed(0)}%</span | |
| > | |
| </span> | |
| </div> | |
| {/each} | |
| </div> | |
| </div> | |
| <!-- Rounds-per-match histogram --> | |
| <div class="rounded-md border bg-card p-4"> | |
| <div class="mb-3"> | |
| <div class="font-heading text-sm font-semibold">Rounds per map</div> | |
| <p class="text-[11px] text-muted-foreground"> | |
| How many maps fall into each round-count band (regulation = 24) | |
| </p> | |
| </div> | |
| <!-- Two rows: bars (definite-height parent so the % heights resolve) + labels. --> | |
| <div class="flex h-32 items-end gap-1.5"> | |
| {#each roundsHist as b (b.range)} | |
| {@const h = Math.max(2, (b.count / roundsMax) * 100)} | |
| <div | |
| class="min-w-0 flex-1 rounded-sm bg-foreground/70 transition-colors hover:bg-foreground" | |
| style="height: {h}%" | |
| title={`${b.range} rounds: ${b.count} maps`} | |
| ></div> | |
| {/each} | |
| </div> | |
| <div class="mt-1 flex gap-1.5"> | |
| {#each roundsHist as b (b.range)} | |
| <div | |
| class="min-w-0 flex-1 truncate text-center font-mono text-[10px] text-muted-foreground/80" | |
| > | |
| {b.range} | |
| </div> | |
| {/each} | |
| </div> | |
| <div class="mt-1 text-center text-[10px] text-muted-foreground/70">rounds played</div> | |
| </div> | |
| </div> | |
| </section> | |