Minor bugfixes connected to the WO and dropped out functions.
This commit is contained in:
1 parent
364895fc84
commit
4f5d071eeb
4 files changed
+87
-26
No files matched your search
+14
-3
@@ -2,7 +2,7 @@
|
|||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from sqlalchemy.orm import Session, joinedload
|
from sqlalchemy.orm import Session, joinedload
|
||||||
from fastapi import HTTPException # <--- Add this at the top with your imports
|
from fastapi import HTTPException
|
||||||
|
|
||||||
from . import logic, models, schemas
|
from . import logic, models, schemas
|
||||||
|
|
||||||
@@ -28,7 +28,12 @@ def create_tournament(db: Session, data: schemas.TournamentCreate):
|
|||||||
duration=data.duration,
|
duration=data.duration,
|
||||||
type=data.type,
|
type=data.type,
|
||||||
)
|
)
|
||||||
new_t.teams = [models.Team(name=n) for n in data.teams]
|
|
||||||
|
new_t.teams = [
|
||||||
|
models.Team(name=nt.name, dropped_out=nt.dropped_out)
|
||||||
|
for nt in data.teams
|
||||||
|
]
|
||||||
|
|
||||||
new_t.courts = db.query(models.Court).filter(models.Court.id.in_(data.courts)).all()
|
new_t.courts = db.query(models.Court).filter(models.Court.id.in_(data.courts)).all()
|
||||||
|
|
||||||
db.add(new_t)
|
db.add(new_t)
|
||||||
@@ -160,9 +165,15 @@ def update_tournament_teams(db: Session, tournament_id: str, new_teams: list[sch
|
|||||||
db.delete(team)
|
db.delete(team)
|
||||||
db.flush()
|
db.flush()
|
||||||
|
|
||||||
t.teams = [models.Team(name=nt.name, tournament_id=t.id) for nt in new_teams]
|
t.teams = [
|
||||||
|
models.Team(name=nt.name, tournament_id=t.id, dropped_out=nt.dropped_out)
|
||||||
|
for nt in new_teams
|
||||||
|
]
|
||||||
db.flush()
|
db.flush()
|
||||||
_rebuild_structure(db, t)
|
_rebuild_structure(db, t)
|
||||||
|
for m in t.matches:
|
||||||
|
logic.check_and_apply_walkover(db, m)
|
||||||
|
|
||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(t)
|
db.refresh(t)
|
||||||
return t
|
return t
|
||||||
|
|||||||
@@ -148,14 +148,34 @@ def update_schedule_times(db: Session, t: models.Tournament):
|
|||||||
court_timers = {c.id: start_of_day for c in all_courts}
|
court_timers = {c.id: start_of_day for c in all_courts}
|
||||||
planned_finish_times = {}
|
planned_finish_times = {}
|
||||||
unscheduled: list[models.Match] = []
|
unscheduled: list[models.Match] = []
|
||||||
|
tournament_has_started = {
|
||||||
|
t_item.id: any(m.status == MatchStatus.FINISHED for m in t_item.matches)
|
||||||
|
for t_item in tournaments
|
||||||
|
}
|
||||||
|
|
||||||
for m in all_matches:
|
for m in all_matches:
|
||||||
|
is_locked = False
|
||||||
|
t_started = tournament_has_started[m.tournament_id]
|
||||||
|
|
||||||
if m.status == MatchStatus.FINISHED and m.start_time and m.court_id:
|
if m.status == MatchStatus.FINISHED and m.start_time and m.court_id:
|
||||||
|
is_locked = True
|
||||||
|
|
||||||
|
elif m.status == MatchStatus.PENDING and m.start_time and m.court_id:
|
||||||
|
if t_started:
|
||||||
|
is_locked = True
|
||||||
|
|
||||||
|
elif m.tournament_id != t.id and m.start_time and m.court_id:
|
||||||
|
if t_started:
|
||||||
|
is_locked = True
|
||||||
|
|
||||||
|
if is_locked and m.start_time and m.court_id:
|
||||||
fin = m.start_time + timedelta(minutes=m.tournament.duration)
|
fin = m.start_time + timedelta(minutes=m.tournament.duration)
|
||||||
planned_finish_times[m.id] = fin
|
planned_finish_times[m.id] = fin
|
||||||
if court_timers.get(m.court_id, start_of_day) < fin:
|
if court_timers.get(m.court_id, start_of_day) < fin:
|
||||||
court_timers[m.court_id] = fin
|
court_timers[m.court_id] = fin
|
||||||
else:
|
else:
|
||||||
|
m.start_time = None
|
||||||
|
m.court_id = None
|
||||||
unscheduled.append(m)
|
unscheduled.append(m)
|
||||||
|
|
||||||
tournament_match_counts = {t_item.id: 0 for t_item in tournaments}
|
tournament_match_counts = {t_item.id: 0 for t_item in tournaments}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class TeamSchema(BaseModel):
|
|||||||
model_config = ConfigDict(from_attributes=True)
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
class TeamUpdate(BaseModel):
|
class TeamUpdate(BaseModel):
|
||||||
id: int
|
id: int | str
|
||||||
name: str
|
name: str
|
||||||
dropped_out: bool = False
|
dropped_out: bool = False
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ class TournamentCreate(BaseModel):
|
|||||||
type: TournamentTypes
|
type: TournamentTypes
|
||||||
timestamp: datetime
|
timestamp: datetime
|
||||||
duration: int
|
duration: int
|
||||||
teams: list[str]
|
teams: list[TeamUpdate]
|
||||||
courts: list[int]
|
courts: list[int]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -169,7 +169,20 @@ export default function TournamentForm({ tournamentId, onSuccess, onDelete }: To
|
|||||||
onSuccess();
|
onSuccess();
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error("Save error:", err);
|
console.error("Save error:", err);
|
||||||
const msg = err?.response?.data?.detail || err?.detail || err?.message || "An error occurred while saving.";
|
let msg = "An error occurred while saving.";
|
||||||
|
if (err?.response?.data?.detail) {
|
||||||
|
const detail = err.response.data.detail;
|
||||||
|
if (Array.isArray(detail)) {
|
||||||
|
msg = detail.map((d: any) => `${d.loc[d.loc.length - 1]}: ${d.msg}`).join(' | ');
|
||||||
|
} else if (typeof detail === 'string') {
|
||||||
|
msg = detail;
|
||||||
|
} else {
|
||||||
|
msg = JSON.stringify(detail);
|
||||||
|
}
|
||||||
|
} else if (err?.message) {
|
||||||
|
msg = err.message;
|
||||||
|
}
|
||||||
|
|
||||||
setError(msg);
|
setError(msg);
|
||||||
} finally {
|
} finally {
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
@@ -292,26 +305,43 @@ export default function TournamentForm({ tournamentId, onSuccess, onDelete }: To
|
|||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{hasStarted ? (
|
<div className="flex items-center gap-1">
|
||||||
<button
|
{/* 1. If dropped out, ALWAYS show Restore */}
|
||||||
type="button"
|
{t.dropped_out && (
|
||||||
onClick={() => toggleDropOut(t.id)}
|
<button
|
||||||
className={`p-1 rounded transition opacity-0 group-hover:opacity-100 focus:opacity-100 ${t.dropped_out ? 'text-red-500 bg-red-50 dark:bg-red-900/30 opacity-100' : 'text-zinc-300 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/30'
|
type="button"
|
||||||
}`}
|
onClick={() => toggleDropOut(t.id)}
|
||||||
title={t.dropped_out ? "Restore Team" : "Mark as Dropped Out"}
|
className="p-1 rounded text-red-500 bg-red-50 dark:bg-red-900/30 opacity-100 transition focus:opacity-100"
|
||||||
>
|
title="Restore Team"
|
||||||
<UserMinus size={14} />
|
>
|
||||||
</button>
|
<UserMinus size={14} />
|
||||||
) : (
|
</button>
|
||||||
<button
|
)}
|
||||||
type="button"
|
|
||||||
onClick={() => removeTeam(t.id)}
|
{/* 2. If started and active, show Drop Out */}
|
||||||
className="p-1 text-zinc-300 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/30 rounded transition opacity-0 group-hover:opacity-100 focus:opacity-100"
|
{hasStarted && !t.dropped_out && (
|
||||||
title="Remove Team"
|
<button
|
||||||
>
|
type="button"
|
||||||
<Trash2 size={14} />
|
onClick={() => toggleDropOut(t.id)}
|
||||||
</button>
|
className="p-1 text-zinc-300 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/30 rounded transition opacity-0 group-hover:opacity-100 focus:opacity-100"
|
||||||
)}
|
title="Mark as Dropped Out"
|
||||||
|
>
|
||||||
|
<UserMinus size={14} />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 3. If NOT started, ALWAYS allow Deletion */}
|
||||||
|
{!hasStarted && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => removeTeam(t.id)}
|
||||||
|
className="p-1 text-zinc-300 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/30 rounded transition opacity-0 group-hover:opacity-100 focus:opacity-100"
|
||||||
|
title="Remove Team entirely"
|
||||||
|
>
|
||||||
|
<Trash2 size={14} />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{teams.length === 0 && (
|
{teams.length === 0 && (
|
||||||
|
|||||||
Reference in new issue
Block a user