boxy/app/inventory/page.tsx
2025-06-25 01:10:53 +05:30

290 lines
11 KiB
TypeScript

'use client';
import { useState, useEffect } from 'react';
import { useAuth } from '../context/AuthContext';
import Header from '../components/Header';
import RewardCard from '../components/RewardCard';
import {
fetchInventoryItems,
getRewardById,
type InventoryItem,
type Reward,
formatDate
} from '../lib/rewards';
interface InventoryItemWithReward extends InventoryItem {
reward: Reward | null;
}
export default function InventoryPage() {
const { user } = useAuth();
const [inventoryItems, setInventoryItems] = useState<InventoryItemWithReward[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [selectedItem, setSelectedItem] = useState<InventoryItemWithReward | null>(null);
const [showRedeemModal, setShowRedeemModal] = useState(false);
const [showTradeModal, setShowTradeModal] = useState(false);
useEffect(() => {
const loadInventory = async () => {
if (!user?.uid) return;
setIsLoading(true);
setError(null);
try {
// Fetch inventory items
const items = await fetchInventoryItems(user.uid);
// Fetch reward details for each item
const itemsWithRewards = await Promise.all(
items.map(async (item) => {
try {
const reward = await getRewardById(item.reward_id);
return { ...item, reward };
} catch (error) {
console.error(`Error fetching reward ${item.reward_id}:`, error);
return { ...item, reward: null };
}
})
);
setInventoryItems(itemsWithRewards);
} catch (err) {
console.error('Error loading inventory:', err);
setError(err instanceof Error ? err.message : 'Failed to load inventory');
} finally {
setIsLoading(false);
}
};
loadInventory();
}, [user?.uid]);
const handleRedeem = (item: InventoryItemWithReward) => {
setSelectedItem(item);
setShowRedeemModal(true);
};
const handleTrade = (item: InventoryItemWithReward) => {
setSelectedItem(item);
setShowTradeModal(true);
};
const handleRedeemConfirm = () => {
// TODO: Implement redeem functionality
console.log('Redeeming item:', selectedItem);
alert('Redeem functionality coming soon!');
setShowRedeemModal(false);
setSelectedItem(null);
};
const handleTradeConfirm = () => {
// TODO: Implement trade functionality
console.log('Trading item:', selectedItem);
alert('Trade functionality coming soon!');
setShowTradeModal(false);
setSelectedItem(null);
};
if (!user) {
return (
<>
<Header />
<div className="min-h-screen bg-gradient-to-b from-background to-background/95 pt-16">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8 text-center">
<h1 className="text-2xl font-bold mb-4">Access Denied</h1>
<p className="text-foreground/60">Please sign in to view your inventory.</p>
</div>
</div>
</>
);
}
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">My Inventory</h1>
<p className="text-foreground/60">
Manage your collected rewards and items
</p>
</div>
{/* Loading State */}
{isLoading && (
<div className="text-center py-12">
<div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-purple-600 mb-4"></div>
<p className="text-foreground/60">Loading your inventory...</p>
</div>
)}
{/* Error State */}
{error && (
<div className="text-center py-12">
<div className="text-6xl mb-4"></div>
<h2 className="text-2xl font-bold mb-2">Oops!</h2>
<p className="text-foreground/60 mb-6">
{error}
</p>
<button
onClick={() => window.location.reload()}
className="px-6 py-3 rounded-lg bg-gradient-to-r from-purple-600 to-pink-600 text-white font-semibold hover:opacity-90 transition-opacity"
>
Try Again
</button>
</div>
)}
{/* Inventory Grid */}
{!isLoading && !error && (
<>
{inventoryItems.length === 0 ? (
<div className="text-center py-12">
<div className="text-6xl mb-4">📦</div>
<h2 className="text-2xl font-bold mb-2">Empty Inventory</h2>
<p className="text-foreground/60 mb-6">
You haven&apos;t collected any rewards yet.
</p>
<button
onClick={() => window.location.href = '/boxes'}
className="px-6 py-3 rounded-lg bg-gradient-to-r from-purple-600 to-pink-600 text-white font-semibold hover:opacity-90 transition-opacity"
>
Open Some Boxes
</button>
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{inventoryItems.map((item) => (
<div
key={item.id}
className="bg-background/50 rounded-xl p-6 border border-foreground/10 hover:border-foreground/20 transition-colors"
>
{/* Reward Card */}
{item.reward ? (
<RewardCard reward={item.reward} showValue={false} className="mb-4" />
) : (
<div className="bg-foreground/5 rounded-lg p-4 mb-4 text-center">
<div className="text-4xl mb-2"></div>
<p className="text-foreground/60 text-sm">Reward details unavailable</p>
</div>
)}
{/* Item Details */}
<div className="space-y-2 mb-4">
<div className="flex justify-between text-sm">
<span className="text-foreground/60">Receipt ID:</span>
<span
className="font-mono text-xs truncate ml-2 max-w-[120px]"
title={item.id}
>
{item.id}
</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-foreground/60">Unlocked:</span>
<span className="text-foreground/80">{formatDate(item.unlocked_on)}</span>
</div>
</div>
{/* Action Buttons */}
<div className="flex gap-2">
<button
onClick={() => handleRedeem(item)}
className="flex-1 px-3 py-2 rounded-lg bg-gradient-to-r from-green-600 to-emerald-600 text-white text-sm font-medium hover:opacity-90 transition-opacity"
>
Redeem
</button>
<button
onClick={() => handleTrade(item)}
className="flex-1 px-3 py-2 rounded-lg bg-gradient-to-r from-blue-600 to-cyan-600 text-white text-sm font-medium hover:opacity-90 transition-opacity"
>
Trade
</button>
</div>
</div>
))}
</div>
)}
</>
)}
</div>
</div>
{/* Redeem Modal */}
{showRedeemModal && selectedItem && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
<div className="bg-background rounded-2xl p-6 max-w-md w-full">
<div className="text-center">
<div className="text-4xl mb-4">🎁</div>
<h2 className="text-2xl font-bold mb-2">Redeem Item</h2>
<p className="text-foreground/60 mb-6">
Are you sure you want to redeem this item?
</p>
{selectedItem.reward && (
<div className="mb-6">
<RewardCard reward={selectedItem.reward} showValue={false} />
</div>
)}
<div className="flex gap-3">
<button
onClick={() => setShowRedeemModal(false)}
className="flex-1 px-4 py-3 rounded-lg border border-foreground/20 hover:bg-foreground/5 transition-colors"
>
Cancel
</button>
<button
onClick={handleRedeemConfirm}
className="flex-1 px-4 py-3 rounded-lg bg-gradient-to-r from-green-600 to-emerald-600 text-white font-semibold hover:opacity-90 transition-opacity"
>
Redeem
</button>
</div>
</div>
</div>
</div>
)}
{/* Trade Modal */}
{showTradeModal && selectedItem && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
<div className="bg-background rounded-2xl p-6 max-w-md w-full">
<div className="text-center">
<div className="text-4xl mb-4">🔄</div>
<h2 className="text-2xl font-bold mb-2">Trade Item</h2>
<p className="text-foreground/60 mb-6">
Are you sure you want to trade this item?
</p>
{selectedItem.reward && (
<div className="mb-6">
<RewardCard reward={selectedItem.reward} showValue={false} />
</div>
)}
<div className="flex gap-3">
<button
onClick={() => setShowTradeModal(false)}
className="flex-1 px-4 py-3 rounded-lg border border-foreground/20 hover:bg-foreground/5 transition-colors"
>
Cancel
</button>
<button
onClick={handleTradeConfirm}
className="flex-1 px-4 py-3 rounded-lg bg-gradient-to-r from-blue-600 to-cyan-600 text-white font-semibold hover:opacity-90 transition-opacity"
>
Trade
</button>
</div>
</div>
</div>
</div>
)}
</>
);
}