ifixkart-storefront/components/store/QuickViewModal.tsx

151 lines
6.1 KiB
TypeScript

/**
* @component QuickViewModal
* @purpose Product Quick View modal dialog previewing images, dynamic variant selection, pricing, specs, and Add to Cart action.
* @a11y ARIA modal role, escape listener
* @dependencies cartStore, catalogService, lucide-react
*/
'use client';
import { useState } from 'react';
import Image from 'next/image';
import { X, Star, ShoppingBag, Check, ShieldCheck, Truck } from 'lucide-react';
import { useCartStore } from '@/store/cartStore';
import { formatCurrency, getImageUrl } from '@/lib/utils';
export function QuickViewModal() {
const product = useCartStore((state) => state.quickViewProduct);
const setQuickViewProduct = useCartStore((state) => state.setQuickViewProduct);
const addToCart = useCartStore((state) => state.addToCart);
const [selectedVariantIndex, setSelectedVariantIndex] = useState(0);
const [added, setAdded] = useState(false);
if (!product) return null;
const variant = product.variants[selectedVariantIndex] || product.variants[0];
const mainImage = getImageUrl(product.images[0]?.image_url);
const price = variant?.price || 0;
const comparePrice = variant?.compare_price;
const handleAddToCart = () => {
addToCart(product, variant, 1);
setAdded(true);
setTimeout(() => setAdded(false), 2000);
};
return (
<div className="fixed inset-0 z-50 overflow-y-auto flex items-center justify-center p-4">
{/* Backdrop */}
<div
onClick={() => setQuickViewProduct(null)}
className="fixed inset-0 bg-black/60 backdrop-blur-xs animate-in fade-in duration-200"
/>
{/* Modal Dialog Box */}
<div className="relative bg-white rounded-2xl shadow-2xl max-w-3xl w-full p-6 overflow-hidden z-10 animate-in zoom-in-95 duration-200 border border-gray-200">
<button
onClick={() => setQuickViewProduct(null)}
className="absolute top-4 right-4 p-1 text-gray-400 hover:text-gray-600 rounded-full hover:bg-gray-100 transition-colors z-20"
>
<X className="w-5 h-5" />
</button>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 items-center">
{/* Left Gallery Image */}
<div className="relative h-64 md:h-80 rounded-xl bg-gray-50 overflow-hidden border border-gray-200">
{mainImage ? (
<Image
src={mainImage}
alt={product.name}
fill
className="object-cover"
/>
) : (
<div className="w-full h-full flex items-center justify-center text-xs text-gray-400">No Image</div>
)}
</div>
{/* Right Product Specifications */}
<div className="flex flex-col justify-between h-full">
<div>
<span className="text-[10px] font-bold text-[#e4382f] uppercase tracking-wider">
Quick Preview
</span>
<h3 className="text-lg font-extrabold text-[#1b2559] mt-1 leading-snug">
{product.name}
</h3>
<div className="flex items-center gap-1 text-amber-400 text-xs mt-2 mb-3">
{Array.from({ length: 5 }).map((_, i) => (
<Star key={i} className="w-3.5 h-3.5 fill-current" />
))}
<span className="text-gray-400 text-[11px] ml-1 font-medium">(42 Reviews)</span>
</div>
<div className="flex items-baseline gap-2 mb-4">
<span className="text-2xl font-black text-[#e4382f]">{formatCurrency(price)}</span>
{comparePrice && (
<span className="text-sm text-gray-400 line-through">
{formatCurrency(comparePrice)}
</span>
)}
</div>
<p className="text-xs text-gray-600 leading-relaxed mb-4">
{product.description}
</p>
{/* Variant Selector */}
{product.variants.length > 1 && (
<div className="mb-4">
<label className="block text-xs font-bold text-[#1b2559] mb-1.5">
Select Variant:
</label>
<div className="flex flex-wrap gap-2">
{product.variants.map((v, idx) => (
<button
key={v.variant_id}
onClick={() => setSelectedVariantIndex(idx)}
className={`px-3 py-1.5 text-xs font-bold rounded-lg border transition-all ${
selectedVariantIndex === idx
? 'border-[#e4382f] bg-red-50 text-[#e4382f]'
: 'border-gray-200 hover:border-gray-300 text-gray-700'
}`}
>
{v.sku} - {formatCurrency(v.price)}
</button>
))}
</div>
</div>
)}
</div>
<div>
<div className="flex items-center gap-4 text-[11px] text-gray-500 mb-4 border-t border-b border-gray-100 py-2">
<span className="flex items-center gap-1"><Truck className="w-3.5 h-3.5 text-[#e4382f]" /> Free Express Shipping</span>
<span className="flex items-center gap-1"><ShieldCheck className="w-3.5 h-3.5 text-[#e4382f]" /> 1 Year Warranty</span>
</div>
<button
onClick={handleAddToCart}
className={`w-full py-3 rounded-lg font-bold text-xs flex items-center justify-center gap-2 transition-colors shadow-md cursor-pointer ${
added ? 'bg-[#01b574] text-white' : 'bg-[#e4382f] hover:bg-[#c92e26] text-white'
}`}
>
{added ? (
<>
<Check className="w-4 h-4" /> Added to Cart!
</>
) : (
<>
<ShoppingBag className="w-4 h-4" /> Add to Cart Now
</>
)}
</button>
</div>
</div>
</div>
</div>
</div>
);
}