conviction-voting-demo/src/components/NarrativePanel.tsx

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>
);
}