Files
oslstats/internal/view/seasonsview/season_league_fixtures.templ
2026-03-05 22:22:32 +11:00

201 lines
6.5 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package seasonsview
import "git.haelnorr.com/h/oslstats/internal/db"
import "git.haelnorr.com/h/oslstats/internal/permissions"
import "git.haelnorr.com/h/oslstats/internal/contexts"
import "fmt"
import "sort"
import "time"
templ SeasonLeagueFixturesPage(season *db.Season, league *db.League, fixtures []*db.Fixture, scheduleMap map[int]*db.FixtureSchedule, resultMap map[int]*db.FixtureResult) {
@SeasonLeagueLayout("fixtures", season, league) {
@SeasonLeagueFixtures(season, league, fixtures, scheduleMap, resultMap)
}
}
templ SeasonLeagueFixtures(season *db.Season, league *db.League, fixtures []*db.Fixture, scheduleMap map[int]*db.FixtureSchedule, resultMap map[int]*db.FixtureResult) {
{{
permCache := contexts.Permissions(ctx)
canManage := permCache.HasPermission(permissions.FixturesManage)
// Group fixtures by game week (only allocated ones)
type gameWeekGroup struct {
Week int
Fixtures []*db.Fixture
}
groups := []gameWeekGroup{}
groupMap := map[int]int{} // week -> index in groups
for _, f := range fixtures {
if f.GameWeek == nil {
continue
}
idx, exists := groupMap[*f.GameWeek]
if !exists {
idx = len(groups)
groupMap[*f.GameWeek] = idx
groups = append(groups, gameWeekGroup{Week: *f.GameWeek, Fixtures: []*db.Fixture{}})
}
groups[idx].Fixtures = append(groups[idx].Fixtures, f)
}
// Sort fixtures within each group by scheduled time
// Scheduled fixtures first (by time), then TBD last
farFuture := time.Date(9999, 1, 1, 0, 0, 0, 0, time.UTC)
for i := range groups {
sort.Slice(groups[i].Fixtures, func(a, b int) bool {
ta := farFuture
tb := farFuture
if sa, ok := scheduleMap[groups[i].Fixtures[a].ID]; ok && sa.ScheduledTime != nil {
ta = *sa.ScheduledTime
}
if sb, ok := scheduleMap[groups[i].Fixtures[b].ID]; ok && sb.ScheduledTime != nil {
tb = *sb.ScheduledTime
}
return ta.Before(tb)
})
}
}}
<div>
if canManage {
<div class="flex justify-end mb-4">
<a
href={ templ.SafeURL(fmt.Sprintf("/seasons/%s/leagues/%s/fixtures/manage", season.ShortName, league.ShortName)) }
class="rounded-lg px-4 py-2 hover:cursor-pointer text-center text-sm
bg-blue hover:bg-blue/80 text-mantle transition"
>
Manage Fixtures
</a>
</div>
}
if len(groups) == 0 {
<div class="bg-surface0 border border-surface1 rounded-lg p-8 text-center">
<p class="text-subtext0 text-lg">No fixtures scheduled yet.</p>
</div>
} else {
<div class="space-y-4">
for _, group := range groups {
{{
playedCount := 0
for _, f := range group.Fixtures {
if res, ok := resultMap[f.ID]; ok && res.Finalized {
playedCount++
}
}
hasPlayed := playedCount > 0
allPlayed := playedCount == len(group.Fixtures)
}}
<div
class="bg-surface0 border border-surface1 rounded-lg overflow-hidden"
x-data="{ showPlayed: false }"
>
<div class="bg-mantle border-b border-surface1 px-4 py-3 flex items-center justify-between">
<h3 class="text-lg font-bold text-text">Game Week { fmt.Sprint(group.Week) }</h3>
if hasPlayed {
<button
type="button"
@click="showPlayed = !showPlayed"
class="text-xs px-2.5 py-1 rounded-lg transition cursor-pointer
bg-surface1 hover:bg-surface2 text-subtext0 hover:text-text"
>
<span x-show="!showPlayed">Show played</span>
<span x-show="showPlayed" x-cloak>Hide played</span>
</button>
}
</div>
<div class="divide-y divide-surface1">
for _, fixture := range group.Fixtures {
{{
sched, hasSchedule := scheduleMap[fixture.ID]
_ = sched
res, hasResult := resultMap[fixture.ID]
_ = res
isPlayed := hasResult && res.Finalized
}}
if isPlayed {
<a
href={ templ.SafeURL(fmt.Sprintf("/fixtures/%d", fixture.ID)) }
x-show="showPlayed"
x-cloak
class="px-4 py-3 flex items-center justify-between hover:bg-surface1 transition hover:cursor-pointer block"
>
@fixtureListItem(fixture, sched, hasSchedule, res, hasResult)
</a>
} else {
<a
href={ templ.SafeURL(fmt.Sprintf("/fixtures/%d", fixture.ID)) }
class="px-4 py-3 flex items-center justify-between hover:bg-surface1 transition hover:cursor-pointer block"
>
@fixtureListItem(fixture, sched, hasSchedule, res, hasResult)
</a>
}
}
</div>
if allPlayed {
<div
x-show="!showPlayed"
class="px-4 py-3 text-center text-xs text-subtext1 italic"
>
All fixtures played
</div>
}
</div>
}
</div>
}
</div>
}
templ fixtureListItem(fixture *db.Fixture, sched *db.FixtureSchedule, hasSchedule bool, res *db.FixtureResult, hasResult bool) {
<div class="flex items-center gap-3">
<span class="text-xs font-mono text-subtext0 bg-mantle px-2 py-0.5 rounded">
R{ fmt.Sprint(fixture.Round) }
</span>
<span class="text-text">
{ fixture.HomeTeam.Name }
</span>
<span class="text-subtext0 text-sm">vs</span>
<span class="text-text">
{ fixture.AwayTeam.Name }
</span>
</div>
if hasResult {
if res.IsForfeit {
<span class="flex items-center gap-2">
if res.ForfeitType != nil && *res.ForfeitType == "mutual" {
<span class="px-2 py-0.5 bg-peach/20 text-peach rounded text-xs font-medium">
Mutual Forfeit
</span>
} else {
<span class="px-2 py-0.5 bg-red/20 text-red rounded text-xs font-medium">
Forfeit
</span>
}
</span>
} else {
<span class="flex items-center gap-2">
if res.Winner == "home" {
<span class="text-sm font-bold text-text">{ fmt.Sprint(res.HomeScore) }</span>
<span class="text-xs text-subtext0"></span>
<span class="text-sm text-subtext0">{ fmt.Sprint(res.AwayScore) }</span>
} else if res.Winner == "away" {
<span class="text-sm text-subtext0">{ fmt.Sprint(res.HomeScore) }</span>
<span class="text-xs text-subtext0"></span>
<span class="text-sm font-bold text-text">{ fmt.Sprint(res.AwayScore) }</span>
} else {
<span class="text-sm text-text">{ fmt.Sprint(res.HomeScore) }</span>
<span class="text-xs text-subtext0"></span>
<span class="text-sm text-text">{ fmt.Sprint(res.AwayScore) }</span>
}
</span>
}
} else if hasSchedule && sched.ScheduledTime != nil {
<span class="text-xs text-green font-medium">
@localtime(sched.ScheduledTime, "short")
</span>
} else {
<span class="text-xs text-subtext1">
TBD
</span>
}
}