update ai commerce section

This commit is contained in:
2026-07-23 10:26:49 +05:30
parent 4e0facc9af
commit a4328a791a
135 changed files with 4081 additions and 391 deletions

View File

@@ -0,0 +1,437 @@
"use client";
import Link from "next/link";
import Image from "next/image";
import { useState, useRef } from "react";
import { usePathname } from "next/navigation";
import {
motion,
AnimatePresence,
useScroll,
useMotionValueEvent,
} from "framer-motion";
import { MaterialIcon } from "@/components/ui/MaterialIcon";
const EASE = [0.22, 1, 0.36, 1] as [number, number, number, number];
type NavChild = { label: string; href: string; icon: string; desc: string };
type NavLink = { label: string; href: string; children?: NavChild[] };
const SOLUTIONS_CHILDREN: NavChild[] = [
{
label: "For Business",
href: "/businesses",
icon: "storefront",
desc: "Run your neighborhood shop online.",
},
{
label: "For People",
href: "/people",
icon: "shopping_bag",
desc: "Order from local stores you trust.",
},
{
label: "For D2C Brands",
href: "/brands",
icon: "auto_awesome",
desc: "Connect regional catalogs direct.",
},
];
const NAV_LINKS: NavLink[] = [
{ label: "Home", href: "/" },
{ label: "Why Nearle", href: "/why-nearle" },
{ label: "Platform", href: "/platform" },
{ label: "Solutions", href: "/businesses", children: SOLUTIONS_CHILDREN },
{ label: "About", href: "/about" },
];
const mobileMenuVariants = {
hidden: { opacity: 0, height: 0 },
visible: {
opacity: 1,
height: "auto",
transition: { duration: 0.3, ease: EASE },
},
exit: {
opacity: 0,
height: 0,
transition: { duration: 0.2, ease: "easeIn" as const },
},
};
const mobileStagger = {
hidden: {},
visible: { transition: { staggerChildren: 0.04, delayChildren: 0.06 } },
};
const mobileItemVariants = {
hidden: { opacity: 0, x: -10 },
visible: { opacity: 1, x: 0, transition: { duration: 0.22, ease: EASE } },
};
const dropdownVariants = {
hidden: { opacity: 0, y: 8, scale: 0.98 },
visible: {
opacity: 1,
y: 0,
scale: 1,
transition: { duration: 0.22, ease: EASE },
},
exit: {
opacity: 0,
y: 6,
scale: 0.98,
transition: { duration: 0.15, ease: "easeIn" as const },
},
};
const mobileSubmenuVariants = {
hidden: { opacity: 0, height: 0 },
visible: {
opacity: 1,
height: "auto",
transition: { duration: 0.26, ease: EASE },
},
exit: {
opacity: 0,
height: 0,
transition: { duration: 0.18, ease: "easeIn" as const },
},
};
export function PremiumNavbar() {
const [mobileOpen, setMobileOpen] = useState(false);
const [mobileSolutionsOpen, setMobileSolutionsOpen] = useState(false);
const [openDropdown, setOpenDropdown] = useState<string | null>(null);
const [scrolled, setScrolled] = useState(false);
const pathname = usePathname();
const { scrollY } = useScroll();
const closeTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
useMotionValueEvent(scrollY, "change", (y) => {
setScrolled(y > 20);
});
const isActiveLink = (href: string) => {
if (href === "/") return pathname === "/";
return pathname.startsWith(href);
};
const isGroupActive = (link: NavLink) => {
if (link.children) {
return link.children.some((c) => isActiveLink(c.href));
}
return isActiveLink(link.href);
};
const openMenu = (label: string) => {
if (closeTimer.current) clearTimeout(closeTimer.current);
setOpenDropdown(label);
};
const scheduleClose = () => {
if (closeTimer.current) clearTimeout(closeTimer.current);
closeTimer.current = setTimeout(() => setOpenDropdown(null), 120);
};
const closeMobile = () => {
setMobileOpen(false);
setMobileSolutionsOpen(false);
};
return (
<motion.header
className={`sticky top-0 z-50 w-full text-white transition-[background-color,box-shadow,backdrop-filter] duration-300 ${
scrolled
? "bg-[#83429f]/82 backdrop-blur-lg shadow-[0_6px_32px_rgba(0,0,0,0.35)] border-b border-white/15"
: "bg-[#83429f] border-b border-white/10"
}`}
initial={{ opacity: 0, y: -8 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, ease: EASE }}
>
<div className="relative mx-auto flex h-16 w-full max-w-[1600px] items-center px-6 md:h-20 md:px-10 xl:px-16">
{/* Logo */}
<Link
href="/"
className="flex shrink-0 items-center transition-transform duration-200 hover:scale-[1.02] active:scale-95"
aria-label="Nearle Home"
>
<Image
src="/logos/logo-light.png"
alt="Nearle"
width={190}
height={48}
className="h-10 w-auto object-contain md:h-12 lg:h-11 min-[1440px]:h-14"
priority
/>
</Link>
{/* Desktop Navigation */}
<nav className="hidden flex-1 items-center justify-center gap-4 lg:flex min-[1440px]:gap-9">
{NAV_LINKS.map((link) => {
const isActive = isGroupActive(link);
if (link.children) {
const isOpen = openDropdown === link.label;
return (
<div
key={link.label}
className="relative py-2"
onMouseEnter={() => openMenu(link.label)}
onMouseLeave={scheduleClose}
>
<button
type="button"
className={`flex items-center gap-1 whitespace-nowrap text-sm font-bold transition-colors duration-200 ${
isActive ? "text-white" : "text-white/70 hover:text-white"
}`}
aria-haspopup="true"
aria-expanded={isOpen}
onClick={() => setOpenDropdown(isOpen ? null : link.label)}
onFocus={() => openMenu(link.label)}
>
{link.label}
<motion.span
className="flex"
animate={{ rotate: isOpen ? 180 : 0 }}
transition={{ duration: 0.2, ease: EASE }}
>
<MaterialIcon icon="expand_more" size={16} />
</motion.span>
</button>
{isActive && (
<motion.span
layoutId="activeNavUnderline"
className="absolute bottom-0 left-0 right-0 h-0.5 rounded-full bg-white"
transition={{ duration: 0.3, ease: EASE }}
/>
)}
<AnimatePresence>
{isOpen && (
<motion.div
variants={dropdownVariants}
initial="hidden"
animate="visible"
exit="exit"
className="absolute left-1/2 top-full z-50 mt-3 w-100 -translate-x-1/2 overflow-hidden rounded-2xl border border-white/12 bg-gradient-to-b from-[#1C0F4E] to-[#0A0524] p-2 shadow-[0_24px_50px_rgba(0,0,0,0.55)] backdrop-blur-2xl"
role="menu"
>
{link.children.map((child) => {
const childActive = isActiveLink(child.href);
return (
<Link
key={child.href}
href={child.href}
role="menuitem"
onClick={() => setOpenDropdown(null)}
className={`group flex items-start gap-3.5 rounded-xl p-3 transition-all duration-200 ${
childActive
? "bg-white/12 text-white shadow-sm"
: "text-white/70 hover:bg-white/[0.07] hover:text-white"
}`}
>
{/* Left — Medallion Icon */}
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg border border-white/10 bg-white/[0.05] text-[#C9A2FF] transition-colors duration-200 group-hover:border-[#C9A2FF]/30 group-hover:bg-[#C9A2FF]/10 group-hover:text-white">
<MaterialIcon icon={child.icon} size={18} />
</div>
{/* Right — Info */}
<div className="flex flex-col text-left">
<span className="text-lg font-bold tracking-tight leading-none mb-1 text-white group-hover:text-[#C9A2FF] transition-colors">
{child.label}
</span>
<span className="text-[12px] font-normal leading-normal text-white/55 transition-colors group-hover:text-white/70">
{child.desc}
</span>
</div>
</Link>
);
})}
</motion.div>
)}
</AnimatePresence>
</div>
);
}
return (
<motion.div
key={link.label}
className="relative py-2"
initial="rest"
whileHover="hover"
animate="rest"
>
<Link
href={link.href}
className={`whitespace-nowrap text-sm font-bold transition-colors duration-200 ${
isActive ? "text-white" : "text-white/70 hover:text-white"
}`}
>
{link.label}
</Link>
{/* Active underline: layoutId lets it slide between links on navigation */}
{isActive && (
<motion.span
layoutId="activeNavUnderline"
className="absolute bottom-0 left-0 right-0 h-0.5 rounded-full bg-white"
transition={{ duration: 0.3, ease: EASE }}
/>
)}
{/* Hover underline: grows from left on inactive links */}
{!isActive && (
<motion.span
className="absolute bottom-0 left-0 right-0 h-0.5 rounded-full bg-white/40"
style={{ originX: 0 }}
variants={{ rest: { scaleX: 0 }, hover: { scaleX: 1 } }}
transition={{ duration: 0.2, ease: EASE }}
/>
)}
</motion.div>
);
})}
</nav>
{/* Desktop Action Button */}
<div className="hidden lg:block">
<Link
href="/contact"
className="inline-flex items-center justify-center rounded-full bg-white px-7 py-3 text-sm font-black uppercase tracking-wider text-[#6330D6] shadow-[0_4px_20px_rgba(0,0,0,0.15)] hover:bg-[#FAF7FD] hover:text-[#5225b8] hover:scale-[1.03] active:scale-95 transition-all duration-200"
>
Contact Us
</Link>
</div>
{/* Mobile Toggle */}
<button
type="button"
className="ml-auto flex h-10 w-10 items-center justify-center rounded-full text-white transition-all duration-200 hover:bg-white/10 active:scale-90 lg:hidden"
onClick={() => setMobileOpen((v) => !v)}
aria-label={mobileOpen ? "Close menu" : "Open menu"}
aria-expanded={mobileOpen}
>
<MaterialIcon icon={mobileOpen ? "close" : "menu"} size={24} />
</button>
</div>
{/* Mobile Menu with enter/exit animation */}
<AnimatePresence>
{mobileOpen && (
<motion.div
key="mobile-menu"
variants={mobileMenuVariants}
initial="hidden"
animate="visible"
exit="exit"
className="overflow-hidden border-t border-white/10 bg-[#30226F]/98 backdrop-blur-xl lg:hidden"
>
<motion.nav
className="mx-auto flex w-full max-w-[1600px] flex-col gap-1 px-6 py-5 md:px-10"
variants={mobileStagger}
initial="hidden"
animate="visible"
>
{NAV_LINKS.map((link) => {
if (link.children) {
const groupActive = isGroupActive(link);
return (
<motion.div key={link.label} variants={mobileItemVariants}>
<button
type="button"
onClick={() => setMobileSolutionsOpen((v) => !v)}
aria-expanded={mobileSolutionsOpen}
className={`flex w-full items-center justify-between rounded-xl px-4 py-3 text-sm font-bold transition-all duration-200 ${
groupActive
? "bg-white/15 text-white"
: "text-white/70 hover:bg-white/10 hover:text-white"
}`}
>
{link.label}
<motion.span
className="flex"
animate={{ rotate: mobileSolutionsOpen ? 180 : 0 }}
transition={{ duration: 0.2, ease: EASE }}
>
<MaterialIcon icon="expand_more" size={20} />
</motion.span>
</button>
<AnimatePresence initial={false}>
{mobileSolutionsOpen && (
<motion.div
variants={mobileSubmenuVariants}
initial="hidden"
animate="visible"
exit="exit"
className="overflow-hidden"
>
<div className="mt-1 flex flex-col gap-1 pl-3">
{link.children.map((child) => {
const childActive = isActiveLink(child.href);
return (
<Link
key={child.href}
href={child.href}
onClick={closeMobile}
className={`flex items-center gap-3 rounded-xl px-4 py-2.5 text-sm font-bold transition-all duration-200 ${
childActive
? "bg-white/12 text-white"
: "text-white/60 hover:bg-white/10 hover:text-white"
}`}
>
<MaterialIcon icon={child.icon} size={18} className="text-[#C9A2FF]" />
{child.label}
</Link>
);
})}
</div>
</motion.div>
)}
</AnimatePresence>
</motion.div>
);
}
const isActive = isActiveLink(link.href);
return (
<motion.div key={link.label} variants={mobileItemVariants}>
<Link
href={link.href}
onClick={closeMobile}
className={`flex items-center justify-between rounded-xl px-4 py-3 text-sm font-bold transition-all duration-200 ${
isActive
? "bg-white/15 text-white"
: "text-white/70 hover:bg-white/10 hover:text-white"
}`}
>
{link.label}
{isActive && (
<span className="h-2 w-2 rounded-full bg-white" />
)}
</Link>
</motion.div>
);
})}
{/* Mobile Contact CTA */}
<motion.div variants={mobileItemVariants} className="mt-4 px-4">
<Link
href="/contact"
onClick={closeMobile}
className="flex w-full items-center justify-center rounded-full bg-white py-3.5 text-sm font-black uppercase tracking-wider text-[#6330D6] shadow-md active:scale-[0.98] transition-all hover:bg-[#FAF7FD] hover:text-[#5225b8]"
>
Contact Us
</Link>
</motion.div>
</motion.nav>
</motion.div>
)}
</AnimatePresence>
</motion.header>
);
}