Update and restyle

This commit is contained in:
2025-01-18 21:40:15 -05:00
parent 887b8c5919
commit 1c3dbdd7f5

View File

@@ -1,8 +1,15 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from "react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Card, CardHeader, CardTitle, CardContent, CardFooter, CardDescription } from "@/components/ui/card"; import {
import { Plus, Minus, Trash2, ArrowLeft } from "lucide-react"; Card,
CardHeader,
CardTitle,
CardContent,
CardFooter,
CardDescription,
} from "@/components/ui/card";
import { Plus, Minus, Trash2, ArrowLeft, Pencil } from "lucide-react";
import { motion, AnimatePresence } from "framer-motion"; import { motion, AnimatePresence } from "framer-motion";
import { Alert, AlertDescription } from "@/components/ui/alert"; import { Alert, AlertDescription } from "@/components/ui/alert";
import { import {
@@ -26,6 +33,7 @@ interface Prize {
id: string; id: string;
name: string; name: string;
points: number; points: number;
quantity: number;
} }
interface Participant { interface Participant {
@@ -35,6 +43,14 @@ interface Participant {
prizes: Prize[]; prizes: Prize[];
} }
interface EditingPrize {
participantId: number;
prizeId: string;
name: string;
points: string;
quantity: string;
}
interface StoredData { interface StoredData {
stage: Stage; stage: Stage;
numParticipants: number; numParticipants: number;
@@ -44,31 +60,40 @@ interface StoredData {
totalPoints: number; totalPoints: number;
} }
type Stage = 'setup' | 'points' | 'tracking'; type Stage = "setup" | "points" | "tracking";
const STORAGE_KEY = 'arcadeTrackerData'; const STORAGE_KEY = "arcadeTrackerData";
const ArcadeTracker: React.FC = () => { const ArcadeTracker: React.FC = () => {
const [stage, setStage] = useState<Stage>('setup'); const [stage, setStage] = useState<Stage>("setup");
const [numParticipants, setNumParticipants] = useState<number>(2); const [numParticipants, setNumParticipants] = useState<number>(3);
const [pointInputs, setPointInputs] = useState<number[]>([]); const [pointInputs, setPointInputs] = useState<number[]>([]);
const [participants, setParticipants] = useState<Participant[]>([]); const [participants, setParticipants] = useState<Participant[]>([]);
const [totalPoints, setTotalPoints] = useState<number>(0); const [totalPoints, setTotalPoints] = useState<number>(0);
const [newPrizeName, setNewPrizeName] = useState<string>(''); const [newPrizeName, setNewPrizeName] = useState<string>("");
const [newPrizePoints, setNewPrizePoints] = useState<string>(''); const [newPrizePoints, setNewPrizePoints] = useState<string>("");
const [participantSetup, setParticipantSetup] = useState<ParticipantSetup[]>([]); const [newPrizeQuantity, setNewPrizeQuantity] = useState<string>("1");
const [participantSetup, setParticipantSetup] = useState<ParticipantSetup[]>(
[]
);
const [showResetDialog, setShowResetDialog] = useState(false); const [showResetDialog, setShowResetDialog] = useState(false);
const [editingParticipantId, setEditingParticipantId] = useState<number | null>(null); const [editingParticipantId, setEditingParticipantId] = useState<
number | null
>(null);
const [editingPrize, setEditingPrize] = useState<EditingPrize | null>(null);
useEffect(() => { useEffect(() => {
// Initialize participant setup when component mounts // Initialize participant setup when component mounts
if (participantSetup.length === 0) { if (participantSetup.length === 0) {
setParticipantSetup([ setParticipantSetup(Array(numParticipants)
{ id: 1, name: 'Player 1' }, .fill(null)
{ id: 2, name: 'Player 2' } .map((_, i) => ({
]); id: i + 1,
name: `Player ${i + 1}`,
}))
);
} }
}, [participantSetup.length]); // Add the dependency }, [participantSetup.length, numParticipants]);
useEffect(() => { useEffect(() => {
const savedData = localStorage.getItem(STORAGE_KEY); const savedData = localStorage.getItem(STORAGE_KEY);
@@ -82,7 +107,7 @@ const ArcadeTracker: React.FC = () => {
setParticipants(parsed.participants); setParticipants(parsed.participants);
setTotalPoints(parsed.totalPoints); setTotalPoints(parsed.totalPoints);
} catch (error) { } catch (error) {
console.error('Error parsing saved data:', error); console.error("Error parsing saved data:", error);
} }
} }
}, []); }, []);
@@ -97,11 +122,18 @@ const ArcadeTracker: React.FC = () => {
totalPoints, totalPoints,
}; };
localStorage.setItem(STORAGE_KEY, JSON.stringify(dataToSave)); localStorage.setItem(STORAGE_KEY, JSON.stringify(dataToSave));
}, [stage, numParticipants, pointInputs, participants, totalPoints, participantSetup]); // Add participantSetup }, [
stage,
numParticipants,
pointInputs,
participants,
totalPoints,
participantSetup,
]); // Add participantSetup
const handleParticipantNameChange = (id: number, name: string) => { const handleParticipantNameChange = (id: number, name: string) => {
setParticipantSetup(prev => setParticipantSetup((prev) =>
prev.map(p => p.id === id ? { ...p, name } : p) prev.map((p) => (p.id === id ? { ...p, name } : p))
); );
}; };
@@ -110,10 +142,12 @@ const ArcadeTracker: React.FC = () => {
setNumParticipants(newNum); setNumParticipants(newNum);
// Update participant setup array // Update participant setup array
setParticipantSetup(prev => { setParticipantSetup((prev) => {
const newSetup = Array(newNum).fill(null).map((_, i) => ({ const newSetup = Array(newNum)
.fill(null)
.map((_, i) => ({
id: i + 1, id: i + 1,
name: prev[i]?.name || `Player ${i + 1}` name: prev[i]?.name || `Player ${i + 1}`,
})); }));
return newSetup; return newSetup;
}); });
@@ -125,78 +159,121 @@ const ArcadeTracker: React.FC = () => {
// Update each participant's points while preserving prizes // Update each participant's points while preserving prizes
const updatedParticipants = participantSetup.map((setup) => { const updatedParticipants = participantSetup.map((setup) => {
const existingParticipant = participants.find(p => p.id === setup.id); const existingParticipant = participants.find((p) => p.id === setup.id);
const currentPrizePoints = existingParticipant?.prizes.reduce((sum, prize) => sum + prize.points, 0) || 0; const currentPrizePoints =
existingParticipant?.prizes.reduce(
(sum, prize) => sum + prize.points,
0
) || 0;
return { return {
id: setup.id, id: setup.id,
name: setup.name, name: setup.name,
pointsAvailable: pointsPerPerson - currentPrizePoints, pointsAvailable: pointsPerPerson - currentPrizePoints,
prizes: existingParticipant?.prizes || [] prizes: existingParticipant?.prizes || [],
}; };
}); });
setTotalPoints(total); setTotalPoints(total);
setParticipants(updatedParticipants); setParticipants(updatedParticipants);
setStage('tracking'); setStage("tracking");
}; };
const handleAddPrize = (participantId: number) => { const handleAddPrize = (participantId: number) => {
// Just add the prize, no validation if (editingPrize) {
// We're editing an existing prize
const points = parseInt(newPrizePoints) || 0; const points = parseInt(newPrizePoints) || 0;
const quantity = parseInt(newPrizeQuantity) || 1;
setParticipants(current => setParticipants((current) =>
current.map(p => { current.map((p) => {
if (p.id === participantId) { if (p.id === participantId) {
const oldPrize = p.prizes.find((prize) => prize.id === editingPrize.prizeId);
const oldPoints = (oldPrize?.points || 0) * (oldPrize?.quantity || 1);
const newPoints = points * quantity;
return { return {
...p, ...p,
pointsAvailable: p.pointsAvailable - points, pointsAvailable: p.pointsAvailable + oldPoints - newPoints,
prizes: [ prizes: p.prizes.map((prize) =>
...p.prizes, prize.id === editingPrize.prizeId
{ ? {
id: Date.now().toString(), ...prize,
name: newPrizeName || 'Prize', name: newPrizeName || "Prize",
points points,
quantity,
} }
] : prize
),
}; };
} }
return p; return p;
}) })
); );
} else {
// We're adding a new prize
const points = parseInt(newPrizePoints) || 0;
const quantity = parseInt(newPrizeQuantity) || 1;
// Clear inputs and close dialog setParticipants((current) =>
setNewPrizeName(''); current.map((p) => {
setNewPrizePoints('');
setEditingParticipantId(null);
};
const removePrize = (participantId: number, prizeId: string) => {
setParticipants(participants.map(p => {
if (p.id === participantId) { if (p.id === participantId) {
const prizeToRemove = p.prizes.find(prize => prize.id === prizeId);
return { return {
...p, ...p,
pointsAvailable: p.pointsAvailable + (prizeToRemove?.points || 0), pointsAvailable: p.pointsAvailable - points * quantity,
prizes: p.prizes.filter(prize => prize.id !== prizeId) prizes: [
...p.prizes,
{
id: Date.now().toString(),
name: newPrizeName || "Prize",
points,
quantity,
},
],
}; };
} }
return p; return p;
})); })
);
}
// Clear inputs and close dialog
setNewPrizeName("");
setNewPrizePoints("");
setNewPrizeQuantity("1");
setEditingParticipantId(null);
setEditingPrize(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) * (prizeToRemove?.quantity || 1),
prizes: p.prizes.filter((prize) => prize.id !== prizeId),
};
}
return p;
})
);
}; };
const handlePointInput = (index: number, value: string) => { const handlePointInput = (index: number, value: string) => {
const newInputs = [...pointInputs]; const newInputs = [...pointInputs];
newInputs[index] = value === '' ? 0 : parseInt(value) || 0; newInputs[index] = value === "" ? 0 : parseInt(value) || 0;
setPointInputs(newInputs); setPointInputs(newInputs);
}; };
const resetApp = () => { const resetApp = () => {
localStorage.removeItem(STORAGE_KEY); localStorage.removeItem(STORAGE_KEY);
setStage('setup'); setStage("setup");
setNumParticipants(2); setNumParticipants(3);
setParticipantSetup([ setParticipantSetup([
{ id: 1, name: 'Player 1' }, { id: 1, name: "Player 1" },
{ id: 2, name: 'Player 2' } { id: 2, name: "Player 2" },
{ id: 3, name: "Player 3" },
]); ]);
setPointInputs([]); setPointInputs([]);
setParticipants([]); setParticipants([]);
@@ -208,13 +285,12 @@ const ArcadeTracker: React.FC = () => {
<div className="p-4 max-w-md mx-auto"> <div className="p-4 max-w-md mx-auto">
<Card className="shadow-lg"> <Card className="shadow-lg">
<CardHeader> <CardHeader>
<CardTitle>Arcade Point Tracker</CardTitle> <CardTitle className="text-2xl">Arcade Point Tracker</CardTitle>
<CardDescription>Set up your group&apos;s point tracking</CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="space-y-6"> <div className="space-y-6">
<div> <div>
<Label>Number of Participants (2-10)</Label> <div>Number of participants:</div>
<div className="flex items-center gap-4 mt-2"> <div className="flex items-center gap-4 mt-2">
<Button <Button
variant="outline" variant="outline"
@@ -224,7 +300,7 @@ const ArcadeTracker: React.FC = () => {
> >
<Minus className="h-4 w-4" /> <Minus className="h-4 w-4" />
</Button> </Button>
<div className="text-2xl font-medium w-8 text-center"> <div className="text-xl font-medium w-8 text-center">
{numParticipants} {numParticipants}
</div> </div>
<Button <Button
@@ -238,12 +314,14 @@ const ArcadeTracker: React.FC = () => {
</div> </div>
</div> </div>
<div className="space-y-4"> <div className="space-y-4">
<Label>Participant Names</Label> <div>Enter participant names:</div>
{participantSetup.map((p, index) => ( {participantSetup.map((p, index) => (
<div key={p.id} className="flex gap-2"> <div key={p.id} className="flex gap-2">
<Input <Input
value={p.name} value={p.name}
onChange={(e) => handleParticipantNameChange(p.id, e.target.value)} onChange={(e) =>
handleParticipantNameChange(p.id, e.target.value)
}
placeholder={`Player ${index + 1}`} placeholder={`Player ${index + 1}`}
onClick={(e) => (e.target as HTMLInputElement).select()} onClick={(e) => (e.target as HTMLInputElement).select()}
className="transition-colors focus:bg-accent focus:text-accent-foreground" className="transition-colors focus:bg-accent focus:text-accent-foreground"
@@ -255,9 +333,9 @@ const ArcadeTracker: React.FC = () => {
className="w-full" className="w-full"
onClick={() => { onClick={() => {
setPointInputs(Array(numParticipants).fill(0)); setPointInputs(Array(numParticipants).fill(0));
setStage('points'); setStage("points");
}} }}
disabled={participantSetup.some(p => !p.name.trim())} disabled={participantSetup.some((p) => !p.name.trim())}
> >
Next Next
</Button> </Button>
@@ -271,20 +349,19 @@ const ArcadeTracker: React.FC = () => {
<div className="p-4 max-w-md mx-auto"> <div className="p-4 max-w-md mx-auto">
<Card className="shadow-lg"> <Card className="shadow-lg">
<CardHeader> <CardHeader>
<CardTitle>Enter Points</CardTitle> <CardTitle className="text-xl">Enter Points</CardTitle>
<CardDescription>Input the points from each card</CardDescription> <CardDescription>Input the points on each participant&apos;s card</CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="space-y-4"> <div className="space-y-4">
{pointInputs.map((points, index) => ( {pointInputs.map((points, index) => (
<div key={index}> <div key={index}>
<Label htmlFor={`card-${index}`}>Card {index + 1} Points</Label> <label htmlFor={`card-${index}`}>{participantSetup[index].name}&apos;s points:</label>
<Input <Input
key={`card-${index}`} // Key helps ensure clean re-render
id={`card-${index}`} id={`card-${index}`}
type="text" // Changed from number type="text"
inputMode="numeric" // Still shows number keyboard on mobile inputMode="numeric"
value={pointInputs[index] === 0 ? '' : pointInputs[index]} // Empty if 0 value={pointInputs[index]}
onChange={(e) => handlePointInput(index, e.target.value)} onChange={(e) => handlePointInput(index, e.target.value)}
className="mt-1" className="mt-1"
placeholder="Enter points" placeholder="Enter points"
@@ -294,19 +371,17 @@ const ArcadeTracker: React.FC = () => {
</div> </div>
</CardContent> </CardContent>
<CardFooter className="flex justify-between"> <CardFooter className="flex justify-between">
<Button <Button variant="outline" onClick={() => setStage("setup")}>
variant="outline"
onClick={() => setStage('setup')}
>
<ArrowLeft className="w-4 h-4 mr-2" /> <ArrowLeft className="w-4 h-4 mr-2" />
Back Back
</Button> </Button>
<Button <Button
onClick={handleUpdatePoints} // Changed from calculatePoints onClick={handleUpdatePoints} // Changed from calculatePoints
disabled={pointInputs.some(p => !p)} disabled={pointInputs.some((p) => !p)}
> >
Calculate Calculate
</Button> </CardFooter> </Button>{" "}
</CardFooter>
</Card> </Card>
</div> </div>
); );
@@ -321,43 +396,80 @@ const ArcadeTracker: React.FC = () => {
<CardTitle className="flex flex-col gap-1"> <CardTitle className="flex flex-col gap-1">
<div>Total Points: {totalPoints}</div> <div>Total Points: {totalPoints}</div>
<div className="text-base font-normal text-muted-foreground"> <div className="text-base font-normal text-muted-foreground">
Points Used: {participants.reduce((total, p) => Points Used:{" "}
total + p.prizes.reduce((sum, prize) => sum + prize.points, 0) {participants.reduce(
, 0)} (total, p) =>
total +
p.prizes.reduce((sum, prize) => sum + prize.points * prize.quantity, 0),
0
)}
</div> </div>
</CardTitle> </CardTitle>
<div className="space-x-2 flex-shrink-0"> <div className="space-x-2 flex-shrink-0">
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
onClick={() => setStage('points')} onClick={() => setStage("points")}
> >
Edit Points <ArrowLeft className="h-4 w-4" />
Edit
</Button> </Button>
<Button <Button
variant="destructive" variant="destructive"
size="sm" size="sm"
onClick={() => setShowResetDialog(true)} onClick={() => setShowResetDialog(true)}
> >
Reset App <Trash2 className="h-4 w-4" />
Reset
</Button> </Button>
</div> </div>
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Progress <Progress
value={(participants.reduce((total, p) => value={
total + p.prizes.reduce((sum, prize) => sum + prize.points, 0) (participants.reduce(
, 0) / totalPoints) * 100} (total, p) =>
className={`h-2 ${(participants.reduce((total, p) => total +
total + p.prizes.reduce((sum, prize) => sum + prize.points, 0) p.prizes.reduce((sum, prize) => sum + prize.points * prize.quantity, 0),
, 0) > totalPoints) ? '[&>div]:bg-destructive' : ''}`} 0
) /
totalPoints) *
100
}
className={`h-2 ${
participants.reduce(
(total, p) =>
total +
p.prizes.reduce((sum, prize) => sum + prize.points * prize.quantity, 0),
0
) > totalPoints
? "[&>div]:bg-destructive"
: ""
}`}
/> />
<CardDescription className={`text-right text-xs ${(participants.reduce((total, p) => <CardDescription
total + p.prizes.reduce((sum, prize) => sum + prize.points, 0) className={`text-right text-xs ${
, 0) > totalPoints) ? 'text-destructive font-medium' : ''}`}> participants.reduce(
{((participants.reduce((total, p) => (total, p) =>
total + p.prizes.reduce((sum, prize) => sum + prize.points, 0) total +
, 0) / totalPoints) * 100).toFixed(1)}% used p.prizes.reduce((sum, prize) => sum + prize.points * prize.quantity, 0),
0
) > totalPoints
? "text-destructive font-medium"
: ""
}`}
>
{(
(participants.reduce(
(total, p) =>
total +
p.prizes.reduce((sum, prize) => sum + prize.points * prize.quantity, 0),
0
) /
totalPoints) *
100
).toFixed(1)}
% used
</CardDescription> </CardDescription>
</div> </div>
</CardHeader> </CardHeader>
@@ -367,7 +479,6 @@ const ArcadeTracker: React.FC = () => {
<div className="max-w-md mx-auto"> <div className="max-w-md mx-auto">
<div className="h-[150px]" /> {/* Spacer for fixed header */} <div className="h-[150px]" /> {/* Spacer for fixed header */}
<div className="p-4 space-y-4"> <div className="p-4 space-y-4">
<Dialog open={showResetDialog} onOpenChange={setShowResetDialog}> <Dialog open={showResetDialog} onOpenChange={setShowResetDialog}>
<DialogContent> <DialogContent>
@@ -377,11 +488,15 @@ const ArcadeTracker: React.FC = () => {
Are you sure you want to reset all data? This action cannot be undone. Are you sure you want to reset all data? This action cannot be undone.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<DialogFooter> <DialogFooter className="flex flex-row gap-2 w-full">
<Button variant="outline" onClick={() => setShowResetDialog(false)}> <Button
variant="secondary"
onClick={() => setShowResetDialog(false)}
className="flex-1"
>
Cancel Cancel
</Button> </Button>
<Button variant="destructive" onClick={resetApp}> <Button variant="default" onClick={resetApp} className="flex-1">
Reset Reset
</Button> </Button>
</DialogFooter> </DialogFooter>
@@ -404,14 +519,37 @@ const ArcadeTracker: React.FC = () => {
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<div className="text-sm text-muted-foreground flex justify-between items-center"> <div className="text-sm text-muted-foreground flex justify-between items-center">
<span>Points Available: {participant.pointsAvailable} / {Math.floor(totalPoints / participants.length)}</span> <span>
<span className={`text-xs ${participant.pointsAvailable < 0 ? 'text-destructive font-medium' : ''}`}> Points Available: {participant.pointsAvailable} /{" "}
{((participant.pointsAvailable / (totalPoints / participants.length)) * 100).toFixed(1)}% {Math.floor(totalPoints / participants.length)}
</span>
<span
className={`text-xs ${
participant.pointsAvailable < 0
? "text-destructive font-medium"
: ""
}`}
>
{(
(participant.pointsAvailable /
(totalPoints / participants.length)) *
100
).toFixed(1)}
%
</span> </span>
</div> </div>
<Progress <Progress
value={100 - ((participant.pointsAvailable / (totalPoints / participants.length)) * 100)} value={
className={`h-2 ${participant.pointsAvailable < 0 ? '[&>div]:bg-destructive' : ''}`} 100 -
(participant.pointsAvailable /
(totalPoints / participants.length)) *
100
}
className={`h-2 ${
participant.pointsAvailable < 0
? "[&>div]:bg-destructive"
: ""
}`}
/> />
</div> </div>
</CardHeader> </CardHeader>
@@ -429,43 +567,139 @@ const ArcadeTracker: React.FC = () => {
type: "spring", type: "spring",
stiffness: 500, stiffness: 500,
damping: 30, damping: 30,
opacity: { duration: 0.2 } opacity: { duration: 0.2 },
}} }}
className="overflow-hidden" className="overflow-hidden"
> >
<div className="flex justify-between items-center p-2 bg-secondary rounded"> <Card className="bg-secondary">
<div> <CardContent className="flex justify-between items-center p-2">
<div className="font-medium">{prize.name}</div> {editingPrize?.prizeId === prize.id ? (
<div className="text-sm text-muted-foreground">{prize.points} points</div> <div className="flex-1 flex gap-4 items-center">
<div className="w-1/2">
<Input
value={editingPrize.name}
onChange={(e) =>
setEditingPrize({ ...editingPrize, name: e.target.value })
}
className="h-8"
/>
</div> </div>
<div className="w-1/4">
<Input
type="text"
inputMode="numeric"
value={editingPrize.points}
onChange={(e) =>
setEditingPrize({ ...editingPrize, points: e.target.value })
}
className="h-8"
/>
</div>
<div className="flex items-center gap-2">
<Button
variant="outline"
size="icon"
className="h-8 w-8"
onClick={() => {
const current = parseInt(editingPrize.quantity) || 1;
setEditingPrize({
...editingPrize,
quantity: Math.max(1, current - 1).toString(),
});
}}
disabled={parseInt(editingPrize.quantity) <= 1}
>
<Minus className="h-3 w-3" />
</Button>
<span className="w-4 text-center">{editingPrize.quantity}</span>
<Button
variant="outline"
size="icon"
className="h-8 w-8"
onClick={() => {
const current = parseInt(editingPrize.quantity) || 1;
setEditingPrize({
...editingPrize,
quantity: (current + 1).toString(),
});
}}
>
<Plus className="h-3 w-3" />
</Button>
</div>
</div>
) : (
<div className="flex gap-2 items-baseline">
<div className="font-medium text-md">{prize.name}</div>
<div className="text-sm text-muted-foreground">
{prize.quantity > 1 ? `${prize.quantity}x ` : ''}{prize.points} points
{prize.quantity > 1 ? ` (${prize.points * prize.quantity} total)` : ''}
</div>
</div>
)}
<div className="flex gap-1">
<Button
variant="ghost"
size="icon"
onClick={() => {
setEditingParticipantId(participant.id);
setEditingPrize({
participantId: participant.id,
prizeId: prize.id,
name: prize.name,
points: prize.points.toString(),
quantity: prize.quantity.toString(),
});
setNewPrizeName(prize.name);
setNewPrizePoints(prize.points.toString());
setNewPrizeQuantity(prize.quantity.toString());
}}
className="h-8 w-8"
>
<Pencil className="h-4 w-4" />
</Button>
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
onClick={() => removePrize(participant.id, prize.id)} onClick={() => removePrize(participant.id, prize.id)}
className="h-8 w-8"
> >
<Trash2 className="h-4 w-4" /> <Trash2 className="h-4 w-4" />
</Button> </Button>
</div> </div>
</CardContent>
</Card>
</motion.div> </motion.div>
))} ))}
</AnimatePresence> </AnimatePresence>
{!participant.prizes.length && ( {!participant.prizes.length && (
<Alert variant="default" className="bg-muted"> <Alert variant="default" className="bg-muted">
<AlertDescription> <AlertDescription className="text-center">
&quot;No prizes added yet. Click &quot;Add Prize&quot; to get started.&quot; No prizes added yet
</AlertDescription> </AlertDescription>
</Alert> </Alert>
)} )}
<Dialog open={editingParticipantId === participant.id} onOpenChange={(open) => { <Dialog
if (!open) setEditingParticipantId(null); open={editingParticipantId === participant.id}
}}> onOpenChange={(open) => {
if (!open) {
setEditingParticipantId(null);
setEditingPrize(null);
setNewPrizeName("");
setNewPrizePoints("");
setNewPrizeQuantity("1");
}
}}
>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button <Button
className="w-full" className="w-full"
variant="outline" variant="outline"
onClick={() => setEditingParticipantId(participant.id)} onClick={() =>
setEditingParticipantId(participant.id)
}
> >
<Plus className="h-4 w-4 mr-2" /> <Plus className="h-4 w-4 mr-2" />
Add Prize Add Prize
@@ -473,43 +707,82 @@ const ArcadeTracker: React.FC = () => {
</DialogTrigger> </DialogTrigger>
<DialogContent> <DialogContent>
<DialogHeader> <DialogHeader>
<DialogTitle>Add Prize for {participant.name}</DialogTitle> <DialogTitle>
{editingPrize ? 'Edit' : 'Add'} Prize for {participant.name}
</DialogTitle>
<DialogDescription> <DialogDescription>
Available Points: {participant.pointsAvailable} Available Points: {participant.pointsAvailable}
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="space-y-4 py-4"> <div className="space-y-4 py-4">
<div className="space-y-2"> <div className="flex gap-4">
<div className="w-2/3 space-y-2">
<Label htmlFor="prize-name">Prize Name</Label> <Label htmlFor="prize-name">Prize Name</Label>
<Input <Input
id="prize-name" id="prize-name"
value={newPrizeName} value={newPrizeName}
onChange={(e) => setNewPrizeName(e.target.value)} onChange={(e) =>
setNewPrizeName(e.target.value)
}
/> />
</div> </div>
<div className="space-y-2"> <div className="w-1/3 space-y-2">
<Label htmlFor="prize-points">Points Cost</Label> <Label htmlFor="prize-points">Points</Label>
<Input <Input
id="prize-points" id="prize-points"
type="text" type="text"
inputMode="numeric" inputMode="numeric"
value={newPrizePoints} value={newPrizePoints}
onChange={(e) => setNewPrizePoints(e.target.value)} onChange={(e) =>
setNewPrizePoints(e.target.value)
}
/> />
</div> </div>
</div> </div>
<DialogFooter> <div>
<Label>Quantity</Label>
<div className="flex items-center gap-4 mt-2">
<Button
variant="outline"
size="icon"
onClick={() => {
const current = parseInt(newPrizeQuantity) || 1;
setNewPrizeQuantity(Math.max(1, current - 1).toString());
}}
disabled={parseInt(newPrizeQuantity) <= 1}
>
<Minus className="h-4 w-4" />
</Button>
<div className="text-xl font-medium w-8 text-center">
{newPrizeQuantity}
</div>
<Button
variant="outline"
size="icon"
onClick={() => {
const current = parseInt(newPrizeQuantity) || 1;
setNewPrizeQuantity((current + 1).toString());
}}
>
<Plus className="h-4 w-4" />
</Button>
</div>
</div>
</div>
<DialogFooter className="flex flex-row gap-2 w-full ">
<Button <Button
variant="outline" variant="outline"
onClick={() => setEditingParticipantId(null)} onClick={() => setEditingParticipantId(null)}
className="flex-1"
> >
Cancel Cancel
</Button> </Button>
<Button <Button
onClick={() => handleAddPrize(participant.id)} onClick={() => handleAddPrize(participant.id)}
disabled={!newPrizeName} disabled={!newPrizeName}
className="flex-1"
> >
Add Prize {editingPrize ? 'Save' : 'Add'} Prize
</Button> </Button>
</DialogFooter> </DialogFooter>
</DialogContent> </DialogContent>
@@ -526,11 +799,11 @@ const ArcadeTracker: React.FC = () => {
); );
switch (stage) { switch (stage) {
case 'setup': case "setup":
return renderSetupStage(); return renderSetupStage();
case 'points': case "points":
return renderPointsStage(); return renderPointsStage();
case 'tracking': case "tracking":
return renderTrackingStage(); return renderTrackingStage();
default: default:
return null; return null;