added free agents
This commit is contained in:
@@ -7,8 +7,10 @@ import (
|
||||
"time"
|
||||
|
||||
"git.haelnorr.com/h/golib/hws"
|
||||
"git.haelnorr.com/h/oslstats/internal/contexts"
|
||||
"git.haelnorr.com/h/oslstats/internal/db"
|
||||
"git.haelnorr.com/h/oslstats/internal/notify"
|
||||
"git.haelnorr.com/h/oslstats/internal/permissions"
|
||||
"git.haelnorr.com/h/oslstats/internal/respond"
|
||||
"git.haelnorr.com/h/oslstats/internal/throw"
|
||||
"git.haelnorr.com/h/oslstats/internal/validation"
|
||||
@@ -43,6 +45,8 @@ func FixtureDetailPage(
|
||||
var userTeamID int
|
||||
var result *db.FixtureResult
|
||||
var rosters map[string][]*db.PlayerWithPlayStatus
|
||||
var nominatedFreeAgents []*db.FixtureFreeAgent
|
||||
var availableFreeAgents []*db.SeasonLeagueFreeAgent
|
||||
|
||||
if ok := conn.WithReadTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
|
||||
var err error
|
||||
@@ -77,6 +81,19 @@ func FixtureDetailPage(
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetFixtureTeamRosters")
|
||||
}
|
||||
// Fetch free agent nominations for this fixture
|
||||
nominatedFreeAgents, err = db.GetNominatedFreeAgents(ctx, tx, fixtureID)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetNominatedFreeAgents")
|
||||
}
|
||||
// Fetch available free agents for nomination (if user can schedule or manage fixtures)
|
||||
canManage := contexts.Permissions(ctx).HasPermission(permissions.FixturesManage)
|
||||
if canSchedule || canManage {
|
||||
availableFreeAgents, err = db.GetFreeAgentsForSeasonLeague(ctx, tx, fixture.SeasonID, fixture.LeagueID)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetFreeAgentsForSeasonLeague")
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}); !ok {
|
||||
return
|
||||
@@ -84,7 +101,7 @@ func FixtureDetailPage(
|
||||
|
||||
renderSafely(seasonsview.FixtureDetailPage(
|
||||
fixture, currentSchedule, history, canSchedule, userTeamID,
|
||||
result, rosters, activeTab,
|
||||
result, rosters, activeTab, nominatedFreeAgents, availableFreeAgents,
|
||||
), s, r, w)
|
||||
})
|
||||
}
|
||||
@@ -108,7 +125,8 @@ func ProposeSchedule(
|
||||
format := timefmt.NewBuilder().Year4().Dash().MonthNumeric2().Dash().
|
||||
DayNumeric2().T().Hour24().Colon().Minute().Build()
|
||||
aest, _ := time.LoadLocation("Australia/Sydney")
|
||||
scheduledTime := getter.TimeInLocation("scheduled_time", format, aest).After(time.Now()).Value
|
||||
// scheduledTime := getter.TimeInLocation("scheduled_time", format, aest).After(time.Now()).Value
|
||||
scheduledTime := getter.TimeInLocation("scheduled_time", format, aest).Value
|
||||
|
||||
if !getter.ValidateAndNotify(s, w, r) {
|
||||
return
|
||||
@@ -147,7 +165,7 @@ func ProposeSchedule(
|
||||
return
|
||||
}
|
||||
|
||||
notify.Success(s, w, r, "Time Proposed", "Your proposed time has been submitted.", nil)
|
||||
notify.SuccessWithDelay(s, w, r, "Time Proposed", "Your proposed time has been submitted.", nil)
|
||||
respond.HXRedirect(w, "/fixtures/%d", fixtureID)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -282,6 +282,21 @@ func UploadMatchLogs(
|
||||
}
|
||||
}
|
||||
|
||||
// Check each player stat: if the player is a registered free agent, mark them
|
||||
for _, ps := range playerStats {
|
||||
if ps.PlayerID == nil {
|
||||
continue
|
||||
}
|
||||
// Check if the player is a registered free agent
|
||||
isFA, err := db.IsFreeAgentRegistered(ctx, tx, fixture.SeasonID, fixture.LeagueID, *ps.PlayerID)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.IsFreeAgentRegistered")
|
||||
}
|
||||
if isFA {
|
||||
ps.IsFreeAgent = true
|
||||
}
|
||||
}
|
||||
|
||||
// Insert result and stats
|
||||
result, err = db.InsertFixtureResult(ctx, tx, result, playerStats, db.NewAuditFromRequest(r))
|
||||
if err != nil {
|
||||
@@ -294,7 +309,6 @@ func UploadMatchLogs(
|
||||
}
|
||||
|
||||
_ = unmappedPlayers // stored for review page redirect
|
||||
notify.Success(s, w, r, "Logs Uploaded", "Match logs have been processed. Please review the result.", nil)
|
||||
respond.HXRedirect(w, "/fixtures/%d/results/review", fixtureID)
|
||||
})
|
||||
}
|
||||
@@ -314,6 +328,7 @@ func ReviewMatchResult(
|
||||
var fixture *db.Fixture
|
||||
var result *db.FixtureResult
|
||||
var unmappedPlayers []string
|
||||
var faWarnings []seasonsview.FreeAgentWarning
|
||||
|
||||
if ok := conn.WithReadTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
|
||||
var err error
|
||||
@@ -335,11 +350,52 @@ func ReviewMatchResult(
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Build unmapped players list from stats
|
||||
// Get nominated free agents for this fixture
|
||||
nominatedFAs, err := db.GetNominatedFreeAgents(ctx, tx, fixtureID)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetNominatedFreeAgents")
|
||||
}
|
||||
// Map player ID to the side ("home"/"away") that nominated them
|
||||
nominatedFASide := map[int]string{}
|
||||
for _, nfa := range nominatedFAs {
|
||||
if nfa.TeamID == fixture.HomeTeamID {
|
||||
nominatedFASide[nfa.PlayerID] = "home"
|
||||
} else {
|
||||
nominatedFASide[nfa.PlayerID] = "away"
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to resolve side to team name
|
||||
teamNameForSide := func(side string) string {
|
||||
if side == "home" {
|
||||
return fixture.HomeTeam.Name
|
||||
}
|
||||
return fixture.AwayTeam.Name
|
||||
}
|
||||
|
||||
// Build unmapped players and free agent warnings from stats
|
||||
seen := map[int]bool{}
|
||||
for _, ps := range result.PlayerStats {
|
||||
if ps.PlayerID == nil && ps.PeriodNum == 3 {
|
||||
if ps.PeriodNum != 3 {
|
||||
continue
|
||||
}
|
||||
if ps.PlayerID == nil {
|
||||
unmappedPlayers = append(unmappedPlayers,
|
||||
ps.PlayerGameUserID+" ("+ps.PlayerUsername+")")
|
||||
} else if ps.IsFreeAgent && !seen[*ps.PlayerID] {
|
||||
seen[*ps.PlayerID] = true
|
||||
nominatedSide, wasNominated := nominatedFASide[*ps.PlayerID]
|
||||
if !wasNominated {
|
||||
faWarnings = append(faWarnings, seasonsview.FreeAgentWarning{
|
||||
Name: ps.PlayerUsername,
|
||||
Reason: "not nominated for this fixture",
|
||||
})
|
||||
} else if nominatedSide != ps.Team {
|
||||
faWarnings = append(faWarnings, seasonsview.FreeAgentWarning{
|
||||
Name: ps.PlayerUsername,
|
||||
Reason: "nominated by " + teamNameForSide(nominatedSide) + ", but played for " + teamNameForSide(ps.Team),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,7 +404,7 @@ func ReviewMatchResult(
|
||||
return
|
||||
}
|
||||
|
||||
renderSafely(seasonsview.FixtureReviewResultPage(fixture, result, unmappedPlayers), s, r, w)
|
||||
renderSafely(seasonsview.FixtureReviewResultPage(fixture, result, unmappedPlayers, faWarnings), s, r, w)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
356
internal/handlers/free_agents.go
Normal file
356
internal/handlers/free_agents.go
Normal file
@@ -0,0 +1,356 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"git.haelnorr.com/h/golib/hws"
|
||||
"git.haelnorr.com/h/oslstats/internal/contexts"
|
||||
"git.haelnorr.com/h/oslstats/internal/db"
|
||||
"git.haelnorr.com/h/oslstats/internal/notify"
|
||||
"git.haelnorr.com/h/oslstats/internal/permissions"
|
||||
"git.haelnorr.com/h/oslstats/internal/respond"
|
||||
"git.haelnorr.com/h/oslstats/internal/throw"
|
||||
"git.haelnorr.com/h/oslstats/internal/validation"
|
||||
"git.haelnorr.com/h/oslstats/internal/view/seasonsview"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
// FreeAgentsListPage renders the free agents tab of a season league page
|
||||
func FreeAgentsListPage(
|
||||
s *hws.Server,
|
||||
conn *db.DB,
|
||||
) 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 league *db.League
|
||||
var freeAgents []*db.SeasonLeagueFreeAgent
|
||||
var availablePlayers []*db.Player
|
||||
|
||||
if ok := conn.WithReadTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
|
||||
var err error
|
||||
sl, err := db.GetSeasonLeague(ctx, tx, seasonStr, leagueStr)
|
||||
if err != nil {
|
||||
if db.IsBadRequest(err) {
|
||||
throw.NotFound(s, w, r, r.URL.Path)
|
||||
return false, nil
|
||||
}
|
||||
return false, errors.Wrap(err, "db.GetSeasonLeague")
|
||||
}
|
||||
season = sl.Season
|
||||
league = sl.League
|
||||
|
||||
freeAgents, err = db.GetFreeAgentsForSeasonLeague(ctx, tx, season.ID, league.ID)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetFreeAgentsForSeasonLeague")
|
||||
}
|
||||
|
||||
availablePlayers, err = db.GetPlayersNotOnTeam(ctx, tx, season.ID, league.ID)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetPlayersNotOnTeam")
|
||||
}
|
||||
|
||||
// Filter out players already registered as free agents
|
||||
faMap := make(map[int]bool, len(freeAgents))
|
||||
for _, fa := range freeAgents {
|
||||
faMap[fa.PlayerID] = true
|
||||
}
|
||||
filtered := make([]*db.Player, 0, len(availablePlayers))
|
||||
for _, p := range availablePlayers {
|
||||
if !faMap[p.ID] {
|
||||
filtered = append(filtered, p)
|
||||
}
|
||||
}
|
||||
availablePlayers = filtered
|
||||
|
||||
return true, nil
|
||||
}); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if r.Method == "GET" {
|
||||
renderSafely(seasonsview.SeasonLeagueFreeAgentsPage(season, league, freeAgents, availablePlayers), s, r, w)
|
||||
} else {
|
||||
renderSafely(seasonsview.SeasonLeagueFreeAgents(season, league, freeAgents, availablePlayers), s, r, w)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// RegisterFreeAgent handles POST to register a player as a free agent
|
||||
func RegisterFreeAgent(
|
||||
s *hws.Server,
|
||||
conn *db.DB,
|
||||
) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
seasonStr := r.PathValue("season_short_name")
|
||||
leagueStr := r.PathValue("league_short_name")
|
||||
|
||||
getter, ok := validation.ParseFormOrNotify(s, w, r)
|
||||
if !ok {
|
||||
respond.BadRequest(w, errors.New("failed to parse form"))
|
||||
return
|
||||
}
|
||||
|
||||
playerID := getter.Int("player_id").Required().Value
|
||||
if !getter.ValidateAndNotify(s, w, r) {
|
||||
respond.BadRequest(w, errors.New("invalid form data"))
|
||||
return
|
||||
}
|
||||
|
||||
if ok := conn.WithNotifyTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
|
||||
sl, err := db.GetSeasonLeague(ctx, tx, seasonStr, leagueStr)
|
||||
if err != nil {
|
||||
if db.IsBadRequest(err) {
|
||||
throw.NotFound(s, w, r, r.URL.Path)
|
||||
return false, nil
|
||||
}
|
||||
return false, errors.Wrap(err, "db.GetSeasonLeague")
|
||||
}
|
||||
|
||||
// Verify player is not on a team in this season_league
|
||||
players, err := db.GetPlayersNotOnTeam(ctx, tx, sl.Season.ID, sl.League.ID)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetPlayersNotOnTeam")
|
||||
}
|
||||
playerFound := false
|
||||
for _, p := range players {
|
||||
if p.ID == playerID {
|
||||
playerFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !playerFound {
|
||||
notify.Warn(s, w, r, "Cannot Register", "Player is already on a team in this league.", nil)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Check if already registered
|
||||
isRegistered, err := db.IsFreeAgentRegistered(ctx, tx, sl.Season.ID, sl.League.ID, playerID)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.IsFreeAgentRegistered")
|
||||
}
|
||||
if isRegistered {
|
||||
notify.Warn(s, w, r, "Already Registered", "Player is already registered as a free agent.", nil)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
err = db.RegisterFreeAgent(ctx, tx, sl.Season.ID, sl.League.ID, playerID, db.NewAuditFromRequest(r))
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.RegisterFreeAgent")
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notify.Success(s, w, r, "Free Agent Registered", "Player has been registered as a free agent.", nil)
|
||||
respond.HXRedirect(w, "/seasons/%s/leagues/%s/free-agents", seasonStr, leagueStr)
|
||||
})
|
||||
}
|
||||
|
||||
// UnregisterFreeAgent handles POST to unregister a player as a free agent
|
||||
func UnregisterFreeAgent(
|
||||
s *hws.Server,
|
||||
conn *db.DB,
|
||||
) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
seasonStr := r.PathValue("season_short_name")
|
||||
leagueStr := r.PathValue("league_short_name")
|
||||
|
||||
getter, ok := validation.ParseFormOrNotify(s, w, r)
|
||||
if !ok {
|
||||
respond.BadRequest(w, errors.New("failed to parse form"))
|
||||
return
|
||||
}
|
||||
|
||||
playerID := getter.Int("player_id").Required().Value
|
||||
if !getter.ValidateAndNotify(s, w, r) {
|
||||
respond.BadRequest(w, errors.New("invalid form data"))
|
||||
return
|
||||
}
|
||||
|
||||
if ok := conn.WithNotifyTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
|
||||
sl, err := db.GetSeasonLeague(ctx, tx, seasonStr, leagueStr)
|
||||
if err != nil {
|
||||
if db.IsBadRequest(err) {
|
||||
throw.NotFound(s, w, r, r.URL.Path)
|
||||
return false, nil
|
||||
}
|
||||
return false, errors.Wrap(err, "db.GetSeasonLeague")
|
||||
}
|
||||
|
||||
err = db.UnregisterFreeAgent(ctx, tx, sl.Season.ID, sl.League.ID, playerID, db.NewAuditFromRequest(r))
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.UnregisterFreeAgent")
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notify.Success(s, w, r, "Free Agent Removed", "Player has been unregistered as a free agent.", nil)
|
||||
respond.HXRedirect(w, "/seasons/%s/leagues/%s/free-agents", seasonStr, leagueStr)
|
||||
})
|
||||
}
|
||||
|
||||
// NominateFreeAgentHandler handles POST to nominate a free agent for a fixture
|
||||
func NominateFreeAgentHandler(
|
||||
s *hws.Server,
|
||||
conn *db.DB,
|
||||
) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fixtureID, err := strconv.Atoi(r.PathValue("fixture_id"))
|
||||
if err != nil {
|
||||
throw.BadRequest(s, w, r, "Invalid fixture ID", err)
|
||||
return
|
||||
}
|
||||
|
||||
getter, ok := validation.ParseFormOrNotify(s, w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
playerID := getter.Int("player_id").Required().Value
|
||||
teamID := getter.Int("team_id").Required().Value
|
||||
if !getter.ValidateAndNotify(s, w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
if ok := conn.WithNotifyTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
|
||||
// Verify fixture exists and user is a manager
|
||||
fixture, err := db.GetFixture(ctx, tx, fixtureID)
|
||||
if err != nil {
|
||||
if db.IsBadRequest(err) {
|
||||
respond.NotFound(w, errors.Wrap(err, "db.GetFixture"))
|
||||
return false, nil
|
||||
}
|
||||
return false, errors.Wrap(err, "db.GetFixture")
|
||||
}
|
||||
|
||||
// Check if user can nominate: either a manager of the nominating team,
|
||||
// or has fixtures.manage permission (can nominate for either team)
|
||||
user := db.CurrentUser(ctx)
|
||||
canManage := contexts.Permissions(ctx).HasPermission(permissions.FixturesManage)
|
||||
if !canManage {
|
||||
canSchedule, userTeamID, err := fixture.CanSchedule(ctx, tx, user)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "fixture.CanSchedule")
|
||||
}
|
||||
if !canSchedule || userTeamID != teamID {
|
||||
throw.Forbidden(s, w, r, "You must be a manager of the nominating team", nil)
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
// Verify the team_id is actually one of the fixture's teams
|
||||
if teamID != fixture.HomeTeamID && teamID != fixture.AwayTeamID {
|
||||
throw.BadRequest(s, w, r, "Invalid team for this fixture", nil)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Verify player is a registered free agent in this season_league
|
||||
isRegistered, err := db.IsFreeAgentRegistered(ctx, tx, fixture.SeasonID, fixture.LeagueID, playerID)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.IsFreeAgentRegistered")
|
||||
}
|
||||
if !isRegistered {
|
||||
notify.Warn(s, w, r, "Not Registered", "Player is not a registered free agent in this league.", nil)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
err = db.NominateFreeAgent(ctx, tx, fixtureID, playerID, teamID, db.NewAuditFromRequest(r))
|
||||
if err != nil {
|
||||
if db.IsBadRequest(err) {
|
||||
notify.Warn(s, w, r, "Cannot Nominate", err.Error(), nil)
|
||||
return false, nil
|
||||
}
|
||||
return false, errors.Wrap(err, "db.NominateFreeAgent")
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notify.Success(s, w, r, "Free Agent Nominated", "Free agent has been nominated for this fixture.", nil)
|
||||
respond.HXRedirect(w, "/fixtures/%d", fixtureID)
|
||||
})
|
||||
}
|
||||
|
||||
// RemoveFreeAgentNominationHandler handles POST to remove a free agent nomination
|
||||
func RemoveFreeAgentNominationHandler(
|
||||
s *hws.Server,
|
||||
conn *db.DB,
|
||||
) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fixtureID, err := strconv.Atoi(r.PathValue("fixture_id"))
|
||||
if err != nil {
|
||||
throw.BadRequest(s, w, r, "Invalid fixture ID", err)
|
||||
return
|
||||
}
|
||||
|
||||
playerID, err := strconv.Atoi(r.PathValue("player_id"))
|
||||
if err != nil {
|
||||
throw.BadRequest(s, w, r, "Invalid player ID", err)
|
||||
return
|
||||
}
|
||||
|
||||
if ok := conn.WithNotifyTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
|
||||
// Check if user can remove: either has fixtures.manage permission,
|
||||
// or is a manager of the team that nominated the free agent
|
||||
canManage := contexts.Permissions(ctx).HasPermission(permissions.FixturesManage)
|
||||
if !canManage {
|
||||
fixture, err := db.GetFixture(ctx, tx, fixtureID)
|
||||
if err != nil {
|
||||
if db.IsBadRequest(err) {
|
||||
respond.NotFound(w, errors.Wrap(err, "db.GetFixture"))
|
||||
return false, nil
|
||||
}
|
||||
return false, errors.Wrap(err, "db.GetFixture")
|
||||
}
|
||||
user := db.CurrentUser(ctx)
|
||||
canSchedule, userTeamID, err := fixture.CanSchedule(ctx, tx, user)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "fixture.CanSchedule")
|
||||
}
|
||||
if !canSchedule {
|
||||
throw.Forbidden(s, w, r, "You must be a team manager to remove nominations", nil)
|
||||
return false, nil
|
||||
}
|
||||
// Verify the nomination belongs to the user's team
|
||||
nominations, err := db.GetNominatedFreeAgentsByTeam(ctx, tx, fixtureID, userTeamID)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.GetNominatedFreeAgentsByTeam")
|
||||
}
|
||||
found := false
|
||||
for _, n := range nominations {
|
||||
if n.PlayerID == playerID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
throw.Forbidden(s, w, r, "You can only remove nominations made by your team", nil)
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
err := db.RemoveFreeAgentNomination(ctx, tx, fixtureID, playerID, db.NewAuditFromRequest(r))
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "db.RemoveFreeAgentNomination")
|
||||
}
|
||||
return true, nil
|
||||
}); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notify.Success(s, w, r, "Nomination Removed", "Free agent nomination has been removed.", nil)
|
||||
respond.HXRedirect(w, "/fixtures/%d", fixtureID)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user