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:
@@ -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>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user