129 lines
4.5 KiB
TypeScript
129 lines
4.5 KiB
TypeScript
'use client';
|
|
|
|
import { motion } from 'framer-motion';
|
|
import { useRouter } from 'next/navigation';
|
|
import { FaPlay, FaShoppingCart, FaInfoCircle } from 'react-icons/fa';
|
|
import { Album } from '@/lib/types';
|
|
import { useCart } from '@/lib/CartContext';
|
|
import { formatPrice } from '@/lib/utils';
|
|
|
|
interface AlbumCardProps {
|
|
album: Album;
|
|
isPurchased: boolean;
|
|
onPlay: (album: Album) => void;
|
|
onPurchase: (album: Album) => void;
|
|
}
|
|
|
|
export default function AlbumCard({ album, isPurchased, onPlay, onPurchase }: AlbumCardProps) {
|
|
const router = useRouter();
|
|
const { addToCart, isInCart } = useCart();
|
|
|
|
const handleAddToCart = (e: React.MouseEvent) => {
|
|
e.stopPropagation();
|
|
if (!isInCart(album.id) && !isPurchased) {
|
|
addToCart(album);
|
|
}
|
|
};
|
|
|
|
const handleViewDetails = (e?: React.MouseEvent) => {
|
|
if (e) {
|
|
e.stopPropagation();
|
|
}
|
|
router.push(`/album/${album.id}`);
|
|
};
|
|
|
|
const handleCardClick = () => {
|
|
router.push(`/album/${album.id}`);
|
|
};
|
|
|
|
return (
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.5 }}
|
|
viewport={{ once: true }}
|
|
whileHover={{ y: -4, transition: { duration: 0.2 } }}
|
|
onClick={handleCardClick}
|
|
className="paper-card-light overflow-hidden group cursor-pointer hover:shadow-paper-lg transition-shadow"
|
|
>
|
|
{/* Album Cover */}
|
|
<div className="relative aspect-square bg-paper-brown overflow-hidden border-b-2 border-paper-gray">
|
|
{album.coverImage ? (
|
|
<img
|
|
src={album.coverImage}
|
|
alt={album.title}
|
|
className="w-full h-full object-cover"
|
|
/>
|
|
) : (
|
|
<div className="w-full h-full flex items-center justify-center">
|
|
<div className="relative z-10 text-6xl font-bold text-paper-dark/20 p-8 text-center">
|
|
{album.title}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Overlay on hover */}
|
|
<div className="absolute inset-0 bg-paper-dark/80 opacity-0 group-hover:opacity-100 transition-all duration-300 flex items-center justify-center gap-3">
|
|
<button
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
onPlay(album);
|
|
}}
|
|
className="p-4 bg-paper-sand hover:bg-paper-gray border-2 border-paper-dark transition-all shadow-paper"
|
|
aria-label="Play preview"
|
|
>
|
|
<FaPlay className="text-2xl text-paper-dark" />
|
|
</button>
|
|
<button
|
|
onClick={(e) => handleViewDetails(e)}
|
|
className="p-4 bg-paper-light hover:bg-paper-sand border-2 border-paper-brown transition-all shadow-paper"
|
|
aria-label="View details"
|
|
>
|
|
<FaInfoCircle className="text-2xl text-paper-dark" />
|
|
</button>
|
|
{!isPurchased && (
|
|
<button
|
|
onClick={handleAddToCart}
|
|
disabled={isInCart(album.id)}
|
|
className={`p-4 ${
|
|
isInCart(album.id)
|
|
? 'bg-paper-gray border-2 border-paper-brown cursor-not-allowed opacity-50'
|
|
: 'bg-paper-brown hover:bg-paper-dark border-2 border-paper-dark shadow-paper'
|
|
} transition-all`}
|
|
aria-label="Add to cart"
|
|
>
|
|
<FaShoppingCart className="text-2xl text-paper-light" />
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
{/* Purchased Badge */}
|
|
{isPurchased && (
|
|
<div className="absolute top-4 right-4 bg-paper-dark text-paper-light px-3 py-1 border-2 border-paper-brown text-sm font-semibold">
|
|
Owned
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Album Info */}
|
|
<div className="p-6 space-y-3 bg-paper-light cardboard-texture">
|
|
<div>
|
|
<h3 className="text-xl font-bold text-paper-dark group-hover:border-b-2 group-hover:border-paper-dark transition-all inline-block">
|
|
{album.title}
|
|
</h3>
|
|
<p className="text-sm text-paper-gray font-medium mt-1">{album.year} • {album.genre}</p>
|
|
</div>
|
|
|
|
<p className="text-paper-dark text-sm line-clamp-2">{album.description}</p>
|
|
|
|
<div className="flex items-center justify-between pt-2 border-t-2 border-paper-gray">
|
|
<span className="text-sm text-paper-brown font-medium">{album.songs.length} tracks</span>
|
|
{!isPurchased && (
|
|
<span className="text-lg font-bold text-paper-dark">{formatPrice(album.price)}</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
);
|
|
}
|