From 1dc399848904488954eeae479ca2746a22e6804b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20S=C3=B6derberg?= Date: Sun, 24 May 2026 18:21:33 +0200 Subject: [PATCH] Fixed live line and auto scrolling on courts page --- backend/app/routes/courts.py | 24 +++++---- frontend/src/pages/Courts.tsx | 95 ++++++++++++++++++++++++----------- 2 files changed, 80 insertions(+), 39 deletions(-) diff --git a/backend/app/routes/courts.py b/backend/app/routes/courts.py index 8b76e51..b491759 100644 --- a/backend/app/routes/courts.py +++ b/backend/app/routes/courts.py @@ -59,22 +59,24 @@ def get_court_schedule(court_id: int, db: Session = Depends(get_db)): .all() ) - schedule = [] - for m in matches: - schedule.append( + return { + "court": court.name, + "matches": [ { "id": m.id, - "tournament_id": m.tournament_id, + "tournament_id": m.tournament.id, "tournament_name": m.tournament.name, - "time": m.start_time.strftime("%H:%M") if m.start_time else "TBD", - "status": m.status, + "duration": m.tournament.duration, + "time": m.start_time.strftime("%H:%M") if m.start_time else None, + "status": m.status.value, "match_number": m.match_number, "p1": m.p1_team.name if m.p1_team else "TBD", "p2": m.p2_team.name if m.p2_team else "TBD", - "p1_sets": sum(1 for s in m.sets if s["p1"] > s["p2"]) if m.sets else 0, - "p2_sets": sum(1 for s in m.sets if s["p2"] > s["p1"]) if m.sets else 0, + "p1_sets": len([s for s in m.sets if s.get("p1", 0) > s.get("p2", 0)]), + "p2_sets": len([s for s in m.sets if s.get("p2", 0) > s.get("p1", 0)]), "ref_name": m.ref_team.name if m.ref_team else m.ref_label, } - ) - - return {"court": court.name, "matches": schedule} + for m in matches + if m.start_time + ], + } diff --git a/frontend/src/pages/Courts.tsx b/frontend/src/pages/Courts.tsx index 250da39..1aa939f 100644 --- a/frontend/src/pages/Courts.tsx +++ b/frontend/src/pages/Courts.tsx @@ -14,6 +14,7 @@ interface CourtMatch { tournament_id: string; tournament_name: string; time: string; + duration: number; status: string; match_number: number; p1: string; @@ -72,32 +73,65 @@ const CourtColumn = ({ courtId, onDelete, role }: { courtId: number, onDelete: ( void fetchSchedule(); }, [courtId]); + let isDayFinished = false; + let activeIndex = -1; + + if (data && data.matches.length > 0 && currentTime) { + const [currH, currM] = currentTime.split(':').map(Number); + const currTotalMins = currH * 60 + currM; + + for (let i = 0; i < data.matches.length; i++) { + const m = data.matches[i]; + if (!m.time) continue; + + const [mH, mM] = m.time.split(':').map(Number); + const mStartTotalMins = mH * 60 + mM; + const duration = m.duration || 30; + const mEndTotalMins = mStartTotalMins + duration; + + if (currTotalMins >= mStartTotalMins && currTotalMins < mEndTotalMins) { + activeIndex = i; + } + } + const lastMatch = data.matches[data.matches.length - 1]; + if (lastMatch.time) { + const [lastH, lastM] = lastMatch.time.split(':').map(Number); + const lastStartTotal = lastH * 60 + lastM; + const lastDuration = lastMatch.duration || 30; + const lastEndTotal = lastStartTotal + lastDuration; + + if (currTotalMins >= lastEndTotal) { + isDayFinished = true; + activeIndex = -1; + } + } + } + // AUTO-SCROLL LOGIC useEffect(() => { if (data && currentTime && !hasScrolled && scrollContainerRef.current) { setTimeout(() => { - const line = scrollContainerRef.current?.querySelector('.now-line'); - if (line) { - line.scrollIntoView({ behavior: 'smooth', block: 'start' }); - setHasScrolled(true); + const container = scrollContainerRef.current; + if (!container) return; + + if (isDayFinished) { + container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' }); + } else { + const line = container.querySelector('.now-line') as HTMLElement; + if (line) { + container.scrollTo({ + top: line.offsetTop - 20, + behavior: 'smooth' + }); + } } + setHasScrolled(true); }, 500); } - }, [data, currentTime, hasScrolled]); + }, [data, currentTime, hasScrolled, isDayFinished]); if (!data) return
; - // --- ACTIVE INDEX LOGIC --- - let activeIndex = -1; - if (currentTime) { - // Find the index of the most recently started match - for (let i = 0; i < data.matches.length; i++) { - if (data.matches[i].time <= currentTime) { - activeIndex = i; - } - } - } - return (
@@ -112,28 +146,22 @@ const CourtColumn = ({ courtId, onDelete, role }: { courtId: number, onDelete: ( )}
-
+
{data.matches.length === 0 && (
No matches
)} {data.matches.map((m, index) => { const isFinished = m.status === 'Finished'; - const isPastSlot = index < activeIndex; // Strictly before the active match - const isLive = index === activeIndex && !isFinished; // Currently active and incomplete + const isPastSlot = isDayFinished || (activeIndex !== -1 && index < activeIndex); + const isLive = !isDayFinished && index === activeIndex; + const showNowLine = !isDayFinished && ((activeIndex !== -1 && index === activeIndex) || (activeIndex === -1 && index === 0)); - // The line is drawn right above the active index (or index 0 if the day hasn't started) - const showNowLine = (activeIndex !== -1 && index === activeIndex) || (activeIndex === -1 && index === 0); - - // Styling configurations based on match state let borderStyle = 'border-zinc-200 dark:border-zinc-800 hover:-translate-y-1 hover:shadow-md'; let textOpacity = 'text-zinc-900 dark:text-white'; let pOpacity = 'text-zinc-800 dark:text-zinc-200'; - if (isFinished) { - borderStyle = 'border-orange-500/30 opacity-60 grayscale hover:opacity-100 hover:grayscale-0'; - textOpacity = 'text-zinc-500'; - } else if (isPastSlot) { + if (isFinished || isPastSlot || isDayFinished) { borderStyle = 'border-zinc-300 dark:border-zinc-700 opacity-50 grayscale hover:opacity-100 hover:grayscale-0'; textOpacity = 'text-zinc-500'; pOpacity = 'text-zinc-500'; @@ -147,7 +175,7 @@ const CourtColumn = ({ courtId, onDelete, role }: { courtId: number, onDelete: ( {/* --- THE --NOW-- LINE --- */} {showNowLine && ( -
+
Now
@@ -194,6 +222,17 @@ const CourtColumn = ({ courtId, onDelete, role }: { courtId: number, onDelete: ( ) })} + + {/* --- THE LINE AT THE VERY END --- */} + {isDayFinished && ( +
+
+
+ All Matched played for today +
+
+
+ )}
);