39357-vm/frontend/src/components/marketing/MarketingLayout.tsx
2026-03-28 05:06:12 +00:00

349 lines
15 KiB
TypeScript

import { mdiArrowRight, mdiChevronDown, mdiClose, mdiMenu } from '@mdi/js';
import Link from 'next/link';
import { useRouter } from 'next/router';
import React, { useEffect, useMemo, useState } from 'react';
import BaseIcon from '../BaseIcon';
import MarketingButton from './MarketingButton';
import NewsletterForm from './NewsletterForm';
import { brand, exportServiceItems, navigationItems } from './marketingData';
type Props = {
children: React.ReactNode;
};
type NavigationItem = (typeof navigationItems)[number];
type NavigationChild = (typeof exportServiceItems)[number];
const isActivePath = (pathname: string, href: string) => {
if (href === '/') {
return pathname === '/';
}
return pathname === href;
};
const isNavItemActive = (pathname: string, item: NavigationItem) => {
if (isActivePath(pathname, item.href)) {
return true;
}
return item.children?.some((child) => isActivePath(pathname, child.href)) ?? false;
};
const getCurrentLabel = (pathname: string) => {
const currentChild = exportServiceItems.find((item) => item.href === pathname);
if (currentChild) {
return currentChild.label;
}
const currentItem = navigationItems.find((item) => item.href === pathname);
return currentItem ? currentItem.label : 'Discover';
};
const getDesktopNavClasses = (isActive: boolean, isHomeHeroMode: boolean) => {
if (isHomeHeroMode) {
return isActive
? 'bg-white/14 text-white shadow-[0_16px_45px_rgba(0,0,0,0.14)]'
: 'text-white/88 hover:bg-white/10';
}
return isActive ? 'bg-[#3E2723] text-[#F5E9DA]' : 'text-[#4d3c35] hover:bg-[#F5E9DA]';
};
const getDesktopDropdownClasses = (isHomeHeroMode: boolean) => {
return isHomeHeroMode
? 'border-white/10 bg-[#241816]/96 text-white shadow-[0_26px_80px_rgba(0,0,0,0.32)]'
: 'border-[#3E2723]/10 bg-white text-[#2c221f] shadow-[0_26px_80px_rgba(35,24,18,0.14)]';
};
const getDesktopDropdownItemClasses = (pathname: string, item: NavigationChild, isHomeHeroMode: boolean) => {
const isActive = isActivePath(pathname, item.href);
if (isHomeHeroMode) {
return isActive
? 'bg-white/12 text-white'
: 'text-white/78 hover:bg-white/8 hover:text-white';
}
return isActive ? 'bg-[#F5E9DA] text-[#3E2723]' : 'text-[#4d3c35] hover:bg-[#F5E9DA]/70';
};
export default function MarketingLayout({ children }: Props) {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [isExportMenuOpen, setIsExportMenuOpen] = useState(false);
const [isScrolled, setIsScrolled] = useState(false);
const router = useRouter();
useEffect(() => {
const handleScroll = () => {
setIsScrolled(window.scrollY > 24);
};
handleScroll();
window.addEventListener('scroll', handleScroll, { passive: true });
return () => window.removeEventListener('scroll', handleScroll);
}, []);
useEffect(() => {
setIsMenuOpen(false);
setIsExportMenuOpen(false);
}, [router.pathname]);
const currentLabel = useMemo(() => getCurrentLabel(router.pathname), [router.pathname]);
const isHomeHeroMode = router.pathname === '/' && !isScrolled && !isMenuOpen;
return (
<div className="font-brand-body min-h-screen bg-[#fcfaf6] text-[#2c221f]">
<header
className={`sticky top-0 z-50 transition-all duration-300 ${
isHomeHeroMode
? 'border-b border-transparent bg-transparent'
: 'border-b border-[#3E2723]/10 bg-[#fcfaf6]/92 shadow-[0_18px_50px_rgba(35,24,18,0.08)] backdrop-blur-xl'
}`}
>
<div className="mx-auto flex max-w-7xl items-center justify-between gap-4 px-5 py-4 lg:px-8">
<Link href="/" className="flex items-center gap-3">
<div
className={`flex h-12 w-12 items-center justify-center rounded-full border text-sm font-semibold shadow-lg transition-colors duration-300 ${
isHomeHeroMode
? 'border-white/25 bg-white/10 text-[#F5E9DA] backdrop-blur-md'
: 'border-[#C8A165]/50 bg-[#3E2723] text-[#F5E9DA]'
}`}
>
AD
</div>
<div>
<p className={`font-brand-display text-lg font-bold transition-colors duration-300 ${isHomeHeroMode ? 'text-[#F5E9DA]' : 'text-[#3E2723]'}`}>
Alem Desta
</p>
<p className={`text-xs uppercase tracking-[0.28em] transition-colors duration-300 ${isHomeHeroMode ? 'text-white/60' : 'text-[#8a6f59]'}`}>
Coffee Export
</p>
</div>
</Link>
<nav className="hidden items-center gap-1 xl:flex">
{navigationItems.map((item) => {
const isActive = isNavItemActive(router.pathname, item);
const classes = getDesktopNavClasses(isActive, isHomeHeroMode);
if (!item.children) {
return (
<Link key={item.href} href={item.href} className={`rounded-full px-4 py-2 text-sm font-medium transition-all duration-300 ${classes}`}>
{item.label}
</Link>
);
}
return (
<div key={item.href} className="group relative">
<Link href={item.href} className={`inline-flex items-center gap-2 rounded-full px-4 py-2 text-sm font-medium transition-all duration-300 ${classes}`}>
{item.label}
<BaseIcon path={mdiChevronDown} className="transition-transform duration-300 group-hover:rotate-180 group-focus-within:rotate-180" />
</Link>
<div
className={`invisible absolute left-0 top-full mt-3 w-[22rem] translate-y-2 rounded-[1.75rem] border p-3 opacity-0 transition-all duration-300 group-hover:visible group-hover:translate-y-0 group-hover:opacity-100 group-hover:pointer-events-auto group-focus-within:visible group-focus-within:translate-y-0 group-focus-within:opacity-100 ${getDesktopDropdownClasses(
isHomeHeroMode,
)}`}
>
{item.children.map((child) => (
<Link
key={child.href}
href={child.href}
className={`block rounded-[1.25rem] px-4 py-3 transition-all duration-300 ${getDesktopDropdownItemClasses(
router.pathname,
child,
isHomeHeroMode,
)}`}
>
<p className="text-sm font-semibold">{child.label}</p>
<p className={`mt-1 text-xs leading-6 ${isHomeHeroMode ? 'text-white/58' : 'text-[#7a6458]'}`}>{child.description}</p>
</Link>
))}
</div>
</div>
);
})}
</nav>
<div className="hidden items-center gap-3 xl:flex">
<MarketingButton href="/login" variant={isHomeHeroMode ? 'secondary' : 'ghost'}>
Admin Login
</MarketingButton>
<MarketingButton href="/contact?type=get_a_quote">Get a Quote</MarketingButton>
</div>
<button
type="button"
onClick={() => setIsMenuOpen((current) => !current)}
className={`inline-flex h-12 w-12 items-center justify-center rounded-full border transition-all duration-300 xl:hidden ${
isHomeHeroMode
? 'border-white/20 bg-white/10 text-[#F5E9DA] backdrop-blur-md'
: 'border-[#3E2723]/10 bg-white text-[#3E2723]'
}`}
aria-label="Toggle navigation"
>
<BaseIcon path={isMenuOpen ? mdiClose : mdiMenu} />
</button>
</div>
{isMenuOpen ? (
<div className="border-t border-[#3E2723]/10 bg-[#fcfaf6] px-5 py-4 xl:hidden">
<div className="mb-4 rounded-2xl border border-[#C8A165]/30 bg-[#F5E9DA] px-4 py-3 text-sm text-[#5d4a42]">
Currently viewing: <span className="font-semibold text-[#3E2723]">{currentLabel}</span>
</div>
<div className="flex flex-col gap-2">
{navigationItems.map((item) => {
if (!item.children) {
return (
<Link
key={item.href}
href={item.href}
className={`rounded-2xl px-4 py-3 text-sm font-medium ${
isNavItemActive(router.pathname, item) ? 'bg-[#3E2723] text-[#F5E9DA]' : 'bg-white text-[#3E2723]'
}`}
onClick={() => setIsMenuOpen(false)}
>
{item.label}
</Link>
);
}
return (
<div key={item.href} className="overflow-hidden rounded-[1.5rem] border border-[#3E2723]/10 bg-white">
<div className="flex items-center">
<Link
href={item.href}
className={`flex-1 px-4 py-3 text-sm font-medium ${
isNavItemActive(router.pathname, item) ? 'text-[#3E2723]' : 'text-[#4d3c35]'
}`}
onClick={() => setIsMenuOpen(false)}
>
{item.label}
</Link>
<button
type="button"
onClick={() => setIsExportMenuOpen((current) => !current)}
className="px-4 py-3 text-[#3E2723]"
aria-label="Toggle export services submenu"
>
<BaseIcon
path={mdiChevronDown}
className={`transition-transform duration-300 ${isExportMenuOpen ? 'rotate-180' : ''}`}
/>
</button>
</div>
{isExportMenuOpen ? (
<div className="border-t border-[#3E2723]/10 bg-[#fcfaf6] p-2">
{item.children.map((child) => (
<Link
key={child.href}
href={child.href}
className={`block rounded-2xl px-4 py-3 ${
isActivePath(router.pathname, child.href) ? 'bg-[#3E2723] text-[#F5E9DA]' : 'text-[#3E2723]'
}`}
onClick={() => setIsMenuOpen(false)}
>
<p className="text-sm font-medium">{child.label}</p>
<p className={`mt-1 text-xs leading-6 ${isActivePath(router.pathname, child.href) ? 'text-white/70' : 'text-[#7a6458]'}`}>
{child.description}
</p>
</Link>
))}
</div>
) : null}
</div>
);
})}
</div>
<div className="mt-4 grid gap-3 sm:grid-cols-2">
<MarketingButton href="/login" variant="ghost">
Admin Login
</MarketingButton>
<MarketingButton href="/contact?type=request_samples">Request Samples</MarketingButton>
</div>
</div>
) : null}
</header>
<main>{children}</main>
<section className="border-y border-[#3E2723]/10 bg-[#3E2723] px-5 py-16 text-white lg:px-8">
<div className="mx-auto flex max-w-7xl flex-col gap-10 lg:flex-row lg:items-center lg:justify-between">
<div className="max-w-3xl">
<p className="text-sm uppercase tracking-[0.35em] text-[#d7b47a]">Ready to source with confidence?</p>
<h2 className="font-brand-display mt-4 text-4xl leading-tight text-[#F5E9DA] md:text-5xl">
Discover Ethiopian coffee backed by heritage, clear communication, and export discipline.
</h2>
</div>
<div className="flex flex-col gap-3 sm:flex-row">
<MarketingButton href="/contact?type=request_samples">Request Samples</MarketingButton>
<MarketingButton href="/contact?type=get_a_quote" variant="secondary">
Get a Quote
</MarketingButton>
</div>
</div>
</section>
<footer className="bg-[#241816] px-5 py-16 text-white lg:px-8">
<div className="mx-auto grid max-w-7xl gap-10 lg:grid-cols-[1.1fr_0.7fr_0.95fr_1fr]">
<div>
<p className="font-brand-display text-3xl text-[#F5E9DA]">{brand.name}</p>
<p className="mt-4 max-w-xl text-sm leading-7 text-white/72">{brand.tagline}</p>
<div className="mt-8 flex flex-wrap gap-3">
<MarketingButton href="/contact?type=partner_with_us">Partner With Us</MarketingButton>
<MarketingButton href="/login" variant="secondary">
Admin Interface
</MarketingButton>
</div>
</div>
<div>
<p className="text-sm font-semibold uppercase tracking-[0.28em] text-[#C8A165]">Main pages</p>
<div className="mt-5 grid gap-3">
{navigationItems.map((item) => (
<Link key={item.href} href={item.href} className="inline-flex items-center gap-2 text-sm text-white/75 transition hover:text-white">
<BaseIcon path={mdiArrowRight} className="text-[#C8A165]" />
{item.label}
</Link>
))}
</div>
</div>
<div>
<p className="text-sm font-semibold uppercase tracking-[0.28em] text-[#C8A165]">Export services</p>
<div className="mt-5 grid gap-3">
{exportServiceItems.map((item) => (
<Link key={item.href} href={item.href} className="inline-flex items-center gap-2 text-sm text-white/75 transition hover:text-white">
<BaseIcon path={mdiArrowRight} className="text-[#C8A165]" />
{item.label}
</Link>
))}
</div>
<div className="mt-8 space-y-2 text-sm text-white/75">
<p>{brand.address}</p>
<p>{brand.email}</p>
{brand.phones.map((phone) => (
<p key={phone}>{phone}</p>
))}
</div>
</div>
<div>
<p className="text-sm font-semibold uppercase tracking-[0.28em] text-[#C8A165]">Newsletter subscription</p>
<p className="mt-4 text-sm leading-7 text-white/72">
Receive market-ready updates, sourcing availability, and company news tailored for international buyers.
</p>
<div className="mt-5">
<NewsletterForm compact source="home_footer" />
</div>
</div>
</div>
</footer>
</div>
);
}