114 lines
8.2 KiB
TypeScript
114 lines
8.2 KiB
TypeScript
// frontend/src/components/Schedule/ScheduleRow.tsx
|
|
|
|
import { CheckCircle, Pencil, Plus, Trophy } from 'lucide-react';
|
|
import React from 'react';
|
|
import { type MatchData } from '../../types';
|
|
import { printName, stringToColor } from '../../utils/helpers';
|
|
import WhistleIcon from "../../assets/whistle.svg?react"
|
|
|
|
interface ScheduleRowProps {
|
|
match: MatchData;
|
|
onMatchClick: (match: MatchData) => void;
|
|
badgeWidth: number;
|
|
}
|
|
|
|
export default function ScheduleRow({ match: m, onMatchClick, badgeWidth }: ScheduleRowProps) {
|
|
const courtColor = stringToColor(m.court);
|
|
const isFinished = m.isFinished;
|
|
const refName = m.ref_team?.name || m.ref_label;
|
|
|
|
return (
|
|
<div className="relative mb-5 transition-colors bg-white dark:bg-zinc-900 print:bg-white! p-4 print:p-3 rounded-2xl print:rounded-none border border-zinc-300 dark:border-zinc-800 print:border-b! print:border-x-0! print:border-t-0! print:border-zinc-300! shadow-sm print:shadow-none! flex items-center justify-between hover:border-orange-500/30">
|
|
<div className="flex gap-4 md:gap-6 print:gap-6 flex-1 min-w-0">
|
|
<div className="flex flex-col gap-1 items-center shrink-0" style={{ minWidth: badgeWidth }}>
|
|
<div className="text-lg md:text-xl print:text-xl font-black font-mono text-zinc-900 dark:text-white print:text-black!">{m.time}</div>
|
|
<div className="text-tiny font-black text-white print:text-zinc-800! px-2 py-1 rounded uppercase w-full truncate text-center print:border! print:border-zinc-400! print:bg-transparent!" style={{ background: courtColor }}>
|
|
{m.court}
|
|
</div>
|
|
<div className="transition-colors block md:hidden print:hidden text-tiny font-black bg-zinc-100 dark:bg-zinc-800 w-full truncate text-center print:bg-transparent! text-zinc-400 print:text-zinc-500! px-2 py-1 rounded">Match #{m.number}</div>
|
|
</div>
|
|
|
|
<div className="flex-1 flex flex-col gap-0.5 min-w-0 justify-center md:justify-between">
|
|
<div className="flex flex-col md:flex-row md:items-end print:flex-row print:items-center gap-1 md:gap-2 print:gap-2">
|
|
{[{ n: m.p1, win: m.winnerName === m.p1, real: m.p1_is_real },
|
|
{ n: m.p2, win: m.winnerName === m.p2, real: m.p2_is_real }].map((p, i) => (
|
|
<React.Fragment key={i}>
|
|
<div className="flex items-center gap-2 min-w-0">
|
|
{p.win && <Trophy size={14} className="text-orange-500 shrink-0 print:hidden" />}
|
|
<span className={`block print:hidden truncate text-sm md:text-base font-bold ${p.win ? 'text-orange-600' : p.real ? 'text-zinc-900 dark:text-zinc-100' : 'text-zinc-400 italic font-normal'}`}>{p.n}</span>
|
|
<span className={`hidden print:inline-block print:whitespace-normal print:overflow-visible print:text-base font-bold ${p.real ? 'print:text-black!' : 'print:text-zinc-600! italic font-normal'}`}>
|
|
{printName(p.n)}
|
|
</span>
|
|
</div>
|
|
{i === 0 && <span className="block print:block text-zinc-300 dark:text-zinc-700 print:text-zinc-400! text-xs font-black md:pb-0.5">VS</span>}
|
|
</React.Fragment>
|
|
))}
|
|
</div>
|
|
|
|
<div className="flex flex-col md:flex-row md:items-center gap-0.5 md:gap-2 mt-0.5 md:mt-1">
|
|
<div className="transition-colors hidden md:block print:block text-tiny font-black bg-zinc-100 dark:bg-zinc-800 print:bg-transparent! text-zinc-400 print:text-zinc-500! px-2 py-1 rounded w-fit">Match #{m.number}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* ACTION BUTTON AREA */}
|
|
<div className="ml-3 flex items-stretch shrink-0 print:hidden py-0.5">
|
|
{isFinished ? (
|
|
<button
|
|
onClick={() => onMatchClick(m)}
|
|
className="flex flex-col justify-between items-center md:justify-center md:items-end hover:bg-zinc-50 dark:hover:bg-zinc-800 p-1.5 md:p-2 rounded-xl transition group/btn min-w-8 md:min-w-20 border border-transparent hover:border-zinc-200 dark:hover:border-zinc-700"
|
|
title="Edit Score"
|
|
>
|
|
<div className={`${m.winnerName === m.p1 ? 'bg-orange-100 dark:bg-orange-900/30 text-orange-700' : 'bg-zinc-100 dark:bg-zinc-800 text-zinc-900 dark:text-zinc-50'} px-2 py-0.5 rounded text-[10px] font-black font-mono border border-zinc-200 dark:border-zinc-700 md:hidden`}>
|
|
{m.p1_sets}
|
|
</div>
|
|
<div className="w-0.5 h-3 bg-zinc-200 dark:bg-zinc-700 rounded-full md:hidden my-1" />
|
|
<div className={`${m.winnerName === m.p2 ? 'bg-orange-100 dark:bg-orange-900/30 text-orange-700' : 'bg-zinc-100 dark:bg-zinc-800 text-zinc-900 dark:text-zinc-50'} px-2 py-0.5 rounded text-[10px] font-black font-mono border border-zinc-200 dark:border-zinc-700 md:hidden`}>
|
|
{m.p2_sets}
|
|
</div>
|
|
|
|
<div className="hidden md:flex group-hover/btn:hidden flex-col items-end">
|
|
<div className="text-orange-500 font-black text-[10px] uppercase flex items-center gap-1">
|
|
<CheckCircle size={12} strokeWidth={3} /> Finished
|
|
</div>
|
|
<div className="text-sm font-black font-mono text-zinc-900 dark:text-zinc-300">
|
|
{m.p1_sets} - {m.p2_sets}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="hidden md:group-hover/btn:flex flex-col items-end text-zinc-500 dark:text-zinc-400 animate-in fade-in zoom-in duration-200">
|
|
<div className="text-[10px] font-black uppercase flex items-center gap-1">
|
|
<Pencil size={12} strokeWidth={3} /> Edit
|
|
</div>
|
|
<div className="text-sm font-black font-mono">
|
|
{m.p1_sets} - {m.p2_sets}
|
|
</div>
|
|
</div>
|
|
</button>
|
|
) : m.isReady && (
|
|
<button onClick={() => onMatchClick(m)} className="bg-orange-600 hover:bg-orange-500 text-white p-2 md:px-4 md:py-2 rounded-xl shadow-lg active:scale-95 transition-all flex items-center gap-2 group/btn self-center">
|
|
<Plus size={18} strokeWidth={3} className="group-hover/btn:rotate-90 transition-transform duration-200" />
|
|
<span className="text-xs font-bold uppercase hidden md:inline">Report score</span>
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
<div className="hidden print:flex items-center gap-4 shrink-0 ml-6">
|
|
<div className="w-8 h-8 border-[1.5px] border-zinc-400 rounded-md flex items-center justify-center font-bold text-sm text-black">
|
|
{isFinished ? m.p1_sets : ''}
|
|
</div>
|
|
<span className="text-zinc-400 font-bold">-</span>
|
|
<div className="w-8 h-8 border-[1.5px] border-zinc-400 rounded-md flex items-center justify-center font-bold text-sm text-black">
|
|
{isFinished ? m.p2_sets : ''}
|
|
</div>
|
|
</div>
|
|
|
|
{refName && !isFinished && (
|
|
<div className="absolute -bottom-3 left-1/2 -translate-x-1/2 bg-zinc-100 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 text-zinc-500 dark:text-zinc-300 text-[9px] md:text-[10px] font-black uppercase tracking-widest px-3 md:px-4 py-1 rounded-full shadow-md whitespace-nowrap z-20 flex items-center gap-1.5 print:hidden max-w-[90%]">
|
|
<WhistleIcon className="text-orange-500 shrink-0" width={12} height={12} />
|
|
<span className="truncate">{refName}</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
} |