podzahr/components/CartSidebar.tsx
nfel 8a7842e263
main: added inital version of music shop
Signed-off-by: nfel <nfilsaraee@gmail.com>
2025-11-20 14:42:58 +03:30

209 lines
8.0 KiB
TypeScript

'use client';
import { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { FaTimes, FaTrash, FaShoppingCart } from 'react-icons/fa';
import { useCart } from '@/lib/CartContext';
import { Album, Purchase } from '@/lib/types';
import PaymentModal from './PaymentModal';
import PurchaseSuccessModal from './PurchaseSuccessModal';
interface CartSidebarProps {
isOpen: boolean;
onClose: () => void;
}
export default function CartSidebar({ isOpen, onClose }: CartSidebarProps) {
const { cartItems, removeFromCart, clearCart, getCartTotal } = useCart();
const [showPaymentModal, setShowPaymentModal] = useState(false);
const [albumToPurchase, setAlbumToPurchase] = useState<Album | null>(null);
const [showSuccessModal, setShowSuccessModal] = useState(false);
const [latestPurchase, setLatestPurchase] = useState<Purchase | null>(null);
const [purchasedAlbums, setPurchasedAlbums] = useState<string[]>([]);
const handleCheckout = () => {
if (cartItems.length === 0) return;
// For simplicity, we'll purchase the first item
// In a real app, you'd handle multiple items differently
if (cartItems.length > 0) {
setAlbumToPurchase(cartItems[0].album);
setShowPaymentModal(true);
}
};
const handlePurchaseSuccess = (albumId: string, transactionId: string) => {
const purchase: Purchase = {
albumId,
transactionId,
purchaseDate: new Date(),
};
// Load existing purchases
const savedPurchases = localStorage.getItem('purchases');
const existingPurchases = savedPurchases ? JSON.parse(savedPurchases) : [];
const updatedPurchases = [...existingPurchases, purchase];
localStorage.setItem('purchases', JSON.stringify(updatedPurchases));
setPurchasedAlbums([...purchasedAlbums, albumId]);
setLatestPurchase(purchase);
// Remove from cart
removeFromCart(albumId);
setShowPaymentModal(false);
setShowSuccessModal(true);
};
const handleCloseSuccessModal = () => {
setShowSuccessModal(false);
setAlbumToPurchase(null);
};
return (
<>
<AnimatePresence>
{isOpen && (
<>
{/* Backdrop */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={onClose}
className="fixed inset-0 bg-black/60 backdrop-blur-sm z-40"
/>
{/* Sidebar */}
<motion.div
initial={{ x: '100%' }}
animate={{ x: 0 }}
exit={{ x: '100%' }}
transition={{ type: 'spring', damping: 30, stiffness: 300 }}
className="fixed right-0 top-0 bottom-0 w-full md:w-96 glass-effect border-l border-white/10 z-50 flex flex-col"
>
{/* Header */}
<div className="flex items-center justify-between p-6 border-b border-white/10">
<div className="flex items-center gap-3">
<FaShoppingCart className="text-2xl text-accent-cyan" />
<h2 className="text-2xl font-bold text-white">
Cart ({cartItems.length})
</h2>
</div>
<button
onClick={onClose}
className="p-2 hover:bg-white/10 rounded-full transition-colors"
aria-label="Close cart"
>
<FaTimes className="text-xl text-gray-400" />
</button>
</div>
{/* Cart Items */}
<div className="flex-1 overflow-y-auto p-6">
{cartItems.length === 0 ? (
<div className="flex flex-col items-center justify-center h-full text-center">
<FaShoppingCart className="text-6xl text-gray-600 mb-4" />
<p className="text-gray-400 text-lg">Your cart is empty</p>
<p className="text-gray-500 text-sm mt-2">Add some albums to get started</p>
</div>
) : (
<div className="space-y-4">
{cartItems.map((item) => (
<motion.div
key={item.album.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
className="glass-effect rounded-xl p-4"
>
<div className="flex gap-4">
{/* Album Cover */}
<div className="w-20 h-20 rounded-lg bg-gradient-to-br from-accent-cyan/20 to-accent-orange/20 flex items-center justify-center flex-shrink-0">
<span className="text-xs text-white/50 font-bold text-center px-2">
{item.album.title}
</span>
</div>
{/* Album Info */}
<div className="flex-1 min-w-0">
<h3 className="text-white font-semibold truncate">
{item.album.title}
</h3>
<p className="text-sm text-gray-400">
{item.album.year} {item.album.songs.length} tracks
</p>
<p className="text-accent-orange font-bold mt-2">
${item.album.price}
</p>
</div>
{/* Remove Button */}
<button
onClick={() => removeFromCart(item.album.id)}
className="p-2 hover:bg-red-500/20 rounded-lg transition-colors self-start"
aria-label="Remove from cart"
>
<FaTrash className="text-red-400" />
</button>
</div>
</motion.div>
))}
</div>
)}
</div>
{/* Footer */}
{cartItems.length > 0 && (
<div className="border-t border-white/10 p-6 space-y-4">
{/* Total */}
<div className="flex items-center justify-between text-xl">
<span className="text-gray-300 font-semibold">Total</span>
<span className="text-accent-orange font-bold">
${getCartTotal().toFixed(2)}
</span>
</div>
{/* Buttons */}
<div className="space-y-3">
<button
onClick={handleCheckout}
className="w-full py-4 bg-gradient-to-r from-accent-orange to-accent-orange/80 hover:from-accent-orange/90 hover:to-accent-orange/70 rounded-lg font-semibold text-white transition-all glow-orange"
>
Proceed to Checkout
</button>
<button
onClick={clearCart}
className="w-full py-3 bg-white/5 hover:bg-white/10 border border-white/10 rounded-lg font-semibold text-gray-300 transition-all"
>
Clear Cart
</button>
</div>
</div>
)}
</motion.div>
</>
)}
</AnimatePresence>
{/* Payment Modal */}
<PaymentModal
album={albumToPurchase}
onClose={() => {
setShowPaymentModal(false);
setAlbumToPurchase(null);
}}
onSuccess={handlePurchaseSuccess}
/>
{/* Purchase Success Modal */}
<PurchaseSuccessModal
show={showSuccessModal}
album={albumToPurchase}
purchase={latestPurchase}
onClose={handleCloseSuccessModal}
/>
</>
);
}