"use client"; import React, { useState, useEffect, useMemo } from 'react'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; import { Star, X, ChevronLeft, ChevronRight } from 'lucide-react'; import { Product } from '../types'; import { formatCurrency, getImageUrl, parseMoney } from '../lib/utils'; import { useCartStore } from '@/store/cartStore'; import { ProductResponse, ProductVariantResponse } from '@/services/api/catalogService'; import { requireLoginForCheckout } from '@/lib/requireLoginForCheckout'; interface QuickViewModalProps { product: Product; products?: Product[]; onClose: () => void; } function toImgStr(v: unknown): string { if (typeof v === 'string') return v; if (v && typeof v === 'object') { const obj = v as Record; return String(obj.image_url ?? obj.url ?? obj.src ?? ''); } return ''; } function productKey(p: Product): string { return String((p as any).product_id || p.slug || p.id || ''); } function getProductGallery(product: Product): string[] { const urls: string[] = []; const push = (raw: unknown) => { const s = toImgStr(raw); if (s && !urls.includes(s)) urls.push(s); }; push(product.image); push(product.imageHover); const extra = (product as any).images; if (Array.isArray(extra)) { extra.forEach((img: any) => push(typeof img === 'string' ? img : img?.image_url)); } return urls.length > 0 ? urls : ['']; } function buildProductResponse(product: Product): { resp: ProductResponse; variant: ProductVariantResponse } { const realProductId = (product as any).product_id || null; const realVariantId = (product as any).first_variant_id || null; const rawId = realProductId ?? product.id ?? product.slug ?? `rnd-${Math.random()}`; const idStr = String(rawId); const productId = realProductId || (idStr.startsWith('prod-') ? idStr : `prod-${idStr}`); const priceVal = parseMoney(product.price); const imageUrl = toImgStr(product.image); const variantId = realVariantId || `var-${idStr}`; const variant: ProductVariantResponse = { variant_id: variantId, product_id: productId, sku: (product as any).sku || `SKU-${idStr}`, barcode: null, price: priceVal, compare_price: parseMoney(product.oldPrice) || null, cost_price: priceVal, low_stock_threshold: 5, status: 'ACTIVE', available_stock: (product as any).totalStock ?? 100, images: [], }; const resp: ProductResponse = { product_id: productId, category_id: product.category || 'uncategorized', brand_id: (product as any).brand || null, device_series_id: null, device_model_id: null, name: product.name, slug: product.slug || idStr, full_path: `/products/${product.slug || idStr}`, description: product.description || null, status: 'ACTIVE', created_at: new Date().toISOString(), images: [ { image_id: `img-${idStr}`, product_id: productId, image_url: imageUrl, alt_text: product.name, sort_order: 1, is_banner: false, }, ], variants: [variant], rating: product.rating, review_count: product.reviewsCount, badge: product.discount ? 'SALE' : null, }; return { resp, variant }; } const DEFAULT_FEATURES = [ 'Upto 50h Playtime', '13mm Drivers', '1.85" Display', ]; export const QuickViewModal: React.FC = ({ product: initialProduct, products: productsProp, onClose, }) => { const router = useRouter(); const addToCart = useCartStore((s) => s.addToCart); const productList = useMemo(() => { if (productsProp && productsProp.length > 0) return productsProp; return [initialProduct]; }, [productsProp, initialProduct]); const initialIndex = Math.max( 0, productList.findIndex((p) => productKey(p) === productKey(initialProduct)) ); const [productIndex, setProductIndex] = useState(initialIndex >= 0 ? initialIndex : 0); const [imageIndex, setImageIndex] = useState(0); const [quantity, setQuantity] = useState(1); const [added, setAdded] = useState(false); const product = productList[productIndex] || initialProduct; const canSwitchProduct = productList.length > 1; const gallery = useMemo(() => getProductGallery(product), [product]); const canSwitchImage = gallery.length > 1; const currentImageUrl = getImageUrl(gallery[imageIndex] || gallery[0] || ''); const features = product.features && product.features.length > 0 ? product.features.slice(0, 4) : DEFAULT_FEATURES; const reviewCount = product.reviewsCount ?? 1; const slug = product.slug || product.id; const stock = typeof (product as any).totalStock === 'number' ? (product as any).totalStock : null; useEffect(() => { setImageIndex(0); setQuantity(1); setAdded(false); }, [productIndex]); useEffect(() => { document.body.style.overflow = 'hidden'; const handleKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); if (e.key === 'ArrowLeft' && canSwitchProduct) { setProductIndex((i) => (i === 0 ? productList.length - 1 : i - 1)); } if (e.key === 'ArrowRight' && canSwitchProduct) { setProductIndex((i) => (i === productList.length - 1 ? 0 : i + 1)); } }; document.addEventListener('keydown', handleKey); return () => { document.body.style.overflow = ''; document.removeEventListener('keydown', handleKey); }; }, [onClose, canSwitchProduct, productList.length]); const goPrevProduct = (e: React.MouseEvent) => { e.stopPropagation(); if (!canSwitchProduct) return; setProductIndex((i) => (i === 0 ? productList.length - 1 : i - 1)); }; const goNextProduct = (e: React.MouseEvent) => { e.stopPropagation(); if (!canSwitchProduct) return; setProductIndex((i) => (i === productList.length - 1 ? 0 : i + 1)); }; const goPrevImage = (e: React.MouseEvent) => { e.stopPropagation(); if (!canSwitchImage) return; setImageIndex((i) => (i === 0 ? gallery.length - 1 : i - 1)); }; const goNextImage = (e: React.MouseEvent) => { e.stopPropagation(); if (!canSwitchImage) return; setImageIndex((i) => (i === gallery.length - 1 ? 0 : i + 1)); }; const handleAddToCart = () => { const { resp, variant } = buildProductResponse(product); void addToCart(resp, variant, quantity); setAdded(true); setTimeout(() => setAdded(false), 1800); }; const handleBuyNow = () => { const { resp, variant } = buildProductResponse(product); void addToCart(resp, variant, quantity); onClose(); if (!requireLoginForCheckout('/checkout')) return; router.push('/checkout'); }; return (
{/* Outside arrows — switch products */} {canSwitchProduct && ( <> )}
e.stopPropagation()} className="bg-white w-full max-w-[1000px] md:h-[min(500px,82vh)] md:w-[min(1000px,calc(82vh*2))] overflow-hidden shadow-2xl relative flex flex-col md:flex-row text-left mx-10 md:mx-16" > {/* Left: square image panel — width = height (half of 2:1 modal) */}
{currentImageUrl ? ( {product.name} ) : (
No Image
)} {canSwitchImage && ( <> )}
{canSwitchImage && (
{gallery.map((_, idx) => (
)}
{/* Right: details */}

{product.name}

{[...Array(5)].map((_, i) => ( ))}
({reviewCount} review{reviewCount === 1 ? '' : 's'})
{product.oldPrice && product.oldPrice > parseMoney(product.price) && ( {formatCurrency(product.oldPrice)} )} {formatCurrency(parseMoney(product.price))}
    {features.map((f) => (
  • {f}
  • ))}
{stock != null && stock > 0 && ( {stock} in stock )}
setQuantity(Math.max(1, Number(e.target.value) || 1))} className="w-14 h-11 text-center border border-gray-300 rounded-md text-[14px] font-medium focus:outline-none focus:border-primary" aria-label="Quantity" />

Categories:{' '} {product.category ? String(product.category) .replace(/[_-]+/g, ' ') .replace(/\b\w/g, (c) => c.toUpperCase()) : 'Our Store, Laptop & Computers, Smart Devices'}

); };