podzahr/app/album/[id]/page.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

295 lines
11 KiB
TypeScript

'use client';
import { useState, useEffect } from 'react';
import { useParams, useRouter } from 'next/navigation';
import { motion } from 'framer-motion';
import { FaPlay, FaShoppingCart, FaArrowLeft, FaClock, FaMusic } from 'react-icons/fa';
import { albums } from '@/lib/data';
import { Album, Purchase } from '@/lib/types';
import { useCart } from '@/lib/CartContext';
import Header from '@/components/Header';
import CartSidebar from '@/components/CartSidebar';
import MusicPlayer from '@/components/MusicPlayer';
import PaymentModal from '@/components/PaymentModal';
import PurchaseSuccessModal from '@/components/PurchaseSuccessModal';
export default function AlbumDetailPage() {
const params = useParams();
const router = useRouter();
const { addToCart, isInCart } = useCart();
const [album, setAlbum] = useState<Album | null>(null);
const [purchasedAlbums, setPurchasedAlbums] = useState<string[]>([]);
const [purchases, setPurchases] = useState<Purchase[]>([]);
const [cartOpen, setCartOpen] = useState(false);
const [currentAlbum, setCurrentAlbum] = useState<Album | null>(null);
const [albumToPurchase, setAlbumToPurchase] = useState<Album | null>(null);
const [showSuccessModal, setShowSuccessModal] = useState(false);
const [latestPurchase, setLatestPurchase] = useState<Purchase | null>(null);
useEffect(() => {
const foundAlbum = albums.find((a) => a.id === params.id);
setAlbum(foundAlbum || null);
// Load purchases
const savedPurchases = localStorage.getItem('purchases');
if (savedPurchases) {
const parsedPurchases = JSON.parse(savedPurchases);
setPurchases(parsedPurchases);
setPurchasedAlbums(parsedPurchases.map((p: Purchase) => p.albumId));
}
}, [params.id]);
const handlePurchaseSuccess = (albumId: string, transactionId: string) => {
const purchase: Purchase = {
albumId,
transactionId,
purchaseDate: new Date(),
};
const updatedPurchases = [...purchases, purchase];
setPurchases(updatedPurchases);
setPurchasedAlbums([...purchasedAlbums, albumId]);
setLatestPurchase(purchase);
localStorage.setItem('purchases', JSON.stringify(updatedPurchases));
setAlbumToPurchase(null);
setShowSuccessModal(true);
};
const handleCloseSuccessModal = () => {
setShowSuccessModal(false);
if (albumToPurchase) {
setCurrentAlbum(albumToPurchase);
}
};
const handleAddToCart = () => {
if (album && !isInCart(album.id)) {
addToCart(album);
}
};
const isPurchased = album ? purchasedAlbums.includes(album.id) : false;
const inCart = album ? isInCart(album.id) : false;
if (!album) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="text-center">
<h1 className="text-3xl font-bold text-white mb-4">Album not found</h1>
<button
onClick={() => router.push('/')}
className="px-6 py-3 bg-accent-cyan hover:bg-accent-cyan/80 rounded-lg text-white transition-all"
>
Go Back Home
</button>
</div>
</div>
);
}
const totalDuration = album.songs.reduce((total, song) => {
const [minutes, seconds] = song.duration.split(':').map(Number);
return total + minutes * 60 + seconds;
}, 0);
const formatTotalDuration = (seconds: number) => {
const hours = Math.floor(seconds / 3600);
const mins = Math.floor((seconds % 3600) / 60);
if (hours > 0) {
return `${hours}h ${mins}m`;
}
return `${mins} min`;
};
return (
<>
<Header onCartClick={() => setCartOpen(true)} />
<div className="min-h-screen pt-20">
{/* Back Button */}
<div className="max-w-7xl mx-auto px-4 md:px-8 py-6">
<button
onClick={() => router.push('/')}
className="flex items-center gap-2 text-gray-400 hover:text-accent-cyan transition-colors"
>
<FaArrowLeft />
Back to Albums
</button>
</div>
{/* Album Header */}
<div className="max-w-7xl mx-auto px-4 md:px-8 pb-12">
<div className="grid md:grid-cols-2 gap-12 items-start">
{/* Album Cover */}
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6 }}
className="relative"
>
<div className="aspect-square rounded-2xl overflow-hidden bg-gradient-to-br from-primary-600/50 to-primary-800/50 flex items-center justify-center border-2 border-accent-cyan/50 glow-cyan">
<div className="absolute inset-0 bg-gradient-to-br from-accent-cyan/20 to-accent-orange/20"></div>
<div className="relative z-10 text-6xl md:text-8xl font-bold text-white/20 p-8 text-center">
{album.title}
</div>
</div>
{isPurchased && (
<div className="absolute top-6 right-6 bg-accent-cyan text-white px-4 py-2 rounded-full text-sm font-semibold">
Owned
</div>
)}
</motion.div>
{/* Album Info */}
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6 }}
className="space-y-6"
>
<div>
<p className="text-sm text-gray-400 mb-2">{album.year} {album.genre}</p>
<h1 className="text-4xl md:text-5xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-accent-cyan to-accent-orange mb-4">
{album.title}
</h1>
<p className="text-xl text-gray-300 leading-relaxed">
{album.description}
</p>
</div>
{/* Album Stats */}
<div className="flex gap-6 text-gray-400">
<div className="flex items-center gap-2">
<FaMusic className="text-accent-cyan" />
<span>{album.songs.length} tracks</span>
</div>
<div className="flex items-center gap-2">
<FaClock className="text-accent-cyan" />
<span>{formatTotalDuration(totalDuration)}</span>
</div>
</div>
{/* Price */}
{!isPurchased && (
<div className="text-3xl font-bold text-accent-orange">
${album.price}
</div>
)}
{/* Action Buttons */}
<div className="flex gap-4 pt-4">
<button
onClick={() => setCurrentAlbum(album)}
className="flex-1 py-4 bg-accent-cyan hover:bg-accent-cyan/80 rounded-lg font-semibold text-white transition-all glow-cyan flex items-center justify-center gap-2"
>
<FaPlay />
{isPurchased ? 'Play Album' : 'Preview'}
</button>
{!isPurchased && (
<>
<button
onClick={() => setAlbumToPurchase(album)}
className="flex-1 py-4 bg-accent-orange hover:bg-accent-orange/80 rounded-lg font-semibold text-white transition-all glow-orange flex items-center justify-center gap-2"
>
Buy Now
</button>
<button
onClick={handleAddToCart}
disabled={inCart}
className={`py-4 px-6 ${
inCart
? 'bg-gray-700 cursor-not-allowed'
: 'bg-white/10 hover:bg-white/20 border border-white/20'
} rounded-lg font-semibold text-white transition-all flex items-center justify-center gap-2`}
>
<FaShoppingCart />
{inCart ? 'In Cart' : 'Add to Cart'}
</button>
</>
)}
</div>
</motion.div>
</div>
</div>
{/* Track List */}
<div className="max-w-7xl mx-auto px-4 md:px-8 pb-20">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
className="glass-effect rounded-2xl p-8"
>
<h2 className="text-2xl font-bold text-white mb-6">Track List</h2>
<div className="space-y-2">
{album.songs.map((song, index) => (
<motion.div
key={song.id}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.4, delay: index * 0.05 }}
className="flex items-center justify-between p-4 rounded-lg hover:bg-white/5 transition-colors group cursor-pointer"
onClick={() => setCurrentAlbum(album)}
>
<div className="flex items-center gap-4 flex-1">
<span className="text-gray-500 font-mono text-sm w-8">
{String(index + 1).padStart(2, '0')}
</span>
<div className="flex-1">
<h3 className="text-white font-medium group-hover:text-accent-cyan transition-colors">
{song.title}
</h3>
</div>
</div>
<div className="flex items-center gap-6">
{!isPurchased && (
<span className="text-xs text-accent-orange px-2 py-1 bg-accent-orange/20 rounded">
Preview
</span>
)}
<span className="text-gray-400 text-sm font-mono">
{song.duration}
</span>
<FaPlay className="text-accent-cyan opacity-0 group-hover:opacity-100 transition-opacity" />
</div>
</motion.div>
))}
</div>
</motion.div>
</div>
</div>
{/* Music Player */}
{currentAlbum && (
<MusicPlayer
album={currentAlbum}
isPurchased={purchasedAlbums.includes(currentAlbum.id)}
onClose={() => setCurrentAlbum(null)}
onPurchase={setAlbumToPurchase}
/>
)}
{/* Payment Modal */}
<PaymentModal
album={albumToPurchase}
onClose={() => setAlbumToPurchase(null)}
onSuccess={handlePurchaseSuccess}
/>
{/* Purchase Success Modal */}
<PurchaseSuccessModal
show={showSuccessModal}
album={albumToPurchase}
purchase={latestPurchase}
onClose={handleCloseSuccessModal}
/>
{/* Cart Sidebar */}
<CartSidebar isOpen={cartOpen} onClose={() => setCartOpen(false)} />
</>
);
}