┌─ web/src/components/PlayerProfile/PlayerBestRuns/PlayerBestRuns.astro ─────┐
│ diff --git a/web/src/components/PlayerProfile/PlayerBestRuns/PlayerBestRuns.astro │
│ b/web/src/components/PlayerProfile/PlayerBestRuns/PlayerBestRuns.astro │
│ index 50bc417..92dbb8b 100644 │
│ --- a/web/src/components/PlayerProfile/PlayerBestRuns/PlayerBestRuns.astro │
│ +++ b/web/src/components/PlayerProfile/PlayerBestRuns/PlayerBestRuns.astro │
│ @@ -10,6 +10,11 @@ import { getDungeonIconUrl } from "../../../lib/dungeonIcons"; │
│ import { getEffectiveRealmSlug } from "../../../lib/realms"; │
│ import "./PlayerBestRuns.scss"; │
│ │
│ +interface SeasonOption { │
│ + id: string; │
│ + label: string; │
│ +} │
│ + │
│ interface Props { │
│ bestRuns?: Record<string, BestRun>; │
│ mode?: "full" | "compact"; │
│ @@ -18,6 +23,8 @@ interface Props { │
│ playerRegion?: string; │
│ playerRealmSlug?: string; │
│ seasonId?: string | number; │
│ + seasonOptions?: SeasonOption[]; │
│ + seasonRuns?: Record<string, Record<string, BestRun>>; │
│ } │
│ │
│ const { │
│ @@ -28,177 +35,258 @@ const { │
│ playerRegion, │
│ playerRealmSlug, │
│ seasonId, │
│ + seasonOptions = [], │
│ + seasonRuns, │
│ } = Astro.props; │
│ │
│ -const runsArray = bestRuns ? Object.values(bestRuns) : []; │
│ -const seasonNumber = seasonId ? Number(seasonId) : undefined; │
│ -const leaderboardUrlOpts = seasonNumber ? { season: seasonNumber } : undefined; │
│ +const baseRunsArray = bestRuns ? Object.values(bestRuns) : []; │
│ +const normalizedSeasonId = │
│ + typeof seasonId === "number" ? String(seasonId) : seasonId || null; │
│ const leaderboardRealmSlug = │
│ getEffectiveRealmSlug(playerRegion, playerRealmSlug) || playerRealmSlug || ""; │
│ +const componentId = "player-best-runs"; │
│ +const seasonSelectId = "player-best-runs-season-select"; │
│ │
│ -const formatTime = (ms: number) => formatDurationMMSS(ms); │
│ - │
│ -const formatDate = (timestamp: number) => { │
│ - return new Date(timestamp).toLocaleDateString("en-US", { │
│ - month: "short", │
│ - day: "numeric", │
│ - year: "numeric", │
│ - }); │
│ +type SeasonEntry = { │
│ + id: string; │
│ + label: string; │
│ + runs: BestRun[]; │
│ }; │
│ + │
│ +const seasonEntries: SeasonEntry[] = │
│ + seasonOptions.length > 0 │
│ + ? seasonOptions.map((option) => ({ │
│ + id: option.id, │
│ + label: option.label, │
│ + runs: │
│ + (seasonRuns && seasonRuns[option.id] │
│ + ? Object.values(seasonRuns[option.id]) │
│ + : option.id === normalizedSeasonId │
│ + ? baseRunsArray │
│ + : []) || [], │
│ + })) │
│ + : [ │
│ + { │
│ + id: normalizedSeasonId || "current", │
│ + label: normalizedSeasonId ? `Season ${normalizedSeasonId}` : "Season", │
│ + runs: baseRunsArray, │
│ + }, │
│ + ]; │
│ + │
│ +const selectedSeasonId = │
│ + seasonEntries.find((entry) => entry.id === normalizedSeasonId)?.id || │
│ + seasonEntries[0]?.id || │
│ + null; │
│ + │
│ +const shouldShowSeasonSelector = seasonEntries.length > 1; │
│ + │
│ +const formatTime = (ms: number) => formatDurationMMSS(ms); │
│ --- │
│ │
│ { │
│ showSectionWrapper && ( │
│ <div class="profile-section"> │
│ <h3 class="section-title">Best Times Per Dungeon</h3> │
│ - <div class={`player-best-runs ${className || ""}`.trim()}> │
│ - {runsArray.length > 0 ? ( │
│ - <Table mode="best-runs"> │
│ - <div slot="header" class="table-header-cell"> │
│ - Dungeon │
│ - </div> │
│ - <div slot="header" class="table-header-cell"> │
│ - Time │
│ - </div> │
│ - <div slot="header" class="table-header-cell"> │
│ - Team │
│ - </div> │
│ - <div slot="header" class="table-header-cell"> │
│ - Global │
│ - </div> │
│ - <div slot="header" class="table-header-cell"> │
│ - Region │
│ - </div> │
│ - <div slot="header" class="table-header-cell"> │
│ - Realm │
│ + <div │
│ + id={componentId} │
│ + class={`player-best-runs ${className || ""}`.trim()} │
│ + data-component="player-best-runs" │
│ + > │
│ + {shouldShowSeasonSelector && ( │
│ + <div class="best-runs-controls"> │
│ + <div class="filter-group"> │
│ + <label for={seasonSelectId}>Season</label> │
│ + <select id={seasonSelectId} data-season-select> │
│ + {seasonEntries.map((entry) => ( │
│ + <option │
│ + value={entry.id} │
│ + selected={entry.id === selectedSeasonId} │
│ + > │
│ + {entry.label} │
│ + </option> │
│ + ))} │
│ + </select> │
│ </div> │
│ + </div> │
│ + )} │
│ │
│ - {runsArray.map((run) => { │
│ - const duration = formatTime(run.duration); │
│ - const dungeonSlug = (run as any).dungeon_slug || ""; │
│ - const iconUrl = getDungeonIconUrl(dungeonSlug, run.dungeon_name); │
│ - │
│ - const globalRankingValue = │
│ - typeof (run as any).global_ranking_filtered === "number" && │
│ - (run as any).global_ranking_filtered > 0 │
│ - ? (run as any).global_ranking_filtered │
│ - : (run as any).global_ranking; │
│ - const globalBracket = │
│ - (run as any).global_percentile_bracket || │
│ - (run as any).percentile_bracket || │
│ - ""; │
│ - │
│ - const regionalRankingValue = │
│ - typeof (run as any).regional_ranking_filtered === "number" && │
│ - (run as any).regional_ranking_filtered > 0 │
│ - ? (run as any).regional_ranking_filtered │
│ - : run.regional_ranking; │
│ - const realmRankingValue = │
│ - typeof (run as any).realm_ranking_filtered === "number" && │
│ - (run as any).realm_ranking_filtered > 0 │
│ - ? (run as any).realm_ranking_filtered │
│ - : run.realm_ranking; │
│ - │
│ - const regionalBracket = │
│ - (run as any).regional_percentile_bracket || │
│ - run.percentile_bracket || │
│ - ""; │
│ - const realmBracket = │
│ - (run as any).realm_percentile_bracket || │
│ - run.percentile_bracket || │
│ - ""; │
│ - │
│ - const globalURL = buildLeaderboardURL( │
│ - "global", │
│ - "all", │
│ - dungeonSlug, │
│ - undefined, │
│ - leaderboardUrlOpts, │
│ - ); │
│ - const regionalURL = playerRegion │
│ - ? buildLeaderboardURL( │
│ - playerRegion, │
│ - "all", │
│ - dungeonSlug, │
│ - undefined, │
│ - leaderboardUrlOpts, │
│ - ) │
│ - : "#"; │
│ - const realmURL = │
│ - playerRegion && leaderboardRealmSlug │
│ - ? buildLeaderboardURL( │
│ - playerRegion, │
│ - leaderboardRealmSlug, │
│ + {seasonEntries.map((entry) => { │
│ + const seasonNumber = Number(entry.id); │
│ + const leaderboardUrlOpts = │
│ + !Number.isNaN(seasonNumber) && entry.id !== "current" │
│ + ? { season: seasonNumber } │
│ + : undefined; │
│ + │
│ + return ( │
│ + <div │
│ + class="best-runs-season" │
│ + data-season={entry.id} │
│ + data-active={entry.id === selectedSeasonId ? "true" : "false"} │
│ + > │
│ + {entry.runs.length > 0 ? ( │
│ + <Table mode="best-runs"> │
│ + <div slot="header" class="table-header-cell"> │
│ + Dungeon │
│ + </div> │
│ + <div slot="header" class="table-header-cell"> │
│ + Time │
│ + </div> │
│ + <div slot="header" class="table-header-cell"> │
│ + Team │
│ + </div> │
│ + <div slot="header" class="table-header-cell"> │
│ + Global │
│ + </div> │
│ + <div slot="header" class="table-header-cell"> │
│ + Region │
│ + </div> │
│ + <div slot="header" class="table-header-cell"> │
│ + Realm │
│ + </div> │
│ + │
│ + {entry.runs.map((run) => { │
│ + const duration = formatTime(run.duration); │
│ + const dungeonSlug = (run as any).dungeon_slug || ""; │
│ + const iconUrl = getDungeonIconUrl( │
│ + dungeonSlug, │
│ + run.dungeon_name, │
│ + ); │
│ + │
│ + const globalRankingValue = │
│ + typeof (run as any).global_ranking_filtered === │
│ + "number" && (run as any).global_ranking_filtered > 0 │
│ + ? (run as any).global_ranking_filtered │
│ + : (run as any).global_ranking; │
│ + const globalBracket = │
│ + (run as any).global_percentile_bracket || │
│ + (run as any).percentile_bracket || │
│ + ""; │
│ + │
│ + const regionalRankingValue = │
│ + typeof (run as any).regional_ranking_filtered === │
│ + "number" && (run as any).regional_ranking_filtered > 0 │
│ + ? (run as any).regional_ranking_filtered │
│ + : run.regional_ranking; │
│ + const realmRankingValue = │
│ + typeof (run as any).realm_ranking_filtered === "number" && │
│ + (run as any).realm_ranking_filtered > 0 │
│ + ? (run as any).realm_ranking_filtered │
│ + : run.realm_ranking; │
│ + │
│ + const regionalBracket = │
│ + (run as any).regional_percentile_bracket || │
│ + run.percentile_bracket || │
│ + ""; │
│ + const realmBracket = │
│ + (run as any).realm_percentile_bracket || │
│ + run.percentile_bracket || │
│ + ""; │
│ + │
│ + const globalURL = buildLeaderboardURL( │
│ + "global", │
│ + "all", │
│ dungeonSlug, │
│ undefined, │
│ leaderboardUrlOpts, │
│ - ) │
│ - : "#"; │
│ - │
│ + ); │
│ + const regionalURL = playerRegion │
│ + ? buildLeaderboardURL( │
│ + playerRegion, │
│ + "all", │
│ + dungeonSlug, │
│ + undefined, │
│ + leaderboardUrlOpts, │
│ + ) │
│ + : "#"; │
│ + const realmURL = │
│ + playerRegion && leaderboardRealmSlug │
│ + ? buildLeaderboardURL( │
│ + playerRegion, │
│ + leaderboardRealmSlug, │
│ + dungeonSlug, │
│ + undefined, │
│ + leaderboardUrlOpts, │
│ + ) │
│ + : "#"; │
│ │
│ - const members = (run as any).all_members || (run as any).team_membe │
│ rs || []; │
│ + const members = │
│ + (run as any).all_members || │
│ + (run as any).team_members || │
│ + []; │
│ │
│ - return ( │
│ - <TableRow> │
│ - <TableCell label="Dungeon"> │
│ - <div class="dungeon-cell"> │
│ - {iconUrl && ( │
│ - <img │
│ - src={iconUrl} │
│ - alt={run.dungeon_name} │
│ - class="dungeon-icon" │
│ - /> │
│ - )} │
│ - <span class="dungeon-name">{run.dungeon_name}</span> │
│ - </div> │
│ - </TableCell> │
│ - <TableCell label="Time">{duration}</TableCell> │
│ - <TableCell label="Team"> │
│ - <TeamComposition │
│ - members={members} │
│ - currentRegion={playerRegion || ""} │
│ - currentRealm={playerRealmSlug || ""} │
│ - /> │
│ - </TableCell> │
│ - <TableCell label="Global" class="ranking-cell ranking-global"> │
│ - <a │
│ - href={globalURL} │
│ - class="rank-link" │
│ - set:html={formatRankingWithBracket( │
│ - globalRankingValue, │
│ - globalBracket, │
│ - )} │
│ - /> │
│ - </TableCell> │
│ - <TableCell label="Region" class="ranking-cell ranking-region"> │
│ - <a │
│ - href={regionalURL} │
│ - class="rank-link" │
│ - set:html={formatRankingWithBracket( │
│ - regionalRankingValue, │
│ - regionalBracket, │
│ - )} │
│ - /> │
│ - </TableCell> │
│ - <TableCell label="Realm" class="ranking-cell ranking-realm"> │
│ - <a │
│ - href={realmURL} │
│ - class="rank-link" │
│ - set:html={formatRankingWithBracket( │
│ - realmRankingValue, │
│ - realmBracket, │
│ - )} │
│ - /> │
│ - </TableCell> │
│ - </TableRow> │
│ - ); │
│ - })} │
│ - </Table> │
│ - ) : ( │
│ - <div class="no-runs-message"> │
│ - <p>No best runs found for this player.</p> │
│ - </div> │
│ - )} │
│ + return ( │
│ + <TableRow> │
│ + <TableCell label="Dungeon"> │
│ + <div class="dungeon-cell"> │
│ + {iconUrl && ( │
│ + <img │
│ + src={iconUrl} │
│ + alt={run.dungeon_name} │
│ + class="dungeon-icon" │
│ + /> │
│ + )} │
│ + <span class="dungeon-name">{run.dungeon_name}</span> │
│ + </div> │
│ + </TableCell> │
│ + <TableCell label="Time">{duration}</TableCell> │
│ + <TableCell label="Team"> │
│ + <TeamComposition │
│ + members={members} │
│ + currentRegion={playerRegion || ""} │
│ + currentRealm={playerRealmSlug || ""} │
│ + /> │
│ + </TableCell> │
│ + <TableCell │
│ + label="Global" │
│ + class="ranking-cell ranking-global" │
│ + > │
│ + <a │
│ + href={globalURL} │
│ + class="rank-link" │
│ + set:html={formatRankingWithBracket( │
│ + globalRankingValue, │
│ + globalBracket, │
│ + )} │
│ + /> │
│ + </TableCell> │
│ + <TableCell │
│ + label="Region" │
│ + class="ranking-cell ranking-region" │
│ + > │
│ + <a │
│ + href={regionalURL} │
│ + class="rank-link" │
│ + set:html={formatRankingWithBracket( │
│ + regionalRankingValue, │
│ + regionalBracket, │
│ + )} │
│ + /> │
│ + </TableCell> │
│ + <TableCell │
│ + label="Realm" │
│ + class="ranking-cell ranking-realm" │
│ + > │
│ + <a │
│ + href={realmURL} │
│ + class="rank-link" │
│ + set:html={formatRankingWithBracket( │
│ + realmRankingValue, │
│ + realmBracket, │
│ + )} │
│ + /> │
│ + </TableCell> │
│ + </TableRow> │
│ + ); │
│ + })} │
│ + </Table> │
│ + ) : ( │
│ + <div class="no-runs-message"> │
│ + <p>No best runs found for this season.</p> │
│ + </div> │
│ + )} │
│ + </div> │
│ + ); │
│ + })} │
│ </div> │
│ </div> │
│ ) │
│ @@ -206,154 +294,236 @@ const formatDate = (timestamp: number) => { │
│ │
│ { │
│ !showSectionWrapper && ( │
│ - <div class={`player-best-runs ${className || ""}`.trim()}> │
│ - {runsArray.length > 0 ? ( │
│ - <Table mode="best-runs"> │
│ - <div slot="header" class="table-header-cell"> │
│ - Dungeon │
│ - </div> │
│ - <div slot="header" class="table-header-cell"> │
│ - Time │
│ - </div> │
│ - <div slot="header" class="table-header-cell"> │
│ - Team │
│ - </div> │
│ - <div slot="header" class="table-header-cell"> │
│ - Global │
│ - </div> │
│ - <div slot="header" class="table-header-cell"> │
│ - Region │
│ - </div> │
│ - <div slot="header" class="table-header-cell"> │
│ - Realm │
│ + <div │
│ + id={componentId} │
│ + class={`player-best-runs ${className || ""}`.trim()} │
│ + data-component="player-best-runs" │
│ + > │
│ + {shouldShowSeasonSelector && ( │
│ + <div class="best-runs-controls"> │
│ + <div class="filter-group"> │
│ + <label for={seasonSelectId}>Season</label> │
│ + <select id={seasonSelectId} data-season-select> │
│ + {seasonEntries.map((entry) => ( │
│ + <option │
│ + value={entry.id} │
│ + selected={entry.id === selectedSeasonId} │
│ + > │
│ + {entry.label} │
│ + </option> │
│ + ))} │
│ + </select> │
│ </div> │
│ + </div> │
│ + )} │
│ + │
│ + {seasonEntries.map((entry) => { │
│ + const seasonNumber = Number(entry.id); │
│ + const leaderboardUrlOpts = │
│ + !Number.isNaN(seasonNumber) && entry.id !== "current" │
│ + ? { season: seasonNumber } │
│ + : undefined; │
│ + │
│ + return ( │
│ + <div │
│ + class="best-runs-season" │
│ + data-season={entry.id} │
│ + data-active={entry.id === selectedSeasonId ? "true" : "false"} │
│ + > │
│ + {entry.runs.length > 0 ? ( │
│ + <Table mode="best-runs"> │
│ + <div slot="header" class="table-header-cell"> │
│ + Dungeon │
│ + </div> │
│ + <div slot="header" class="table-header-cell"> │
│ + Time │
│ + </div> │
│ + <div slot="header" class="table-header-cell"> │
│ + Team │
│ + </div> │
│ + <div slot="header" class="table-header-cell"> │
│ + Global │
│ + </div> │
│ + <div slot="header" class="table-header-cell"> │
│ + Region │
│ + </div> │
│ + <div slot="header" class="table-header-cell"> │
│ + Realm │
│ + </div> │
│ │
│ - {runsArray.map((run) => { │
│ - const duration = formatTime(run.duration); │
│ - const dungeonSlug = (run as any).dungeon_slug || ""; │
│ - const iconUrl = getDungeonIconUrl(dungeonSlug, run.dungeon_name); │
│ - │
│ - const globalRankingValue = │
│ - typeof (run as any).global_ranking_filtered === "number" && │
│ - (run as any).global_ranking_filtered > 0 │
│ - ? (run as any).global_ranking_filtered │
│ - : (run as any).global_ranking; │
│ - const globalBracket = │
│ - (run as any).global_percentile_bracket || │
│ - (run as any).percentile_bracket || │
│ - ""; │
│ - │
│ - const regionalRankingValue = │
│ - typeof (run as any).regional_ranking_filtered === "number" && │
│ - (run as any).regional_ranking_filtered > 0 │
│ - ? (run as any).regional_ranking_filtered │
│ - : run.regional_ranking; │
│ - const realmRankingValue = │
│ - typeof (run as any).realm_ranking_filtered === "number" && │
│ - (run as any).realm_ranking_filtered > 0 │
│ - ? (run as any).realm_ranking_filtered │
│ - : run.realm_ranking; │
│ - │
│ - const regionalBracket = │
│ - (run as any).regional_percentile_bracket || │
│ - run.percentile_bracket || │
│ - ""; │
│ - const realmBracket = │
│ - (run as any).realm_percentile_bracket || │
│ - run.percentile_bracket || │
│ - ""; │
│ - │
│ - const globalURL = buildLeaderboardURL( │
│ - "global", │
│ - "all", │
│ - dungeonSlug, │
│ - undefined, │
│ - leaderboardUrlOpts, │
│ - ); │
│ - const regionalURL = playerRegion │
│ - ? buildLeaderboardURL( │
│ - playerRegion, │
│ - "all", │
│ - dungeonSlug, │
│ - undefined, │
│ - leaderboardUrlOpts, │
│ - ) │
│ - : "#"; │
│ - const realmURL = │
│ - playerRegion && leaderboardRealmSlug │
│ - ? buildLeaderboardURL( │
│ - playerRegion, │
│ - leaderboardRealmSlug, │
│ + {entry.runs.map((run) => { │
│ + const duration = formatTime(run.duration); │
│ + const dungeonSlug = (run as any).dungeon_slug || ""; │
│ + const iconUrl = getDungeonIconUrl( │
│ + dungeonSlug, │
│ + run.dungeon_name, │
│ + ); │
│ + │
│ + const globalRankingValue = │
│ + typeof (run as any).global_ranking_filtered === "number" && │
│ + (run as any).global_ranking_filtered > 0 │
│ + ? (run as any).global_ranking_filtered │
│ + : (run as any).global_ranking; │
│ + const globalBracket = │
│ + (run as any).global_percentile_bracket || │
│ + (run as any).percentile_bracket || │
│ + ""; │
│ + │
│ + const regionalRankingValue = │
│ + typeof (run as any).regional_ranking_filtered === │
│ + "number" && (run as any).regional_ranking_filtered > 0 │
│ + ? (run as any).regional_ranking_filtered │
│ + : run.regional_ranking; │
│ + const realmRankingValue = │
│ + typeof (run as any).realm_ranking_filtered === "number" && │
│ + (run as any).realm_ranking_filtered > 0 │
│ + ? (run as any).realm_ranking_filtered │
│ + : run.realm_ranking; │
│ + │
│ + const regionalBracket = │
│ + (run as any).regional_percentile_bracket || │
│ + run.percentile_bracket || │
│ + ""; │
│ + const realmBracket = │
│ + (run as any).realm_percentile_bracket || │
│ + run.percentile_bracket || │
│ + ""; │
│ + │
│ + const globalURL = buildLeaderboardURL( │
│ + "global", │
│ + "all", │
│ dungeonSlug, │
│ undefined, │
│ leaderboardUrlOpts, │
│ - ) │
│ - : "#"; │
│ - │
│ - │
│ - const members = (run as any).all_members || (run as any).team_members │
│ || []; │
│ - │
│ - return ( │
│ - <TableRow> │
│ - <TableCell label="Dungeon"> │
│ - <div class="dungeon-cell"> │
│ - {iconUrl && ( │
│ - <img │
│ - src={iconUrl} │
│ - alt={run.dungeon_name} │
│ - class="dungeon-icon" │
│ - /> │
│ - )} │
│ - <span class="dungeon-name">{run.dungeon_name}</span> │
│ - </div> │
│ - </TableCell> │
│ - <TableCell label="Time">{duration}</TableCell> │
│ - <TableCell label="Team"> │
│ - <TeamComposition │
│ - members={members} │
│ - currentRegion={playerRegion || ""} │
│ - currentRealm={playerRealmSlug || ""} │
│ - /> │
│ - </TableCell> │
│ - <TableCell label="Global" class="ranking-cell ranking-global"> │
│ - <a │
│ - href={globalURL} │
│ - class="rank-link" │
│ - set:html={formatRankingWithBracket( │
│ - globalRankingValue, │
│ - globalBracket, │
│ - )} │
│ - /> │
│ - </TableCell> │
│ - <TableCell label="Region" class="ranking-cell ranking-region"> │
│ - <a │
│ - href={regionalURL} │
│ - class="rank-link" │
│ - set:html={formatRankingWithBracket( │
│ - regionalRankingValue, │
│ - regionalBracket, │
│ - )} │
│ - /> │
│ - </TableCell> │
│ - <TableCell label="Realm" class="ranking-cell ranking-realm"> │
│ - <a │
│ - href={realmURL} │
│ - class="rank-link" │
│ - set:html={formatRankingWithBracket( │
│ - realmRankingValue, │
│ - realmBracket, │
│ - )} │
│ - /> │
│ - </TableCell> │
│ - </TableRow> │
│ - ); │
│ - })} │
│ - </Table> │
│ - ) : ( │
│ - <div class="no-runs-message"> │
│ - <p>No best runs found for this player.</p> │
│ - </div> │
│ - )} │
│ + ); │
│ + const regionalURL = playerRegion │
│ + ? buildLeaderboardURL( │
│ + playerRegion, │
│ + "all", │
│ + dungeonSlug, │
│ + undefined, │
│ + leaderboardUrlOpts, │
│ + ) │
│ + : "#"; │
│ + const realmURL = │
│ + playerRegion && leaderboardRealmSlug │
│ + ? buildLeaderboardURL( │
│ + playerRegion, │
│ + leaderboardRealmSlug, │
│ + dungeonSlug, │
│ + undefined, │
│ + leaderboardUrlOpts, │
│ + ) │
│ + : "#"; │
│ + │
│ + const members = │
│ + (run as any).all_members || (run as any).team_members || []; │
│ + │
│ + return ( │
│ + <TableRow> │
│ + <TableCell label="Dungeon"> │
│ + <div class="dungeon-cell"> │
│ + {iconUrl && ( │
│ + <img │
│ + src={iconUrl} │
│ + alt={run.dungeon_name} │
│ + class="dungeon-icon" │
│ + /> │
│ + )} │
│ + <span class="dungeon-name">{run.dungeon_name}</span> │
│ + </div> │
│ + </TableCell> │
│ + <TableCell label="Time">{duration}</TableCell> │
│ + <TableCell label="Team"> │
│ + <TeamComposition │
│ + members={members} │
│ + currentRegion={playerRegion || ""} │
│ + currentRealm={playerRealmSlug || ""} │
│ + /> │
│ + </TableCell> │
│ + <TableCell │
│ + label="Global" │
│ + class="ranking-cell ranking-global" │
│ + > │
│ + <a │
│ + href={globalURL} │
│ + class="rank-link" │
│ + set:html={formatRankingWithBracket( │
│ + globalRankingValue, │
│ + globalBracket, │
│ + )} │
│ + /> │
│ + </TableCell> │
│ + <TableCell │
│ + label="Region" │
│ + class="ranking-cell ranking-region" │
│ + > │
│ + <a │
│ + href={regionalURL} │
│ + class="rank-link" │
│ + set:html={formatRankingWithBracket( │
│ + regionalRankingValue, │
│ + regionalBracket, │
│ + )} │
│ + /> │
│ + </TableCell> │
│ + <TableCell │
│ + label="Realm" │
│ + class="ranking-cell ranking-realm" │
│ + > │
│ + <a │
│ + href={realmURL} │
│ + class="rank-link" │
│ + set:html={formatRankingWithBracket( │
│ + realmRankingValue, │
│ + realmBracket, │
│ + )} │
│ + /> │
│ + </TableCell> │
│ + </TableRow> │
│ + ); │
│ + })} │
│ + </Table> │
│ + ) : ( │
│ + <div class="no-runs-message"> │
│ + <p>No best runs found for this season.</p> │
│ + </div> │
│ + )} │
│ + </div> │
│ + ); │
│ + })} │
│ </div> │
│ ) │
│ } │
│ + │
│ +<script is:inline> │
│ + (() => { │
│ + const container = document.getElementById("player-best-runs"); │
│ + if (!container) return; │
│ + const select = container.querySelector("[data-season-select]"); │
│ + if (!select) return; │
│ + │
│ + select.addEventListener("change", (event) => { │
│ + const target = event.target; │
│ + if (!(target instanceof HTMLSelectElement)) { │
│ + return; │
│ + } │
│ + const selectedSeason = target.value; │
│ + container.querySelectorAll("[data-season]").forEach((section) => { │
│ + const isActive = section.getAttribute("data-season") === selectedSeason; │
│ + section.setAttribute("data-active", isActive ? "true" : "false"); │
│ + }); │
│ + │
│ + const url = new URL(window.location.href); │
│ + if (selectedSeason) { │
│ + url.searchParams.set("season", selectedSeason); │
│ + } else { │
│ + url.searchParams.delete("season"); │
│ + } │
│ + │
│ + const newSearch = url.searchParams.toString(); │
│ + const nextUrl = newSearch ? `${url.pathname}?${newSearch}` : url.pathname; │
│ + window.history.replaceState({}, "", nextUrl); │
│ + }); │
│ + })(); │
│ +</script> │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...BestRuns/PlayerBestRuns.astro ───┐
│ diff --git a/web/src/components/PlayerProfil │
│ e/PlayerBestRuns/PlayerBestRuns.astro b/web/ │
│ src/components/PlayerProfile/PlayerBestRuns/ │
│ PlayerBestRuns.astro │
│ index 50bc417..92dbb8b 100644 │
│ --- a/web/src/components/PlayerProfile/Playe │
│ rBestRuns/PlayerBestRuns.astro │
│ +++ b/web/src/components/PlayerProfile/Playe │
│ rBestRuns/PlayerBestRuns.astro │
│ @@ -10,6 +10,11 @@ import { getDungeonIconUr │
│ l } from "../../../lib/dungeonIcons"; │
│ import { getEffectiveRealmSlug } from "../. │
│ ./../lib/realms"; │
│ import "./PlayerBestRuns.scss"; │
│ │
│ +interface SeasonOption { │
│ + id: string; │
│ + label: string; │
│ +} │
│ + │
│ interface Props { │
│ bestRuns?: Record<string, BestRun>; │
│ mode?: "full" | "compact"; │
│ @@ -18,6 +23,8 @@ interface Props { │
│ playerRegion?: string; │
│ playerRealmSlug?: string; │
│ seasonId?: string | number; │
│ + seasonOptions?: SeasonOption[]; │
│ + seasonRuns?: Record<string, Record<string │
│ , BestRun>>; │
│ } │
│ │
│ const { │
│ @@ -28,177 +35,258 @@ const { │
│ playerRegion, │
│ playerRealmSlug, │
│ seasonId, │
│ + seasonOptions = [], │
│ + seasonRuns, │
│ } = Astro.props; │
│ │
│ -const runsArray = bestRuns ? Object.values( │
│ bestRuns) : []; │
│ -const seasonNumber = seasonId ? Number(seas │
│ onId) : undefined; │
│ -const leaderboardUrlOpts = seasonNumber ? { │
│ season: seasonNumber } : undefined; │
│ +const baseRunsArray = bestRuns ? Object.val │
│ ues(bestRuns) : []; │
│ +const normalizedSeasonId = │
│ + typeof seasonId === "number" ? String(sea │
│ sonId) : seasonId || null; │
│ const leaderboardRealmSlug = │
│ getEffectiveRealmSlug(playerRegion, playe │
│ rRealmSlug) || playerRealmSlug || ""; │
│ +const componentId = "player-best-runs"; │
│ +const seasonSelectId = "player-best-runs-se │
│ ason-select"; │
│ │
│ -const formatTime = (ms: number) => formatDu │
│ rationMMSS(ms); │
│ - │
│ -const formatDate = (timestamp: number) => { │
│ - return new Date(timestamp).toLocaleDateSt │
│ ring("en-US", { │
│ - month: "short", │
│ - day: "numeric", │
│ - year: "numeric", │
│ - }); │
│ +type SeasonEntry = { │
│ + id: string; │
│ + label: string; │
│ + runs: BestRun[]; │
│ }; │
│ + │
│ +const seasonEntries: SeasonEntry[] = │
│ + seasonOptions.length > 0 │
│ + ? seasonOptions.map((option) => ({ │
│ + id: option.id, │
│ + label: option.label, │
│ + runs: │
│ + (seasonRuns && seasonRuns[option. │
│ id] │
│ + ? Object.values(seasonRuns[opti │
│ on.id]) │
│ + : option.id === normalizedSeaso │
│ nId │
│ + ? baseRunsArray │
│ + : []) || [], │
│ + })) │
│ + : [ │
│ + { │
│ + id: normalizedSeasonId || "curren │
│ t", │
│ + label: normalizedSeasonId ? `Seas │
│ on ${normalizedSeasonId}` : "Season", │
│ + runs: baseRunsArray, │
│ + }, │
│ + ]; │
│ + │
│ +const selectedSeasonId = │
│ + seasonEntries.find((entry) => entry.id == │
│ = normalizedSeasonId)?.id || │
│ + seasonEntries[0]?.id || │
│ + null; │
│ + │
│ +const shouldShowSeasonSelector = seasonEntr │
│ ies.length > 1; │
│ + │
│ +const formatTime = (ms: number) => formatDu │
│ rationMMSS(ms); │
│ --- │
│ │
│ { │
│ showSectionWrapper && ( │
│ <div class="profile-section"> │
│ <h3 class="section-title">Best Times │
│ Per Dungeon</h3> │
│ - <div class={`player-best-runs ${class │
│ Name || ""}`.trim()}> │
│ - {runsArray.length > 0 ? ( │
│ - <Table mode="best-runs"> │
│ - <div slot="header" class="table │
│ -header-cell"> │
│ - Dungeon │
│ - </div> │
│ - <div slot="header" class="table │
│ -header-cell"> │
│ - Time │
│ - </div> │
│ - <div slot="header" class="table │
│ -header-cell"> │
│ - Team │
│ - </div> │
│ - <div slot="header" class="table │
│ -header-cell"> │
│ - Global │
│ - </div> │
│ - <div slot="header" class="table │
│ -header-cell"> │
│ - Region │
│ - </div> │
│ - <div slot="header" class="table │
│ -header-cell"> │
│ - Realm │
│ + <div │
│ + id={componentId} │
│ + class={`player-best-runs ${classNam │
│ e || ""}`.trim()} │
│ + data-component="player-best-runs" │
│ + > │
│ + {shouldShowSeasonSelector && ( │
│ + <div class="best-runs-controls"> │
│ + <div class="filter-group"> │
│ + <label for={seasonSelectId}>S │
│ eason</label> │
│ + <select id={seasonSelectId} d │
│ ata-season-select> │
│ + {seasonEntries.map((entry) │
│ => ( │
│ + <option │
│ + value={entry.id} │
│ + selected={entry.id === │
│ selectedSeasonId} │
│ + > │
│ + {entry.label} │
│ + </option> │
│ + ))} │
│ + </select> │
│ </div> │
│ + </div> │
│ + )} │
│ │
│ - {runsArray.map((run) => { │
│ - const duration = formatTime(r │
│ un.duration); │
│ - const dungeonSlug = (run as a │
│ ny).dungeon_slug || ""; │
│ - const iconUrl = getDungeonIco │
│ nUrl(dungeonSlug, run.dungeon_name); │
│ - │
│ - const globalRankingValue = │
│ - typeof (run as any).global_ │
│ ranking_filtered === "number" && │
│ - (run as any).global_ranking │
│ _filtered > 0 │
│ - ? (run as any).global_ran │
│ king_filtered │
│ - : (run as any).global_ran │
│ king; │
│ - const globalBracket = │
│ - (run as any).global_percent │
│ ile_bracket || │
│ - (run as any).percentile_bra │
│ cket || │
│ - ""; │
│ - │
│ - const regionalRankingValue = │
│ - typeof (run as any).regiona │
│ l_ranking_filtered === "number" && │
│ - (run as any).regional_ranki │
│ ng_filtered > 0 │
│ - ? (run as any).regional_r │
│ anking_filtered │
│ - : run.regional_ranking; │
│ - const realmRankingValue = │
│ - typeof (run as any).realm_r │
│ anking_filtered === "number" && │
│ - (run as any).realm_ranking_ │
│ filtered > 0 │
│ - ? (run as any).realm_rank │
│ ing_filtered │
│ - : run.realm_ranking; │
│ - │
│ - const regionalBracket = │
│ - (run as any).regional_perce │
│ ntile_bracket || │
│ - run.percentile_bracket || │
│ - ""; │
│ - const realmBracket = │
│ - (run as any).realm_percenti │
│ le_bracket || │
│ - run.percentile_bracket || │
│ - ""; │
│ - │
│ - const globalURL = buildLeader │
│ boardURL( │
│ - "global", │
│ - "all", │
│ - dungeonSlug, │
│ - undefined, │
│ - leaderboardUrlOpts, │
│ - ); │
│ - const regionalURL = playerReg │
│ ion │
│ - ? buildLeaderboardURL( │
│ - playerRegion, │
│ - "all", │
│ - dungeonSlug, │
│ - undefined, │
│ - leaderboardUrlOpts, │
│ - ) │
│ - : "#"; │
│ - const realmURL = │
│ - playerRegion && leaderboard │
│ RealmSlug │
│ - ? buildLeaderboardURL( │
│ - playerRegion, │
│ - leaderboardRealmSlug, │
│ + {seasonEntries.map((entry) => { │
│ + const seasonNumber = Number(entry │
│ .id); │
│ + const leaderboardUrlOpts = │
│ + !Number.isNaN(seasonNumber) && │
│ entry.id !== "current" │
│ + ? { season: seasonNumber } │
│ + : undefined; │
│ + │
│ + return ( │
│ + <div │
│ + class="best-runs-season" │
│ + data-season={entry.id} │
│ + data-active={entry.id === sel │
│ ectedSeasonId ? "true" : "false"} │
│ + > │
│ + {entry.runs.length > 0 ? ( │
│ + <Table mode="best-runs"> │
│ + <div slot="header" class= │
│ "table-header-cell"> │
│ + Dungeon │
│ + </div> │
│ + <div slot="header" class= │
│ "table-header-cell"> │
│ + Time │
│ + </div> │
│ + <div slot="header" class= │
│ "table-header-cell"> │
│ + Team │
│ + </div> │
│ + <div slot="header" class= │
│ "table-header-cell"> │
│ + Global │
│ + </div> │
│ + <div slot="header" class= │
│ "table-header-cell"> │
│ + Region │
│ + </div> │
│ + <div slot="header" class= │
│ "table-header-cell"> │
│ + Realm │
│ + </div> │
│ + │
│ + {entry.runs.map((run) => │
│ { │
│ + const duration = format │
│ Time(run.duration); │
│ + const dungeonSlug = (ru │
│ n as any).dungeon_slug || ""; │
│ + const iconUrl = getDung │
│ eonIconUrl( │
│ + dungeonSlug, │
│ + run.dungeon_name, │
│ + ); │
│ + │
│ + const globalRankingValu │
│ e = │
│ + typeof (run as any).g │
│ lobal_ranking_filtered === │
│ + "number" && (run as │
│ any).global_ranking_filtered > 0 │
│ + ? (run as any).glob │
│ al_ranking_filtered │
│ + : (run as any).glob │
│ al_ranking; │
│ + const globalBracket = │
│ + (run as any).global_p │
│ ercentile_bracket || │
│ + (run as any).percenti │
│ le_bracket || │
│ + ""; │
│ + │
│ + const regionalRankingVa │
│ lue = │
│ + typeof (run as any).r │
│ egional_ranking_filtered === │
│ + "number" && (run as │
│ any).regional_ranking_filtered > 0 │
│ + ? (run as any).regi │
│ onal_ranking_filtered │
│ + : run.regional_rank │
│ ing; │
│ + const realmRankingValue │
│ = │
│ + typeof (run as any).r │
│ ealm_ranking_filtered === "number" && │
│ + (run as any).realm_ra │
│ nking_filtered > 0 │
│ + ? (run as any).real │
│ m_ranking_filtered │
│ + : run.realm_ranking │
│ ; │
│ + │
│ + const regionalBracket = │
│ + (run as any).regional │
│ _percentile_bracket || │
│ + run.percentile_bracke │
│ t || │
│ + ""; │
│ + const realmBracket = │
│ + (run as any).realm_pe │
│ rcentile_bracket || │
│ + run.percentile_bracke │
│ t || │
│ + ""; │
│ + │
│ + const globalURL = build │
│ LeaderboardURL( │
│ + "global", │
│ + "all", │
│ dungeonSlug, │
│ undefined, │
│ leaderboardUrlOpts, │
│ - ) │
│ - : "#"; │
│ - │
│ + ); │
│ + const regionalURL = pla │
│ yerRegion │
│ + ? buildLeaderboardURL │
│ ( │
│ + playerRegion, │
│ + "all", │
│ + dungeonSlug, │
│ + undefined, │
│ + leaderboardUrlOpt │
│ s, │
│ + ) │
│ + : "#"; │
│ + const realmURL = │
│ + playerRegion && leade │
│ rboardRealmSlug │
│ + ? buildLeaderboardU │
│ RL( │
│ + playerRegion, │
│ + leaderboardReal │
│ mSlug, │
│ + dungeonSlug, │
│ + undefined, │
│ + leaderboardUrlO │
│ pts, │
│ + ) │
│ + : "#"; │
│ │
│ - const members = (run as any). │
│ all_members || (run as any).team_members || │
│ []; │
│ + const members = │
│ + (run as any).all_memb │
│ ers || │
│ + (run as any).team_mem │
│ bers || │
│ + []; │
│ │
│ - return ( │
│ - <TableRow> │
│ - <TableCell label="Dungeon │
│ "> │
│ - <div class="dungeon-cel │
│ l"> │
│ - {iconUrl && ( │
│ - <img │
│ - src={iconUrl} │
│ - alt={run.dungeon_ │
│ name} │
│ - class="dungeon-ic │
│ on" │
│ - /> │
│ - )} │
│ - <span class="dungeon- │
│ name">{run.dungeon_name}</span> │
│ - </div> │
│ - </TableCell> │
│ - <TableCell label="Time">{ │
│ duration}</TableCell> │
│ - <TableCell label="Team"> │
│ - <TeamComposition │
│ - members={members} │
│ - currentRegion={player │
│ Region || ""} │
│ - currentRealm={playerR │
│ ealmSlug || ""} │
│ - /> │
│ - </TableCell> │
│ - <TableCell label="Global" │
│ class="ranking-cell ranking-global"> │
│ - <a │
│ - href={globalURL} │
│ - class="rank-link" │
│ - set:html={formatRanki │
│ ngWithBracket( │
│ - globalRankingValue, │
│ - globalBracket, │
│ - )} │
│ - /> │
│ - </TableCell> │
│ - <TableCell label="Region" │
│ class="ranking-cell ranking-region"> │
│ - <a │
│ - href={regionalURL} │
│ - class="rank-link" │
│ - set:html={formatRanki │
│ ngWithBracket( │
│ - regionalRankingValu │
│ e, │
│ - regionalBracket, │
│ - )} │
│ - /> │
│ - </TableCell> │
│ - <TableCell label="Realm" │
│ class="ranking-cell ranking-realm"> │
│ - <a │
│ - href={realmURL} │
│ - class="rank-link" │
│ - set:html={formatRanki │
│ ngWithBracket( │
│ - realmRankingValue, │
│ - realmBracket, │
│ - )} │
│ - /> │
│ - </TableCell> │
│ - </TableRow> │
│ - ); │
│ - })} │
│ - </Table> │
│ - ) : ( │
│ - <div class="no-runs-message"> │
│ - <p>No best runs found for this │
│ player.</p> │
│ - </div> │
│ - )} │
│ + return ( │
│ + <TableRow> │
│ + <TableCell label="D │
│ ungeon"> │
│ + <div class="dunge │
│ on-cell"> │
│ + {iconUrl && ( │
│ + <img │
│ + src={iconUr │
│ l} │
│ + alt={run.du │
│ ngeon_name} │
│ + class="dung │
│ eon-icon" │
│ + /> │
│ + )} │
│ + <span class="du │
│ ngeon-name">{run.dungeon_name}</span> │
│ + </div> │
│ + </TableCell> │
│ + <TableCell label="T │
│ ime">{duration}</TableCell> │
│ + <TableCell label="T │
│ eam"> │
│ + <TeamComposition │
│ + members={member │
│ s} │
│ + currentRegion={ │
│ playerRegion || ""} │
│ + currentRealm={p │
│ layerRealmSlug || ""} │
│ + /> │
│ + </TableCell> │
│ + <TableCell │
│ + label="Global" │
│ + class="ranking-ce │
│ ll ranking-global" │
│ + > │
│ + <a │
│ + href={globalURL │
│ } │
│ + class="rank-lin │
│ k" │
│ + set:html={forma │
│ tRankingWithBracket( │
│ + globalRanking │
│ Value, │
│ + globalBracket │
│ , │
│ + )} │
│ + /> │
│ + </TableCell> │
│ + <TableCell │
│ + label="Region" │
│ + class="ranking-ce │
│ ll ranking-region" │
│ + > │
│ + <a │
│ + href={regionalU │
│ RL} │
│ + class="rank-lin │
│ k" │
│ + set:html={forma │
│ tRankingWithBracket( │
│ + regionalRanki │
│ ngValue, │
│ + regionalBrack │
│ et, │
│ + )} │
│ + /> │
│ + </TableCell> │
│ + <TableCell │
│ + label="Realm" │
│ + class="ranking-ce │
│ ll ranking-realm" │
│ + > │
│ + <a │
│ + href={realmURL} │
│ + class="rank-lin │
│ k" │
│ + set:html={forma │
│ tRankingWithBracket( │
│ + realmRankingV │
│ alue, │
│ + realmBracket, │
│ + )} │
│ + /> │
│ + </TableCell> │
│ + </TableRow> │
│ + ); │
│ + })} │
│ + </Table> │
│ + ) : ( │
│ + <div class="no-runs-message │
│ "> │
│ + <p>No best runs found for │
│ this season.</p> │
│ + </div> │
│ + )} │
│ + </div> │
│ + ); │
│ + })} │
│ </div> │
│ </div> │
│ ) │
│ @@ -206,154 +294,236 @@ const formatDate = ( │
│ timestamp: number) => { │
│ │
│ { │
│ !showSectionWrapper && ( │
│ - <div class={`player-best-runs ${classNa │
│ me || ""}`.trim()}> │
│ - {runsArray.length > 0 ? ( │
│ - <Table mode="best-runs"> │
│ - <div slot="header" class="table-h │
│ eader-cell"> │
│ - Dungeon │
│ - </div> │
│ - <div slot="header" class="table-h │
│ eader-cell"> │
│ - Time │
│ - </div> │
│ - <div slot="header" class="table-h │
│ eader-cell"> │
│ - Team │
│ - </div> │
│ - <div slot="header" class="table-h │
│ eader-cell"> │
│ - Global │
│ - </div> │
│ - <div slot="header" class="table-h │
│ eader-cell"> │
│ - Region │
│ - </div> │
│ - <div slot="header" class="table-h │
│ eader-cell"> │
│ - Realm │
│ + <div │
│ + id={componentId} │
│ + class={`player-best-runs ${className │
│ || ""}`.trim()} │
│ + data-component="player-best-runs" │
│ + > │
│ + {shouldShowSeasonSelector && ( │
│ + <div class="best-runs-controls"> │
│ + <div class="filter-group"> │
│ + <label for={seasonSelectId}>Sea │
│ son</label> │
│ + <select id={seasonSelectId} dat │
│ a-season-select> │
│ + {seasonEntries.map((entry) => │
│ ( │
│ + <option │
│ + value={entry.id} │
│ + selected={entry.id === se │
│ lectedSeasonId} │
│ + > │
│ + {entry.label} │
│ + </option> │
│ + ))} │
│ + </select> │
│ </div> │
│ + </div> │
│ + )} │
│ + │
│ + {seasonEntries.map((entry) => { │
│ + const seasonNumber = Number(entry.i │
│ d); │
│ + const leaderboardUrlOpts = │
│ + !Number.isNaN(seasonNumber) && en │
│ try.id !== "current" │
│ + ? { season: seasonNumber } │
│ + : undefined; │
│ + │
│ + return ( │
│ + <div │
│ + class="best-runs-season" │
│ + data-season={entry.id} │
│ + data-active={entry.id === selec │
│ tedSeasonId ? "true" : "false"} │
│ + > │
│ + {entry.runs.length > 0 ? ( │
│ + <Table mode="best-runs"> │
│ + <div slot="header" class="t │
│ able-header-cell"> │
│ + Dungeon │
│ + </div> │
│ + <div slot="header" class="t │
│ able-header-cell"> │
│ + Time │
│ + </div> │
│ + <div slot="header" class="t │
│ able-header-cell"> │
│ + Team │
│ + </div> │
│ + <div slot="header" class="t │
│ able-header-cell"> │
│ + Global │
│ + </div> │
│ + <div slot="header" class="t │
│ able-header-cell"> │
│ + Region │
│ + </div> │
│ + <div slot="header" class="t │
│ able-header-cell"> │
│ + Realm │
│ + </div> │
│ │
│ - {runsArray.map((run) => { │
│ - const duration = formatTime(run │
│ .duration); │
│ - const dungeonSlug = (run as any │
│ ).dungeon_slug || ""; │
│ - const iconUrl = getDungeonIconU │
│ rl(dungeonSlug, run.dungeon_name); │
│ - │
│ - const globalRankingValue = │
│ - typeof (run as any).global_ra │
│ nking_filtered === "number" && │
│ - (run as any).global_ranking_f │
│ iltered > 0 │
│ - ? (run as any).global_ranki │
│ ng_filtered │
│ - : (run as any).global_ranki │
│ ng; │
│ - const globalBracket = │
│ - (run as any).global_percentil │
│ e_bracket || │
│ - (run as any).percentile_brack │
│ et || │
│ - ""; │
│ - │
│ - const regionalRankingValue = │
│ - typeof (run as any).regional_ │
│ ranking_filtered === "number" && │
│ - (run as any).regional_ranking │
│ _filtered > 0 │
│ - ? (run as any).regional_ran │
│ king_filtered │
│ - : run.regional_ranking; │
│ - const realmRankingValue = │
│ - typeof (run as any).realm_ran │
│ king_filtered === "number" && │
│ - (run as any).realm_ranking_fi │
│ ltered > 0 │
│ - ? (run as any).realm_rankin │
│ g_filtered │
│ - : run.realm_ranking; │
│ - │
│ - const regionalBracket = │
│ - (run as any).regional_percent │
│ ile_bracket || │
│ - run.percentile_bracket || │
│ - ""; │
│ - const realmBracket = │
│ - (run as any).realm_percentile │
│ _bracket || │
│ - run.percentile_bracket || │
│ - ""; │
│ - │
│ - const globalURL = buildLeaderbo │
│ ardURL( │
│ - "global", │
│ - "all", │
│ - dungeonSlug, │
│ - undefined, │
│ - leaderboardUrlOpts, │
│ - ); │
│ - const regionalURL = playerRegio │
│ n │
│ - ? buildLeaderboardURL( │
│ - playerRegion, │
│ - "all", │
│ - dungeonSlug, │
│ - undefined, │
│ - leaderboardUrlOpts, │
│ - ) │
│ - : "#"; │
│ - const realmURL = │
│ - playerRegion && leaderboardRe │
│ almSlug │
│ - ? buildLeaderboardURL( │
│ - playerRegion, │
│ - leaderboardRealmSlug, │
│ + {entry.runs.map((run) => { │
│ + const duration = formatTi │
│ me(run.duration); │
│ + const dungeonSlug = (run │
│ as any).dungeon_slug || ""; │
│ + const iconUrl = getDungeo │
│ nIconUrl( │
│ + dungeonSlug, │
│ + run.dungeon_name, │
│ + ); │
│ + │
│ + const globalRankingValue │
│ = │
│ + typeof (run as any).glo │
│ bal_ranking_filtered === "number" && │
│ + (run as any).global_ran │
│ king_filtered > 0 │
│ + ? (run as any).global │
│ _ranking_filtered │
│ + : (run as any).global │
│ _ranking; │
│ + const globalBracket = │
│ + (run as any).global_per │
│ centile_bracket || │
│ + (run as any).percentile │
│ _bracket || │
│ + ""; │
│ + │
│ + const regionalRankingValu │
│ e = │
│ + typeof (run as any).reg │
│ ional_ranking_filtered === │
│ + "number" && (run as a │
│ ny).regional_ranking_filtered > 0 │
│ + ? (run as any).region │
│ al_ranking_filtered │
│ + : run.regional_rankin │
│ g; │
│ + const realmRankingValue = │
│ + typeof (run as any).rea │
│ lm_ranking_filtered === "number" && │
│ + (run as any).realm_rank │
│ ing_filtered > 0 │
│ + ? (run as any).realm_ │
│ ranking_filtered │
│ + : run.realm_ranking; │
│ + │
│ + const regionalBracket = │
│ + (run as any).regional_p │
│ ercentile_bracket || │
│ + run.percentile_bracket │
│ || │
│ + ""; │
│ + const realmBracket = │
│ + (run as any).realm_perc │
│ entile_bracket || │
│ + run.percentile_bracket │
│ || │
│ + ""; │
│ + │
│ + const globalURL = buildLe │
│ aderboardURL( │
│ + "global", │
│ + "all", │
│ dungeonSlug, │
│ undefined, │
│ leaderboardUrlOpts, │
│ - ) │
│ - : "#"; │
│ - │
│ - │
│ - const members = (run as any).al │
│ l_members || (run as any).team_members || [] │
│ ; │
│ - │
│ - return ( │
│ - <TableRow> │
│ - <TableCell label="Dungeon"> │
│ - <div class="dungeon-cell" │
│ > │
│ - {iconUrl && ( │
│ - <img │
│ - src={iconUrl} │
│ - alt={run.dungeon_na │
│ me} │
│ - class="dungeon-icon │
│ " │
│ - /> │
│ - )} │
│ - <span class="dungeon-na │
│ me">{run.dungeon_name}</span> │
│ - </div> │
│ - </TableCell> │
│ - <TableCell label="Time">{du │
│ ration}</TableCell> │
│ - <TableCell label="Team"> │
│ - <TeamComposition │
│ - members={members} │
│ - currentRegion={playerRe │
│ gion || ""} │
│ - currentRealm={playerRea │
│ lmSlug || ""} │
│ - /> │
│ - </TableCell> │
│ - <TableCell label="Global" c │
│ lass="ranking-cell ranking-global"> │
│ - <a │
│ - href={globalURL} │
│ - class="rank-link" │
│ - set:html={formatRanking │
│ WithBracket( │
│ - globalRankingValue, │
│ - globalBracket, │
│ - )} │
│ - /> │
│ - </TableCell> │
│ - <TableCell label="Region" c │
│ lass="ranking-cell ranking-region"> │
│ - <a │
│ - href={regionalURL} │
│ - class="rank-link" │
│ - set:html={formatRanking │
│ WithBracket( │
│ - regionalRankingValue, │
│ - regionalBracket, │
│ - )} │
│ - /> │
│ - </TableCell> │
│ - <TableCell label="Realm" cl │
│ ass="ranking-cell ranking-realm"> │
│ - <a │
│ - href={realmURL} │
│ - class="rank-link" │
│ - set:html={formatRanking │
│ WithBracket( │
│ - realmRankingValue, │
│ - realmBracket, │
│ - )} │
│ - /> │
│ - </TableCell> │
│ - </TableRow> │
│ - ); │
│ - })} │
│ - </Table> │
│ - ) : ( │
│ - <div class="no-runs-message"> │
│ - <p>No best runs found for this pl │
│ ayer.</p> │
│ - </div> │
│ - )} │
│ + ); │
│ + const regionalURL = playe │
│ rRegion │
│ + ? buildLeaderboardURL( │
│ + playerRegion, │
│ + "all", │
│ + dungeonSlug, │
│ + undefined, │
│ + leaderboardUrlOpts, │
│ + ) │
│ + : "#"; │
│ + const realmURL = │
│ + playerRegion && leaderb │
│ oardRealmSlug │
│ + ? buildLeaderboardURL │
│ ( │
│ + playerRegion, │
│ + leaderboardRealmS │
│ lug, │
│ + dungeonSlug, │
│ + undefined, │
│ + leaderboardUrlOpt │
│ s, │
│ + ) │
│ + : "#"; │
│ + │
│ + const members = │
│ + (run as any).all_member │
│ s || (run as any).team_members || []; │
│ + │
│ + return ( │
│ + <TableRow> │
│ + <TableCell label="Dun │
│ geon"> │
│ + <div class="dungeon │
│ -cell"> │
│ + {iconUrl && ( │
│ + <img │
│ + src={iconUrl} │
│ + alt={run.dung │
│ eon_name} │
│ + class="dungeo │
│ n-icon" │
│ + /> │
│ + )} │
│ + <span class="dung │
│ eon-name">{run.dungeon_name}</span> │
│ + </div> │
│ + </TableCell> │
│ + <TableCell label="Tim │
│ e">{duration}</TableCell> │
│ + <TableCell label="Tea │
│ m"> │
│ + <TeamComposition │
│ + members={members} │
│ + currentRegion={pl │
│ ayerRegion || ""} │
│ + currentRealm={pla │
│ yerRealmSlug || ""} │
│ + /> │
│ + </TableCell> │
│ + <TableCell │
│ + label="Global" │
│ + class="ranking-cell │
│ ranking-global" │
│ + > │
│ + <a │
│ + href={globalURL} │
│ + class="rank-link" │
│ + set:html={formatR │
│ ankingWithBracket( │
│ + globalRankingVa │
│ lue, │
│ + globalBracket, │
│ + )} │
│ + /> │
│ + </TableCell> │
│ + <TableCell │
│ + label="Region" │
│ + class="ranking-cell │
│ ranking-region" │
│ + > │
│ + <a │
│ + href={regionalURL │
│ } │
│ + class="rank-link" │
│ + set:html={formatR │
│ ankingWithBracket( │
│ + regionalRanking │
│ Value, │
│ + regionalBracket │
│ , │
│ + )} │
│ + /> │
│ + </TableCell> │
│ + <TableCell │
│ + label="Realm" │
│ + class="ranking-cell │
│ ranking-realm" │
│ + > │
│ + <a │
│ + href={realmURL} │
│ + class="rank-link" │
│ + set:html={formatR │
│ ankingWithBracket( │
│ + realmRankingVal │
│ ue, │
│ + realmBracket, │
│ + )} │
│ + /> │
│ + </TableCell> │
│ + </TableRow> │
│ + ); │
│ + })} │
│ + </Table> │
│ + ) : ( │
│ + <div class="no-runs-message"> │
│ + <p>No best runs found for t │
│ his season.</p> │
│ + </div> │
│ + )} │
│ + </div> │
│ + ); │
│ + })} │
│ </div> │
│ ) │
│ } │
│ + │
│ +<script is:inline> │
│ + (() => { │
│ + const container = document.getElementBy │
│ Id("player-best-runs"); │
│ + if (!container) return; │
│ + const select = container.querySelector( │
│ "[data-season-select]"); │
│ + if (!select) return; │
│ + │
│ + select.addEventListener("change", (even │
│ t) => { │
│ + const target = event.target; │
│ + if (!(target instanceof HTMLSelectEle │
│ ment)) { │
│ + return; │
│ + } │
│ + const selectedSeason = target.value; │
│ + container.querySelectorAll("[data-sea │
│ son]").forEach((section) => { │
│ + const isActive = section.getAttribu │
│ te("data-season") === selectedSeason; │
│ + section.setAttribute("data-active", │
│ isActive ? "true" : "false"); │
│ + }); │
│ + │
│ + const url = new URL(window.location.h │
│ ref); │
│ + if (selectedSeason) { │
│ + url.searchParams.set("season", sele │
│ ctedSeason); │
│ + } else { │
│ + url.searchParams.delete("season"); │
│ + } │
│ + │
│ + const newSearch = url.searchParams.to │
│ String(); │
│ + const nextUrl = newSearch ? `${url.pa │
│ thname}?${newSearch}` : url.pathname; │
│ + window.history.replaceState({}, "", n │
│ extUrl); │
│ + }); │
│ + })(); │
│ +</script> │
└──────────────────────────────────────────────┘