added player stats to profile
This commit is contained in:
93
internal/handlers/player_stats_filter.go
Normal file
93
internal/handlers/player_stats_filter.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"git.haelnorr.com/h/golib/hws"
|
||||
"git.haelnorr.com/h/oslstats/internal/db"
|
||||
playersview "git.haelnorr.com/h/oslstats/internal/view/playersview"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
// PlayerStatsFilter handles HTMX POST requests to filter player stats
|
||||
// by season or team. Only one filter can be active at a time.
|
||||
// Query params: filter=season|team, filter_id=<id>
|
||||
func PlayerStatsFilter(
|
||||
s *hws.Server,
|
||||
conn *db.DB,
|
||||
) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
player, _, ok := resolvePlayerAndOwner(s, conn, w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
filterType := r.URL.Query().Get("filter")
|
||||
filterIDStr := r.URL.Query().Get("filter_id")
|
||||
|
||||
var stats *db.PlayerAllTimeStats
|
||||
var seasons []*db.Season
|
||||
var teams []*db.Team
|
||||
var activeFilter string
|
||||
var activeFilterID int
|
||||
|
||||
if ok := conn.WithReadTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
|
||||
var err error
|
||||
|
||||
// Load filter dropdown data
|
||||
seasons, err = db.GetPlayerSeasonsList(ctx, tx, player.ID)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetPlayerSeasonsList")
|
||||
}
|
||||
teams, err = db.GetPlayerTeamsList(ctx, tx, player.ID)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetPlayerTeamsList")
|
||||
}
|
||||
|
||||
// Apply filter
|
||||
filterID, _ := strconv.Atoi(filterIDStr)
|
||||
switch filterType {
|
||||
case "season":
|
||||
if filterID > 0 {
|
||||
stats, err = db.GetPlayerStatsBySeason(ctx, tx, player.ID, filterID)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetPlayerStatsBySeason")
|
||||
}
|
||||
activeFilter = "season"
|
||||
activeFilterID = filterID
|
||||
}
|
||||
case "team":
|
||||
if filterID > 0 {
|
||||
stats, err = db.GetPlayerStatsByTeam(ctx, tx, player.ID, filterID)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetPlayerStatsByTeam")
|
||||
}
|
||||
activeFilter = "team"
|
||||
activeFilterID = filterID
|
||||
}
|
||||
}
|
||||
|
||||
// Default to all-time stats if no valid filter
|
||||
if stats == nil {
|
||||
stats, err = db.GetPlayerAllTimeStats(ctx, tx, player.ID)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetPlayerAllTimeStats")
|
||||
}
|
||||
activeFilter = ""
|
||||
activeFilterID = 0
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
renderSafely(playersview.PlayerStatsTab(
|
||||
player, stats, seasons, teams,
|
||||
activeFilter, activeFilterID,
|
||||
), s, r, w)
|
||||
})
|
||||
}
|
||||
@@ -32,53 +32,155 @@ func ProfileRedirect(
|
||||
})
|
||||
}
|
||||
|
||||
// PlayerView renders the player profile page.
|
||||
// If the player has no SlapID and the viewer is the player's owner, show the link prompt.
|
||||
// If the player has no SlapID and the viewer is not the owner, show 404.
|
||||
func PlayerView(
|
||||
// resolvePlayerAndOwner is a helper that resolves the player from the URL path
|
||||
// and determines if the current user is the owner of the player.
|
||||
// Returns false from the outer handler if resolution failed (404 already thrown).
|
||||
func resolvePlayerAndOwner(
|
||||
s *hws.Server,
|
||||
conn *db.DB,
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
) (player *db.Player, isOwner bool, ok bool) {
|
||||
playerIDStr := r.PathValue("player_id")
|
||||
playerID, err := strconv.Atoi(playerIDStr)
|
||||
if err != nil {
|
||||
throw.NotFound(s, w, r, r.URL.Path)
|
||||
return nil, false, false
|
||||
}
|
||||
|
||||
if ok := conn.WithReadTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
|
||||
var err error
|
||||
player, err = db.GetPlayer(ctx, tx, playerID)
|
||||
if err != nil {
|
||||
if db.IsBadRequest(err) {
|
||||
throw.NotFound(s, w, r, r.URL.Path)
|
||||
return false, nil
|
||||
}
|
||||
return false, errors.Wrap(err, "db.GetPlayer")
|
||||
}
|
||||
|
||||
user := db.CurrentUser(ctx)
|
||||
if user != nil && player.UserID != nil && *player.UserID == user.ID {
|
||||
isOwner = true
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}); !ok {
|
||||
return nil, false, false
|
||||
}
|
||||
|
||||
// If player has no SlapID and viewer is not the owner, show 404
|
||||
if player.SlapID == nil && !isOwner {
|
||||
throw.NotFound(s, w, r, r.URL.Path)
|
||||
return nil, false, false
|
||||
}
|
||||
|
||||
return player, isOwner, true
|
||||
}
|
||||
|
||||
// PlayerViewStats renders the player profile page with the stats tab active.
|
||||
// GET renders the full page layout. POST renders just the tab content.
|
||||
func PlayerViewStats(
|
||||
s *hws.Server,
|
||||
conn *db.DB,
|
||||
) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
playerIDStr := r.PathValue("player_id")
|
||||
|
||||
playerID, err := strconv.Atoi(playerIDStr)
|
||||
if err != nil {
|
||||
throw.NotFound(s, w, r, r.URL.Path)
|
||||
player, isOwner, ok := resolvePlayerAndOwner(s, conn, w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var player *db.Player
|
||||
var isOwner bool
|
||||
var stats *db.PlayerAllTimeStats
|
||||
var seasons []*db.Season
|
||||
var teams []*db.Team
|
||||
|
||||
if ok := conn.WithReadTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
|
||||
var err error
|
||||
player, err = db.GetPlayer(ctx, tx, playerID)
|
||||
stats, err = db.GetPlayerAllTimeStats(ctx, tx, player.ID)
|
||||
if err != nil {
|
||||
if db.IsBadRequest(err) {
|
||||
throw.NotFound(s, w, r, r.URL.Path)
|
||||
return false, nil
|
||||
}
|
||||
return false, errors.Wrap(err, "db.GetPlayer")
|
||||
return false, errors.Wrap(err, "db.GetPlayerAllTimeStats")
|
||||
}
|
||||
|
||||
// Check if the current user owns this player
|
||||
user := db.CurrentUser(ctx)
|
||||
if user != nil && player.UserID != nil && *player.UserID == user.ID {
|
||||
isOwner = true
|
||||
seasons, err = db.GetPlayerSeasonsList(ctx, tx, player.ID)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetPlayerSeasonsList")
|
||||
}
|
||||
teams, err = db.GetPlayerTeamsList(ctx, tx, player.ID)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetPlayerTeamsList")
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// If player has no SlapID and viewer is not the owner, show 404
|
||||
if player.SlapID == nil && !isOwner {
|
||||
throw.NotFound(s, w, r, r.URL.Path)
|
||||
if r.Method == "GET" {
|
||||
renderSafely(playersview.PlayerStatsPage(player, isOwner, stats, seasons, teams), s, r, w)
|
||||
} else {
|
||||
renderSafely(playersview.PlayerStatsTab(player, stats, seasons, teams, "", 0), s, r, w)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// PlayerViewTeams renders the teams tab of the player profile page.
|
||||
func PlayerViewTeams(
|
||||
s *hws.Server,
|
||||
conn *db.DB,
|
||||
) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
player, isOwner, ok := resolvePlayerAndOwner(s, conn, w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
renderSafely(playersview.PlayerPage(player, isOwner), s, r, w)
|
||||
var teamInfos []*db.PlayerTeamInfo
|
||||
|
||||
if ok := conn.WithReadTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
|
||||
var err error
|
||||
teamInfos, err = db.GetPlayerTeams(ctx, tx, player.ID)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetPlayerTeams")
|
||||
}
|
||||
return true, nil
|
||||
}); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if r.Method == "GET" {
|
||||
renderSafely(playersview.PlayerTeamsPage(player, isOwner, teamInfos), s, r, w)
|
||||
} else {
|
||||
renderSafely(playersview.PlayerTeamsTab(teamInfos), s, r, w)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// PlayerViewSeasons renders the seasons tab of the player profile page.
|
||||
func PlayerViewSeasons(
|
||||
s *hws.Server,
|
||||
conn *db.DB,
|
||||
) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
player, isOwner, ok := resolvePlayerAndOwner(s, conn, w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var seasonInfos []*db.PlayerSeasonInfo
|
||||
|
||||
if ok := conn.WithReadTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
|
||||
var err error
|
||||
seasonInfos, err = db.GetPlayerSeasons(ctx, tx, player.ID)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetPlayerSeasons")
|
||||
}
|
||||
return true, nil
|
||||
}); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if r.Method == "GET" {
|
||||
renderSafely(playersview.PlayerSeasonsPage(player, isOwner, seasonInfos), s, r, w)
|
||||
} else {
|
||||
renderSafely(playersview.PlayerSeasonsTab(seasonInfos), s, r, w)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user