'use client'; import React, { useEffect, useMemo, useState } from 'react'; import Link from 'next/link'; import { Star, PackageX } from 'lucide-react'; import { catalogService, CategoryResponse, ProductCardResponse, } from '@/services/api/catalogService'; import { formatCurrency, getImageUrl, parseMoney } from '@/lib/utils'; import { CmsMegaMenuPanel, hasCmsMegaGroups, } from '@/components/CmsMegaMenuPanel'; import type { MegaMenuData } from '@/services/api/storefrontService'; import { storefrontService } from '@/services/api/storefrontService'; type DynamicTab = { id: string; label: string; slug?: string; }; type MegaProduct = { id: string; name: string; slug: string; image: string; price: number; oldPrice?: number; discount?: number; rating: number; categoryId?: string; }; function mapCard(p: ProductCardResponse): MegaProduct { const price = parseMoney(p.price); const old = p.compare_price != null && parseMoney(p.compare_price) > price ? parseMoney(p.compare_price) : undefined; const discount = parseMoney(p.discount_percent) || (old ? Math.round(((old - price) / old) * 100) : undefined); return { id: p.product_id, name: p.name, slug: p.slug, image: getImageUrl(p.thumbnail_url) || '', price, oldPrice: old, discount: discount || undefined, rating: Number(p.rating) || 4, categoryId: p.category_id, }; } function MegaProductCard({ product }: { product: MegaProduct }) { const rating = Math.round(product.rating || 4); const [imgFailed, setImgFailed] = useState(false); const showImage = Boolean(product.image) && !imgFailed; return (
{product.discount ? ( -{product.discount}% ) : null} {showImage ? ( // eslint-disable-next-line @next/next/no-img-element {product.name} setImgFailed(true)} className="max-h-full max-w-[85%] object-contain group-hover/card:scale-[1.03] transition-transform duration-300" /> ) : (
)}
{product.name}
{Array.from({ length: 5 }).map((_, i) => ( ))}
{product.oldPrice != null && product.oldPrice > product.price ? ( {formatCurrency(product.oldPrice)} ) : null} {formatCurrency(product.price)}
); } export const ProductsMegaMenu: React.FC<{ cms?: MegaMenuData | null }> = ({ cms, }) => { const [activeTabId, setActiveTabId] = useState('all'); const [allProducts, setAllProducts] = useState([]); const [categories, setCategories] = useState([]); const [loading, setLoading] = useState(true); const [remote, setRemote] = useState(cms ?? null); useEffect(() => { if (hasCmsMegaGroups(cms)) { setRemote(cms ?? null); return; } let cancelled = false; storefrontService.getMegaMenu().then((menus) => { if (cancelled) return; setRemote(menus.products || null); }); return () => { cancelled = true; }; }, [cms]); useEffect(() => { if (hasCmsMegaGroups(remote) || hasCmsMegaGroups(cms)) return; let cancelled = false; const load = async () => { setLoading(true); try { const [cats, productRes] = await Promise.all([ catalogService.getCategories(), catalogService.getProductsPaginated({ limit: 48 }), ]); if (cancelled) return; const catList = cats || []; setCategories(catList); setAllProducts((productRes.products || []).map((p) => mapCard(p))); const rootCats = catList.filter((c) => !c.parent_category_id); if (rootCats.length > 0) { setActiveTabId(rootCats[0].category_id); } else if (catList.length > 0) { setActiveTabId(catList[0].category_id); } } finally { if (!cancelled) setLoading(false); } }; void load(); return () => { cancelled = true; }; }, [cms, remote]); const tabs: DynamicTab[] = useMemo(() => { const rootCats = categories.filter((c) => !c.parent_category_id); const topCats = rootCats.length > 0 ? rootCats : categories; const dynamic = topCats.slice(0, 6).map((c) => ({ id: c.category_id, label: c.name, slug: c.slug, })); return dynamic.length > 0 ? dynamic : [{ id: 'all', label: 'All Products' }]; }, [categories]); const filteredProducts = useMemo(() => { if (activeTabId === 'all') { return allProducts.slice(0, 5); } const childIds = new Set( categories .filter((c) => c.parent_category_id === activeTabId) .map((c) => c.category_id) ); childIds.add(activeTabId); const matches = allProducts.filter( (p) => p.categoryId && childIds.has(p.categoryId) ); return (matches.length > 0 ? matches : allProducts).slice(0, 5); }, [allProducts, categories, activeTabId]); if (hasCmsMegaGroups(remote)) { return ; } const activeTabLabel = tabs.find((t) => t.id === activeTabId)?.label || 'Products'; return (
{/* Centered tabs — dynamic from database categories */}
{tabs.map((tab, idx) => ( {idx > 0 && ( | )} ))}
{loading ? (
{Array.from({ length: 5 }).map((_, i) => (
))}
) : filteredProducts.length === 0 ? (

No products in {activeTabLabel}

View all products
) : (
{Array.from({ length: 5 }).map((_, i) => { const product = filteredProducts[i]; if (!product) { return
; } return ( ); })}
)}
); }; export function isProductsNavLabel(label: string): boolean { const n = label.toLowerCase().trim(); if (n === 'products' || n === 'product') return true; if (n.includes('featured product')) return true; return n === 'hot products' || n === 'new products'; }