diff --git a/internal/embedfs/web/css/output.css b/internal/embedfs/web/css/output.css index 59614a2..d44edbf 100644 --- a/internal/embedfs/web/css/output.css +++ b/internal/embedfs/web/css/output.css @@ -20,6 +20,7 @@ --container-3xl: 48rem; --container-4xl: 56rem; --container-5xl: 64rem; + --container-6xl: 72rem; --container-7xl: 80rem; --text-xs: 0.75rem; --text-xs--line-height: calc(1 / 0.75); @@ -371,9 +372,6 @@ .mt-24 { margin-top: calc(var(--spacing) * 24); } - .mt-25 { - margin-top: calc(var(--spacing) * 25); - } .mt-auto { margin-top: auto; } @@ -395,6 +393,9 @@ .mb-8 { margin-bottom: calc(var(--spacing) * 8); } + .mb-12 { + margin-bottom: calc(var(--spacing) * 12); + } .mb-auto { margin-bottom: auto; } @@ -582,6 +583,9 @@ .max-w-5xl { max-width: var(--container-5xl); } + .max-w-6xl { + max-width: var(--container-6xl); + } .max-w-7xl { max-width: var(--container-7xl); } @@ -711,6 +715,9 @@ .place-content-center { place-content: center; } + .items-baseline { + align-items: baseline; + } .items-center { align-items: center; } @@ -1399,6 +1406,9 @@ .whitespace-pre-wrap { white-space: pre-wrap; } + .text-base { + color: var(--base); + } .text-blue { color: var(--blue); } @@ -1578,6 +1588,13 @@ } } } + .group-hover\:text-blue { + &:is(:where(.group):hover *) { + @media (hover: hover) { + color: var(--blue); + } + } + } .group-hover\:opacity-100 { &:is(:where(.group):hover *) { @media (hover: hover) { diff --git a/internal/handlers/index.go b/internal/handlers/index.go index 6eaa23a..f647a9b 100644 --- a/internal/handlers/index.go +++ b/internal/handlers/index.go @@ -1,22 +1,85 @@ package handlers import ( + "context" "net/http" "git.haelnorr.com/h/golib/hws" + "git.haelnorr.com/h/oslstats/internal/db" "git.haelnorr.com/h/oslstats/internal/throw" homeview "git.haelnorr.com/h/oslstats/internal/view/homeview" + "github.com/pkg/errors" + "github.com/uptrace/bun" ) // Index handles responses to the / path. Also serves a 404 Page for paths that // don't have explicit handlers -func Index(s *hws.Server) http.Handler { +func Index(s *hws.Server, conn *db.DB) http.Handler { return http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { throw.NotFound(s, w, r, r.URL.Path) + return } - renderSafely(homeview.IndexPage(), s, r, w) + + var season *db.Season + var standings []homeview.LeagueStandings + + if ok := conn.WithReadTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) { + // Get the most recent season + seasons, err := db.ListSeasons(ctx, tx, &db.PageOpts{ + Page: 1, + PerPage: 1, + Order: bun.OrderDesc, + OrderBy: "start_date", + }) + if err != nil { + return false, errors.Wrap(err, "db.ListSeasons") + } + + if seasons.Total == 0 || len(seasons.Items) == 0 { + return true, nil + } + + season = seasons.Items[0] + + // Build leaderboards for each league in this season + standings = make([]homeview.LeagueStandings, 0, len(season.Leagues)) + for _, league := range season.Leagues { + _, l, teams, err := db.GetSeasonLeagueWithTeams(ctx, tx, season.ShortName, league.ShortName) + if err != nil { + return false, errors.Wrap(err, "db.GetSeasonLeagueWithTeams") + } + + fixtures, err := db.GetAllocatedFixtures(ctx, tx, season.ID, l.ID) + if err != nil { + return false, errors.Wrap(err, "db.GetAllocatedFixtures") + } + + fixtureIDs := make([]int, len(fixtures)) + for i, f := range fixtures { + fixtureIDs[i] = f.ID + } + + resultMap, err := db.GetFinalizedResultsForFixtures(ctx, tx, fixtureIDs) + if err != nil { + return false, errors.Wrap(err, "db.GetFinalizedResultsForFixtures") + } + + leaderboard := db.ComputeLeaderboard(teams, fixtures, resultMap) + + standings = append(standings, homeview.LeagueStandings{ + League: l, + Leaderboard: leaderboard, + }) + } + + return true, nil + }); !ok { + return + } + + renderSafely(homeview.IndexPage(season, standings), s, r, w) }, ) } diff --git a/internal/server/routes.go b/internal/server/routes.go index cda9e57..b7afe09 100644 --- a/internal/server/routes.go +++ b/internal/server/routes.go @@ -39,7 +39,7 @@ func addRoutes( { Path: "/", Method: hws.MethodGET, - Handler: handlers.Index(s), + Handler: handlers.Index(s, conn), }, } diff --git a/internal/view/homeview/external_links.templ b/internal/view/homeview/external_links.templ new file mode 100644 index 0000000..9f08eab --- /dev/null +++ b/internal/view/homeview/external_links.templ @@ -0,0 +1,33 @@ +package homeview + +// ExternalLinks renders card tiles for external community resources +templ ExternalLinks() { +
+ Connect with other players, find teams, and stay up to date with league announcements. +
+ + ++ Visit the official Slapshot website to learn more about the game. +
+ ++ The Oceanic Slapshot League (OSL) is a community for casual and competitive play of Slapshot: Rebound. + It is managed by a small group of community members, and aims to provide a place for players in the Oceanic + region (primarily Australia and New Zealand) to compete and play in organised League competitions, as well as + casual pick-up games (RPUGs) and public matches (in-game matchmaking). + The league is open to everyone, regardless of skill level. +
+No standings available yet.
+No teams in this league yet.
+| # | +Team | +GP | +W | +OTW | +OTL | +L | +GF | +GA | +GD | +PTS | +
|---|