import React, { useEffect, useState } from 'react'; import Dialog from '@mui/material/Dialog'; import { createTheme, ThemeProvider } from '@mui/material/styles'; const darkTheme = createTheme({ palette: { mode: 'dark' } }); const DIALOG_SX = { '& .MuiDialog-paper': { background: '#07090d', backgroundImage: ` linear-gradient(rgba(35, 111, 135, 0.08) 1px, transparent 1px), linear-gradient(90deg, rgba(35, 111, 135, 0.08) 1px, transparent 1px) `, backgroundSize: '46px 46px', border: '1px solid rgba(35, 111, 135, 0.4)', borderRadius: '12px', boxShadow: '0 0 80px rgba(35, 111, 135, 0.15), 0 32px 80px rgba(0,0,0,0.9)', width: '580px', maxWidth: '94vw', overflow: 'hidden', color: '#fff', }, '& .MuiBackdrop-root': { background: 'rgba(2, 4, 8, 0.88)', backdropFilter: 'blur(4px)', }, }; const RESULT_META = { win: { label: 'Victory', color: '#5ee89a', bg: 'rgba(42,158,96,0.15)', border: 'rgba(42,158,96,0.4)', icon: 'fa-trophy', }, loss: { label: 'Defeated', color: '#f67d52', bg: 'rgba(173,10,5,0.15)', border: 'rgba(173,10,5,0.4)', icon: 'fa-flag', }, draw: { label: 'Draw', color: '#95cff5', bg: 'rgba(149,207,245,0.1)', border: 'rgba(149,207,245,0.3)', icon: 'fa-minus', }, }; function Avatar({ name, color, avatarUrl, bonusPoints = 0 }) { const isRed = 'red' === color; const initials = (name || '?').slice(0, 2).toUpperCase(); const gradient = isRed ? 'linear-gradient(135deg, rgba(173,10,5,0.6) 0%, rgba(246,125,82,0.4) 100%)' : 'linear-gradient(135deg, rgba(35,111,135,0.6) 0%, rgba(41,128,185,0.4) 100%)'; const glow = isRed ? '0 0 0 3px rgba(173,10,5,0.2), 0 0 28px rgba(173,10,5,0.35)' : '0 0 0 3px rgba(35,111,135,0.2), 0 0 28px rgba(35,111,135,0.35)'; const border = isRed ? 'rgba(173,10,5,0.5)' : 'rgba(35,111,135,0.5)'; const textColor = isRed ? '#f67d52' : '#95cff5'; return (
{avatarUrl ? ( {name} ) : ( initials )}
{0 < bonusPoints && (
)}
{name} {isRed ? 'Red' : 'Blue'}
); } function StatRow({ icon, label, value, valueColor }) { return (
{label} {value}
); } export default function BattleDialog({ games }) { const [open, setOpen] = useState(false); const [game, setGame] = useState(null); const [copied, setCopied] = useState(false); useEffect(() => { const handler = e => { const row = e.target.closest('[data-game-index]'); if (!row) return; const idx = parseInt(row.dataset.gameIndex, 10); if (!isNaN(idx) && games[idx]) { setGame(games[idx]); setOpen(true); } }; document.addEventListener('click', handler); return () => document.removeEventListener('click', handler); }, [games]); if (!game) { return ; } const meta = RESULT_META[game.result] ?? RESULT_META.draw; const resign = game.resign; const endReason = resign ? `${resign.charAt(0).toUpperCase() + resign.slice(1)} resigned` : 'Points'; const shareUrl = `${window.location.origin}/battle/${game.uuid}`; const formatDuration = (from, to) => { if (!from || !to) return null; const diffMs = new Date(to.replace(' ', 'T')) - new Date(from.replace(' ', 'T')); if (isNaN(diffMs) || 0 >= diffMs) return null; const totalSec = Math.floor(diffMs / 1000); const h = Math.floor(totalSec / 3600); const m = Math.floor((totalSec % 3600) / 60); const s = totalSec % 60; if (0 < h) return `${h}h ${m}m ${s}s`; if (0 < m) return `${m}m ${s}s`; return `${s}s`; }; const duration = formatDuration(game.created, game.date); const pointDiff = Math.abs((game.redPoints ?? 0) - (game.bluePoints ?? 0)); const winnerColor = (game.redPoints ?? 0) > (game.bluePoints ?? 0) ? '#f67d52' : (game.bluePoints ?? 0) > (game.redPoints ?? 0) ? '#95cff5' : 'rgba(255,255,255,0.45)'; const handleShare = () => { navigator.clipboard.writeText(shareUrl).then(() => { setCopied(true); setTimeout(() => setCopied(false), 2200); }); }; return ( setOpen(false)} sx={DIALOG_SX}>
Battle Report

Match Details

game.blueBonusPoints ? game.redBonusPoints : 0} />
{game.redPoints ?? '—'} : {game.bluePoints ?? '—'}
{(game.redBonusPoints ?? 0).toFixed(1)} : {(game.blueBonusPoints ?? 0).toFixed(1)}
VS
{meta.label}
game.redBonusPoints ? game.blueBonusPoints : 0} />
{game.created && game.date && game.created !== game.date && ( )} {duration && ( )} {0 < pointDiff && ( )}
{(0 < game.redBonusPoints || 0 < game.blueBonusPoints || game.redBonusStats?.blindHits || game.blueBonusStats?.blindHits ) && (
{/* Red Bonus */}
Red Bonus Statistics
{0 < game.redBonusStats?.blindHits && } {0 < game.redBonusStats?.chainBest && } {0 < game.redBonusStats?.edgeMines && } {0 < game.redBonusStats?.lastMineHits && } {0 < game.redBonusStats?.biggestReveal && } {!game.redBonusStats?.blindHits && !game.redBonusStats?.chainBest && !game.redBonusStats?.edgeMines && !game.redBonusStats?.lastMineHits && !game.redBonusStats?.biggestReveal && }
{/* Blue Bonus */}
Blue Bonus Statistics
{0 < game.blueBonusStats?.blindHits && } {0 < game.blueBonusStats?.chainBest && } {0 < game.blueBonusStats?.edgeMines && } {0 < game.blueBonusStats?.lastMineHits && } {0 < game.blueBonusStats?.biggestReveal && } {!game.blueBonusStats?.blindHits && !game.blueBonusStats?.chainBest && !game.blueBonusStats?.edgeMines && !game.blueBonusStats?.lastMineHits && !game.blueBonusStats?.biggestReveal && }
)}
); }