105 lines
3.3 KiB
TypeScript
105 lines
3.3 KiB
TypeScript
'use client';
|
|
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
import { useSimulationStore } from '@/stores/simulation';
|
|
|
|
export function NarrativePanel() {
|
|
const narrativeStep = useSimulationStore((s) => s.narrativeStep);
|
|
const narrativeSteps = useSimulationStore((s) => s.narrativeSteps);
|
|
const nextNarrativeStep = useSimulationStore((s) => s.nextNarrativeStep);
|
|
const prevNarrativeStep = useSimulationStore((s) => s.prevNarrativeStep);
|
|
const resetNarrative = useSimulationStore((s) => s.resetNarrative);
|
|
const setMode = useSimulationStore((s) => s.setMode);
|
|
|
|
const currentStep = narrativeSteps[narrativeStep];
|
|
const isFirst = narrativeStep === 0;
|
|
const isLast = narrativeStep === narrativeSteps.length - 1;
|
|
|
|
if (!currentStep) return null;
|
|
|
|
return (
|
|
<motion.div
|
|
className="p-6 bg-gradient-to-br from-indigo-50 to-purple-50 rounded-xl border-2 border-indigo-200"
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
>
|
|
{/* Progress indicator */}
|
|
<div className="flex gap-1 mb-4">
|
|
{narrativeSteps.map((_, i) => (
|
|
<div
|
|
key={i}
|
|
className={`
|
|
h-1 flex-1 rounded-full transition-all duration-300
|
|
${i <= narrativeStep ? 'bg-indigo-500' : 'bg-gray-200'}
|
|
`}
|
|
/>
|
|
))}
|
|
</div>
|
|
|
|
{/* Step counter */}
|
|
<div className="text-sm text-indigo-600 font-medium mb-2">
|
|
Step {narrativeStep + 1} of {narrativeSteps.length}
|
|
</div>
|
|
|
|
{/* Content */}
|
|
<AnimatePresence mode="wait">
|
|
<motion.div
|
|
key={narrativeStep}
|
|
initial={{ opacity: 0, x: 20 }}
|
|
animate={{ opacity: 1, x: 0 }}
|
|
exit={{ opacity: 0, x: -20 }}
|
|
transition={{ duration: 0.3 }}
|
|
>
|
|
<h2 className="text-xl font-bold text-gray-800 mb-3">
|
|
{currentStep.title}
|
|
</h2>
|
|
<p className="text-gray-600 leading-relaxed whitespace-pre-line">
|
|
{currentStep.description}
|
|
</p>
|
|
</motion.div>
|
|
</AnimatePresence>
|
|
|
|
{/* Navigation */}
|
|
<div className="flex gap-3 mt-6">
|
|
<button
|
|
onClick={prevNarrativeStep}
|
|
disabled={isFirst}
|
|
className={`
|
|
flex-1 py-2 px-4 rounded-lg font-medium transition-colors
|
|
${
|
|
isFirst
|
|
? 'bg-gray-100 text-gray-400 cursor-not-allowed'
|
|
: 'bg-gray-200 text-gray-700 hover:bg-gray-300'
|
|
}
|
|
`}
|
|
>
|
|
← Back
|
|
</button>
|
|
{isLast ? (
|
|
<button
|
|
onClick={() => setMode('simulation')}
|
|
className="flex-1 py-2 px-4 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg font-medium transition-colors"
|
|
>
|
|
Try Simulation Mode →
|
|
</button>
|
|
) : (
|
|
<button
|
|
onClick={nextNarrativeStep}
|
|
className="flex-1 py-2 px-4 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg font-medium transition-colors"
|
|
>
|
|
Next →
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
{/* Restart option */}
|
|
<button
|
|
onClick={resetNarrative}
|
|
className="mt-3 w-full py-1 text-sm text-gray-500 hover:text-gray-700"
|
|
>
|
|
Restart from beginning
|
|
</button>
|
|
</motion.div>
|
|
);
|
|
}
|