Files
oslstats/internal/view/seasonsview/season_league_stats.templ
2026-03-07 13:13:09 +11:00

328 lines
13 KiB
Plaintext

package seasonsview
import "git.haelnorr.com/h/oslstats/internal/db"
import "git.haelnorr.com/h/oslstats/internal/view/component/links"
import "fmt"
templ SeasonLeagueStatsPage(
season *db.Season,
league *db.League,
topGoals []*db.LeagueTopGoalScorer,
topAssists []*db.LeagueTopAssister,
topSaves []*db.LeagueTopSaver,
allStats []*db.LeaguePlayerStats,
) {
@SeasonLeagueLayout("stats", season, league) {
@SeasonLeagueStats(season, league, topGoals, topAssists, topSaves, allStats)
}
}
templ SeasonLeagueStats(
season *db.Season,
league *db.League,
topGoals []*db.LeagueTopGoalScorer,
topAssists []*db.LeagueTopAssister,
topSaves []*db.LeagueTopSaver,
allStats []*db.LeaguePlayerStats,
) {
<script src="/static/js/sortable-table.js"></script>
if len(topGoals) == 0 && len(topAssists) == 0 && len(topSaves) == 0 && len(allStats) == 0 {
<div class="bg-surface0 border border-surface1 rounded-lg p-8 text-center">
<p class="text-subtext0 text-lg">No stats available yet.</p>
<p class="text-subtext1 text-sm mt-2">Player statistics will appear here once games are finalized.</p>
</div>
} else {
<div class="space-y-8">
<!-- Trophy Leaders Section -->
if len(topGoals) > 0 || len(topAssists) > 0 || len(topSaves) > 0 {
<div class="space-y-4">
<h2 class="text-xl font-bold text-text text-center">Trophy Leaders</h2>
<!-- Triangle layout: two side-by-side on wide screens, saves centered below -->
<div class="flex flex-col items-center gap-6 w-full">
<!-- Top row: Goals and Assists side by side when room allows -->
<div class="flex flex-col lg:flex-row gap-6 justify-center items-stretch w-full lg:w-auto">
@topGoalScorersTable(season, league, topGoals)
@topAssistersTable(season, league, topAssists)
</div>
<!-- Bottom row: Saves centered -->
@topSaversTable(season, league, topSaves)
</div>
</div>
}
<!-- All Stats Section -->
if len(allStats) > 0 {
<div class="space-y-4">
<h2 class="text-xl font-bold text-text text-center">All Stats</h2>
@allStatsTable(season, league, allStats)
</div>
}
</div>
}
}
templ topGoalScorersTable(season *db.Season, league *db.League, goals []*db.LeagueTopGoalScorer) {
<div class="bg-surface0 border border-surface1 rounded-lg overflow-hidden min-w-0 w-full lg:w-auto">
<div class="bg-mantle border-b border-surface1 px-4 py-3">
<h3 class="text-sm font-semibold text-text">
Top Goal Scorers
</h3>
</div>
<!-- Sorting key -->
<div class="bg-mantle border-b border-surface1 px-4 py-1.5 flex items-center gap-3 text-xs text-subtext0">
<span class="font-semibold text-subtext1">Sort:</span>
<span>G &#8595;</span>
<span>PP &#8593;</span>
<span>SH &#8593;</span>
</div>
if len(goals) == 0 {
<div class="p-6 text-center">
<p class="text-subtext0 text-sm">No goal data available yet.</p>
</div>
} else {
<div class="overflow-x-auto">
<table class="w-full">
<thead class="bg-mantle border-b border-surface1">
<tr>
<th class="px-3 py-2 text-center text-xs font-semibold text-subtext0 w-10">#</th>
<th class="px-3 py-2 text-left text-xs font-semibold text-text">Player</th>
<th class="px-3 py-2 text-left text-xs font-semibold text-text">Team</th>
<th class="px-2 py-2 text-center text-xs font-semibold text-blue" title="Goals">G</th>
<th class="px-2 py-2 text-center text-xs font-semibold text-text" title="Periods Played">PP</th>
<th class="px-2 py-2 text-center text-xs font-semibold text-text" title="Shots">SH</th>
</tr>
</thead>
<tbody class="divide-y divide-surface1">
for i, gs := range goals {
<tr class="hover:bg-surface1 transition-colors">
<td class="px-3 py-2 text-center text-sm font-medium text-subtext0">
{ fmt.Sprint(i + 1) }
</td>
<td class="px-3 py-2 text-sm font-medium whitespace-nowrap">
@links.PlayerLinkFromStats(gs.PlayerID, gs.PlayerName)
</td>
<td class="px-3 py-2 text-sm whitespace-nowrap">
@teamColorName(gs.TeamID, gs.TeamName, gs.TeamColor, season, league)
</td>
<td class="px-2 py-2 text-center text-sm font-bold text-blue">{ fmt.Sprint(gs.Goals) }</td>
<td class="px-2 py-2 text-center text-sm text-subtext0">{ fmt.Sprint(gs.PeriodsPlayed) }</td>
<td class="px-2 py-2 text-center text-sm text-subtext0">{ fmt.Sprint(gs.Shots) }</td>
</tr>
}
</tbody>
</table>
</div>
}
</div>
}
templ topAssistersTable(season *db.Season, league *db.League, assists []*db.LeagueTopAssister) {
<div class="bg-surface0 border border-surface1 rounded-lg overflow-hidden min-w-0 w-full lg:w-auto">
<div class="bg-mantle border-b border-surface1 px-4 py-3">
<h3 class="text-sm font-semibold text-text">
Top Assisters
</h3>
</div>
<!-- Sorting key -->
<div class="bg-mantle border-b border-surface1 px-4 py-1.5 flex items-center gap-3 text-xs text-subtext0">
<span class="font-semibold text-subtext1">Sort:</span>
<span>A &#8595;</span>
<span>PP &#8593;</span>
<span>PA &#8595;</span>
</div>
if len(assists) == 0 {
<div class="p-6 text-center">
<p class="text-subtext0 text-sm">No assist data available yet.</p>
</div>
} else {
<div class="overflow-x-auto">
<table class="w-full">
<thead class="bg-mantle border-b border-surface1">
<tr>
<th class="px-3 py-2 text-center text-xs font-semibold text-subtext0 w-10">#</th>
<th class="px-3 py-2 text-left text-xs font-semibold text-text">Player</th>
<th class="px-3 py-2 text-left text-xs font-semibold text-text">Team</th>
<th class="px-2 py-2 text-center text-xs font-semibold text-blue" title="Assists">A</th>
<th class="px-2 py-2 text-center text-xs font-semibold text-text" title="Periods Played">PP</th>
<th class="px-2 py-2 text-center text-xs font-semibold text-text" title="Primary Assists">PA</th>
</tr>
</thead>
<tbody class="divide-y divide-surface1">
for i, as := range assists {
<tr class="hover:bg-surface1 transition-colors">
<td class="px-3 py-2 text-center text-sm font-medium text-subtext0">
{ fmt.Sprint(i + 1) }
</td>
<td class="px-3 py-2 text-sm font-medium whitespace-nowrap">
@links.PlayerLinkFromStats(as.PlayerID, as.PlayerName)
</td>
<td class="px-3 py-2 text-sm whitespace-nowrap">
@teamColorName(as.TeamID, as.TeamName, as.TeamColor, season, league)
</td>
<td class="px-2 py-2 text-center text-sm font-bold text-blue">{ fmt.Sprint(as.Assists) }</td>
<td class="px-2 py-2 text-center text-sm text-subtext0">{ fmt.Sprint(as.PeriodsPlayed) }</td>
<td class="px-2 py-2 text-center text-sm text-subtext0">{ fmt.Sprint(as.PrimaryAssists) }</td>
</tr>
}
</tbody>
</table>
</div>
}
</div>
}
templ topSaversTable(season *db.Season, league *db.League, saves []*db.LeagueTopSaver) {
<div class="bg-surface0 border border-surface1 rounded-lg overflow-hidden min-w-0 w-full lg:w-auto">
<div class="bg-mantle border-b border-surface1 px-4 py-3">
<h3 class="text-sm font-semibold text-text">
Top Saves
</h3>
</div>
<!-- Sorting key -->
<div class="bg-mantle border-b border-surface1 px-4 py-1.5 flex items-center gap-3 text-xs text-subtext0">
<span class="font-semibold text-subtext1">Sort:</span>
<span>SV &#8595;</span>
<span>PP &#8593;</span>
<span>BLK &#8595;</span>
</div>
if len(saves) == 0 {
<div class="p-6 text-center">
<p class="text-subtext0 text-sm">No save data available yet.</p>
</div>
} else {
<div class="overflow-x-auto">
<table class="w-full">
<thead class="bg-mantle border-b border-surface1">
<tr>
<th class="px-3 py-2 text-center text-xs font-semibold text-subtext0 w-10">#</th>
<th class="px-3 py-2 text-left text-xs font-semibold text-text">Player</th>
<th class="px-3 py-2 text-left text-xs font-semibold text-text">Team</th>
<th class="px-2 py-2 text-center text-xs font-semibold text-blue" title="Saves">SV</th>
<th class="px-2 py-2 text-center text-xs font-semibold text-text" title="Periods Played">PP</th>
<th class="px-2 py-2 text-center text-xs font-semibold text-text" title="Blocks">BLK</th>
</tr>
</thead>
<tbody class="divide-y divide-surface1">
for i, sv := range saves {
<tr class="hover:bg-surface1 transition-colors">
<td class="px-3 py-2 text-center text-sm font-medium text-subtext0">
{ fmt.Sprint(i + 1) }
</td>
<td class="px-3 py-2 text-sm font-medium whitespace-nowrap">
@links.PlayerLinkFromStats(sv.PlayerID, sv.PlayerName)
</td>
<td class="px-3 py-2 text-sm whitespace-nowrap">
@teamColorName(sv.TeamID, sv.TeamName, sv.TeamColor, season, league)
</td>
<td class="px-2 py-2 text-center text-sm font-bold text-blue">{ fmt.Sprint(sv.Saves) }</td>
<td class="px-2 py-2 text-center text-sm text-subtext0">{ fmt.Sprint(sv.PeriodsPlayed) }</td>
<td class="px-2 py-2 text-center text-sm text-subtext0">{ fmt.Sprint(sv.Blocks) }</td>
</tr>
}
</tbody>
</table>
</div>
}
</div>
}
templ allStatsTable(season *db.Season, league *db.League, allStats []*db.LeaguePlayerStats) {
<div
class="bg-surface0 border border-surface1 rounded-lg overflow-hidden"
x-data="sortableTable('score', 'desc')"
>
<div class="overflow-x-auto">
<table class="w-full">
<thead class="bg-mantle border-b border-surface1">
<tr>
<th class="px-3 py-2 text-left text-xs font-semibold text-text">Player</th>
<th class="px-3 py-2 text-left text-xs font-semibold text-text">Team</th>
@sortableCol("gp", "GP", "Games Played")
@sortableCol("pp", "PP", "Periods Played")
@sortableCol("score", "SC", "Score")
@sortableCol("goals", "G", "Goals")
@sortableCol("assists", "A", "Assists")
@sortableCol("pa", "PA", "Primary Assists")
@sortableCol("sa", "SA", "Secondary Assists")
@sortableCol("saves", "SV", "Saves")
@sortableCol("shots", "SH", "Shots")
@sortableCol("blocks", "BLK", "Blocks")
@sortableCol("passes", "PAS", "Passes")
</tr>
</thead>
<tbody class="divide-y divide-surface1" x-ref="tbody">
for _, ps := range allStats {
<tr
class="hover:bg-surface1 transition-colors"
data-name={ ps.PlayerName }
data-team={ ps.TeamName }
data-gp={ fmt.Sprint(ps.GamesPlayed) }
data-pp={ fmt.Sprint(ps.PeriodsPlayed) }
data-score={ fmt.Sprint(ps.Score) }
data-goals={ fmt.Sprint(ps.Goals) }
data-assists={ fmt.Sprint(ps.Assists) }
data-pa={ fmt.Sprint(ps.PrimaryAssists) }
data-sa={ fmt.Sprint(ps.SecondaryAssists) }
data-saves={ fmt.Sprint(ps.Saves) }
data-shots={ fmt.Sprint(ps.Shots) }
data-blocks={ fmt.Sprint(ps.Blocks) }
data-passes={ fmt.Sprint(ps.Passes) }
>
<td class="px-3 py-2 text-sm font-medium">
@links.PlayerLinkFromStats(ps.PlayerID, ps.PlayerName)
</td>
<td class="px-3 py-2 text-sm">
@teamColorName(ps.TeamID, ps.TeamName, ps.TeamColor, season, league)
</td>
<td class="px-2 py-2 text-center text-sm text-subtext0">{ fmt.Sprint(ps.GamesPlayed) }</td>
<td class="px-2 py-2 text-center text-sm text-subtext0">{ fmt.Sprint(ps.PeriodsPlayed) }</td>
<td class="px-2 py-2 text-center text-sm text-text font-medium">{ fmt.Sprint(ps.Score) }</td>
<td class="px-2 py-2 text-center text-sm text-text">{ fmt.Sprint(ps.Goals) }</td>
<td class="px-2 py-2 text-center text-sm text-text">{ fmt.Sprint(ps.Assists) }</td>
<td class="px-2 py-2 text-center text-sm text-subtext0">{ fmt.Sprint(ps.PrimaryAssists) }</td>
<td class="px-2 py-2 text-center text-sm text-subtext0">{ fmt.Sprint(ps.SecondaryAssists) }</td>
<td class="px-2 py-2 text-center text-sm text-text">{ fmt.Sprint(ps.Saves) }</td>
<td class="px-2 py-2 text-center text-sm text-subtext0">{ fmt.Sprint(ps.Shots) }</td>
<td class="px-2 py-2 text-center text-sm text-subtext0">{ fmt.Sprint(ps.Blocks) }</td>
<td class="px-2 py-2 text-center text-sm text-subtext0">{ fmt.Sprint(ps.Passes) }</td>
</tr>
}
</tbody>
</table>
</div>
</div>
}
templ sortableCol(field string, label string, title string) {
<th
class="px-2 py-2 text-center text-xs font-semibold text-text select-none hover:cursor-pointer hover:text-blue transition-colors"
title={ title }
@click={ fmt.Sprintf("sort('%s')", field) }
>
<span class="inline-flex items-center gap-0.5">
{ label }
<template x-if={ fmt.Sprintf("sortField === '%s'", field) }>
<span class="text-blue" x-text={ "sortDir === 'asc' ? '↑' : '↓'" }></span>
</template>
</span>
</th>
}
templ teamColorName(teamID int, teamName string, teamColor string, season *db.Season, league *db.League) {
if teamID > 0 && teamName != "" {
<a
href={ templ.SafeURL(fmt.Sprintf("/seasons/%s/leagues/%s/teams/%d", season.ShortName, league.ShortName, teamID)) }
class="flex items-center gap-2 hover:text-blue transition"
>
if teamColor != "" {
<span
class="w-3 h-3 rounded-full shrink-0"
style={ "background-color: " + templ.SafeCSS(teamColor) }
></span>
}
<span class="text-sm font-medium whitespace-nowrap">{ teamName }</span>
</a>
} else {
<span class="text-sm text-subtext0 italic">—</span>
}
}