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 { GameAnalysisModal } from "./GameAnalysisModal";
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 { detectMissedTactics, uciToSan, DetectedTactic } from "@/lib/tacticDetection";
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 [showAnalysisModal, setShowAnalysisModal] = 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 [moveHistory, setMoveHistory] = useState<MoveHistoryItem[]>([]);
const [selectedPersonality, setSelectedPersonality] = useState<Personality>(initialPersonality);
@@ -599,13 +600,13 @@ export default function ChessGame({ gameId, initialFen, initialPgn, initialPerso
updateCapturedPieces();
};
const handleResign = useCallback(async () => {
const handleResignClick = useCallback(() => {
if (gameOverState) return;
setShowResignConfirm(true);
}, [gameOverState]);
// Show confirmation dialog
const confirmed = window.confirm(t.game.resignConfirm);
if (!confirmed) return;
const handleResignConfirm = useCallback(async () => {
setShowResignConfirm(false);
setIsAnalyzing(false);
const currentFen = gameRef.current.fen();
@@ -635,7 +636,11 @@ export default function ChessGame({ gameId, initialFen, initialPgn, initialPerso
result,
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 pgn = gameRef.current.pgn();
@@ -784,7 +789,7 @@ export default function ChessGame({ gameId, initialFen, initialPgn, initialPerso
</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"
disabled={!!gameOverState}
>
@@ -970,6 +975,60 @@ export default function ChessGame({ gameId, initialFen, initialPgn, initialPerso
</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>
)}
</>
);
}