This commit is contained in:
@@ -35,7 +35,7 @@ export async function load({ params, url }: { params: { guid: string }; url: URL
|
||||
Number(url.searchParams.get('offset') ?? 0),
|
||||
options
|
||||
);
|
||||
if (!profile) error(404, 'No recorded stats found for this player');
|
||||
if (!profile) error(404, 'Player not found');
|
||||
return {
|
||||
profile,
|
||||
returnTo: leaderboardReturnHref(url.searchParams.get('returnTo')),
|
||||
|
||||
@@ -156,6 +156,12 @@
|
||||
onRetry={() => void refresh()}
|
||||
/>{/snippet}
|
||||
</SectionHeader>
|
||||
{#if !profile.records}
|
||||
<EmptyState
|
||||
title="No qualifying match history yet"
|
||||
description="Stats appear after a recorded game ends, provided this player was observed at round 2 or later. Games in progress and interrupted games do not contribute."
|
||||
/>
|
||||
{/if}
|
||||
<nav class="jump-links" aria-label="Player sections">
|
||||
<a href="#overview">Overview</a><a href="#maps-title">Maps</a><a href="#history-title"
|
||||
>Matches</a
|
||||
@@ -185,12 +191,13 @@
|
||||
><a href={scopedLeaderboard}>Selected leaderboard</a>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span>Lifetime best round</span><strong>{number.format(stats.highestRound)}</strong
|
||||
><RecordSource
|
||||
evidence={profile.records.highestRound}
|
||||
{returnTo}
|
||||
metric="Lifetime best round"
|
||||
/>
|
||||
<span>Lifetime best round</span><strong
|
||||
>{profile.records ? number.format(stats.highestRound) : '—'}</strong
|
||||
>{#if profile.records}<RecordSource
|
||||
evidence={profile.records.highestRound}
|
||||
{returnTo}
|
||||
metric="Lifetime best round"
|
||||
/>{/if}
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span>Lifetime games played</span><strong>{number.format(stats.gamesPlayed)}</strong
|
||||
@@ -219,54 +226,60 @@
|
||||
map. Tied records share a rank. Each record links to the match that established it.
|
||||
</p>
|
||||
</div>
|
||||
<div class="mobile-records">
|
||||
<PlayerMapCards maps={profile.maps} metric={scope.metric} {returnTo} />
|
||||
</div>
|
||||
<!-- svelte-ignore a11y_no_noninteractive_tabindex (Scrollable records for keyboard users.) -->
|
||||
<div
|
||||
class="table-scroll desktop-records"
|
||||
role="region"
|
||||
aria-label="Per-map player records"
|
||||
tabindex="0"
|
||||
>
|
||||
<table>
|
||||
<thead
|
||||
><tr
|
||||
><th scope="col">Map</th>{#each records as record}<th scope="col"
|
||||
>{record.label}</th
|
||||
>{/each}<th scope="col">Games</th><th scope="col">Total kills</th><th
|
||||
scope="col">Total rounds</th
|
||||
></tr
|
||||
></thead
|
||||
>
|
||||
<tbody
|
||||
>{#each profile.maps as map (`${map.game}:${map.key}`)}<tr
|
||||
><th scope="row"
|
||||
><a
|
||||
href={`/leaderboard?${new URLSearchParams({ game: map.game, map: map.key, metric: scope.metric })}`}
|
||||
>{map.name}</a
|
||||
><small>{gameName(map.game)}</small></th
|
||||
>{#each records as record}<td
|
||||
{#if profile.maps.length}
|
||||
<div class="mobile-records">
|
||||
<PlayerMapCards maps={profile.maps} metric={scope.metric} {returnTo} />
|
||||
</div>
|
||||
<!-- svelte-ignore a11y_no_noninteractive_tabindex (Scrollable records for keyboard users.) -->
|
||||
<div
|
||||
class="table-scroll desktop-records"
|
||||
role="region"
|
||||
aria-label="Per-map player records"
|
||||
tabindex="0"
|
||||
>
|
||||
<table>
|
||||
<thead
|
||||
><tr
|
||||
><th scope="col">Map</th>{#each records as record}<th scope="col"
|
||||
>{record.label}</th
|
||||
>{/each}<th scope="col">Games</th><th scope="col">Total kills</th
|
||||
><th scope="col">Total rounds</th></tr
|
||||
></thead
|
||||
>
|
||||
<tbody
|
||||
>{#each profile.maps as map (`${map.game}:${map.key}`)}<tr
|
||||
><th scope="row"
|
||||
><a
|
||||
href={contextHref(
|
||||
`/matches/${map.records[record.key].matchId}`,
|
||||
$page.url
|
||||
)}
|
||||
aria-label={`${map.name} ${record.label}: ${number.format(map[record.key])}, Match #${map.records[record.key].matchId}`}
|
||||
title={map.records[record.key].endedAt
|
||||
? $timeFormat.dateTime(map.records[record.key].endedAt!)
|
||||
: 'Completed match'}>{number.format(map[record.key])}</a
|
||||
><small
|
||||
aria-label={`${map.name} ${record.label} rank ${map.ranks[record.key]}`}
|
||||
>Rank #{number.format(map.ranks[record.key])}</small
|
||||
></td
|
||||
>{/each}<td>{number.format(map.gamesPlayed)}</td><td
|
||||
>{number.format(map.kills)}</td
|
||||
><td>{number.format(map.roundsSurvived)}</td></tr
|
||||
>{/each}</tbody
|
||||
>
|
||||
</table>
|
||||
</div>
|
||||
href={`/leaderboard?${new URLSearchParams({ game: map.game, map: map.key, metric: scope.metric })}`}
|
||||
>{map.name}</a
|
||||
><small>{gameName(map.game)}</small></th
|
||||
>{#each records as record}<td
|
||||
><a
|
||||
href={contextHref(
|
||||
`/matches/${map.records[record.key].matchId}`,
|
||||
$page.url
|
||||
)}
|
||||
aria-label={`${map.name} ${record.label}: ${number.format(map[record.key])}, Match #${map.records[record.key].matchId}`}
|
||||
title={map.records[record.key].endedAt
|
||||
? $timeFormat.dateTime(
|
||||
map.records[record.key].endedAt!
|
||||
)
|
||||
: 'Completed match'}
|
||||
>{number.format(map[record.key])}</a
|
||||
><small
|
||||
aria-label={`${map.name} ${record.label} rank ${map.ranks[record.key]}`}
|
||||
>Rank #{number.format(map.ranks[record.key])}</small
|
||||
></td
|
||||
>{/each}<td>{number.format(map.gamesPlayed)}</td><td
|
||||
>{number.format(map.kills)}</td
|
||||
><td>{number.format(map.roundsSurvived)}</td></tr
|
||||
>{/each}</tbody
|
||||
>
|
||||
</table>
|
||||
</div>
|
||||
{:else}
|
||||
<EmptyState headingLevel={3} title="No map records yet" />
|
||||
{/if}
|
||||
</section>
|
||||
<section class="panel" aria-labelledby="history-title">
|
||||
<div class="panel-heading">
|
||||
@@ -333,6 +346,10 @@
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{:else if !profile.records}<EmptyState
|
||||
headingLevel={3}
|
||||
title="No qualifying matches recorded"
|
||||
/>
|
||||
{:else}<EmptyState
|
||||
headingLevel={3}
|
||||
title="No completed matches in this selection"
|
||||
@@ -352,7 +369,12 @@
|
||||
<dl class="metric-list">
|
||||
{#each otherMetrics as metric}<div>
|
||||
<dt>{metric.label}</dt>
|
||||
<dd>{number.format(stats[metric.key])}</dd>
|
||||
<dd>
|
||||
{!profile.records &&
|
||||
(metric.key === 'bestKills' || metric.key === 'bestScore')
|
||||
? '—'
|
||||
: number.format(stats[metric.key])}
|
||||
</dd>
|
||||
</div>{/each}{#each efficiencyMetrics as metric}<div>
|
||||
<dt>{metric.label}</dt>
|
||||
<dd>{formatEfficiencyValue(efficiencyValue(stats, metric.key), metric.key)}</dd>
|
||||
|
||||
Reference in New Issue
Block a user