import React, { useState, useEffect } from 'react'; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Card, CardHeader, CardTitle, CardContent, CardFooter, CardDescription } from "@/components/ui/card"; import { Plus, Minus, Trash2, ArrowLeft } from "lucide-react"; import { motion, AnimatePresence } from "framer-motion"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { Label } from "@/components/ui/label"; import { Progress } from "@/components/ui/progress"; interface ParticipantSetup { id: number; name: string; } interface Prize { id: string; name: string; points: number; } interface Participant { id: number; name: string; pointsAvailable: number; prizes: Prize[]; } interface StoredData { stage: Stage; numParticipants: number; participantSetup: ParticipantSetup[]; pointInputs: number[]; participants: Participant[]; totalPoints: number; } type Stage = 'setup' | 'points' | 'tracking'; const STORAGE_KEY = 'arcadeTrackerData'; const ArcadeTracker: React.FC = () => { const [stage, setStage] = useState('setup'); const [numParticipants, setNumParticipants] = useState(2); const [pointInputs, setPointInputs] = useState([]); const [participants, setParticipants] = useState([]); const [totalPoints, setTotalPoints] = useState(0); const [newPrizeName, setNewPrizeName] = useState(''); const [newPrizePoints, setNewPrizePoints] = useState(''); const [participantSetup, setParticipantSetup] = useState([]); const [showResetDialog, setShowResetDialog] = useState(false); const [editingParticipantId, setEditingParticipantId] = useState(null); useEffect(() => { // Initialize participant setup when component mounts if (participantSetup.length === 0) { setParticipantSetup([ { id: 1, name: 'Player 1' }, { id: 2, name: 'Player 2' } ]); } }, [participantSetup.length]); // Add the dependency useEffect(() => { const savedData = localStorage.getItem(STORAGE_KEY); if (savedData) { try { const parsed = JSON.parse(savedData) as StoredData; setStage(parsed.stage); setNumParticipants(parsed.numParticipants); setParticipantSetup(parsed.participantSetup || []); setPointInputs(parsed.pointInputs); setParticipants(parsed.participants); setTotalPoints(parsed.totalPoints); } catch (error) { console.error('Error parsing saved data:', error); } } }, []); useEffect(() => { const dataToSave: StoredData = { stage, numParticipants, participantSetup, pointInputs, participants, totalPoints, }; localStorage.setItem(STORAGE_KEY, JSON.stringify(dataToSave)); }, [stage, numParticipants, pointInputs, participants, totalPoints, participantSetup]); // Add participantSetup const handleParticipantNameChange = (id: number, name: string) => { setParticipantSetup(prev => prev.map(p => p.id === id ? { ...p, name } : p) ); }; const handleNumParticipantsChange = (change: number) => { const newNum = Math.max(2, Math.min(10, numParticipants + change)); setNumParticipants(newNum); // Update participant setup array setParticipantSetup(prev => { const newSetup = Array(newNum).fill(null).map((_, i) => ({ id: i + 1, name: prev[i]?.name || `Player ${i + 1}` })); return newSetup; }); }; const handleUpdatePoints = () => { const total = pointInputs.reduce((sum, points) => sum + points, 0); const pointsPerPerson = Math.floor(total / numParticipants); // Update each participant's points while preserving prizes const updatedParticipants = participantSetup.map((setup) => { const existingParticipant = participants.find(p => p.id === setup.id); const currentPrizePoints = existingParticipant?.prizes.reduce((sum, prize) => sum + prize.points, 0) || 0; return { id: setup.id, name: setup.name, pointsAvailable: pointsPerPerson - currentPrizePoints, prizes: existingParticipant?.prizes || [] }; }); setTotalPoints(total); setParticipants(updatedParticipants); setStage('tracking'); }; const handleAddPrize = (participantId: number) => { // Just add the prize, no validation const points = parseInt(newPrizePoints) || 0; setParticipants(current => current.map(p => { if (p.id === participantId) { return { ...p, pointsAvailable: p.pointsAvailable - points, prizes: [ ...p.prizes, { id: Date.now().toString(), name: newPrizeName || 'Prize', points } ] }; } return p; }) ); // Clear inputs and close dialog setNewPrizeName(''); setNewPrizePoints(''); setEditingParticipantId(null); }; const removePrize = (participantId: number, prizeId: string) => { setParticipants(participants.map(p => { if (p.id === participantId) { const prizeToRemove = p.prizes.find(prize => prize.id === prizeId); return { ...p, pointsAvailable: p.pointsAvailable + (prizeToRemove?.points || 0), prizes: p.prizes.filter(prize => prize.id !== prizeId) }; } return p; })); }; const handlePointInput = (index: number, value: string) => { const newInputs = [...pointInputs]; newInputs[index] = value === '' ? 0 : parseInt(value) || 0; setPointInputs(newInputs); }; const resetApp = () => { localStorage.removeItem(STORAGE_KEY); setStage('setup'); setNumParticipants(2); setParticipantSetup([ { id: 1, name: 'Player 1' }, { id: 2, name: 'Player 2' } ]); setPointInputs([]); setParticipants([]); setTotalPoints(0); setShowResetDialog(false); }; const renderSetupStage = () => (
Arcade Point Tracker Set up your group's point tracking
{numParticipants}
{participantSetup.map((p, index) => (
handleParticipantNameChange(p.id, e.target.value)} placeholder={`Player ${index + 1}`} onClick={(e) => (e.target as HTMLInputElement).select()} className="transition-colors focus:bg-accent focus:text-accent-foreground" />
))}
); const renderPointsStage = () => (
Enter Points Input the points from each card
{pointInputs.map((points, index) => (
handlePointInput(index, e.target.value)} className="mt-1" placeholder="Enter points" />
))}
); const renderTrackingStage = () => ( <>
Total Points: {totalPoints}
Points Used: {participants.reduce((total, p) => total + p.prizes.reduce((sum, prize) => sum + prize.points, 0) , 0)}
total + p.prizes.reduce((sum, prize) => sum + prize.points, 0) , 0) / totalPoints) * 100} className={`h-2 ${(participants.reduce((total, p) => total + p.prizes.reduce((sum, prize) => sum + prize.points, 0) , 0) > totalPoints) ? '[&>div]:bg-destructive' : ''}`} /> total + p.prizes.reduce((sum, prize) => sum + prize.points, 0) , 0) > totalPoints) ? 'text-destructive font-medium' : ''}`}> {((participants.reduce((total, p) => total + p.prizes.reduce((sum, prize) => sum + prize.points, 0) , 0) / totalPoints) * 100).toFixed(1)}% used
{/* Spacer for fixed header */}
Reset Application Are you sure you want to reset all data? This action cannot be undone. {participants.map((participant, index) => (
{participant.name}
Points Available: {participant.pointsAvailable} / {Math.floor(totalPoints / participants.length)} {((participant.pointsAvailable / (totalPoints / participants.length)) * 100).toFixed(1)}%
div]:bg-destructive' : ''}`} />
{participant.prizes.map((prize) => (
{prize.name}
{prize.points} points
))}
{!participant.prizes.length && ( "No prizes added yet. Click "Add Prize" to get started." )} { if (!open) setEditingParticipantId(null); }}> Add Prize for {participant.name} Available Points: {participant.pointsAvailable}
setNewPrizeName(e.target.value)} />
setNewPrizePoints(e.target.value)} />
))}
); switch (stage) { case 'setup': return renderSetupStage(); case 'points': return renderPointsStage(); case 'tracking': return renderTrackingStage(); default: return null; } }; export default ArcadeTracker;