fixed fixture page to use htmx tab pattern
This commit is contained in:
@@ -2,6 +2,7 @@ package handlers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
@@ -20,8 +21,7 @@ import (
|
|||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FixtureDetailPage renders the fixture detail page with scheduling UI, history,
|
// FixtureDetailPage redirects to the default tab (overview)
|
||||||
// result display, and team rosters
|
|
||||||
func FixtureDetailPage(
|
func FixtureDetailPage(
|
||||||
s *hws.Server,
|
s *hws.Server,
|
||||||
conn *db.DB,
|
conn *db.DB,
|
||||||
@@ -33,9 +33,230 @@ func FixtureDetailPage(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
activeTab := r.URL.Query().Get("tab")
|
http.Redirect(w, r, fmt.Sprintf("/fixtures/%d/overview", fixtureID), http.StatusSeeOther)
|
||||||
if activeTab == "" {
|
})
|
||||||
activeTab = "overview"
|
}
|
||||||
|
|
||||||
|
// FixtureDetailOverviewPage renders the overview tab of the fixture detail page
|
||||||
|
func FixtureDetailOverviewPage(
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
var fixture *db.Fixture
|
||||||
|
var currentSchedule *db.FixtureSchedule
|
||||||
|
var canSchedule bool
|
||||||
|
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
|
||||||
|
fixture, err = db.GetFixture(ctx, tx, fixtureID)
|
||||||
|
if err != nil {
|
||||||
|
if db.IsBadRequest(err) {
|
||||||
|
throw.NotFound(s, w, r, r.URL.Path)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, errors.Wrap(err, "db.GetFixture")
|
||||||
|
}
|
||||||
|
currentSchedule, err = db.GetCurrentFixtureSchedule(ctx, tx, fixtureID)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.Wrap(err, "db.GetCurrentFixtureSchedule")
|
||||||
|
}
|
||||||
|
user := db.CurrentUser(ctx)
|
||||||
|
canSchedule, userTeamID, err = fixture.CanSchedule(ctx, tx, user)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.Wrap(err, "fixture.CanSchedule")
|
||||||
|
}
|
||||||
|
result, err = db.GetFixtureResult(ctx, tx, fixtureID)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.Wrap(err, "db.GetFixtureResult")
|
||||||
|
}
|
||||||
|
rosters, err = db.GetFixtureTeamRosters(ctx, tx, fixture, result)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.Wrap(err, "db.GetFixtureTeamRosters")
|
||||||
|
}
|
||||||
|
nominatedFreeAgents, err = db.GetNominatedFreeAgents(ctx, tx, fixtureID)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.Wrap(err, "db.GetNominatedFreeAgents")
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Method == "GET" {
|
||||||
|
renderSafely(seasonsview.FixtureDetailOverviewPage(
|
||||||
|
fixture, currentSchedule, canSchedule, userTeamID,
|
||||||
|
result, rosters, nominatedFreeAgents, availableFreeAgents,
|
||||||
|
), s, r, w)
|
||||||
|
} else {
|
||||||
|
renderSafely(seasonsview.FixtureDetailOverviewContent(
|
||||||
|
fixture, currentSchedule, canSchedule, userTeamID,
|
||||||
|
result, rosters, nominatedFreeAgents, availableFreeAgents,
|
||||||
|
), s, r, w)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// FixtureDetailPreviewPage renders the match preview tab of the fixture detail page
|
||||||
|
func FixtureDetailPreviewPage(
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
var fixture *db.Fixture
|
||||||
|
var result *db.FixtureResult
|
||||||
|
var rosters map[string][]*db.PlayerWithPlayStatus
|
||||||
|
var previewData *db.MatchPreviewData
|
||||||
|
|
||||||
|
if ok := conn.WithReadTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
|
||||||
|
var err error
|
||||||
|
fixture, err = db.GetFixture(ctx, tx, fixtureID)
|
||||||
|
if err != nil {
|
||||||
|
if db.IsBadRequest(err) {
|
||||||
|
throw.NotFound(s, w, r, r.URL.Path)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, errors.Wrap(err, "db.GetFixture")
|
||||||
|
}
|
||||||
|
result, err = db.GetFixtureResult(ctx, tx, fixtureID)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.Wrap(err, "db.GetFixtureResult")
|
||||||
|
}
|
||||||
|
rosters, err = db.GetFixtureTeamRosters(ctx, tx, fixture, result)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.Wrap(err, "db.GetFixtureTeamRosters")
|
||||||
|
}
|
||||||
|
previewData, err = db.ComputeMatchPreview(ctx, tx, fixture)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.Wrap(err, "db.ComputeMatchPreview")
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}); !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If finalized, redirect to analysis instead
|
||||||
|
if result != nil && result.Finalized {
|
||||||
|
if r.Method == "GET" {
|
||||||
|
http.Redirect(w, r, fmt.Sprintf("/fixtures/%d/analysis", fixtureID), http.StatusSeeOther)
|
||||||
|
} else {
|
||||||
|
respond.HXRedirect(w, "/fixtures/%d/analysis", fixtureID)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Method == "GET" {
|
||||||
|
renderSafely(seasonsview.FixtureDetailPreviewPage(
|
||||||
|
fixture, result, rosters, previewData,
|
||||||
|
), s, r, w)
|
||||||
|
} else {
|
||||||
|
renderSafely(seasonsview.FixtureDetailPreviewContent(
|
||||||
|
fixture, rosters, previewData,
|
||||||
|
), s, r, w)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// FixtureDetailAnalysisPage renders the match analysis tab of the fixture detail page
|
||||||
|
func FixtureDetailAnalysisPage(
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
var fixture *db.Fixture
|
||||||
|
var result *db.FixtureResult
|
||||||
|
var rosters map[string][]*db.PlayerWithPlayStatus
|
||||||
|
var previewData *db.MatchPreviewData
|
||||||
|
|
||||||
|
if ok := conn.WithReadTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
|
||||||
|
var err error
|
||||||
|
fixture, err = db.GetFixture(ctx, tx, fixtureID)
|
||||||
|
if err != nil {
|
||||||
|
if db.IsBadRequest(err) {
|
||||||
|
throw.NotFound(s, w, r, r.URL.Path)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, errors.Wrap(err, "db.GetFixture")
|
||||||
|
}
|
||||||
|
result, err = db.GetFixtureResult(ctx, tx, fixtureID)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.Wrap(err, "db.GetFixtureResult")
|
||||||
|
}
|
||||||
|
rosters, err = db.GetFixtureTeamRosters(ctx, tx, fixture, result)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.Wrap(err, "db.GetFixtureTeamRosters")
|
||||||
|
}
|
||||||
|
previewData, err = db.ComputeMatchPreview(ctx, tx, fixture)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.Wrap(err, "db.ComputeMatchPreview")
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}); !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not finalized, redirect to preview instead
|
||||||
|
if result == nil || !result.Finalized {
|
||||||
|
if r.Method == "GET" {
|
||||||
|
http.Redirect(w, r, fmt.Sprintf("/fixtures/%d/preview", fixtureID), http.StatusSeeOther)
|
||||||
|
} else {
|
||||||
|
respond.HXRedirect(w, "/fixtures/%d/preview", fixtureID)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Method == "GET" {
|
||||||
|
renderSafely(seasonsview.FixtureDetailAnalysisPage(
|
||||||
|
fixture, result, rosters, previewData,
|
||||||
|
), s, r, w)
|
||||||
|
} else {
|
||||||
|
renderSafely(seasonsview.FixtureDetailAnalysisContent(
|
||||||
|
fixture, result, rosters, previewData,
|
||||||
|
), s, r, w)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// FixtureDetailSchedulePage renders the schedule tab of the fixture detail page
|
||||||
|
func FixtureDetailSchedulePage(
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
var fixture *db.Fixture
|
var fixture *db.Fixture
|
||||||
@@ -44,10 +265,6 @@ func FixtureDetailPage(
|
|||||||
var canSchedule bool
|
var canSchedule bool
|
||||||
var userTeamID int
|
var userTeamID int
|
||||||
var result *db.FixtureResult
|
var result *db.FixtureResult
|
||||||
var rosters map[string][]*db.PlayerWithPlayStatus
|
|
||||||
var nominatedFreeAgents []*db.FixtureFreeAgent
|
|
||||||
var availableFreeAgents []*db.SeasonLeagueFreeAgent
|
|
||||||
var previewData *db.MatchPreviewData
|
|
||||||
|
|
||||||
if ok := conn.WithReadTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
|
if ok := conn.WithReadTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) {
|
||||||
var err error
|
var err error
|
||||||
@@ -72,48 +289,34 @@ func FixtureDetailPage(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, errors.Wrap(err, "fixture.CanSchedule")
|
return false, errors.Wrap(err, "fixture.CanSchedule")
|
||||||
}
|
}
|
||||||
// Fetch fixture result if it exists
|
|
||||||
result, err = db.GetFixtureResult(ctx, tx, fixtureID)
|
result, err = db.GetFixtureResult(ctx, tx, fixtureID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, errors.Wrap(err, "db.GetFixtureResult")
|
return false, errors.Wrap(err, "db.GetFixtureResult")
|
||||||
}
|
}
|
||||||
// Fetch team rosters with play status
|
|
||||||
rosters, err = db.GetFixtureTeamRosters(ctx, tx, fixture, result)
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch match preview data for preview and analysis tabs
|
|
||||||
if activeTab == "preview" || activeTab == "analysis" {
|
|
||||||
previewData, err = db.ComputeMatchPreview(ctx, tx, fixture)
|
|
||||||
if err != nil {
|
|
||||||
return false, errors.Wrap(err, "db.ComputeMatchPreview")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
}); !ok {
|
}); !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSafely(seasonsview.FixtureDetailPage(
|
// If finalized, redirect to overview (scheduling tab is hidden)
|
||||||
fixture, currentSchedule, history, canSchedule, userTeamID,
|
if result != nil && result.Finalized {
|
||||||
result, rosters, activeTab, nominatedFreeAgents, availableFreeAgents,
|
if r.Method == "GET" {
|
||||||
previewData,
|
http.Redirect(w, r, fmt.Sprintf("/fixtures/%d/overview", fixtureID), http.StatusSeeOther)
|
||||||
), s, r, w)
|
} else {
|
||||||
|
respond.HXRedirect(w, "/fixtures/%d/overview", fixtureID)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Method == "GET" {
|
||||||
|
renderSafely(seasonsview.FixtureDetailSchedulePage(
|
||||||
|
fixture, currentSchedule, history, canSchedule, userTeamID,
|
||||||
|
), s, r, w)
|
||||||
|
} else {
|
||||||
|
renderSafely(seasonsview.FixtureDetailScheduleContent(
|
||||||
|
fixture, currentSchedule, history, canSchedule, userTeamID,
|
||||||
|
), s, r, w)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -219,6 +219,27 @@ func addRoutes(
|
|||||||
Method: hws.MethodDELETE,
|
Method: hws.MethodDELETE,
|
||||||
Handler: perms.RequirePermission(s, permissions.FixturesDelete)(handlers.DeleteFixture(s, conn)),
|
Handler: perms.RequirePermission(s, permissions.FixturesDelete)(handlers.DeleteFixture(s, conn)),
|
||||||
},
|
},
|
||||||
|
// Fixture detail tab routes
|
||||||
|
{
|
||||||
|
Path: "/fixtures/{fixture_id}/overview",
|
||||||
|
Methods: []hws.Method{hws.MethodGET, hws.MethodPOST},
|
||||||
|
Handler: handlers.FixtureDetailOverviewPage(s, conn),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/fixtures/{fixture_id}/preview",
|
||||||
|
Methods: []hws.Method{hws.MethodGET, hws.MethodPOST},
|
||||||
|
Handler: handlers.FixtureDetailPreviewPage(s, conn),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/fixtures/{fixture_id}/analysis",
|
||||||
|
Methods: []hws.Method{hws.MethodGET, hws.MethodPOST},
|
||||||
|
Handler: handlers.FixtureDetailAnalysisPage(s, conn),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/fixtures/{fixture_id}/scheduling",
|
||||||
|
Methods: []hws.Method{hws.MethodGET, hws.MethodPOST},
|
||||||
|
Handler: handlers.FixtureDetailSchedulePage(s, conn),
|
||||||
|
},
|
||||||
// Fixture scheduling routes
|
// Fixture scheduling routes
|
||||||
{
|
{
|
||||||
Path: "/fixtures/{fixture_id}/schedule",
|
Path: "/fixtures/{fixture_id}/schedule",
|
||||||
|
|||||||
@@ -9,39 +9,12 @@ import "fmt"
|
|||||||
import "sort"
|
import "sort"
|
||||||
import "strings"
|
import "strings"
|
||||||
|
|
||||||
templ FixtureDetailPage(
|
// FixtureDetailLayout renders the fixture detail page layout with header and
|
||||||
fixture *db.Fixture,
|
// tab navigation. Tab content is rendered as children.
|
||||||
currentSchedule *db.FixtureSchedule,
|
templ FixtureDetailLayout(activeTab string, fixture *db.Fixture, result *db.FixtureResult) {
|
||||||
history []*db.FixtureSchedule,
|
|
||||||
canSchedule bool,
|
|
||||||
userTeamID int,
|
|
||||||
result *db.FixtureResult,
|
|
||||||
rosters map[string][]*db.PlayerWithPlayStatus,
|
|
||||||
activeTab string,
|
|
||||||
nominatedFreeAgents []*db.FixtureFreeAgent,
|
|
||||||
availableFreeAgents []*db.SeasonLeagueFreeAgent,
|
|
||||||
previewData *db.MatchPreviewData,
|
|
||||||
) {
|
|
||||||
{{
|
{{
|
||||||
permCache := contexts.Permissions(ctx)
|
|
||||||
canManage := permCache.HasPermission(permissions.FixturesManage)
|
|
||||||
backURL := fmt.Sprintf("/seasons/%s/leagues/%s/fixtures", fixture.Season.ShortName, fixture.League.ShortName)
|
backURL := fmt.Sprintf("/seasons/%s/leagues/%s/fixtures", fixture.Season.ShortName, fixture.League.ShortName)
|
||||||
isFinalized := result != nil && result.Finalized
|
isFinalized := result != nil && result.Finalized
|
||||||
if activeTab == "" {
|
|
||||||
activeTab = "overview"
|
|
||||||
}
|
|
||||||
// Force overview if schedule tab is hidden (result finalized)
|
|
||||||
if isFinalized && activeTab == "schedule" {
|
|
||||||
activeTab = "overview"
|
|
||||||
}
|
|
||||||
// Redirect preview → analysis once finalized
|
|
||||||
if isFinalized && activeTab == "preview" {
|
|
||||||
activeTab = "analysis"
|
|
||||||
}
|
|
||||||
// Redirect analysis → preview if not finalized
|
|
||||||
if !isFinalized && activeTab == "analysis" {
|
|
||||||
activeTab = "preview"
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
@baseview.Layout(fmt.Sprintf("%s vs %s", fixture.HomeTeam.Name, fixture.AwayTeam.Name)) {
|
@baseview.Layout(fmt.Sprintf("%s vs %s", fixture.HomeTeam.Name, fixture.AwayTeam.Name)) {
|
||||||
<div class="max-w-screen-lg mx-auto px-4 py-8">
|
<div class="max-w-screen-lg mx-auto px-4 py-8">
|
||||||
@@ -81,29 +54,24 @@ templ FixtureDetailPage(
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Tab Navigation -->
|
<!-- Tab Navigation -->
|
||||||
<nav class="bg-surface0 border-b border-surface1">
|
<nav class="bg-surface0 border-b border-surface1" data-tab-nav="fixture-detail-content">
|
||||||
<ul class="flex flex-wrap">
|
<ul class="flex flex-wrap">
|
||||||
@fixtureTabItem("overview", "Overview", activeTab, fixture)
|
@fixtureTabItem("overview", "Overview", activeTab, fixture)
|
||||||
if isFinalized {
|
if isFinalized {
|
||||||
@fixtureTabItem("analysis", "Match Analysis", activeTab, fixture)
|
@fixtureTabItem("analysis", "Match Analysis", activeTab, fixture)
|
||||||
} else {
|
} else {
|
||||||
@fixtureTabItem("preview", "Match Preview", activeTab, fixture)
|
@fixtureTabItem("preview", "Match Preview", activeTab, fixture)
|
||||||
@fixtureTabItem("schedule", "Schedule", activeTab, fixture)
|
@fixtureTabItem("scheduling", "Schedule", activeTab, fixture)
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<!-- Tab Content -->
|
<!-- Content Area -->
|
||||||
if activeTab == "overview" {
|
<main id="fixture-detail-content">
|
||||||
@fixtureOverviewTab(fixture, currentSchedule, result, rosters, canManage, canSchedule, userTeamID, nominatedFreeAgents, availableFreeAgents)
|
{ children... }
|
||||||
} else if activeTab == "preview" && previewData != nil {
|
</main>
|
||||||
@fixtureMatchPreviewTab(fixture, rosters, previewData)
|
|
||||||
} else if activeTab == "analysis" && result != nil && result.Finalized {
|
|
||||||
@fixtureMatchAnalysisTab(fixture, result, rosters, previewData)
|
|
||||||
} else if activeTab == "schedule" {
|
|
||||||
@fixtureScheduleTab(fixture, currentSchedule, history, canSchedule, canManage, userTeamID)
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
|
<script src="/static/js/tabs.js" defer></script>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,14 +81,15 @@ templ fixtureTabItem(section string, label string, activeTab string, fixture *db
|
|||||||
baseClasses := "inline-block px-6 py-3 transition-colors cursor-pointer border-b-2"
|
baseClasses := "inline-block px-6 py-3 transition-colors cursor-pointer border-b-2"
|
||||||
activeClasses := "border-blue text-blue font-semibold"
|
activeClasses := "border-blue text-blue font-semibold"
|
||||||
inactiveClasses := "border-transparent text-subtext0 hover:text-text hover:border-surface2"
|
inactiveClasses := "border-transparent text-subtext0 hover:text-text hover:border-surface2"
|
||||||
url := fmt.Sprintf("/fixtures/%d", fixture.ID)
|
url := fmt.Sprintf("/fixtures/%d/%s", fixture.ID, section)
|
||||||
if section != "overview" {
|
|
||||||
url = fmt.Sprintf("/fixtures/%d?tab=%s", fixture.ID, section)
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
<li class="inline-block">
|
<li class="inline-block">
|
||||||
<a
|
<a
|
||||||
href={ templ.SafeURL(url) }
|
href={ templ.SafeURL(url) }
|
||||||
|
hx-post={ url }
|
||||||
|
hx-target="#fixture-detail-content"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-push-url={ url }
|
||||||
class={ baseClasses, templ.KV(activeClasses, isActive), templ.KV(inactiveClasses, !isActive) }
|
class={ baseClasses, templ.KV(activeClasses, isActive), templ.KV(inactiveClasses, !isActive) }
|
||||||
>
|
>
|
||||||
{ label }
|
{ label }
|
||||||
@@ -128,6 +97,107 @@ templ fixtureTabItem(section string, label string, activeTab string, fixture *db
|
|||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== Full page wrappers (for GET requests / direct navigation) ====================
|
||||||
|
|
||||||
|
templ FixtureDetailOverviewPage(
|
||||||
|
fixture *db.Fixture,
|
||||||
|
currentSchedule *db.FixtureSchedule,
|
||||||
|
canSchedule bool,
|
||||||
|
userTeamID int,
|
||||||
|
result *db.FixtureResult,
|
||||||
|
rosters map[string][]*db.PlayerWithPlayStatus,
|
||||||
|
nominatedFreeAgents []*db.FixtureFreeAgent,
|
||||||
|
availableFreeAgents []*db.SeasonLeagueFreeAgent,
|
||||||
|
) {
|
||||||
|
@FixtureDetailLayout("overview", fixture, result) {
|
||||||
|
@FixtureDetailOverviewContent(fixture, currentSchedule, canSchedule, userTeamID, result, rosters, nominatedFreeAgents, availableFreeAgents)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
templ FixtureDetailPreviewPage(
|
||||||
|
fixture *db.Fixture,
|
||||||
|
result *db.FixtureResult,
|
||||||
|
rosters map[string][]*db.PlayerWithPlayStatus,
|
||||||
|
previewData *db.MatchPreviewData,
|
||||||
|
) {
|
||||||
|
@FixtureDetailLayout("preview", fixture, result) {
|
||||||
|
@FixtureDetailPreviewContent(fixture, rosters, previewData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
templ FixtureDetailAnalysisPage(
|
||||||
|
fixture *db.Fixture,
|
||||||
|
result *db.FixtureResult,
|
||||||
|
rosters map[string][]*db.PlayerWithPlayStatus,
|
||||||
|
previewData *db.MatchPreviewData,
|
||||||
|
) {
|
||||||
|
@FixtureDetailLayout("analysis", fixture, result) {
|
||||||
|
@FixtureDetailAnalysisContent(fixture, result, rosters, previewData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
templ FixtureDetailSchedulePage(
|
||||||
|
fixture *db.Fixture,
|
||||||
|
currentSchedule *db.FixtureSchedule,
|
||||||
|
history []*db.FixtureSchedule,
|
||||||
|
canSchedule bool,
|
||||||
|
userTeamID int,
|
||||||
|
) {
|
||||||
|
@FixtureDetailLayout("scheduling", fixture, nil) {
|
||||||
|
@FixtureDetailScheduleContent(fixture, currentSchedule, history, canSchedule, userTeamID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== Tab content components (for POST requests / HTMX swaps) ====================
|
||||||
|
|
||||||
|
templ FixtureDetailOverviewContent(
|
||||||
|
fixture *db.Fixture,
|
||||||
|
currentSchedule *db.FixtureSchedule,
|
||||||
|
canSchedule bool,
|
||||||
|
userTeamID int,
|
||||||
|
result *db.FixtureResult,
|
||||||
|
rosters map[string][]*db.PlayerWithPlayStatus,
|
||||||
|
nominatedFreeAgents []*db.FixtureFreeAgent,
|
||||||
|
availableFreeAgents []*db.SeasonLeagueFreeAgent,
|
||||||
|
) {
|
||||||
|
{{
|
||||||
|
permCache := contexts.Permissions(ctx)
|
||||||
|
canManage := permCache.HasPermission(permissions.FixturesManage)
|
||||||
|
}}
|
||||||
|
@fixtureOverviewTab(fixture, currentSchedule, result, rosters, canManage, canSchedule, userTeamID, nominatedFreeAgents, availableFreeAgents)
|
||||||
|
}
|
||||||
|
|
||||||
|
templ FixtureDetailPreviewContent(
|
||||||
|
fixture *db.Fixture,
|
||||||
|
rosters map[string][]*db.PlayerWithPlayStatus,
|
||||||
|
previewData *db.MatchPreviewData,
|
||||||
|
) {
|
||||||
|
@fixtureMatchPreviewTab(fixture, rosters, previewData)
|
||||||
|
}
|
||||||
|
|
||||||
|
templ FixtureDetailAnalysisContent(
|
||||||
|
fixture *db.Fixture,
|
||||||
|
result *db.FixtureResult,
|
||||||
|
rosters map[string][]*db.PlayerWithPlayStatus,
|
||||||
|
previewData *db.MatchPreviewData,
|
||||||
|
) {
|
||||||
|
@fixtureMatchAnalysisTab(fixture, result, rosters, previewData)
|
||||||
|
}
|
||||||
|
|
||||||
|
templ FixtureDetailScheduleContent(
|
||||||
|
fixture *db.Fixture,
|
||||||
|
currentSchedule *db.FixtureSchedule,
|
||||||
|
history []*db.FixtureSchedule,
|
||||||
|
canSchedule bool,
|
||||||
|
userTeamID int,
|
||||||
|
) {
|
||||||
|
{{
|
||||||
|
permCache := contexts.Permissions(ctx)
|
||||||
|
canManage := permCache.HasPermission(permissions.FixturesManage)
|
||||||
|
}}
|
||||||
|
@fixtureScheduleTab(fixture, currentSchedule, history, canSchedule, canManage, userTeamID)
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== Overview Tab ====================
|
// ==================== Overview Tab ====================
|
||||||
templ fixtureOverviewTab(
|
templ fixtureOverviewTab(
|
||||||
fixture *db.Fixture,
|
fixture *db.Fixture,
|
||||||
|
|||||||
Reference in New Issue
Block a user