Replace system confirm dialog with styled resign confirmation modal

- Replace window.confirm() with a custom styled modal dialog
- Match existing modal styling (header, content, footer pattern)
- Add Flag icon and AlertTriangle warning icon
- Use localized Cancel button text from common translations
- Improved UX with X close button and proper button styling
This commit is contained in:
Stefan
2025-12-03 10:09:08 +01:00
parent 6aa7d5bdd8
commit 3787696aa8
+67 -8
View File
@@ -13,7 +13,7 @@ import { SupportedLanguage } from "@/lib/i18n/translations";
import { lookupOpening, lookupPossibleOpenings, extractMoveSequenceFromPGN, OpeningMetadata } from "@/lib/openings"; import { lookupOpening, lookupPossibleOpenings, extractMoveSequenceFromPGN, OpeningMetadata } from "@/lib/openings";
import { GameAnalysisModal } from "./GameAnalysisModal"; import { GameAnalysisModal } from "./GameAnalysisModal";
import { GameOverModal, MoveHistoryItem } from "./GameOverModal"; import { GameOverModal, MoveHistoryItem } from "./GameOverModal";
import { Brain, ArrowLeft, Download, Flag } from "lucide-react"; import { Brain, ArrowLeft, Download, Flag, AlertTriangle, X } from "lucide-react";
import { CapturedPieces } from "./CapturedPieces"; import { CapturedPieces } from "./CapturedPieces";
import { detectMissedTactics, uciToSan, DetectedTactic } from "@/lib/tacticDetection"; import { detectMissedTactics, uciToSan, DetectedTactic } from "@/lib/tacticDetection";
import { upsertSavedGame } from "@/lib/savedGames"; import { upsertSavedGame } from "@/lib/savedGames";
@@ -65,6 +65,7 @@ export default function ChessGame({ gameId, initialFen, initialPgn, initialPerso
const [playerColor, setPlayerColor] = useState<'white' | 'black'>(initialColor); const [playerColor, setPlayerColor] = useState<'white' | 'black'>(initialColor);
const [showAnalysisModal, setShowAnalysisModal] = useState(false); const [showAnalysisModal, setShowAnalysisModal] = useState(false);
const [showDownloadModal, setShowDownloadModal] = useState(false); const [showDownloadModal, setShowDownloadModal] = useState(false);
const [showResignConfirm, setShowResignConfirm] = useState(false);
const [gameOverState, setGameOverState] = useState<{ result: string, winner: "White" | "Black" | "Draw" } | null>(null); const [gameOverState, setGameOverState] = useState<{ result: string, winner: "White" | "Black" | "Draw" } | null>(null);
const [moveHistory, setMoveHistory] = useState<MoveHistoryItem[]>([]); const [moveHistory, setMoveHistory] = useState<MoveHistoryItem[]>([]);
const [selectedPersonality, setSelectedPersonality] = useState<Personality>(initialPersonality); const [selectedPersonality, setSelectedPersonality] = useState<Personality>(initialPersonality);
@@ -599,13 +600,13 @@ export default function ChessGame({ gameId, initialFen, initialPgn, initialPerso
updateCapturedPieces(); updateCapturedPieces();
}; };
const handleResign = useCallback(async () => { const handleResignClick = useCallback(() => {
if (gameOverState) return; if (gameOverState) return;
setShowResignConfirm(true);
}, [gameOverState]);
// Show confirmation dialog const handleResignConfirm = useCallback(async () => {
const confirmed = window.confirm(t.game.resignConfirm); setShowResignConfirm(false);
if (!confirmed) return;
setIsAnalyzing(false); setIsAnalyzing(false);
const currentFen = gameRef.current.fen(); const currentFen = gameRef.current.fen();
@@ -635,7 +636,11 @@ export default function ChessGame({ gameId, initialFen, initialPgn, initialPerso
result, result,
winner, winner,
}); });
}, [gameOverState, moveHistory, playerColor, stockfish, stockfishDepth, t.game.resignConfirm, t.game.resignation]); }, [moveHistory, playerColor, stockfish, stockfishDepth, t.game.resignation]);
const handleResignCancel = useCallback(() => {
setShowResignConfirm(false);
}, []);
const handleDownloadPGN = () => { const handleDownloadPGN = () => {
const pgn = gameRef.current.pgn(); const pgn = gameRef.current.pgn();
@@ -784,7 +789,7 @@ export default function ChessGame({ gameId, initialFen, initialPgn, initialPerso
</button> </button>
<button <button
onClick={handleResign} onClick={handleResignClick}
className="flex items-center gap-1 text-red-600 dark:text-red-400 hover:text-red-700 dark:hover:text-red-300 transition-colors" className="flex items-center gap-1 text-red-600 dark:text-red-400 hover:text-red-700 dark:hover:text-red-300 transition-colors"
disabled={!!gameOverState} disabled={!!gameOverState}
> >
@@ -970,6 +975,60 @@ export default function ChessGame({ gameId, initialFen, initialPgn, initialPerso
</div> </div>
</div> </div>
)} )}
{/* Resign Confirmation Modal */}
{showResignConfirm && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-2xl max-w-md w-full overflow-hidden">
{/* Header */}
<div className="bg-red-50 dark:bg-red-900/20 px-6 py-4 border-b border-red-100 dark:border-red-900/30">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="p-2 bg-red-100 dark:bg-red-900/40 rounded-full">
<Flag className="w-5 h-5 text-red-600 dark:text-red-400" />
</div>
<h2 className="text-xl font-bold text-gray-900 dark:text-white">
{t.game.resign}?
</h2>
</div>
<button
onClick={handleResignCancel}
className="p-1 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
>
<X size={20} />
</button>
</div>
</div>
{/* Content */}
<div className="px-6 py-5">
<div className="flex items-start gap-3">
<AlertTriangle className="w-5 h-5 text-amber-500 flex-shrink-0 mt-0.5" />
<p className="text-gray-600 dark:text-gray-300">
{t.game.resignConfirm}
</p>
</div>
</div>
{/* Footer */}
<div className="px-6 py-4 bg-gray-50 dark:bg-gray-900/50 flex justify-end gap-3">
<button
onClick={handleResignCancel}
className="px-4 py-2 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg font-medium transition-colors"
>
{t.common.cancel}
</button>
<button
onClick={handleResignConfirm}
className="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 font-medium shadow-sm transition-colors flex items-center gap-2"
>
<Flag size={16} />
{t.game.resign}
</button>
</div>
</div>
</div>
)}
</> </>
); );
} }