228 lines
8.9 KiB
Plaintext
228 lines
8.9 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,
|
|
) {
|
|
@SeasonLeagueLayout("stats", season, league) {
|
|
@SeasonLeagueStats(season, league, topGoals, topAssists, topSaves)
|
|
}
|
|
}
|
|
|
|
templ SeasonLeagueStats(
|
|
season *db.Season,
|
|
league *db.League,
|
|
topGoals []*db.LeagueTopGoalScorer,
|
|
topAssists []*db.LeagueTopAssister,
|
|
topSaves []*db.LeagueTopSaver,
|
|
) {
|
|
if len(topGoals) == 0 && len(topAssists) == 0 && len(topSaves) == 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 {
|
|
<!-- Triangle layout: two side-by-side on wide screens, saves centered below -->
|
|
<div class="flex flex-col items-center gap-6">
|
|
<!-- Top row: Goals and Assists side by side when room allows -->
|
|
<div class="flex flex-col lg:flex-row gap-6 w-full justify-center items-center lg:items-start">
|
|
@topGoalScorersTable(season, league, topGoals)
|
|
@topAssistersTable(season, league, topAssists)
|
|
</div>
|
|
<!-- Bottom row: Saves centered -->
|
|
@topSaversTable(season, league, topSaves)
|
|
</div>
|
|
}
|
|
}
|
|
|
|
templ topGoalScorersTable(season *db.Season, league *db.League, goals []*db.LeagueTopGoalScorer) {
|
|
<div class="bg-surface0 border border-surface1 rounded-lg overflow-hidden w-full max-w-lg">
|
|
<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 ↓</span>
|
|
<span>PP ↑</span>
|
|
<span>SH ↑</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">
|
|
@links.PlayerLinkFromStats(gs.PlayerID, gs.PlayerName)
|
|
</td>
|
|
<td class="px-3 py-2 text-sm">
|
|
@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 w-full max-w-lg">
|
|
<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 ↓</span>
|
|
<span>PP ↑</span>
|
|
<span>PA ↓</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">
|
|
@links.PlayerLinkFromStats(as.PlayerID, as.PlayerName)
|
|
</td>
|
|
<td class="px-3 py-2 text-sm">
|
|
@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 w-full max-w-lg">
|
|
<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 ↓</span>
|
|
<span>PP ↑</span>
|
|
<span>BLK ↓</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">
|
|
@links.PlayerLinkFromStats(sv.PlayerID, sv.PlayerName)
|
|
</td>
|
|
<td class="px-3 py-2 text-sm">
|
|
@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 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">{ teamName }</span>
|
|
</a>
|
|
} else {
|
|
<span class="text-sm text-subtext0 italic">—</span>
|
|
}
|
|
}
|