added free agents
This commit is contained in:
@@ -17,6 +17,8 @@ templ FixtureDetailPage(
|
||||
result *db.FixtureResult,
|
||||
rosters map[string][]*db.PlayerWithPlayStatus,
|
||||
activeTab string,
|
||||
nominatedFreeAgents []*db.FixtureFreeAgent,
|
||||
availableFreeAgents []*db.SeasonLeagueFreeAgent,
|
||||
) {
|
||||
{{
|
||||
permCache := contexts.Permissions(ctx)
|
||||
@@ -78,10 +80,10 @@ templ FixtureDetailPage(
|
||||
</nav>
|
||||
}
|
||||
</div>
|
||||
<!-- Tab Content -->
|
||||
if activeTab == "overview" {
|
||||
@fixtureOverviewTab(fixture, currentSchedule, result, rosters, canManage)
|
||||
} else if activeTab == "schedule" {
|
||||
<!-- Tab Content -->
|
||||
if activeTab == "overview" {
|
||||
@fixtureOverviewTab(fixture, currentSchedule, result, rosters, canManage, canSchedule, userTeamID, nominatedFreeAgents, availableFreeAgents)
|
||||
} else if activeTab == "schedule" {
|
||||
@fixtureScheduleTab(fixture, currentSchedule, history, canSchedule, canManage, userTeamID)
|
||||
}
|
||||
</div>
|
||||
@@ -116,6 +118,10 @@ templ fixtureOverviewTab(
|
||||
result *db.FixtureResult,
|
||||
rosters map[string][]*db.PlayerWithPlayStatus,
|
||||
canManage bool,
|
||||
canSchedule bool,
|
||||
userTeamID int,
|
||||
nominatedFreeAgents []*db.FixtureFreeAgent,
|
||||
availableFreeAgents []*db.SeasonLeagueFreeAgent,
|
||||
) {
|
||||
<div class="space-y-6">
|
||||
<!-- Result + Schedule Row -->
|
||||
@@ -135,6 +141,10 @@ templ fixtureOverviewTab(
|
||||
@fixtureScheduleSummary(fixture, currentSchedule, result)
|
||||
</div>
|
||||
</div>
|
||||
<!-- Free Agent Nominations (hidden when result is finalized) -->
|
||||
if (result == nil || !result.Finalized) && (canSchedule || canManage || len(nominatedFreeAgents) > 0) {
|
||||
@fixtureFreeAgentSection(fixture, canSchedule, canManage, userTeamID, nominatedFreeAgents, availableFreeAgents)
|
||||
}
|
||||
<!-- Team Rosters -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
@fixtureTeamSection(fixture.HomeTeam, rosters["home"], "home", result)
|
||||
@@ -400,6 +410,11 @@ templ fixtureTeamSection(team *db.Team, players []*db.PlayerWithPlayStatus, side
|
||||
★
|
||||
</span>
|
||||
}
|
||||
if p.IsFreeAgent {
|
||||
<span class="px-1.5 py-0.5 bg-peach/20 text-peach rounded text-xs font-medium">
|
||||
FREE AGENT
|
||||
</span>
|
||||
}
|
||||
</span>
|
||||
</td>
|
||||
if p.Stats != nil {
|
||||
@@ -456,6 +471,11 @@ templ fixtureTeamSection(team *db.Team, players []*db.PlayerWithPlayStatus, side
|
||||
★ Manager
|
||||
</span>
|
||||
}
|
||||
if p.IsFreeAgent {
|
||||
<span class="px-1.5 py-0.5 bg-peach/20 text-peach rounded text-xs font-medium">
|
||||
FREE AGENT
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@@ -482,6 +502,256 @@ templ fixtureTeamSection(team *db.Team, players []*db.PlayerWithPlayStatus, side
|
||||
</div>
|
||||
}
|
||||
|
||||
// ==================== Free Agent Section ====================
|
||||
templ fixtureFreeAgentSection(
|
||||
fixture *db.Fixture,
|
||||
canSchedule bool,
|
||||
canManage bool,
|
||||
userTeamID int,
|
||||
nominated []*db.FixtureFreeAgent,
|
||||
available []*db.SeasonLeagueFreeAgent,
|
||||
) {
|
||||
{{
|
||||
// Split nominated by team
|
||||
homeNominated := []*db.FixtureFreeAgent{}
|
||||
awayNominated := []*db.FixtureFreeAgent{}
|
||||
for _, n := range nominated {
|
||||
if n.TeamID == fixture.HomeTeamID {
|
||||
homeNominated = append(homeNominated, n)
|
||||
} else {
|
||||
awayNominated = append(awayNominated, n)
|
||||
}
|
||||
}
|
||||
|
||||
// Filter available: exclude already nominated players
|
||||
nominatedIDs := map[int]bool{}
|
||||
for _, n := range nominated {
|
||||
nominatedIDs[n.PlayerID] = true
|
||||
}
|
||||
filteredAvailable := []*db.SeasonLeagueFreeAgent{}
|
||||
for _, fa := range available {
|
||||
if !nominatedIDs[fa.PlayerID] {
|
||||
filteredAvailable = append(filteredAvailable, fa)
|
||||
}
|
||||
}
|
||||
|
||||
// Can the user nominate?
|
||||
canNominate := (canSchedule || canManage) && len(filteredAvailable) > 0
|
||||
}}
|
||||
<div class="bg-mantle border border-surface1 rounded-lg overflow-hidden" x-data="{ showNominateModal: false, selectedPlayerId: '', selectedTeamId: '' }">
|
||||
<div class="bg-surface0 border-b border-surface1 px-4 py-3 flex items-center justify-between">
|
||||
<h2 class="text-lg font-bold text-text">Free Agent Nominations</h2>
|
||||
if canNominate {
|
||||
<button
|
||||
@click="showNominateModal = true"
|
||||
class="rounded-lg px-3 py-1.5 hover:cursor-pointer text-center text-xs
|
||||
bg-peach hover:bg-peach/75 text-mantle transition"
|
||||
>
|
||||
Nominate Free Agent
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
<div class="p-4">
|
||||
if len(nominated) == 0 {
|
||||
<p class="text-subtext1 text-sm text-center py-2">No free agents nominated for this fixture.</p>
|
||||
} else {
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<!-- Home team nominations -->
|
||||
<div>
|
||||
<p class="text-xs text-subtext0 font-semibold uppercase mb-2">{ fixture.HomeTeam.Name }</p>
|
||||
if len(homeNominated) == 0 {
|
||||
<p class="text-subtext1 text-xs italic">None</p>
|
||||
} else {
|
||||
<div class="space-y-1">
|
||||
for _, n := range homeNominated {
|
||||
<div class="flex items-center justify-between px-2 py-1.5 rounded hover:bg-surface0 transition">
|
||||
<span class="flex items-center gap-2">
|
||||
<span class="text-sm text-text">{ n.Player.DisplayName() }</span>
|
||||
<span class="px-1.5 py-0.5 bg-peach/20 text-peach rounded text-xs font-medium">
|
||||
FA
|
||||
</span>
|
||||
</span>
|
||||
if canManage || (canSchedule && userTeamID == fixture.HomeTeamID) {
|
||||
<form
|
||||
hx-post={ fmt.Sprintf("/fixtures/%d/free-agents/%d/remove", fixture.ID, n.PlayerID) }
|
||||
hx-swap="none"
|
||||
class="inline"
|
||||
>
|
||||
<button
|
||||
type="submit"
|
||||
class="px-2 py-0.5 text-xs bg-red/20 hover:bg-red/40 text-red rounded
|
||||
transition hover:cursor-pointer"
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
</form>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<!-- Away team nominations -->
|
||||
<div>
|
||||
<p class="text-xs text-subtext0 font-semibold uppercase mb-2">{ fixture.AwayTeam.Name }</p>
|
||||
if len(awayNominated) == 0 {
|
||||
<p class="text-subtext1 text-xs italic">None</p>
|
||||
} else {
|
||||
<div class="space-y-1">
|
||||
for _, n := range awayNominated {
|
||||
<div class="flex items-center justify-between px-2 py-1.5 rounded hover:bg-surface0 transition">
|
||||
<span class="flex items-center gap-2">
|
||||
<span class="text-sm text-text">{ n.Player.DisplayName() }</span>
|
||||
<span class="px-1.5 py-0.5 bg-peach/20 text-peach rounded text-xs font-medium">
|
||||
FA
|
||||
</span>
|
||||
</span>
|
||||
if canManage || (canSchedule && userTeamID == fixture.AwayTeamID) {
|
||||
<form
|
||||
hx-post={ fmt.Sprintf("/fixtures/%d/free-agents/%d/remove", fixture.ID, n.PlayerID) }
|
||||
hx-swap="none"
|
||||
class="inline"
|
||||
>
|
||||
<button
|
||||
type="submit"
|
||||
class="px-2 py-0.5 text-xs bg-red/20 hover:bg-red/40 text-red rounded
|
||||
transition hover:cursor-pointer"
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
</form>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<!-- Nominate Modal -->
|
||||
if canNominate {
|
||||
<div
|
||||
x-show="showNominateModal"
|
||||
@keydown.escape.window="showNominateModal = false"
|
||||
class="fixed inset-0 z-50 overflow-y-auto"
|
||||
style="display: none;"
|
||||
>
|
||||
<div
|
||||
class="fixed inset-0 bg-crust/80 transition-opacity"
|
||||
@click="showNominateModal = false"
|
||||
></div>
|
||||
<div class="flex min-h-full items-center justify-center p-4">
|
||||
<div
|
||||
class="relative bg-mantle border-2 border-surface1 rounded-xl shadow-xl max-w-md w-full p-6"
|
||||
@click.stop
|
||||
>
|
||||
<h3 class="text-2xl font-bold text-text mb-4">Nominate Free Agent</h3>
|
||||
<form
|
||||
hx-post={ fmt.Sprintf("/fixtures/%d/free-agents/nominate", fixture.ID) }
|
||||
hx-swap="none"
|
||||
>
|
||||
if canManage && !canSchedule {
|
||||
<!-- Manager (not on either team): show team selector -->
|
||||
<div class="mb-4">
|
||||
<label for="fa_team_id" class="block text-sm font-medium mb-2">Nominating Team</label>
|
||||
<select
|
||||
id="fa_team_id"
|
||||
name="team_id"
|
||||
x-model="selectedTeamId"
|
||||
required
|
||||
class="w-full py-3 px-4 rounded-lg text-sm bg-base border-2 border-overlay0
|
||||
focus:border-blue outline-none"
|
||||
>
|
||||
<option value="">Choose a team...</option>
|
||||
<option value={ fmt.Sprint(fixture.HomeTeamID) }>
|
||||
{ fixture.HomeTeam.Name }
|
||||
</option>
|
||||
<option value={ fmt.Sprint(fixture.AwayTeamID) }>
|
||||
{ fixture.AwayTeam.Name }
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
} else if canManage && canSchedule {
|
||||
<!-- Manager who is also on a team: show team selector pre-filled -->
|
||||
<div class="mb-4">
|
||||
<label for="fa_team_id" class="block text-sm font-medium mb-2">Nominating Team</label>
|
||||
<select
|
||||
id="fa_team_id"
|
||||
name="team_id"
|
||||
x-model="selectedTeamId"
|
||||
x-init={ fmt.Sprintf("selectedTeamId = '%d'", userTeamID) }
|
||||
required
|
||||
class="w-full py-3 px-4 rounded-lg text-sm bg-base border-2 border-overlay0
|
||||
focus:border-blue outline-none"
|
||||
>
|
||||
<option value="">Choose a team...</option>
|
||||
<option value={ fmt.Sprint(fixture.HomeTeamID) }>
|
||||
{ fixture.HomeTeam.Name }
|
||||
</option>
|
||||
<option value={ fmt.Sprint(fixture.AwayTeamID) }>
|
||||
{ fixture.AwayTeam.Name }
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
} else {
|
||||
<!-- Regular team manager: fixed to their team -->
|
||||
<input type="hidden" name="team_id" value={ fmt.Sprint(userTeamID) }/>
|
||||
}
|
||||
<div class="mb-4">
|
||||
<label for="fa_player_id" class="block text-sm font-medium mb-2">Select Free Agent</label>
|
||||
<select
|
||||
id="fa_player_id"
|
||||
name="player_id"
|
||||
x-model="selectedPlayerId"
|
||||
required
|
||||
class="w-full py-3 px-4 rounded-lg text-sm bg-base border-2 border-overlay0
|
||||
focus:border-blue outline-none"
|
||||
>
|
||||
<option value="">Choose a free agent...</option>
|
||||
for _, fa := range filteredAvailable {
|
||||
<option value={ fmt.Sprint(fa.PlayerID) }>
|
||||
{ fa.Player.DisplayName() }
|
||||
</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex gap-3 justify-end">
|
||||
<button
|
||||
type="button"
|
||||
@click="showNominateModal = false"
|
||||
class="px-4 py-2 rounded-lg bg-surface0 hover:bg-surface1 text-text transition hover:cursor-pointer"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
if canManage {
|
||||
<button
|
||||
type="submit"
|
||||
:disabled="!selectedPlayerId || !selectedTeamId"
|
||||
class="px-4 py-2 rounded-lg bg-peach hover:bg-peach/75 text-mantle transition
|
||||
disabled:bg-peach/40 disabled:cursor-not-allowed hover:cursor-pointer"
|
||||
>
|
||||
Nominate
|
||||
</button>
|
||||
} else {
|
||||
<button
|
||||
type="submit"
|
||||
:disabled="!selectedPlayerId"
|
||||
class="px-4 py-2 rounded-lg bg-peach hover:bg-peach/75 text-mantle transition
|
||||
disabled:bg-peach/40 disabled:cursor-not-allowed hover:cursor-pointer"
|
||||
>
|
||||
Nominate
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
// ==================== Schedule Tab ====================
|
||||
templ fixtureScheduleTab(
|
||||
fixture *db.Fixture,
|
||||
|
||||
Reference in New Issue
Block a user