updated team stats
This commit is contained in:
@@ -1152,6 +1152,9 @@
|
|||||||
.py-4 {
|
.py-4 {
|
||||||
padding-block: calc(var(--spacing) * 4);
|
padding-block: calc(var(--spacing) * 4);
|
||||||
}
|
}
|
||||||
|
.py-5 {
|
||||||
|
padding-block: calc(var(--spacing) * 5);
|
||||||
|
}
|
||||||
.py-6 {
|
.py-6 {
|
||||||
padding-block: calc(var(--spacing) * 6);
|
padding-block: calc(var(--spacing) * 6);
|
||||||
}
|
}
|
||||||
@@ -2041,11 +2044,6 @@
|
|||||||
scale: var(--tw-scale-x) var(--tw-scale-y);
|
scale: var(--tw-scale-x) var(--tw-scale-y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.sm\:grid-cols-4 {
|
|
||||||
@media (width >= 40rem) {
|
|
||||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.sm\:flex-row {
|
.sm\:flex-row {
|
||||||
@media (width >= 40rem) {
|
@media (width >= 40rem) {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@@ -2177,11 +2175,6 @@
|
|||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.lg\:grid-cols-8 {
|
|
||||||
@media (width >= 64rem) {
|
|
||||||
grid-template-columns: repeat(8, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.lg\:items-end {
|
.lg\:items-end {
|
||||||
@media (width >= 64rem) {
|
@media (width >= 64rem) {
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ func SeasonLeagueTeamDetailPage(
|
|||||||
var scheduleMap map[int]*db.FixtureSchedule
|
var scheduleMap map[int]*db.FixtureSchedule
|
||||||
var resultMap map[int]*db.FixtureResult
|
var resultMap map[int]*db.FixtureResult
|
||||||
var playerStats []*db.AggregatedPlayerStats
|
var playerStats []*db.AggregatedPlayerStats
|
||||||
|
var leaderboard []*db.LeaderboardEntry
|
||||||
|
|
||||||
if ok := conn.WithReadTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
|
if ok := conn.WithReadTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
|
||||||
var err error
|
var err error
|
||||||
@@ -72,12 +73,51 @@ func SeasonLeagueTeamDetailPage(
|
|||||||
return false, errors.Wrap(err, "db.GetPlayersNotOnTeam")
|
return false, errors.Wrap(err, "db.GetPlayersNotOnTeam")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get all teams and all fixtures for the league to compute leaderboard
|
||||||
|
var allTeams []*db.Team
|
||||||
|
err = tx.NewSelect().
|
||||||
|
Model(&allTeams).
|
||||||
|
Join("INNER JOIN team_participations AS tp ON tp.team_id = t.id").
|
||||||
|
Where("tp.season_id = ? AND tp.league_id = ?", twr.Season.ID, twr.League.ID).
|
||||||
|
Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.Wrap(err, "tx.NewSelect allTeams")
|
||||||
|
}
|
||||||
|
|
||||||
|
allFixtures, err := db.GetAllocatedFixtures(ctx, tx, twr.Season.ID, twr.League.ID)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.Wrap(err, "db.GetAllocatedFixtures")
|
||||||
|
}
|
||||||
|
allFixtureIDs := make([]int, len(allFixtures))
|
||||||
|
for i, f := range allFixtures {
|
||||||
|
allFixtureIDs[i] = f.ID
|
||||||
|
}
|
||||||
|
allResultMap, err := db.GetFinalizedResultsForFixtures(ctx, tx, allFixtureIDs)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.Wrap(err, "db.GetFinalizedResultsForFixtures allFixtures")
|
||||||
|
}
|
||||||
|
|
||||||
|
leaderboard = db.ComputeLeaderboard(allTeams, allFixtures, allResultMap)
|
||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
}); !ok {
|
}); !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
record := db.ComputeTeamRecord(teamID, fixtures, resultMap)
|
// Find this team's position and record from the leaderboard
|
||||||
renderSafely(seasonsview.SeasonLeagueTeamDetailPage(twr, fixtures, available, scheduleMap, resultMap, record, playerStats), s, r, w)
|
var position int
|
||||||
|
var record *db.TeamRecord
|
||||||
|
for _, entry := range leaderboard {
|
||||||
|
if entry.Team.ID == teamID {
|
||||||
|
position = entry.Position
|
||||||
|
record = entry.Record
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if record == nil {
|
||||||
|
record = &db.TeamRecord{}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSafely(seasonsview.SeasonLeagueTeamDetailPage(twr, fixtures, available, scheduleMap, resultMap, record, playerStats, position, len(leaderboard)), s, r, w)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -260,3 +260,20 @@ func intPtrStr(v *int) string {
|
|||||||
}
|
}
|
||||||
return fmt.Sprint(*v)
|
return fmt.Sprint(*v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ordinal(n int) string {
|
||||||
|
suffix := "th"
|
||||||
|
if n%100 >= 11 && n%100 <= 13 {
|
||||||
|
// 11th, 12th, 13th
|
||||||
|
} else {
|
||||||
|
switch n % 10 {
|
||||||
|
case 1:
|
||||||
|
suffix = "st"
|
||||||
|
case 2:
|
||||||
|
suffix = "nd"
|
||||||
|
case 3:
|
||||||
|
suffix = "rd"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%d%s", n, suffix)
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import "fmt"
|
|||||||
import "sort"
|
import "sort"
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
templ SeasonLeagueTeamDetailPage(twr *db.TeamWithRoster, fixtures []*db.Fixture, available []*db.Player, scheduleMap map[int]*db.FixtureSchedule, resultMap map[int]*db.FixtureResult, record *db.TeamRecord, playerStats []*db.AggregatedPlayerStats) {
|
templ SeasonLeagueTeamDetailPage(twr *db.TeamWithRoster, fixtures []*db.Fixture, available []*db.Player, scheduleMap map[int]*db.FixtureSchedule, resultMap map[int]*db.FixtureResult, record *db.TeamRecord, playerStats []*db.AggregatedPlayerStats, position int, totalTeams int) {
|
||||||
{{
|
{{
|
||||||
team := twr.Team
|
team := twr.Team
|
||||||
season := twr.Season
|
season := twr.Season
|
||||||
@@ -62,14 +62,48 @@ templ SeasonLeagueTeamDetailPage(twr *db.TeamWithRoster, fixtures []*db.Fixture,
|
|||||||
</div>
|
</div>
|
||||||
<!-- Content -->
|
<!-- Content -->
|
||||||
<div class="bg-crust p-6">
|
<div class="bg-crust p-6">
|
||||||
<!-- Top row: Roster (left) + Fixtures (right) -->
|
{{
|
||||||
|
// Split fixtures into upcoming and completed
|
||||||
|
var upcoming []*db.Fixture
|
||||||
|
var completed []*db.Fixture
|
||||||
|
for _, f := range fixtures {
|
||||||
|
if _, hasResult := resultMap[f.ID]; hasResult {
|
||||||
|
completed = append(completed, f)
|
||||||
|
} else {
|
||||||
|
upcoming = append(upcoming, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Sort completed by scheduled time descending (most recent first)
|
||||||
|
sort.Slice(completed, func(i, j int) bool {
|
||||||
|
ti := time.Time{}
|
||||||
|
tj := time.Time{}
|
||||||
|
if si, ok := scheduleMap[completed[i].ID]; ok && si.ScheduledTime != nil {
|
||||||
|
ti = *si.ScheduledTime
|
||||||
|
}
|
||||||
|
if sj, ok := scheduleMap[completed[j].ID]; ok && sj.ScheduledTime != nil {
|
||||||
|
tj = *sj.ScheduledTime
|
||||||
|
}
|
||||||
|
return ti.After(tj)
|
||||||
|
})
|
||||||
|
// Limit to 5 most recent results
|
||||||
|
recentResults := completed
|
||||||
|
if len(recentResults) > 5 {
|
||||||
|
recentResults = recentResults[:5]
|
||||||
|
}
|
||||||
|
}}
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||||
|
<!-- Top Left: Team Standing -->
|
||||||
|
@teamRecordCard(record, position, totalTeams)
|
||||||
|
<!-- Top Right: Results -->
|
||||||
|
@teamResultsSection(twr.Team, recentResults, resultMap)
|
||||||
|
<!-- Bottom Left: Roster -->
|
||||||
@TeamRosterSection(twr, available)
|
@TeamRosterSection(twr, available)
|
||||||
@teamFixturesPane(twr.Team, fixtures, scheduleMap, resultMap)
|
<!-- Bottom Right: Upcoming -->
|
||||||
|
@teamUpcomingSection(twr.Team, upcoming, scheduleMap)
|
||||||
</div>
|
</div>
|
||||||
<!-- Stats below both -->
|
<!-- Player Stats (full width) -->
|
||||||
<div class="mt-6">
|
<div class="mt-6">
|
||||||
@teamStatsSection(record, playerStats)
|
@playerStatsSection(playerStats)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -405,68 +439,45 @@ templ manageRosterModal(twr *db.TeamWithRoster, available []*db.Player, rosterPl
|
|||||||
</script>
|
</script>
|
||||||
}
|
}
|
||||||
|
|
||||||
templ teamFixturesPane(team *db.Team, fixtures []*db.Fixture, scheduleMap map[int]*db.FixtureSchedule, resultMap map[int]*db.FixtureResult) {
|
templ teamResultsSection(team *db.Team, recentResults []*db.Fixture, resultMap map[int]*db.FixtureResult) {
|
||||||
{{
|
<section>
|
||||||
// Split fixtures into upcoming and completed
|
<div class="flex justify-between items-center mb-4">
|
||||||
var upcoming []*db.Fixture
|
<h2 class="text-2xl font-bold text-text">
|
||||||
var completed []*db.Fixture
|
Results
|
||||||
for _, f := range fixtures {
|
<span class="text-sm font-normal text-subtext0">(last 5)</span>
|
||||||
if _, hasResult := resultMap[f.ID]; hasResult {
|
</h2>
|
||||||
completed = append(completed, f)
|
|
||||||
} else {
|
|
||||||
upcoming = append(upcoming, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Sort completed by scheduled time descending (most recent first)
|
|
||||||
sort.Slice(completed, func(i, j int) bool {
|
|
||||||
ti := time.Time{}
|
|
||||||
tj := time.Time{}
|
|
||||||
if si, ok := scheduleMap[completed[i].ID]; ok && si.ScheduledTime != nil {
|
|
||||||
ti = *si.ScheduledTime
|
|
||||||
}
|
|
||||||
if sj, ok := scheduleMap[completed[j].ID]; ok && sj.ScheduledTime != nil {
|
|
||||||
tj = *sj.ScheduledTime
|
|
||||||
}
|
|
||||||
return ti.After(tj)
|
|
||||||
})
|
|
||||||
// Limit to 5 most recent results
|
|
||||||
recentResults := completed
|
|
||||||
if len(recentResults) > 5 {
|
|
||||||
recentResults = recentResults[:5]
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
<section class="space-y-6">
|
|
||||||
<!-- Results -->
|
|
||||||
<div>
|
|
||||||
<h2 class="text-2xl font-bold text-text mb-4">Results</h2>
|
|
||||||
if len(recentResults) == 0 {
|
|
||||||
<div class="bg-surface0 border border-surface1 rounded-lg p-8 text-center">
|
|
||||||
<p class="text-subtext0 text-lg">No results yet.</p>
|
|
||||||
<p class="text-subtext1 text-sm mt-2">Match results will appear here once games are played.</p>
|
|
||||||
</div>
|
|
||||||
} else {
|
|
||||||
<div class="bg-surface0 border border-surface1 rounded-lg overflow-hidden divide-y divide-surface1">
|
|
||||||
for _, fixture := range recentResults {
|
|
||||||
@teamResultRow(team, fixture, resultMap)
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
<!-- Upcoming -->
|
if len(recentResults) == 0 {
|
||||||
<div>
|
<div class="bg-surface0 border border-surface1 rounded-lg p-8 text-center">
|
||||||
<h2 class="text-2xl font-bold text-text mb-4">Upcoming</h2>
|
<p class="text-subtext0 text-lg">No results yet.</p>
|
||||||
if len(upcoming) == 0 {
|
<p class="text-subtext1 text-sm mt-2">Match results will appear here once games are played.</p>
|
||||||
<div class="bg-surface0 border border-surface1 rounded-lg p-8 text-center">
|
</div>
|
||||||
<p class="text-subtext0 text-lg">No upcoming fixtures.</p>
|
} else {
|
||||||
</div>
|
<div class="bg-surface0 border border-surface1 rounded-lg overflow-hidden divide-y divide-surface1">
|
||||||
} else {
|
for _, fixture := range recentResults {
|
||||||
<div class="bg-surface0 border border-surface1 rounded-lg overflow-hidden divide-y divide-surface1">
|
@teamResultRow(team, fixture, resultMap)
|
||||||
for _, fixture := range upcoming {
|
}
|
||||||
@teamFixtureRow(team, fixture, scheduleMap)
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</section>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
templ teamUpcomingSection(team *db.Team, upcoming []*db.Fixture, scheduleMap map[int]*db.FixtureSchedule) {
|
||||||
|
<section>
|
||||||
|
<div class="flex justify-between items-center mb-4">
|
||||||
|
<h2 class="text-2xl font-bold text-text">Upcoming</h2>
|
||||||
</div>
|
</div>
|
||||||
|
if len(upcoming) == 0 {
|
||||||
|
<div class="bg-surface0 border border-surface1 rounded-lg p-8 text-center">
|
||||||
|
<p class="text-subtext0 text-lg">No upcoming fixtures.</p>
|
||||||
|
</div>
|
||||||
|
} else {
|
||||||
|
<div class="bg-surface0 border border-surface1 rounded-lg overflow-hidden divide-y divide-surface1">
|
||||||
|
for _, fixture := range upcoming {
|
||||||
|
@teamFixtureRow(team, fixture, scheduleMap)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</section>
|
</section>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -595,69 +606,96 @@ templ teamResultRow(team *db.Team, fixture *db.Fixture, resultMap map[int]*db.Fi
|
|||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
|
|
||||||
templ teamStatsSection(record *db.TeamRecord, playerStats []*db.AggregatedPlayerStats) {
|
templ teamRecordCard(record *db.TeamRecord, position int, totalTeams int) {
|
||||||
<section>
|
<section>
|
||||||
<div class="mb-4">
|
<div class="flex justify-between items-center mb-4">
|
||||||
<h2 class="text-2xl font-bold text-text">Stats</h2>
|
<h2 class="text-2xl font-bold text-text">Standing</h2>
|
||||||
</div>
|
</div>
|
||||||
if record.Played == 0 {
|
if record.Played == 0 {
|
||||||
<div class="bg-surface0 border border-surface1 rounded-lg p-8 text-center">
|
<div class="bg-surface0 border border-surface1 rounded-lg p-8 text-center">
|
||||||
<p class="text-subtext0 text-lg">No stats yet.</p>
|
<p class="text-subtext0 text-lg">No games played yet.</p>
|
||||||
<p class="text-subtext1 text-sm mt-2">Team statistics will appear here once games are played.</p>
|
|
||||||
</div>
|
</div>
|
||||||
} else {
|
} else {
|
||||||
<!-- Team Record Summary -->
|
<div class="bg-surface0 border border-surface1 rounded-lg overflow-hidden">
|
||||||
<div class="bg-surface0 border border-surface1 rounded-lg overflow-hidden mb-4">
|
<!-- Position & Points Header -->
|
||||||
<div class="grid grid-cols-4 sm:grid-cols-4 lg:grid-cols-8 divide-x divide-surface1">
|
<div class="flex items-center justify-between px-6 py-5 border-b border-surface1">
|
||||||
@statCell("Played", fmt.Sprint(record.Played), "")
|
<div class="flex items-center gap-3">
|
||||||
|
<span class="text-4xl font-bold text-text">{ ordinal(position) }</span>
|
||||||
|
<div>
|
||||||
|
<p class="text-xs text-subtext0 uppercase font-medium">Position</p>
|
||||||
|
<p class="text-sm text-subtext1">of { fmt.Sprint(totalTeams) } teams</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-right">
|
||||||
|
<p class="text-xs text-subtext0 uppercase font-medium">Points</p>
|
||||||
|
<p class="text-3xl font-bold text-blue">{ fmt.Sprint(record.Points) }</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Record Grid -->
|
||||||
|
<div class="grid grid-cols-4 divide-x divide-surface1">
|
||||||
@statCell("W", fmt.Sprint(record.Wins), "text-green")
|
@statCell("W", fmt.Sprint(record.Wins), "text-green")
|
||||||
@statCell("OTW", fmt.Sprint(record.OvertimeWins), "text-teal")
|
@statCell("OTW", fmt.Sprint(record.OvertimeWins), "text-teal")
|
||||||
@statCell("OTL", fmt.Sprint(record.OvertimeLosses), "text-peach")
|
@statCell("OTL", fmt.Sprint(record.OvertimeLosses), "text-peach")
|
||||||
@statCell("L", fmt.Sprint(record.Losses), "text-red")
|
@statCell("L", fmt.Sprint(record.Losses), "text-red")
|
||||||
|
</div>
|
||||||
|
<!-- Goals Row -->
|
||||||
|
<div class="grid grid-cols-3 divide-x divide-surface1 border-t border-surface1">
|
||||||
|
@statCell("Played", fmt.Sprint(record.Played), "")
|
||||||
@statCell("GF", fmt.Sprint(record.GoalsFor), "")
|
@statCell("GF", fmt.Sprint(record.GoalsFor), "")
|
||||||
@statCell("GA", fmt.Sprint(record.GoalsAgainst), "")
|
@statCell("GA", fmt.Sprint(record.GoalsAgainst), "")
|
||||||
@statCell("PTS", fmt.Sprint(record.Points), "text-blue")
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Player Stats Leaderboard -->
|
}
|
||||||
if len(playerStats) > 0 {
|
</section>
|
||||||
<div class="bg-surface0 border border-surface1 rounded-lg overflow-hidden">
|
}
|
||||||
<div class="overflow-x-auto">
|
|
||||||
<table class="w-full">
|
templ playerStatsSection(playerStats []*db.AggregatedPlayerStats) {
|
||||||
<thead class="bg-mantle border-b border-surface1">
|
<section>
|
||||||
<tr>
|
<div class="flex justify-between items-center mb-4">
|
||||||
<th class="px-3 py-2 text-left text-xs font-semibold text-text">Player</th>
|
<h2 class="text-2xl font-bold text-text">Player Stats</h2>
|
||||||
<th class="px-2 py-2 text-center text-xs font-semibold text-text" title="Games Played">GP</th>
|
</div>
|
||||||
<th class="px-2 py-2 text-center text-xs font-semibold text-text" title="Periods Played">PP</th>
|
if len(playerStats) == 0 {
|
||||||
<th class="px-2 py-2 text-center text-xs font-semibold text-text" title="Score">SC</th>
|
<div class="bg-surface0 border border-surface1 rounded-lg p-8 text-center">
|
||||||
<th class="px-2 py-2 text-center text-xs font-semibold text-text" title="Goals">G</th>
|
<p class="text-subtext0 text-lg">No player stats yet.</p>
|
||||||
<th class="px-2 py-2 text-center text-xs font-semibold text-text" title="Assists">A</th>
|
<p class="text-subtext1 text-sm mt-2">Player statistics will appear here once games are played.</p>
|
||||||
<th class="px-2 py-2 text-center text-xs font-semibold text-text" title="Saves">SV</th>
|
</div>
|
||||||
<th class="px-2 py-2 text-center text-xs font-semibold text-text" title="Shots">SH</th>
|
} else {
|
||||||
<th class="px-2 py-2 text-center text-xs font-semibold text-text" title="Blocks">BL</th>
|
<div class="bg-surface0 border border-surface1 rounded-lg overflow-hidden">
|
||||||
<th class="px-2 py-2 text-center text-xs font-semibold text-text" title="Passes">PA</th>
|
<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-2 py-2 text-center text-xs font-semibold text-text" title="Games Played">GP</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="Score">SC</th>
|
||||||
|
<th class="px-2 py-2 text-center text-xs font-semibold text-text" title="Goals">G</th>
|
||||||
|
<th class="px-2 py-2 text-center text-xs font-semibold text-text" title="Assists">A</th>
|
||||||
|
<th class="px-2 py-2 text-center text-xs font-semibold text-text" title="Saves">SV</th>
|
||||||
|
<th class="px-2 py-2 text-center text-xs font-semibold text-text" title="Shots">SH</th>
|
||||||
|
<th class="px-2 py-2 text-center text-xs font-semibold text-text" title="Blocks">BL</th>
|
||||||
|
<th class="px-2 py-2 text-center text-xs font-semibold text-text" title="Passes">PA</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="divide-y divide-surface1">
|
||||||
|
for _, ps := range playerStats {
|
||||||
|
<tr class="hover:bg-surface1 transition-colors">
|
||||||
|
<td class="px-3 py-2 text-sm text-text">{ ps.PlayerName }</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 font-medium text-text">{ 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-text">{ fmt.Sprint(ps.Saves) }</td>
|
||||||
|
<td class="px-2 py-2 text-center text-sm text-text">{ fmt.Sprint(ps.Shots) }</td>
|
||||||
|
<td class="px-2 py-2 text-center text-sm text-text">{ fmt.Sprint(ps.Blocks) }</td>
|
||||||
|
<td class="px-2 py-2 text-center text-sm text-text">{ fmt.Sprint(ps.Passes) }</td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
}
|
||||||
<tbody class="divide-y divide-surface1">
|
</tbody>
|
||||||
for _, ps := range playerStats {
|
</table>
|
||||||
<tr class="hover:bg-surface1 transition-colors">
|
|
||||||
<td class="px-3 py-2 text-sm text-text">{ ps.PlayerName }</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 font-medium text-text">{ 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-text">{ fmt.Sprint(ps.Saves) }</td>
|
|
||||||
<td class="px-2 py-2 text-center text-sm text-text">{ fmt.Sprint(ps.Shots) }</td>
|
|
||||||
<td class="px-2 py-2 text-center text-sm text-text">{ fmt.Sprint(ps.Blocks) }</td>
|
|
||||||
<td class="px-2 py-2 text-center text-sm text-text">{ fmt.Sprint(ps.Passes) }</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
</div>
|
||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ templ teamSeasonCard(team *db.Team, info *db.TeamSeasonInfo) {
|
|||||||
<div class="flex items-center gap-1.5">
|
<div class="flex items-center gap-1.5">
|
||||||
<span class="text-xs text-subtext0 uppercase font-medium">Position</span>
|
<span class="text-xs text-subtext0 uppercase font-medium">Position</span>
|
||||||
<span class="text-2xl font-bold text-text">
|
<span class="text-2xl font-bold text-text">
|
||||||
{ fmt.Sprint(info.Position) }
|
{ ordinal(info.Position) }
|
||||||
</span>
|
</span>
|
||||||
<span class="text-sm text-subtext0">
|
<span class="text-sm text-subtext0">
|
||||||
/ { fmt.Sprint(info.TotalTeams) }
|
/ { fmt.Sprint(info.TotalTeams) }
|
||||||
@@ -84,3 +84,20 @@ templ teamSeasonCard(team *db.Team, info *db.TeamSeasonInfo) {
|
|||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ordinal(n int) string {
|
||||||
|
suffix := "th"
|
||||||
|
if n%100 >= 11 && n%100 <= 13 {
|
||||||
|
// 11th, 12th, 13th
|
||||||
|
} else {
|
||||||
|
switch n % 10 {
|
||||||
|
case 1:
|
||||||
|
suffix = "st"
|
||||||
|
case 2:
|
||||||
|
suffix = "nd"
|
||||||
|
case 3:
|
||||||
|
suffix = "rd"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%d%s", n, suffix)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user