235 lines
8.6 KiB
TypeScript
235 lines
8.6 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import { useAuth } from '../context/AuthContext';
|
|
import Header from '../components/Header';
|
|
import PaymentModal from '../components/PaymentModal';
|
|
|
|
// Mock data for boxes - replace with actual data from your backend
|
|
const MOCK_BOXES = [
|
|
{
|
|
id: '1',
|
|
tier: 'Small',
|
|
price: 100,
|
|
color: 'from-blue-500 to-blue-600',
|
|
remaining: 2500,
|
|
total: 3000,
|
|
image: '/box-small.png',
|
|
description: 'Perfect for beginners. Contains common to rare items.',
|
|
},
|
|
{
|
|
id: '2',
|
|
tier: 'Medium',
|
|
price: 1000,
|
|
color: 'from-purple-500 to-purple-600',
|
|
remaining: 1500,
|
|
total: 2000,
|
|
image: '/box-medium.png',
|
|
description: 'For serious collectors. Contains rare to epic items.',
|
|
},
|
|
{
|
|
id: '3',
|
|
tier: 'Big',
|
|
price: 10000,
|
|
color: 'from-pink-500 to-pink-600',
|
|
remaining: 500,
|
|
total: 1000,
|
|
image: '/box-big.png',
|
|
description: 'Premium boxes with epic to legendary items.',
|
|
},
|
|
];
|
|
|
|
type SortOption = 'price-asc' | 'price-desc' | 'remaining-asc' | 'remaining-desc';
|
|
type FilterTier = 'all' | 'small' | 'medium' | 'big';
|
|
|
|
export default function BoxesPage() {
|
|
const { user } = useAuth();
|
|
const [sortBy, setSortBy] = useState<SortOption>('price-asc');
|
|
const [filterTier, setFilterTier] = useState<FilterTier>('all');
|
|
const [selectedBox, setSelectedBox] = useState<typeof MOCK_BOXES[0] | null>(null);
|
|
const [isPaymentModalOpen, setIsPaymentModalOpen] = useState(false);
|
|
|
|
const filteredAndSortedBoxes = MOCK_BOXES
|
|
.filter(box => filterTier === 'all' || box.tier.toLowerCase() === filterTier)
|
|
.sort((a, b) => {
|
|
switch (sortBy) {
|
|
case 'price-asc':
|
|
return a.price - b.price;
|
|
case 'price-desc':
|
|
return b.price - a.price;
|
|
case 'remaining-asc':
|
|
return a.remaining - b.remaining;
|
|
case 'remaining-desc':
|
|
return b.remaining - a.remaining;
|
|
default:
|
|
return 0;
|
|
}
|
|
});
|
|
|
|
const handleOpenBox = (box: typeof MOCK_BOXES[0]) => {
|
|
if (!user) {
|
|
// Redirect to login or show login modal
|
|
return;
|
|
}
|
|
setSelectedBox(box);
|
|
setIsPaymentModalOpen(true);
|
|
};
|
|
|
|
const handleClosePaymentModal = () => {
|
|
setIsPaymentModalOpen(false);
|
|
setSelectedBox(null);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<Header />
|
|
<div className="min-h-screen bg-gradient-to-b from-background to-background/95 pt-16">
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
|
{/* Page Header */}
|
|
<div className="mb-8">
|
|
<h1 className="text-3xl font-bold mb-2">Mystery Boxes</h1>
|
|
<p className="text-foreground/60">
|
|
Choose your box and discover amazing prizes
|
|
</p>
|
|
</div>
|
|
|
|
{/* Filters and Sort */}
|
|
<div className="flex flex-col sm:flex-row gap-4 mb-8">
|
|
{/* Tier Filter */}
|
|
<div className="flex gap-2">
|
|
<button
|
|
onClick={() => setFilterTier('all')}
|
|
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
|
filterTier === 'all'
|
|
? 'bg-purple-600 text-white'
|
|
: 'bg-foreground/5 hover:bg-foreground/10'
|
|
}`}
|
|
>
|
|
All
|
|
</button>
|
|
<button
|
|
onClick={() => setFilterTier('small')}
|
|
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
|
filterTier === 'small'
|
|
? 'bg-blue-600 text-white'
|
|
: 'bg-foreground/5 hover:bg-foreground/10'
|
|
}`}
|
|
>
|
|
Small
|
|
</button>
|
|
<button
|
|
onClick={() => setFilterTier('medium')}
|
|
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
|
filterTier === 'medium'
|
|
? 'bg-purple-600 text-white'
|
|
: 'bg-foreground/5 hover:bg-foreground/10'
|
|
}`}
|
|
>
|
|
Medium
|
|
</button>
|
|
<button
|
|
onClick={() => setFilterTier('big')}
|
|
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
|
filterTier === 'big'
|
|
? 'bg-pink-600 text-white'
|
|
: 'bg-foreground/5 hover:bg-foreground/10'
|
|
}`}
|
|
>
|
|
Big
|
|
</button>
|
|
</div>
|
|
|
|
{/* Sort Dropdown */}
|
|
<select
|
|
value={sortBy}
|
|
onChange={(e) => setSortBy(e.target.value as SortOption)}
|
|
className="ml-auto px-4 py-2 rounded-lg bg-foreground/5 border border-foreground/10 text-sm focus:outline-none focus:ring-2 focus:ring-purple-500"
|
|
>
|
|
<option value="price-asc">Price: Low to High</option>
|
|
<option value="price-desc">Price: High to Low</option>
|
|
<option value="remaining-desc">Most Available</option>
|
|
<option value="remaining-asc">Least Available</option>
|
|
</select>
|
|
</div>
|
|
|
|
{/* Boxes Grid */}
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
{filteredAndSortedBoxes.map((box) => (
|
|
<div
|
|
key={box.id}
|
|
className="relative group rounded-2xl overflow-hidden bg-gradient-to-br p-[1px] hover:scale-[1.02] transition-transform"
|
|
>
|
|
<div className={`absolute inset-0 bg-gradient-to-br ${box.color} opacity-20`} />
|
|
<div className="relative bg-background/95 p-6 rounded-2xl">
|
|
{/* Box Image */}
|
|
<div className="aspect-square mb-4 rounded-lg bg-foreground/5 flex items-center justify-center">
|
|
<div className="text-4xl">🎁</div>
|
|
</div>
|
|
|
|
{/* Box Info */}
|
|
<div className="space-y-4">
|
|
<div>
|
|
<h3 className="text-2xl font-bold mb-1">{box.tier} Box</h3>
|
|
<p className="text-foreground/60 text-sm">{box.description}</p>
|
|
</div>
|
|
|
|
{/* Progress Bar */}
|
|
<div className="space-y-1">
|
|
<div className="flex justify-between text-sm">
|
|
<span className="text-foreground/60">Remaining</span>
|
|
<span className="font-medium">{box.remaining}/{box.total}</span>
|
|
</div>
|
|
<div className="h-2 bg-foreground/5 rounded-full overflow-hidden">
|
|
<div
|
|
className={`h-full bg-gradient-to-r ${box.color} transition-all`}
|
|
style={{ width: `${(box.remaining / box.total) * 100}%` }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Price and Action */}
|
|
<div className="flex items-center justify-between pt-2">
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-lg font-semibold">${box.price}</span>
|
|
<span className="text-foreground/60 text-sm">USD</span>
|
|
</div>
|
|
<button
|
|
onClick={() => handleOpenBox(box)}
|
|
className={`px-4 py-2 rounded-full text-sm font-medium transition-opacity ${
|
|
user
|
|
? 'bg-gradient-to-r from-purple-600 to-pink-600 text-white hover:opacity-90'
|
|
: 'bg-foreground/10 text-foreground/60 cursor-not-allowed'
|
|
}`}
|
|
disabled={!user}
|
|
>
|
|
{user ? 'Open Box' : 'Sign in to Open'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Empty State */}
|
|
{filteredAndSortedBoxes.length === 0 && (
|
|
<div className="text-center py-12">
|
|
<div className="text-4xl mb-4">🔍</div>
|
|
<h3 className="text-xl font-semibold mb-2">No boxes found</h3>
|
|
<p className="text-foreground/60">
|
|
Try adjusting your filters to see more boxes
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Payment Modal */}
|
|
<PaymentModal
|
|
isOpen={isPaymentModalOpen}
|
|
onClose={handleClosePaymentModal}
|
|
box={selectedBox}
|
|
/>
|
|
</>
|
|
);
|
|
}
|