General clean-up
This commit is contained in:
@@ -1,56 +1,13 @@
|
||||
// frontend/src/pages/Dashboard.jsx
|
||||
|
||||
import { Calendar, ChevronDown, ChevronUp, History, Plus, SlidersHorizontal, Users, MapPin, Clock } from 'lucide-react';
|
||||
import { Calendar, ChevronDown, ChevronUp, History, Plus } from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useNavigate, useOutletContext } from 'react-router-dom';
|
||||
import DashCard from '../components/Dashboard/DashCard';
|
||||
import TournamentForm from '../components/Forms/TournamentForm';
|
||||
import Modal from '../components/UI/Modal';
|
||||
import api, { WS_URL } from '../services/api';
|
||||
|
||||
// --- RESTORED DASHCARD DESIGN ---
|
||||
const DashCard = ({ t, isAdmin, onSelect, onEdit }) => (
|
||||
<div
|
||||
onClick={() => onSelect(t.id)}
|
||||
className="block bg-white dark:bg-zinc-900 p-6 rounded-xl shadow-sm border border-zinc-200 dark:border-zinc-800 relative group hover:shadow-md hover:scale-[1.02] transition-all duration-200 will-change-transform transform-gpu cursor-pointer"
|
||||
>
|
||||
<div className="flex justify-between items-start mb-4">
|
||||
<div className="min-w-0 pr-4">
|
||||
<h2 className="text-xl font-bold truncate text-zinc-900 dark:text-white group-hover:text-orange-500 dark:group-hover:text-orange-400 transition">
|
||||
{t.name}
|
||||
</h2>
|
||||
<div className="text-xs text-zinc-400 dark:text-zinc-500 mt-1 font-mono flex items-center gap-2">
|
||||
{/* Parse date safely */}
|
||||
<span>{t.timestamp ? new Date(t.timestamp).toLocaleDateString() : 'TBD'}</span>
|
||||
<span>{t.timestamp ? new Date(t.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) : ''}</span>
|
||||
</div>
|
||||
</div>
|
||||
<span className="bg-orange-100 dark:bg-orange-900/50 text-orange-700 dark:text-orange-300 text-[10px] px-2 py-1 rounded font-mono border border-orange-200 dark:border-orange-800 uppercase tracking-tight shrink-0">
|
||||
{t.type}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4 text-sm text-zinc-500 dark:text-zinc-400 items-center">
|
||||
<div className="flex items-center gap-1.5 font-medium">
|
||||
<Users size={16} className="text-orange-500" />
|
||||
{t.team_count} Teams
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 font-medium">
|
||||
<MapPin size={16} className="text-orange-500" />
|
||||
{t.court_count} Courts
|
||||
</div>
|
||||
|
||||
{isAdmin && (
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); onEdit(t); }}
|
||||
className="ml-auto hover:text-orange-500 transition z-10 h-8 w-8 flex items-center justify-center rounded-full text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800"
|
||||
title="Tournament Settings"
|
||||
>
|
||||
<SlidersHorizontal size={18} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
import { SlidersHorizontal, PlusCircle } from 'lucide-react';
|
||||
|
||||
export default function Dashboard() {
|
||||
const { setNavTitle, setNavSubtitle, isAdmin, showSettings, setShowSettings } = useOutletContext();
|
||||
@@ -140,8 +97,11 @@ export default function Dashboard() {
|
||||
|
||||
{groups.live.length > 0 && (
|
||||
<section>
|
||||
<h2 className="text-[10px] font-black text-green-500 uppercase tracking-[0.4em] mb-6 flex items-center gap-3">
|
||||
<div className="w-2.5 h-2.5 bg-green-500 rounded-full animate-ping shadow-lg shadow-green-500/50" /> Live Events
|
||||
<h2 className="text-xs font-black text-green-500 uppercase tracking-[0.2em] mb-6 flex items-center gap-3">
|
||||
<span class="relative flex size-3">
|
||||
<span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-green-400 opacity-75"></span>
|
||||
<span class="relative inline-flex size-3 rounded-full bg-green-500"></span>
|
||||
</span> Live Events
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{groups.live.map(t => <DashCard key={t.id} t={t} isAdmin={isAdmin} onSelect={(id) => navigate(`/tournaments/${id}`)} onEdit={handleEdit} />)}
|
||||
@@ -149,8 +109,8 @@ export default function Dashboard() {
|
||||
</section>
|
||||
)}
|
||||
|
||||
<section>
|
||||
<h2 className="text-[10px] font-black text-zinc-400 dark:text-zinc-600 uppercase tracking-[0.4em] mb-6 flex items-center gap-3">
|
||||
<section className="mb-10">
|
||||
<h2 className="text-xs font-black text-zinc-400 dark:text-zinc-600 uppercase tracking-[0.2em] mb-6 flex items-center gap-3">
|
||||
<Calendar size={18} /> Upcoming
|
||||
</h2>
|
||||
{groups.future.length > 0 ? (
|
||||
@@ -160,7 +120,7 @@ export default function Dashboard() {
|
||||
</div>
|
||||
{groups.future.length > 4 && (
|
||||
<div className="mt-8 text-center">
|
||||
<button onClick={() => setShowAllFuture(!showAllFuture)} className="text-[10px] font-black uppercase tracking-[0.2em] text-zinc-500 hover:text-orange-500 transition border-b-2 border-transparent hover:border-orange-500 pb-1 flex items-center justify-center gap-1 mx-auto">
|
||||
<button onClick={() => setShowAllFuture(!showAllFuture)} className="text-xs font-black uppercase tracking-[0.1em] text-zinc-500 hover:text-orange-500 transition border-b-2 border-transparent hover:border-orange-500 pb-1 flex items-center justify-center gap-1 mx-auto">
|
||||
{showAllFuture ? 'Show Less' : `Show All (${groups.future.length})`}
|
||||
{showAllFuture ? <ChevronUp size={14} /> : <ChevronDown size={14} />}
|
||||
</button>
|
||||
@@ -173,11 +133,11 @@ export default function Dashboard() {
|
||||
{groups.past.length > 0 && (
|
||||
<section>
|
||||
<button onClick={() => setShowPast(!showPast)} className="w-full flex items-center justify-between group py-6 border-t border-zinc-300 dark:border-zinc-800 transition-colors hover:border-zinc-400">
|
||||
<h2 className="text-[10px] font-black text-zinc-400 dark:text-zinc-600 uppercase tracking-[0.4em] mb-6 flex items-center gap-3"><History size={18} />Archive</h2>
|
||||
<h2 className="text-xs font-black text-zinc-400 dark:text-zinc-600 uppercase tracking-[0.2em] flex items-center gap-3"><History size={18} />Archive</h2>
|
||||
{showPast ? <ChevronUp size={20} /> : <ChevronDown size={20} />}
|
||||
</button>
|
||||
{showPast && (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4 opacity-75 hover:opacity-100 transition-opacity">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 opacity-75 hover:opacity-100 transition-opacity">
|
||||
{groups.past.map(t => <DashCard key={t.id} t={t} isAdmin={isAdmin} onSelect={(id) => navigate(`/tournaments/${id}`)} onEdit={handleEdit} />)}
|
||||
</div>
|
||||
)}
|
||||
@@ -185,7 +145,7 @@ export default function Dashboard() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Modal isOpen={showSettings} onClose={() => { setShowSettings(false); setEditTarget(null); }} title={editTarget ? 'Modify Event' : 'Initialize Event'}>
|
||||
<Modal isOpen={showSettings} onClose={() => { setShowSettings(false); setEditTarget(null); }} title={editTarget ? 'Tournament Settings' : 'New Tournament'} icon={editTarget ? SlidersHorizontal : PlusCircle}>
|
||||
<TournamentForm
|
||||
tournamentId={editTarget?.id}
|
||||
onSuccess={handleSuccess}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { Loader2, Volleyball } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import api from '../services/api';
|
||||
|
||||
export default function Login() {
|
||||
@@ -37,7 +37,7 @@ export default function Login() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 className="text-2xl font-black text-center text-zinc-900 dark:text-white tracking-tight mb-2">System Access</h1>
|
||||
<h1 className="text-2xl font-black text-center text-zinc-900 dark:text-white tracking-tight mb-2">Admin Access</h1>
|
||||
<p className="text-center text-zinc-500 text-sm font-medium mb-8">Enter administrative credentials</p>
|
||||
|
||||
{error && (
|
||||
@@ -48,12 +48,12 @@ export default function Login() {
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
<div className="space-y-2">
|
||||
<label className="text-[10px] font-black uppercase tracking-[0.2em] text-zinc-500">Identity</label>
|
||||
<input name="username" placeholder="Admin UID" required className="w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 p-4 rounded-2xl dark:text-white outline-none focus:border-orange-500 transition font-bold" />
|
||||
<label className="text-[10px] font-black uppercase tracking-[0.2em] text-zinc-500">Username</label>
|
||||
<input name="username" placeholder="Username" required className="w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 p-4 rounded-2xl dark:text-white outline-none focus:border-orange-500 transition font-bold" />
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-[10px] font-black uppercase tracking-[0.2em] text-zinc-500">Secret Key</label>
|
||||
<label className="text-[10px] font-black uppercase tracking-[0.2em] text-zinc-500">Password</label>
|
||||
<input name="password" type="password" placeholder="••••••••" required className="w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 p-4 rounded-2xl dark:text-white outline-none focus:border-orange-500 transition font-bold" />
|
||||
</div>
|
||||
|
||||
@@ -68,7 +68,9 @@ export default function Login() {
|
||||
</div>
|
||||
|
||||
<div className="bg-zinc-50 dark:bg-zinc-950/50 p-4 text-center border-t border-zinc-200 dark:border-zinc-800">
|
||||
<a href="/" className="text-[10px] font-black uppercase tracking-widest text-zinc-400 hover:text-orange-600 transition">Back to Dashboard</a>
|
||||
<Link to="/" className="text-[10px] font-black uppercase tracking-widest text-zinc-400 hover:text-orange-600 transition">
|
||||
Back to Dashboard
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// frontend/src/pages/Tournament.jsx
|
||||
|
||||
import { CalendarDays, Loader2, Network, Settings } from 'lucide-react';
|
||||
import { CalendarDays, Loader2, Network, SlidersHorizontal } from 'lucide-react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useOutletContext, useParams } from 'react-router-dom';
|
||||
import BracketView from '../components/Bracket/BracketView';
|
||||
@@ -53,6 +53,7 @@ export default function Tournament() {
|
||||
const hasTeams = !!(m.p1_team_id && m.p2_team_id);
|
||||
const isFinished = m.status === "Finished";
|
||||
const isReady = hasTeams && !isFinished;
|
||||
const winnerName = m.winner_team_id ? teamMap[m.winner_team_id] : null;
|
||||
|
||||
return {
|
||||
...m,
|
||||
@@ -61,6 +62,7 @@ export default function Tournament() {
|
||||
number: m.match_number,
|
||||
p1,
|
||||
p2,
|
||||
winnerName,
|
||||
p1_is_real: !!m.p1_team_id,
|
||||
p2_is_real: !!m.p2_team_id,
|
||||
isReady,
|
||||
@@ -109,8 +111,8 @@ export default function Tournament() {
|
||||
if (loading) return <div className="flex h-full items-center justify-center"><Loader2 className="animate-spin text-orange-600" size={48} /></div>;
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
<div className="border-b border-zinc-200 dark:border-zinc-800 bg-white/50 dark:bg-zinc-900/50 backdrop-blur px-6 py-3 flex justify-between items-center shrink-0 z-20">
|
||||
<div className="h-full flex flex-col print:h-auto print:block">
|
||||
<div className="border-b border-zinc-200 dark:border-zinc-800 bg-white/50 dark:bg-zinc-900/50 backdrop-blur px-6 py-3 flex justify-between items-center shrink-0 z-20 print:hidden">
|
||||
<div className="flex gap-2">
|
||||
<button onClick={() => handleViewChange('bracket')} className={`flex items-center gap-2 px-4 py-2 rounded-xl text-xs font-black uppercase tracking-wider transition ${view === 'bracket' ? 'bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-400' : 'text-zinc-500 hover:bg-zinc-100 dark:hover:bg-zinc-800'}`}>
|
||||
<Network size={16} /> Bracket
|
||||
@@ -119,10 +121,10 @@ export default function Tournament() {
|
||||
<CalendarDays size={16} /> Schedule
|
||||
</button>
|
||||
</div>
|
||||
{isAdmin && <button onClick={() => setShowSettings(true)} className="p-2 text-zinc-400 hover:text-orange-600 transition rounded-lg hover:bg-zinc-100 dark:hover:bg-zinc-800"><Settings size={20} /></button>}
|
||||
{isAdmin && <button onClick={() => setShowSettings(true)} className="p-2 text-zinc-400 hover:text-orange-600 transition rounded-lg hover:bg-zinc-100 dark:hover:bg-zinc-800"><SlidersHorizontal size={20} /></button>}
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-hidden relative">
|
||||
<div className="flex-1 overflow-hidden relative print:overflow-visible print:h-auto print:block">
|
||||
{view === 'bracket'
|
||||
? <BracketView matches={matches} onMatchClick={setScoreMatch} />
|
||||
: <ScheduleView schedule={matches} onMatchClick={setScoreMatch} />
|
||||
@@ -137,11 +139,7 @@ export default function Tournament() {
|
||||
/>
|
||||
)}
|
||||
<Modal isOpen={showSettings} onClose={() => setShowSettings(false)} title="Edit Tournament">
|
||||
<TournamentForm
|
||||
tournamentId={id}
|
||||
onSuccess={() => { setShowSettings(false); fetchData(); }}
|
||||
onDelete={handleDeleteTournament}
|
||||
/>
|
||||
<TournamentForm tournamentId={id} onSuccess={() => { setShowSettings(false); fetchData(); }} onDelete={handleDeleteTournament} />
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user