added log file uploading and match results

This commit is contained in:
2026-02-21 22:25:21 +11:00
parent 6439bf782b
commit 680ba3fe50
20 changed files with 2595 additions and 61 deletions

View File

@@ -4,14 +4,16 @@ 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) {
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)
@SeasonLeagueFixtures(season, league, fixtures, scheduleMap, resultMap)
}
}
templ SeasonLeagueFixtures(season *db.Season, league *db.League, fixtures []*db.Fixture, scheduleMap map[int]*db.FixtureSchedule) {
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)
@@ -35,6 +37,23 @@ templ SeasonLeagueFixtures(season *db.Season, league *db.League, fixtures []*db.
}
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 {
@@ -55,47 +74,113 @@ templ SeasonLeagueFixtures(season *db.Season, league *db.League, fixtures []*db.
} else {
<div class="space-y-4">
for _, group := range groups {
<div class="bg-surface0 border border-surface1 rounded-lg overflow-hidden">
<div class="bg-mantle border-b border-surface1 px-4 py-3">
{{
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
}}
<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"
>
<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 hasSchedule && sched.ScheduledTime != nil {
<span class="text-xs text-green font-medium">
{ sched.ScheduledTime.Format("Mon 2 Jan 3:04 PM") }
</span>
} else {
<span class="text-xs text-subtext1">
TBD
</span>
}
</a>
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 {
<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>
}
}