Files
oslstats/internal/view/homeview/latest_standings.templ
2026-03-07 14:18:06 +11:00

152 lines
5.5 KiB
Plaintext

package homeview
import "git.haelnorr.com/h/oslstats/internal/db"
import "git.haelnorr.com/h/oslstats/internal/view/component/links"
import "fmt"
// LeagueStandings holds the data needed to render a single league's table
type LeagueStandings struct {
League *db.League
Leaderboard []*db.LeaderboardEntry
}
// LatestStandings renders the latest standings section with tabs to switch
// between leagues from the most recent season
templ LatestStandings(season *db.Season, standings []LeagueStandings) {
<div class="space-y-4">
<div class="flex items-baseline gap-3">
<h2 class="text-2xl font-bold text-text">Latest Standings</h2>
if season != nil {
<a
href={ templ.SafeURL(fmt.Sprintf("/seasons/%s", season.ShortName)) }
class="text-sm text-subtext0 hover:text-blue transition"
>
{ season.Name }
</a>
}
</div>
if season == nil || len(standings) == 0 {
<div class="bg-surface0 border border-surface1 rounded-lg p-8 text-center">
<p class="text-subtext0 text-lg">No standings available yet.</p>
</div>
} else {
<div x-data={ fmt.Sprintf("{ activeTab: '%s' }", standings[0].League.ShortName) }>
if len(standings) > 1 {
<div class="flex gap-1 mb-4 border-b border-surface1">
for _, s := range standings {
<button
x-on:click={ fmt.Sprintf("activeTab = '%s'", s.League.ShortName) }
class="px-4 py-2 text-sm font-medium transition hover:cursor-pointer"
x-bind:class={ fmt.Sprintf("activeTab === '%s' ? 'text-blue border-b-2 border-blue' : 'text-subtext0 hover:text-text'", s.League.ShortName) }
>
{ s.League.Name }
</button>
}
</div>
}
for _, s := range standings {
<div x-show={ fmt.Sprintf("activeTab === '%s'", s.League.ShortName) }>
@standingsTable(season, s.League, s.Leaderboard)
</div>
}
</div>
}
</div>
}
templ standingsTable(season *db.Season, league *db.League, leaderboard []*db.LeaderboardEntry) {
if len(leaderboard) == 0 {
<div class="bg-surface0 border border-surface1 rounded-lg p-8 text-center">
<p class="text-subtext0 text-lg">No teams in this league yet.</p>
</div>
} else {
<div class="bg-surface0 border border-surface1 rounded-lg overflow-hidden">
<div class="bg-mantle border-b border-surface1 px-4 py-2 flex items-center gap-4 text-xs text-subtext0">
<span class="font-semibold text-subtext1">Points:</span>
<span>W = { fmt.Sprint(db.PointsWin) }</span>
<span>OTW = { fmt.Sprint(db.PointsOvertimeWin) }</span>
<span>OTL = { fmt.Sprint(db.PointsOvertimeLoss) }</span>
<span>L = { fmt.Sprint(db.PointsLoss) }</span>
</div>
<div class="overflow-x-auto">
<table class="w-full">
<thead class="bg-mantle border-b border-surface1">
<tr>
<th class="px-3 py-3 text-center text-xs font-semibold text-subtext0 w-10">#</th>
<th class="px-4 py-3 text-left text-xs font-semibold text-text">Team</th>
<th class="px-3 py-3 text-center text-xs font-semibold text-text" title="Games Played">GP</th>
<th class="px-3 py-3 text-center text-xs font-semibold text-text" title="Wins">W</th>
<th class="px-3 py-3 text-center text-xs font-semibold text-text" title="Overtime Wins">OTW</th>
<th class="px-3 py-3 text-center text-xs font-semibold text-text" title="Overtime Losses">OTL</th>
<th class="px-3 py-3 text-center text-xs font-semibold text-text" title="Losses">L</th>
<th class="px-3 py-3 text-center text-xs font-semibold text-text" title="Goals For">GF</th>
<th class="px-3 py-3 text-center text-xs font-semibold text-text" title="Goals Against">GA</th>
<th class="px-3 py-3 text-center text-xs font-semibold text-text" title="Goal Differential">GD</th>
<th class="px-3 py-3 text-center text-xs font-semibold text-blue" title="Points">PTS</th>
</tr>
</thead>
<tbody class="divide-y divide-surface1">
for _, entry := range leaderboard {
@standingsRow(entry, season, league)
}
</tbody>
</table>
</div>
</div>
}
}
templ standingsRow(entry *db.LeaderboardEntry, season *db.Season, league *db.League) {
{{
r := entry.Record
goalDiff := r.GoalsFor - r.GoalsAgainst
var gdStr string
if goalDiff > 0 {
gdStr = fmt.Sprintf("+%d", goalDiff)
} else {
gdStr = fmt.Sprint(goalDiff)
}
}}
<tr class="hover:bg-surface1 transition-colors">
<td class="px-3 py-3 text-center text-sm font-medium text-subtext0">
{ fmt.Sprint(entry.Position) }
</td>
<td class="px-4 py-3">
@links.TeamLinkInSeason(entry.Team, season, league)
</td>
<td class="px-3 py-3 text-center text-sm text-subtext0">
{ fmt.Sprint(r.Played) }
</td>
<td class="px-3 py-3 text-center text-sm text-green">
{ fmt.Sprint(r.Wins) }
</td>
<td class="px-3 py-3 text-center text-sm text-teal">
{ fmt.Sprint(r.OvertimeWins) }
</td>
<td class="px-3 py-3 text-center text-sm text-peach">
{ fmt.Sprint(r.OvertimeLosses) }
</td>
<td class="px-3 py-3 text-center text-sm text-red">
{ fmt.Sprint(r.Losses) }
</td>
<td class="px-3 py-3 text-center text-sm text-text">
{ fmt.Sprint(r.GoalsFor) }
</td>
<td class="px-3 py-3 text-center text-sm text-text">
{ fmt.Sprint(r.GoalsAgainst) }
</td>
<td class="px-3 py-3 text-center text-sm">
if goalDiff > 0 {
<span class="text-green">{ gdStr }</span>
} else if goalDiff < 0 {
<span class="text-red">{ gdStr }</span>
} else {
<span class="text-subtext0">{ gdStr }</span>
}
</td>
<td class="px-3 py-3 text-center text-sm font-bold text-blue">
{ fmt.Sprint(r.Points) }
</td>
</tr>
}