Pageview beacony uz neposielaju player_id z klienta (nedovereny vstup na neautentifikovanom endpointe) -- prihlasenie sa eviduje server-side ako event "login" (aj z registracie), s player_id skutocneho uctu. Admin dashboard dostal prepinac scope vsetci/prihlaseni, ktory konzistentne pocita grafy aj tabulky (kazdy login samostatne, navstevnicky den pre anonymnu navstevnost). PageView.country teraz uklada cely anglicky nazov krajiny namiesto ISO kodu (potrebna zmena schemy). Pridane sledovanie klikov na "Pravidla hry" aj z GameList. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
106 lines
3.9 KiB
TypeScript
106 lines
3.9 KiB
TypeScript
import { useState } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { useGameStore } from '../store/gameStore';
|
|
import { emit, socket, setAuthToken } from '../lib/socket';
|
|
import { trackEvent } from '../lib/track';
|
|
import NameModal from '../components/NameModal';
|
|
import RulesModal from '../components/RulesModal';
|
|
|
|
export default function GameList() {
|
|
const navigate = useNavigate();
|
|
const games = useGameStore((s) => s.games);
|
|
const account = useGameStore((s) => s.account);
|
|
const [showCreate, setShowCreate] = useState(false);
|
|
const [showRules, setShowRules] = useState(false);
|
|
|
|
const handleShowRules = () => {
|
|
setShowRules(true);
|
|
trackEvent('rules_view');
|
|
};
|
|
|
|
const handleLogout = () => {
|
|
localStorage.removeItem('bridzik_token');
|
|
localStorage.removeItem('bridzik_player');
|
|
setAuthToken(null);
|
|
useGameStore.getState().logout();
|
|
// Drop the authenticated server-side session for this connection.
|
|
socket.disconnect();
|
|
socket.connect();
|
|
navigate('/auth', { replace: true });
|
|
};
|
|
|
|
return (
|
|
<div className="max-w-md mx-auto p-4 pt-8 min-h-screen">
|
|
<div className="flex items-center justify-between mb-6">
|
|
<h1 className="font-serif text-2xl text-gold tracking-wide">Bridžik</h1>
|
|
<div className="flex items-center gap-3 text-sm">
|
|
<span className="text-green-dim">{account?.username}</span>
|
|
<button onClick={() => navigate('/history')} className="text-gold hover:text-gold-bright">
|
|
História
|
|
</button>
|
|
<button onClick={handleLogout} className="text-green-dim hover:text-gold">
|
|
Odhlásiť
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex flex-col gap-3 mb-6">
|
|
{games.length === 0 && (
|
|
<p className="text-center text-green-dim py-4">Žiadne hry. Vytvor prvú!</p>
|
|
)}
|
|
{games.map((g) => {
|
|
const full = g.players.length >= 4;
|
|
const isMember =
|
|
!!account && g.players.some((p) => p.player_id === account.player_id);
|
|
const canResume = g.started && isMember;
|
|
const unavailable = !canResume && (full || g.started);
|
|
const label = canResume ? 'Pokračovať' : full ? 'Plná' : g.started ? 'Začatá' : 'Vstúp';
|
|
return (
|
|
<div
|
|
key={g.gid}
|
|
className="flex items-center justify-between bg-header border border-[#142018] rounded-xl px-4 py-3"
|
|
>
|
|
<div>
|
|
<p className="font-serif text-green-score">{g.name}</p>
|
|
<p className="text-xs text-green-dim">
|
|
{g.players.length}/4 hráčov
|
|
{g.started ? ' · začatá' : ''}
|
|
</p>
|
|
</div>
|
|
<button
|
|
disabled={unavailable}
|
|
onClick={() => (canResume ? emit.rejoinGame(g.gid) : emit.registerPlayer(g.gid))}
|
|
className={`px-4 py-1.5 rounded-lg text-sm font-serif font-semibold disabled:opacity-40 disabled:cursor-default transition-colors ${
|
|
canResume
|
|
? 'bg-gold text-table hover:bg-gold-bright'
|
|
: 'border border-gold/40 text-gold hover:bg-gold hover:text-table'
|
|
}`}
|
|
>
|
|
{label}
|
|
</button>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
<button
|
|
onClick={() => setShowCreate(true)}
|
|
className="w-full py-2 rounded-xl bg-gold text-table font-serif font-semibold text-base mb-3 hover:bg-gold-bright transition-colors"
|
|
>
|
|
Vytvoriť novú hru
|
|
</button>
|
|
|
|
<button
|
|
onClick={handleShowRules}
|
|
className="w-full py-2 rounded-xl border border-gold/20 text-sm text-green-score hover:border-gold/50 transition-colors"
|
|
>
|
|
Pravidlá hry
|
|
</button>
|
|
|
|
{showRules && <RulesModal onClose={() => setShowRules(false)} />}
|
|
|
|
{showCreate && <NameModal onClose={() => setShowCreate(false)} />}
|
|
</div>
|
|
);
|
|
}
|