'use client'; import React, { useCallback, useEffect, useState } from 'react'; import { createPortal } from 'react-dom'; import Link from 'next/link'; import { AnimatePresence, motion } from 'framer-motion'; import { ChevronRight, ChevronLeft, CircleUserRound, X, Loader2 } from 'lucide-react'; import { catalogService, CategoryResponse, HierarchyBrandItem } from '@/services/api/catalogService'; import { useAuthStore } from '@/store/authStore'; export type AllMenuItem = { id: string; label: string; href?: string; children?: AllMenuItem[]; heading?: boolean; loadBrandsFor?: string; categorySlug?: string; }; type Panel = { title: string; items: AllMenuItem[]; }; const PANEL_WIDTH = 365; function sortCategories(list: CategoryResponse[]) { return [...list].sort((a, b) => { const orderA = parseInt(String(a.sort_order || '0'), 10); const orderB = parseInt(String(b.sort_order || '0'), 10); return orderA - orderB; }); } function categoryChildren(all: CategoryResponse[], parentId: string) { return sortCategories(all.filter((c) => c.parent_category_id === parentId)); } function categoryHref(slug: string, extra?: string) { return extra ? `/shop?category=${slug}&${extra}` : `/shop?category=${slug}`; } function buildCategorySubmenu(cat: CategoryResponse, all: CategoryResponse[]): AllMenuItem[] { const kids = categoryChildren(all, cat.category_id); const items: AllMenuItem[] = [ { id: `${cat.category_id}-all`, label: `See all ${cat.name}`, href: categoryHref(cat.slug) }, ]; if (kids.length > 0) { items.push({ id: `${cat.category_id}-sub-h`, label: 'Shop by subcategory', heading: true }); kids.forEach((kid) => { const grandkids = categoryChildren(all, kid.category_id); items.push({ id: kid.category_id, label: kid.name, href: categoryHref(kid.slug), children: grandkids.length > 0 ? buildCategorySubmenu(kid, all) : undefined, }); }); } items.push( { id: `${cat.category_id}-shop-h`, label: 'More in this category', heading: true }, { id: `${cat.category_id}-new`, label: 'New Releases', href: categoryHref(cat.slug, 'sort=newest') }, { id: `${cat.category_id}-featured`, label: 'Featured Items', href: categoryHref(cat.slug, 'featured=true') }, { id: `${cat.category_id}-deals`, label: 'Best Prices', href: categoryHref(cat.slug, 'sort=price_asc') }, ); return items; } function brandsToItems(categorySlug: string, brands: HierarchyBrandItem[]): AllMenuItem[] { return brands.map((brand) => { const seriesItems: AllMenuItem[] | undefined = brand.series?.length > 0 ? [ { id: `${brand.brand_id}-all`, label: `See all ${brand.name}`, href: `/shop?category=${categorySlug}&brand=${brand.slug}`, }, { id: `${brand.brand_id}-series-h`, label: 'Series', heading: true }, ...brand.series.map((series) => ({ id: series.series_id, label: series.name, href: `/shop?category=${categorySlug}&brand=${brand.slug}&series=${series.slug}`, children: series.models?.length > 0 ? [ { id: `${series.series_id}-all`, label: `See all ${series.name}`, href: `/shop?category=${categorySlug}&brand=${brand.slug}&series=${series.slug}`, }, { id: `${series.series_id}-models-h`, label: 'Models', heading: true }, ...series.models.map((model) => ({ id: model.model_id, label: model.name, href: `/shop?category=${categorySlug}&brand=${brand.slug}&series=${series.slug}&model=${model.slug}`, })), ] : undefined, })), ] : undefined; return { id: brand.brand_id, label: brand.name, href: `/shop?category=${categorySlug}&brand=${brand.slug}`, children: seriesItems, }; }); } function buildRootItems(categories: CategoryResponse[]): AllMenuItem[] { const topLevel = sortCategories( categories.filter((c) => !c.parent_category_id) ); const shopCats = topLevel.length > 0 ? topLevel : sortCategories(categories); return [ { id: 'h-trending', label: 'Trending', heading: true }, { id: 'bestsellers', label: 'Bestsellers', href: '/shop?sort=bestselling' }, { id: 'new-releases', label: 'New Releases', href: '/shop?sort=newest' }, { id: 'deals', label: 'Today\'s Deals', href: '/shop?tag=deals' }, { id: 'h-shop', label: 'Shop by Category', heading: true }, ...shopCats.map((cat) => ({ id: cat.category_id, label: cat.name, href: categoryHref(cat.slug), children: buildCategorySubmenu(cat, categories), loadBrandsFor: cat.is_parent_feature ? cat.category_id : undefined, categorySlug: cat.slug, })), { id: 'h-programs', label: 'Programs & Features', heading: true }, { id: 'repairs', label: 'Device Repairs', href: '/repair-services' }, { id: 'all-cats', label: 'All Categories', href: '/categories' }, { id: 'h-help', label: 'Help & Settings', heading: true }, { id: 'account', label: 'Your Account', href: '/account' }, { id: 'contact', label: 'Customer Service', href: '/contact' }, ]; } const slideVariants = { enter: (dir: number) => ({ x: dir >= 0 ? '100%' : '-100%' }), center: { x: 0 }, exit: (dir: number) => ({ x: dir >= 0 ? '-40%' : '100%' }), }; export function AllCategoriesDrawer() { const [mounted, setMounted] = useState(false); const [isOpen, setIsOpen] = useState(false); const [stack, setStack] = useState([]); const [direction, setDirection] = useState(1); const { isAuthenticated, customer } = useAuthStore(); useEffect(() => { setMounted(true); }, []); const close = useCallback(() => { setIsOpen(false); }, []); const open = useCallback(() => { setStack([{ title: 'Menu', items: [] }]); setDirection(1); setIsOpen(true); }, []); useEffect(() => { const handleOpen = () => open(); window.addEventListener('openAllCategoriesDrawer', handleOpen); return () => window.removeEventListener('openAllCategoriesDrawer', handleOpen); }, [open]); useEffect(() => { if (!isOpen) return; let cancelled = false; catalogService.getCategories().then((res) => { if (cancelled) return; const list = Array.isArray(res) ? res : []; setStack([{ title: 'Menu', items: buildRootItems(list) }]); setDirection(1); }); const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') close(); }; document.addEventListener('keydown', onKey); const prevOverflow = document.body.style.overflow; document.body.style.overflow = 'hidden'; return () => { cancelled = true; document.removeEventListener('keydown', onKey); document.body.style.overflow = prevOverflow; }; }, [isOpen, close]); const current = stack[stack.length - 1]; const greeting = isAuthenticated ? customer?.first_name || 'Account' : 'Login'; const accountHref = '/account'; const openChildPanel = async (item: AllMenuItem) => { if (!item.children?.length && !item.loadBrandsFor) return; setDirection(1); setStack((prev) => [ ...prev, { title: item.label, items: item.children?.length ? item.children : [], }, ]); if (!item.loadBrandsFor) return; const hierarchy = await catalogService.getParentHierarchy(item.loadBrandsFor); const slug = item.categorySlug || item.loadBrandsFor; const brandItems = hierarchy?.brands?.length ? brandsToItems(slug, hierarchy.brands) : []; setStack((prev) => { const next = [...prev]; const last = next[next.length - 1]; if (!last) return prev; const withoutPlaceholder = last.items.filter((row) => !row.loadBrandsFor); next[next.length - 1] = { ...last, items: brandItems.length ? [...withoutPlaceholder, { id: `${item.id}-brands-h`, label: 'Shop by Brand', heading: true }, ...brandItems] : withoutPlaceholder, }; return next; }); }; const goBack = () => { if (stack.length <= 1) return; setDirection(-1); setStack((prev) => prev.slice(0, -1)); }; if (!mounted) return null; return createPortal( {isOpen && (
My Account {greeting}
{current && ( {stack.length > 1 && ( )} {stack.length > 1 && (

{current.title}

)} {current.items.length === 0 && (
Loading…
)}
)}
)} , document.body ); }