added leagues
This commit is contained in:
34
internal/handlers/leagues_list.go
Normal file
34
internal/handlers/leagues_list.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"git.haelnorr.com/h/golib/hws"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/uptrace/bun"
|
||||
|
||||
"git.haelnorr.com/h/oslstats/internal/db"
|
||||
"git.haelnorr.com/h/oslstats/internal/view/leaguesview"
|
||||
)
|
||||
|
||||
func LeaguesList(
|
||||
s *hws.Server,
|
||||
conn *bun.DB,
|
||||
) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var leagues []*db.League
|
||||
if ok := db.WithReadTx(s, w, r, conn, func(ctx context.Context, tx bun.Tx) (bool, error) {
|
||||
var err error
|
||||
leagues, err = db.GetLeagues(ctx, tx)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetLeagues")
|
||||
}
|
||||
return true, nil
|
||||
}); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
renderSafely(leaguesview.ListPage(leagues), s, r, w)
|
||||
})
|
||||
}
|
||||
96
internal/handlers/leagues_new.go
Normal file
96
internal/handlers/leagues_new.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"git.haelnorr.com/h/golib/hws"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/uptrace/bun"
|
||||
|
||||
"git.haelnorr.com/h/oslstats/internal/auditlog"
|
||||
"git.haelnorr.com/h/oslstats/internal/db"
|
||||
"git.haelnorr.com/h/oslstats/internal/notify"
|
||||
"git.haelnorr.com/h/oslstats/internal/validation"
|
||||
leaguesview "git.haelnorr.com/h/oslstats/internal/view/leaguesview"
|
||||
)
|
||||
|
||||
func NewLeague(
|
||||
s *hws.Server,
|
||||
conn *bun.DB,
|
||||
) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "GET" {
|
||||
renderSafely(leaguesview.NewPage(), s, r, w)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func NewLeagueSubmit(
|
||||
s *hws.Server,
|
||||
conn *bun.DB,
|
||||
audit *auditlog.Logger,
|
||||
) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
getter, ok := validation.ParseFormOrNotify(s, w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
name := getter.String("name").
|
||||
TrimSpace().Required().
|
||||
MaxLength(50).MinLength(3).Value
|
||||
shortname := getter.String("short_name").
|
||||
TrimSpace().Required().
|
||||
MaxLength(10).MinLength(2).Value
|
||||
description := getter.String("description").
|
||||
TrimSpace().MaxLength(500).Value
|
||||
if !getter.ValidateAndNotify(s, w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
nameUnique := false
|
||||
shortNameUnique := false
|
||||
var league *db.League
|
||||
if ok := db.WithNotifyTx(s, w, r, conn, func(ctx context.Context, tx bun.Tx) (bool, error) {
|
||||
var err error
|
||||
nameUnique, err = db.IsUnique(ctx, tx, (*db.League)(nil), "name", name)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.IsLeagueNameUnique")
|
||||
}
|
||||
shortNameUnique, err = db.IsUnique(ctx, tx, (*db.League)(nil), "short_name", shortname)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.IsLeagueShortNameUnique")
|
||||
}
|
||||
if !nameUnique || !shortNameUnique {
|
||||
return true, nil
|
||||
}
|
||||
league = &db.League{
|
||||
Name: name,
|
||||
ShortName: shortname,
|
||||
Description: description,
|
||||
}
|
||||
err = db.Insert(tx, league).WithAudit(r, audit.Callback()).Exec(ctx)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.Insert")
|
||||
}
|
||||
return true, nil
|
||||
}); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if !nameUnique {
|
||||
notify.Warn(s, w, r, "Duplicate Name", "This league name is already taken.", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if !shortNameUnique {
|
||||
notify.Warn(s, w, r, "Duplicate Short Name", "This short name is already taken.", nil)
|
||||
return
|
||||
}
|
||||
w.Header().Set("HX-Redirect", fmt.Sprintf("/leagues/%s", league.ShortName))
|
||||
w.WriteHeader(http.StatusOK)
|
||||
notify.SuccessWithDelay(s, w, r, "League Created", fmt.Sprintf("Successfully created league: %s", name), nil)
|
||||
})
|
||||
}
|
||||
@@ -93,6 +93,10 @@ func notifyLoop(ctx context.Context, c *hws.Client, ws *websocket.Conn) error {
|
||||
// Parse error code and stacktrace from Details field
|
||||
code, stacktrace := parseErrorDetails(nt.Details)
|
||||
err = popup.ErrorModalWS(code, stacktrace, nt, count).Render(ctx, w)
|
||||
case notify.LevelInfo:
|
||||
err = popup.Toast(nt, count, 6000).Render(ctx, w)
|
||||
case notify.LevelSuccess:
|
||||
err = popup.Toast(nt, count, 3000).Render(ctx, w)
|
||||
default:
|
||||
err = popup.Toast(nt, count, 6000).Render(ctx, w)
|
||||
}
|
||||
|
||||
@@ -24,12 +24,17 @@ func SeasonEditPage(
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
seasonStr := r.PathValue("season_short_name")
|
||||
var season *db.Season
|
||||
var allLeagues []*db.League
|
||||
if ok := db.WithReadTx(s, w, r, conn, func(ctx context.Context, tx bun.Tx) (bool, error) {
|
||||
var err error
|
||||
season, err = db.GetSeason(ctx, tx, seasonStr)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetSeason")
|
||||
}
|
||||
allLeagues, err = db.GetLeagues(ctx, tx)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetLeagues")
|
||||
}
|
||||
return true, nil
|
||||
}); !ok {
|
||||
return
|
||||
@@ -38,7 +43,7 @@ func SeasonEditPage(
|
||||
throw.NotFound(s, w, r, r.URL.Path)
|
||||
return
|
||||
}
|
||||
renderSafely(seasonsview.EditPage(season), s, r, w)
|
||||
renderSafely(seasonsview.EditPage(season, allLeagues), s, r, w)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -60,10 +65,12 @@ func SeasonEditSubmit(
|
||||
MonthNumeric2().Slash().
|
||||
Year4().Build()
|
||||
|
||||
startDate := getter.Time("start_date", format).Required().Value
|
||||
endDate := getter.Time("end_date", format).Value
|
||||
finalsStartDate := getter.Time("finals_start_date", format).Value
|
||||
finalsEndDate := getter.Time("finals_end_date", format).Value
|
||||
version := getter.String("slap_version").
|
||||
TrimSpace().Required().AllowedValues([]string{"rebound", "slapshot1"}).Value
|
||||
start := getter.Time("start_date", format).Required().Value
|
||||
end := getter.Time("end_date", format).Value
|
||||
finalsStart := getter.Time("finals_start_date", format).Value
|
||||
finalsEnd := getter.Time("finals_end_date", format).Value
|
||||
|
||||
if !getter.ValidateAndNotify(s, w, r) {
|
||||
return
|
||||
@@ -79,9 +86,9 @@ func SeasonEditSubmit(
|
||||
if season == nil {
|
||||
return false, errors.New("season does not exist")
|
||||
}
|
||||
season.Update(startDate, endDate, finalsStartDate, finalsEndDate)
|
||||
season.Update(version, start, end, finalsStart, finalsEnd)
|
||||
err = db.Update(tx, season).WherePK().
|
||||
Column("start_date", "end_date", "finals_start_date", "finals_end_date").
|
||||
Column("slap_version", "start_date", "end_date", "finals_start_date", "finals_end_date").
|
||||
WithAudit(r, audit.Callback()).Exec(ctx)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.Update")
|
||||
|
||||
134
internal/handlers/season_leagues.go
Normal file
134
internal/handlers/season_leagues.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"git.haelnorr.com/h/golib/hws"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/uptrace/bun"
|
||||
|
||||
"git.haelnorr.com/h/oslstats/internal/auditlog"
|
||||
"git.haelnorr.com/h/oslstats/internal/db"
|
||||
"git.haelnorr.com/h/oslstats/internal/notify"
|
||||
"git.haelnorr.com/h/oslstats/internal/view/seasonsview"
|
||||
)
|
||||
|
||||
func SeasonAddLeague(
|
||||
s *hws.Server,
|
||||
conn *bun.DB,
|
||||
audit *auditlog.Logger,
|
||||
) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
seasonStr := r.PathValue("season_short_name")
|
||||
leagueStr := r.PathValue("league_short_name")
|
||||
|
||||
var season *db.Season
|
||||
var allLeagues []*db.League
|
||||
if ok := db.WithNotifyTx(s, w, r, conn, func(ctx context.Context, tx bun.Tx) (bool, error) {
|
||||
var err error
|
||||
season, err = db.GetSeason(ctx, tx, seasonStr)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetSeason")
|
||||
}
|
||||
if season == nil {
|
||||
return false, errors.New("season not found")
|
||||
}
|
||||
|
||||
league, err := db.GetLeague(ctx, tx, leagueStr)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetLeague")
|
||||
}
|
||||
if league == nil {
|
||||
return false, errors.New("league not found")
|
||||
}
|
||||
|
||||
// Create the many-to-many relationship
|
||||
seasonLeague := &db.SeasonLeague{
|
||||
SeasonID: season.ID,
|
||||
LeagueID: league.ID,
|
||||
}
|
||||
err = db.Insert(tx, seasonLeague).WithAudit(r, audit.Callback()).Exec(ctx)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.Insert")
|
||||
}
|
||||
|
||||
// Reload season with updated leagues
|
||||
season, err = db.GetSeason(ctx, tx, seasonStr)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetSeason")
|
||||
}
|
||||
|
||||
allLeagues, err = db.GetLeagues(ctx, tx)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetLeagues")
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notify.Success(s, w, r, "League Added", "League successfully added to season", nil)
|
||||
renderSafely(seasonsview.LeaguesSection(season, allLeagues), s, r, w)
|
||||
})
|
||||
}
|
||||
|
||||
func SeasonRemoveLeague(
|
||||
s *hws.Server,
|
||||
conn *bun.DB,
|
||||
audit *auditlog.Logger,
|
||||
) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
seasonStr := r.PathValue("season_short_name")
|
||||
leagueStr := r.PathValue("league_short_name")
|
||||
|
||||
var season *db.Season
|
||||
var allLeagues []*db.League
|
||||
if ok := db.WithNotifyTx(s, w, r, conn, func(ctx context.Context, tx bun.Tx) (bool, error) {
|
||||
var err error
|
||||
season, err = db.GetSeason(ctx, tx, seasonStr)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetSeason")
|
||||
}
|
||||
if season == nil {
|
||||
return false, errors.New("season not found")
|
||||
}
|
||||
|
||||
league, err := db.GetLeague(ctx, tx, leagueStr)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetLeague")
|
||||
}
|
||||
if league == nil {
|
||||
return false, errors.New("league not found")
|
||||
}
|
||||
|
||||
// Delete the many-to-many relationship
|
||||
err = db.DeleteItem[db.SeasonLeague](tx).
|
||||
Where("season_id = ? AND league_id = ?", season.ID, league.ID).
|
||||
WithAudit(r, audit.Callback()).
|
||||
Delete(ctx)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.DeleteItem")
|
||||
}
|
||||
|
||||
// Reload season with updated leagues
|
||||
season, err = db.GetSeason(ctx, tx, seasonStr)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetSeason")
|
||||
}
|
||||
|
||||
allLeagues, err = db.GetLeagues(ctx, tx)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetLeagues")
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notify.Success(s, w, r, "League Removed", "League successfully removed from season", nil)
|
||||
renderSafely(seasonsview.LeaguesSection(season, allLeagues), s, r, w)
|
||||
})
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
// SeasonsPage renders the full page with the seasons list, for use with GET requests
|
||||
func SeasonsPage(
|
||||
s *hws.Server,
|
||||
conn *bun.DB,
|
||||
@@ -35,6 +36,7 @@ func SeasonsPage(
|
||||
})
|
||||
}
|
||||
|
||||
// SeasonsList renders just the seasons list, for use with POST requests and HTMX
|
||||
func SeasonsList(
|
||||
s *hws.Server,
|
||||
conn *bun.DB,
|
||||
|
||||
@@ -41,14 +41,16 @@ func NewSeasonSubmit(
|
||||
name := getter.String("name").
|
||||
TrimSpace().Required().
|
||||
MaxLength(20).MinLength(5).Value
|
||||
shortName := getter.String("short_name").
|
||||
shortname := getter.String("short_name").
|
||||
TrimSpace().ToUpper().Required().
|
||||
MaxLength(6).MinLength(2).Value
|
||||
version := getter.String("slap_version").
|
||||
TrimSpace().Required().AllowedValues([]string{"rebound", "slapshot1"}).Value
|
||||
format := timefmt.NewBuilder().
|
||||
DayNumeric2().Slash().
|
||||
MonthNumeric2().Slash().
|
||||
Year4().Build()
|
||||
startDate := getter.Time("start_date", format).Required().Value
|
||||
start := getter.Time("start_date", format).Required().Value
|
||||
if !getter.ValidateAndNotify(s, w, r) {
|
||||
return
|
||||
}
|
||||
@@ -62,14 +64,14 @@ func NewSeasonSubmit(
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.IsSeasonNameUnique")
|
||||
}
|
||||
shortNameUnique, err = db.IsUnique(ctx, tx, (*db.Season)(nil), "short_name", shortName)
|
||||
shortNameUnique, err = db.IsUnique(ctx, tx, (*db.Season)(nil), "short_name", shortname)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.IsSeasonShortNameUnique")
|
||||
}
|
||||
if !nameUnique || !shortNameUnique {
|
||||
return true, nil
|
||||
}
|
||||
season = db.NewSeason(name, shortName, startDate)
|
||||
season = db.NewSeason(name, version, shortname, start)
|
||||
err = db.Insert(tx, season).WithAudit(r, audit.Callback()).Exec(ctx)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.Insert")
|
||||
|
||||
Reference in New Issue
Block a user