2025-05-08 00:26:26 +01:00

759 строки
32 KiB
TypeScript

import { useState, useRef, useEffect } from "react";
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { DossierPanel } from "@/components/game/DossierPanel";
import { LoadingOverlay } from "@/components/game/LoadingOverlay";
import { BriefingAudio } from "@/components/game/BriefingAudio";
import { GameBackground } from "@/components/GameBackground";
import { MonthTransition } from "@/components/MonthTransition";
import { IntroDialog } from "../components/game/IntroDialog";
import { useGameStages, OPERATION_NAMES, useLoadingMessages } from "@/components/game/constants";
import { ChoiceID } from "@/components/game/constants/metrics";
import { DossierEntry, GameStage } from "@/components/game/types";
import { Badge } from "@/components/ui/badge";
import { AlertCircle, Lock, Shield } from "lucide-react";
import { ChevronRightIcon } from "@heroicons/react/24/outline";
import { ChartBarIcon } from "@heroicons/react/24/outline";
import { playAcceptMissionSound, playDeployStratagemSound, playClickSound, stopBackgroundMusic, switchToFinalMusic, stopFinalMusic } from "@/utils/audio";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { TransitionStyle } from "@/components/MonthTransition";
import { ChoiceCard } from "@/components/game/ChoiceCard";
import { FinalReport } from '../components/game/FinalReport';
import { StrategyAnimation } from '@/components/game/StrategyAnimation';
import { Footer } from '../components/Footer';
import { useTranslation } from 'react-i18next';
import { LanguageSwitcher } from '@/components/LanguageSwitcher';
import '@/i18n/config';
import { MetricsDisplay } from "@/components/game/MetricsDisplay";
import { MuteButton } from '@/components/MuteButton';
import { DevPanel } from "@/components/game/DevPanel";
import { motion } from "framer-motion";
import { getMonthConfig } from "@/utils/months";
import { toast } from "sonner";
import { ProgressionIndicator } from '@/components/game/ProgressionIndicator';
import { EndGameDialog } from '../components/game/EndGameDialog';
import { STAGE_CHOICES } from '@/components/game/constants';
import { LearningSection } from '@/components/game/LearningSection';
import { useLearnings } from '@/hooks/useLearnings';
import { CollapsibleLearningSection } from '@/components/game/CollapsibleLearningSection';
const Index = () => {
const { t, i18n } = useTranslation();
const audioRef = useRef<HTMLAudioElement | null>(null);
const [previousChoices, setPreviousChoices] = useState<ChoiceID[]>([]);
const stages = useGameStages(audioRef);
const operationNameKey = OPERATION_NAMES[Math.floor(Math.random() * OPERATION_NAMES.length)];
const operationName = t(`operations.${operationNameKey}`);
const [agentNumber] = useState(Math.floor(Math.random() * 999).toString().padStart(3, '0'));
const [currentStage, setCurrentStage] = useState(0);
const [gameStarted, setGameStarted] = useState(false);
const [showingResult, setShowingResult] = useState(false);
const [currentResult, setCurrentResult] = useState<GameStage["choices"][0]["result"] | null>(null);
const [dossierEntries, setDossierEntries] = useState<DossierEntry[]>([]);
const [showingMonthTransition, setShowingMonthTransition] = useState(false);
const [nextStage, setNextStage] = useState<number | null>(null);
const [transitionStyle, setTransitionStyle] = useState<TransitionStyle>(TransitionStyle.NUMBER_CYCLE);
const [isLoading, setIsLoading] = useState(false);
const [loadingMessage, setLoadingMessage] = useState('');
const [loadingProgress, setLoadingProgress] = useState(0);
const [showingInitialTransition, setShowingInitialTransition] = useState(false);
const [showIntroDialog, setShowIntroDialog] = useState(true);
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
const [selectedChoice, setSelectedChoice] = useState<GameStage["choices"][0] | null>(null);
const [gameComplete, setGameComplete] = useState(false);
const [playerChoices, setPlayerChoices] = useState<string[]>([]);
const [gameKey, setGameKey] = useState(0);
const loadingMessages = useLoadingMessages();
const [shouldStartAudio, setShouldStartAudio] = useState(false);
const [showDevPanel, setShowDevPanel] = useState(false);
const [showFinalFade, setShowFinalFade] = useState(false);
const [showFinalReport, setShowFinalReport] = useState(false);
const [showEndGameDialog, setShowEndGameDialog] = useState(false);
const learningData = useLearnings(selectedChoice?.choiceId);
const [showImpactDetails, setShowImpactDetails] = useState(false);
const [showStrategyDetails, setShowStrategyDetails] = useState(true);
// Dev panel toggle
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.ctrlKey && e.key === 'd') {
e.preventDefault();
setShowDevPanel(prev => !prev);
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, []);
const handleJumpToMonth = (monthIndex: number) => {
setCurrentStage(monthIndex);
setShowDevPanel(false);
setGameStarted(true);
setShowIntroDialog(false);
setShowingInitialTransition(false);
};
const handleRandomizeChoices = () => {
const randomChoices: ChoiceID[] = [];
const newDossierEntries: DossierEntry[] = [];
const newPlayerChoices: string[] = [];
console.log('\n=== Starting Randomization ===');
// For each stage up to current stage, randomly select between A or B
for (let i = 0; i < currentStage; i++) {
const stagePair = STAGE_CHOICES[i];
// Randomly select 0 or 1 to pick between the two choices
const randomIndex = Math.floor(Math.random() * 2);
const choiceId = stagePair[randomIndex] as ChoiceID;
randomChoices.push(choiceId);
// Get the corresponding stage and choice from the stages array
const stage = stages[i];
const choice = stage.choices.find(c => c.choiceId === choiceId);
if (choice) {
// Add to player choices - convert choice.id to string
newPlayerChoices.push(String(choice.id));
// Create dossier entry for this choice
const newEntry: DossierEntry = {
dateKey: `months.${getMonthConfig(i + 1)?.key}`,
titleKey: `stages.${i + 1}.choices.${String(choice.id)}.result.title`,
insightKeys: Array.from({ length: 4 }, (_, idx) => `stages.${i + 1}.choices.${String(choice.id)}.result.insights.${idx}`),
strategicNoteKey: `stages.${i + 1}.choices.${String(choice.id)}.result.nextStepHint`
};
newDossierEntries.push(newEntry);
}
// Calculate and log cumulative metrics up to this point
// const currentMetrics = calculateMetrics(randomChoices);
// console.log('\nCumulative Metrics after this choice:');
// console.log('Network Reach:', currentMetrics.reach + '%');
// console.log('Core Loyalists:', currentMetrics.loyalists + '%');
// console.log('Virality Multiplier:', currentMetrics.virality + 'x');
// console.log('---');
}
// Update game state
setPreviousChoices(randomChoices);
setDossierEntries(newDossierEntries);
setPlayerChoices(newPlayerChoices);
setShowDevPanel(false);
};
const handleStartGame = () => {
playAcceptMissionSound();
setShowIntroDialog(false);
setShowingInitialTransition(true);
};
const handleInitialTransitionComplete = () => {
setShowingInitialTransition(false);
setGameStarted(true);
toast(t('mission.welcome.description'));
};
const handleChoice = async (choice: GameStage["choices"][0]) => {
playDeployStratagemSound();
// Add the choice to our list
const choiceId = choice.choiceId;
setPreviousChoices(prev => [...prev, choiceId]);
setPlayerChoices(prev => [...prev, String(choice.id)]);
setIsLoading(true);
setLoadingProgress(0);
const messages = loadingMessages.getMessagesForChoice(choice.loadingMessageKey);
let totalDuration = 0;
for (const message of messages) {
totalDuration += message.duration;
}
let elapsed = 0;
for (const message of messages) {
setLoadingMessage(message.action);
await new Promise(resolve => setTimeout(resolve, message.duration));
elapsed += message.duration;
setLoadingProgress((elapsed / totalDuration) * 100);
}
// Engage the final stage
// Keep loading overlay at 100% for a moment
// Start the fade to black and fade out loading overlay
// Wait for fade to complete
// Ensure language state is preserved before showing endgame
// Set game complete after fade is done
if (currentStage === stages.length - 1) {
// Keep loading overlay at 100% for a moment
await new Promise(resolve => setTimeout(resolve, 500));
// Start the fade to black and fade out loading overlay
setShowFinalFade(true);
setIsLoading(false);
// Wait for fade to complete
await new Promise(resolve => setTimeout(resolve, 1500));
// Ensure language state is preserved before showing endgame
const currentLang = localStorage.getItem('i18nextLng');
if (currentLang) {
i18n.changeLanguage(currentLang);
}
// Start the final music
switchToFinalMusic();
// Show end game dialog instead of setting game complete
setShowEndGameDialog(true);
return;
}
setIsLoading(false);
setCurrentResult(choice.result);
setShowingResult(true);
const newEntry: DossierEntry = {
dateKey: `months.${getMonthConfig(currentStage + 1)?.key}`,
titleKey: `stages.${currentStage + 1}.choices.${choice.id}.result.title`,
insightKeys: Array.from({ length: 4 }, (_, i) => `stages.${currentStage + 1}.choices.${choice.id}.result.insights.${i}`),
strategicNoteKey: `stages.${currentStage + 1}.choices.${choice.id}.result.nextStepHint`
};
setDossierEntries(prev => [...prev, newEntry]);
};
const handleContinue = () => {
playDeployStratagemSound();
setShowingResult(false);
// Check if this was the last stage
if (currentStage >= stages.length - 1) {
setGameComplete(true);
return;
}
// Otherwise, continue to next stage
setShowingMonthTransition(true);
setNextStage(currentStage + 1);
};
const handleTransitionComplete = () => {
setShowingMonthTransition(false);
if (nextStage !== null) {
setCurrentStage(nextStage);
setNextStage(null);
}
};
const handleStrategyClick = (choice: GameStage["choices"][0]) => {
playClickSound();
setSelectedChoice(choice);
setShowConfirmDialog(true);
};
const handleConfirmStrategy = async () => {
if (!selectedChoice) return;
setShowConfirmDialog(false);
await handleChoice(selectedChoice);
};
const handleRestart = () => {
setGameKey(prev => prev + 1);
setCurrentStage(0);
setGameStarted(false);
setShowingResult(false);
setCurrentResult(null);
setDossierEntries([]);
setPreviousChoices([]);
setGameComplete(false);
setPlayerChoices([]);
setShowIntroDialog(true);
setShowingInitialTransition(false);
setSelectedChoice(null);
setShowFinalReport(false);
setShowFinalFade(false);
if (audioRef.current) {
audioRef.current.pause();
audioRef.current = null;
}
// Stop both background and final music when restarting
stopBackgroundMusic();
stopFinalMusic();
};
const renderContent = () => {
if (!gameStarted) {
if (showingInitialTransition) {
return (
<div className="relative min-h-screen overflow-hidden">
<GameBackground shouldStartAudio={shouldStartAudio} />
<div className="relative min-h-screen bg-transparent p-4 flex items-center justify-center">
<div className="max-w-4xl mx-auto w-full relative">
<MonthTransition
stage={1}
onComplete={handleInitialTransitionComplete}
style={TransitionStyle.NUMBER_CYCLE}
/>
</div>
</div>
</div>
);
}
return (
<div className="relative min-h-screen overflow-hidden">
<GameBackground shouldStartAudio={shouldStartAudio} />
<div className="relative min-h-screen bg-transparent flex items-center justify-center p-4">
{showIntroDialog && <IntroDialog onStartAudio={() => setShouldStartAudio(true)} />}
<Card className="w-full md:max-w-5xl bg-black/50 text-white border-gray-700 transition-all duration-1000 animate-fade-in backdrop-blur-sm">
<CardHeader className="text-center space-y-4 p-4 md:p-6">
<div className="flex justify-between items-center px-4">
<Badge variant="outline" className="text-yellow-500 border-yellow-500">
<Lock className="w-3 h-3 mr-1" />
{t('mission.topSecret')}
</Badge>
<MuteButton />
<Badge variant="outline" className="text-red-500 border-red-500">
<AlertCircle className="w-3 h-3 mr-1" />
{t('mission.classified')}
</Badge>
</div>
<div className="relative">
<CardTitle className="text-2xl md:text-3xl mb-2 relative z-10">
{t('mission.title', { operationName })}
</CardTitle>
<div className="absolute -rotate-12 opacity-30 top-0 left-1/2 -translate-x-1/2 border-8 border-red-500 rounded w-full py-8 z-0">
<span className="text-red-500 text-4xl font-bold absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
{t('mission.classified')}
</span>
</div>
</div>
<CardDescription className="text-yellow-500 font-mono text-sm flex items-center justify-center gap-2">
<Shield className="w-4 h-4" />
{t('mission.clearanceLevel')}
<Shield className="w-4 h-4" />
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="pb-4">
<div className="flex flex-col space-y-4">
<div className="flex justify-between items-center text-sm border-b border-gray-700 pb-3">
<p className="font-mono text-yellow-500 font-semibold tracking-wider">{t('mission.directorate')}</p>
</div>
<div className="text-gray-300 font-mono text-sm space-y-1">
<p>{t('mission.to', { agentNumber })}</p>
<p>{t('mission.subject', { operationName })}</p>
<p className="text-xs text-gray-500">Date: {new Date().toLocaleDateString('en-GB')}</p>
</div>
</div>
<div className="mt-6 text-gray-300 font-mono leading-relaxed space-y-4">
<div className="flex items-center gap-2 mb-4">
<h2 className="text-yellow-500 font-mono text-sm">Briefing Audio</h2>
<BriefingAudio
stage="INTRO"
audioRef={audioRef}
className="bg-transparent hover:bg-black/40"
/>
</div>
<div className="space-y-4">
<p>{t('mission.briefing.part1')}</p>
</div>
<div className="space-y-4">
<p>{t('mission.briefing.part2')}</p>
</div>
<p className="pt-2 text-yellow-500 font-bold">{t('mission.briefing.warning')}</p>
</div>
</div>
<div className="border-t border-gray-700 pt-4">
<p className="text-yellow-500 italic font-mono text-center mb-6 text-sm tracking-wider">
{t('mission.quote')}
</p>
<div className="flex flex-col items-center gap-2">
<Button
onClick={handleStartGame}
className="bg-yellow-500 hover:bg-yellow-600 text-black px-8 py-6 text-lg transition-all duration-500 font-mono relative group"
>
<span className="group-hover:animate-pulse">{t('buttons.acceptMission')}</span>
</Button>
<p className="text-red-500 text-sm font-mono">
{t('warnings.selfDestruct')}
</p>
</div>
</div>
</CardContent>
</Card>
</div>
</div>
);
}
const currentStageData = stages[currentStage];
if (gameComplete) {
return (
<>
<FinalReport
key={gameKey}
choices={previousChoices}
onRestart={handleRestart}
agentNumber={agentNumber}
/>
</>
);
}
if (!currentStageData) {
return (
<div className="relative min-h-screen overflow-hidden">
<GameBackground shouldStartAudio={shouldStartAudio} />
<div className="relative min-h-screen bg-transparent p-4">
<Card className="w-full max-w-4xl mx-auto bg-black/50 text-white border-gray-700 transition-all duration-1000 animate-fade-in">
<CardHeader>
<CardTitle className="text-2xl text-center text-yellow-500">
Operation Status Unknown
</CardTitle>
<CardDescription className="text-gray-300 text-center">
Please restart the mission.
</CardDescription>
</CardHeader>
<CardContent className="flex justify-center pt-4">
<Button
onClick={() => {
setCurrentStage(0);
setGameStarted(false);
setPreviousChoices([]);
setGameComplete(false);
}}
className="bg-yellow-500 hover:bg-yellow-600 text-black px-8 py-4 text-lg transition-all duration-500"
>
Start New Operation
</Button>
</CardContent>
</Card>
</div>
</div>
);
}
if (showingResult && currentResult) {
return (
<div className="relative min-h-screen overflow-hidden">
<GameBackground shouldStartAudio={shouldStartAudio} />
<div className="relative min-h-screen bg-transparent p-4 flex flex-col">
<div className="flex-grow flex items-center justify-center">
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
transition={{
duration: 0.6,
ease: [0.22, 1, 0.36, 1]
}}
className="w-full max-w-3xl"
>
<Card className="relative border-gray-700 bg-black/30">
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.3, duration: 0.5 }}
className="bg-gray-800/30 p-6 rounded-t-md border border-gray-700"
>
<h3 className="text-yellow-500 font-semibold mb-4">{t('analysis.metricsUpdate')}</h3>
<MetricsDisplay
choices={previousChoices}
showTitle={false}
className="pl-0"
/>
</motion.div>
<CardHeader>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.5, duration: 0.5 }}
className="flex flex-col gap-4"
>
<CardDescription className="text-emerald-400/90 italic">
{t('analysis.intelligenceGathered.description')}
</CardDescription>
<div className="flex justify-between items-center">
<CardTitle className="text-xl md:text-2xl text-yellow-500">{currentResult.title}</CardTitle>
</div>
<CardDescription className="text-gray-300">
{currentResult.description}
</CardDescription>
</motion.div>
</CardHeader>
<CardContent className="space-y-6">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.7, duration: 0.5 }}
>
<h3 className="text-yellow-500 font-semibold mb-3">{t('analysis.keyInsights')}</h3>
<ul className="space-y-2">
{currentResult.insights.map((insight, index) => (
<motion.li
key={index}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.8 + (index * 0.1), duration: 0.4 }}
className="flex items-start gap-2 text-gray-300"
>
<span className="text-yellow-500"></span>
{insight}
</motion.li>
))}
</ul>
</motion.div>
{/* <motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 1.2, duration: 0.5 }}
className="border-t border-gray-700 pt-4"
>
<p className="text-gray-400 italic">
<span className="text-yellow-500 font-semibold">{t('analysis.strategicInsight')} </span>
{currentResult.nextStepHint}
</p>
</motion.div> */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 1.4, duration: 0.5 }}
className="flex justify-center pt-4"
>
<Button
onClick={handleContinue}
className="bg-yellow-500 hover:bg-yellow-600 text-black px-8 py-4 text-lg transition-all duration-500"
>
{t('buttons.proceedToNext')}
</Button>
</motion.div>
</CardContent>
</Card>
</motion.div>
</div>
</div>
</div>
);
}
if (showingMonthTransition && nextStage !== null && stages[nextStage]) {
return (
<div className="relative min-h-screen overflow-hidden">
<GameBackground shouldStartAudio={shouldStartAudio} />
<div className="relative min-h-screen bg-transparent p-4 flex items-center justify-center">
<MonthTransition
stage={nextStage + 1}
onComplete={handleTransitionComplete}
style={transitionStyle}
/>
</div>
</div>
);
}
return (
<div className="relative min-h-screen overflow-hidden">
<GameBackground shouldStartAudio={shouldStartAudio} />
<div className="relative min-h-screen bg-transparent md:p-4 flex flex-col">
<div className="w-full max-w-4xl mx-auto px-4 mt-4 mb-6">
<ProgressionIndicator
currentStage={currentStage}
totalStages={stages.length}
previousChoices={previousChoices}
/>
</div>
<div className="flex-grow flex items-center">
<div className="w-full h-full md:max-w-4xl mx-auto md:px-4">
<Card className="bg-black/50 text-white border-gray-700 transition-all duration-1000 animate-fade-in h-full md:h-auto md:rounded-lg border-0 md:border">
<CardHeader className="p-3 md:p-6">
<div className="flex flex-col gap-4">
<div className="flex justify-between items-center">
<div className="flex items-center gap-4">
<MuteButton />
<span className="text-yellow-500 font-mono text-lg">
{(() => {
console.log('Index - currentStageData:', currentStageData);
const monthConfig = getMonthConfig(currentStage + 1);
return t(`months.${monthConfig?.key}`);
})()}
</span>
</div>
{currentStage > 0 && <DossierPanel entries={dossierEntries} choices={previousChoices} />}
</div>
<CardDescription className="text-gray-300">
{currentStageData.description}
</CardDescription>
</div>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{(() => {
console.log('Index - Rendering stage:', currentStage);
return currentStageData.choices.map((choice, index) => (
<ChoiceCard
key={choice.id}
choice={choice}
previousChoices={previousChoices}
onClick={() => handleStrategyClick(choice)}
disabled={showingResult || isLoading}
optionNumber={index + 1}
/>
));
})()}
</div>
</CardContent>
<div className="mt-4 border-t border-gray-700/50">
<div className="flex justify-center py-4">
<LanguageSwitcher />
</div>
</div>
</Card>
</div>
</div>
<Footer />
</div>
<Dialog open={showConfirmDialog} onOpenChange={setShowConfirmDialog}>
<DialogContent className="bg-black/90 text-white border-gray-700 w-[95vw] max-w-3xl mx-auto max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle className="text-2xl text-yellow-500">
{selectedChoice?.text}
</DialogTitle>
<DialogDescription className="text-gray-300 space-y-6 pt-4">
{selectedChoice && (
<div className="w-full" style={{ maxWidth: '100%' }}>
<StrategyAnimation
type={selectedChoice.animation.type}
className="mb-6 w-full"
/>
</div>
)}
{selectedChoice && (
<>
{(selectedChoice.strengthenedBy?.some(choice => previousChoices.includes(choice)) ||
selectedChoice.weakenedBy?.some(choice => previousChoices.includes(choice))) && (
<div className="text-sm space-y-3 mb-4">
{selectedChoice.strengthenedBy?.some(choice => previousChoices.includes(choice)) && (
<div className="flex items-start gap-2">
<span className="text-green-400"></span>
<div>
<span className="text-green-400">Enhanced</span>
<span className="text-gray-400"> by: </span>
{selectedChoice.strengthenedBy
.filter(choice => previousChoices.includes(choice))
.join(', ')}
</div>
</div>
)}
{selectedChoice.weakenedBy?.some(choice => previousChoices.includes(choice)) && (
<div className="flex items-start gap-2">
<span className="text-red-400"></span>
<div>
<span className="text-red-400">Weakened</span>
<span className="text-gray-400"> by: </span>
{selectedChoice.weakenedBy
.filter(choice => previousChoices.includes(choice))
.join(', ')}
</div>
</div>
)}
</div>
)}
</>
)}
<div>
<h3 className="text-yellow-500 font-semibold mb-2 flex items-center gap-2 cursor-pointer"
onClick={() => setShowStrategyDetails(!showStrategyDetails)}>
{/* {t('analysis.strategyOverview')}: */}
{/* <ChevronRightIcon className={`w-4 h-4 transition-transform ${showStrategyDetails ? 'rotate-90' : ''}`} /> */}
</h3>
<p className="text-gray-300">{selectedChoice?.description}</p>
</div>
<div>
<h3 className="text-yellow-500 font-semibold mb-2 flex items-center gap-2 cursor-pointer"
onClick={() => setShowImpactDetails(!showImpactDetails)}>
<ChartBarIcon className="w-5 h-5" />
{t('analysis.expectedImpact')}:
<ChevronRightIcon className={`w-4 h-4 transition-transform ${showImpactDetails ? 'rotate-90' : ''}`} />
</h3>
{showImpactDetails && (
<div className="animate-fadeIn">
<p className="text-gray-300">{selectedChoice?.impact}</p>
{/* <p className="text-gray-300 mt-3">{selectedChoice?.explainer}</p> */}
</div>
)}
</div>
<CollapsibleLearningSection
learningData={learningData}
initiallyExpanded={false}
/>
<div className="flex justify-center pt-4">
<Button
onClick={handleConfirmStrategy}
className="bg-yellow-500 hover:bg-yellow-600 text-black px-8 py-4 text-lg transition-all duration-500"
>
{t('buttons.deployStratagem')}
</Button>
</div>
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
{showFinalFade && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 1.5 }}
className="fixed inset-0 bg-black z-[50]"
/>
)}
{isLoading && <LoadingOverlay message={loadingMessage} progress={loadingProgress} />}
</div>
);
};
return (
<>
{renderContent()}
{showEndGameDialog && !gameComplete && (
<EndGameDialog
onContinue={() => {
setShowEndGameDialog(false);
setGameComplete(true);
}}
/>
)}
<DevPanel
open={showDevPanel}
onOpenChange={setShowDevPanel}
onJumpToMonth={handleJumpToMonth}
onRandomizeChoices={handleRandomizeChoices}
/>
</>
);
};
export default Index;