Label fixes, Websocket reconnects and more detailed court view cards.

This commit is contained in:
2026-06-14 20:57:19 +02:00 Verified
parent b904879326
commit 55a13934ef
8 changed files with 222 additions and 76 deletions
@@ -1,7 +1,7 @@
// frontend/src/components/Tournament/ScoreModal.tsx
import { type SetData } from '../../types';
import { Clock, Eraser, Trophy } from 'lucide-react';
import { Clock, Eraser, Trophy, Loader2 } from 'lucide-react';
import { useState } from 'react';
import Modal from '../UI/Modal';
import WhistleIcon from "../../assets/whistle.svg?react"
@@ -33,14 +33,35 @@ const ScoreForm = ({ match, isAuthenticated, onSubmit, onClear }: ScoreFormProps
const [sets, setSets] = useState<SetData[]>(match.sets && match.sets.length ? match.sets : [{ p1: '', p2: '' }]);
const [code, setCode] = useState<string>('');
const [error, setError] = useState<string | null>(null);
const [isSubmitting, setIsSubmitting] = useState<boolean>(false); // <-- NEW: Lock state
const refName = match.ref_team?.name || match.ref_label;
const handleSubmit = async () => {
if (isSubmitting) return; // Prevent double-execution
setIsSubmitting(true);
setError(null);
try {
await onSubmit(match.id, sets, code);
} catch (err: unknown) {
const error = err as { detail?: string };
setError(typeof error?.detail === 'string' ? error.detail : "Check code or scores");
setIsSubmitting(false); // Only re-enable the button if it failed (if it succeeds, modal closes)
}
};
const handleClear = async () => {
if (isSubmitting) return; // Prevent double-execution
setIsSubmitting(true);
setError(null);
try {
await onClear(match.id, code);
} catch (err: unknown) {
const error = err as { detail?: string };
setError(typeof error?.detail === 'string' ? error.detail : "Failed to clear score");
setIsSubmitting(false); // Re-enable on error
}
};
@@ -72,9 +93,15 @@ const ScoreForm = ({ match, isAuthenticated, onSubmit, onClear }: ScoreFormProps
{/* Dedicated Ref Row (Always visible) */}
{refName && (
<div className="flex justify-center items-center gap-2 bg-orange-50 dark:bg-orange-900/10 p-2.5 rounded-lg border border-orange-100 dark:border-orange-900/30">
<WhistleIcon className="text-orange-500 shrink-0" width={14} height={14} />
<span className="text-xs font-bold text-orange-700 dark:text-orange-500 uppercase tracking-wide truncate">
<div className={`flex justify-center items-center gap-2 p-2.5 rounded-lg border transition-colors ${match.ref_team
? 'bg-orange-50 dark:bg-orange-900/10 border-orange-100 dark:border-orange-900/30'
: 'bg-zinc-50 dark:bg-zinc-950 border-gray-200 dark:border-zinc-800'
}`}>
<WhistleIcon className={`${match.ref_team ? 'text-orange-500' : 'text-zinc-400 dark:text-zinc-600'} shrink-0`} width={14} height={14} />
<span className={`truncate ${match.ref_team
? 'text-xs font-bold text-orange-700 dark:text-orange-500 uppercase tracking-wide'
: 'text-sm italic text-zinc-500 dark:text-zinc-400 font-medium'
}`}>
{refName}
</span>
</div>
@@ -147,7 +174,6 @@ const ScoreForm = ({ match, isAuthenticated, onSubmit, onClear }: ScoreFormProps
className="w-full bg-gray-50 dark:bg-zinc-950 border border-gray-300 dark:border-zinc-800 p-2 rounded text-center focus:border-orange-500 outline-none text-zinc-900 dark:text-white"
/>
</div>
</div>
</div>
))}
@@ -158,17 +184,24 @@ const ScoreForm = ({ match, isAuthenticated, onSubmit, onClear }: ScoreFormProps
<div className="flex gap-2">
{match.isFinished && (
<button
onClick={() => onClear(match.id, code)}
className="w-1/3 bg-red-100 dark:bg-red-900/50 hover:bg-red-200 dark:hover:bg-red-900 text-red-600 dark:text-red-300 py-3 rounded-lg font-bold transition text-sm"
onClick={handleClear}
disabled={isSubmitting}
className={`w-1/3 bg-red-100 dark:bg-red-900/50 hover:bg-red-200 dark:hover:bg-red-900 text-red-600 dark:text-red-300 py-3 rounded-lg font-bold transition text-sm flex justify-center items-center ${isSubmitting ? 'opacity-50 cursor-not-allowed' : ''}`}
>
Clear
{isSubmitting ? <Loader2 size={16} className="animate-spin" /> : 'Clear'}
</button>
)}
<button
onClick={handleSubmit}
className={`${match.isFinished ? 'w-2/3' : 'w-full'} bg-orange-600 hover:bg-orange-500 py-3 rounded-lg font-bold shadow-lg shadow-orange-900/20 transition text-white active:scale-95`}
disabled={isSubmitting}
className={`${match.isFinished ? 'w-2/3' : 'w-full'} bg-orange-600 hover:bg-orange-500 py-3 rounded-lg font-bold shadow-lg shadow-orange-900/20 transition text-white active:scale-95 flex justify-center items-center gap-2 ${isSubmitting ? 'opacity-70 cursor-not-allowed' : ''}`}
>
Submit Result
{isSubmitting ? (
<>
<Loader2 size={18} className="animate-spin" />
<span>Saving...</span>
</>
) : 'Submit Result'}
</button>
</div>
</div>