- Header/footer logo now reads from GET /tenants/{domain}/website/favicon
(logoDarkUrl on the navy bar, logoUrl on the white footer) with a local
fallback, matching the favicon's existing dynamic behavior.
- Mobile header rebuilt as its own 3-column row: hamburger right, logo
centered, cart left. Fixed a missing justify-center that left the
hamburger icon (and its X state) off-center inside its square button.
- Header action buttons (cart, user/login) restyled to solid primary
pills/circles per a reference screenshot; a search button was tried and
then removed since the site has no search backend yet.
- Navbar reverted to exactly 5 static items (home, workshops, blog, about,
contact) per instruction — a categories-driven "محصولات" dropdown was
built and then removed the same session; /products, /store, /portfolios
remain real pages, just unlinked from the header for now.
- Hero: second avatar raised so it's not hidden behind the character.
WhyChooseUs: mirrored the two bottom floating badges into symmetric
corners (were overlapping). FeatureCards: restored the 4th feature
("انتخاب زمان کلاس") that had been silently dropped, gave the icon
badges a nested white-circle treatment, and gave the cards a visible
border.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
138 lines
5.0 KiB
TypeScript
138 lines
5.0 KiB
TypeScript
"use client";
|
|
|
|
import Image from "next/image";
|
|
import Link from "next/link";
|
|
import { usePathname } from "next/navigation";
|
|
import { useState } from "react";
|
|
import { CONTACT, NAV_LINKS } from "@/lib/site";
|
|
import type { CustomerUser } from "@/lib/meshkee/types";
|
|
import { CartButton } from "@/components/cart/CartButton";
|
|
import { UserMenu } from "@/components/UserMenu";
|
|
import { IconPhone } from "./icons";
|
|
|
|
const ACTIVE_LINK = "rounded-full bg-white px-4 py-2 text-sm font-semibold text-secondary";
|
|
const INACTIVE_LINK = "whitespace-nowrap rounded-full px-4 py-2 text-sm font-medium text-white/80 transition-colors hover:text-primary";
|
|
|
|
export function Header({
|
|
user,
|
|
isAdmin,
|
|
logoUrl,
|
|
logoDarkUrl,
|
|
}: {
|
|
user: CustomerUser | null;
|
|
isAdmin: boolean;
|
|
logoUrl: string | null;
|
|
logoDarkUrl: string | null;
|
|
}) {
|
|
const pathname = usePathname();
|
|
const [open, setOpen] = useState(false);
|
|
|
|
// Header sits on the navy bar (dark background): prefer the dashboard's
|
|
// dark-background logo variant, then its light variant, then the bundled
|
|
// local mark — only the local fallback needs the CSS invert (see
|
|
// CONTEXT.md "Dynamic logo/favicon").
|
|
const headerLogoSrc = logoDarkUrl || logoUrl || "/images/logo.png";
|
|
const headerLogoNeedsInvert = !logoDarkUrl && !logoUrl;
|
|
|
|
const Logo = (
|
|
<Link href="/" className="shrink-0" onClick={() => setOpen(false)}>
|
|
<Image
|
|
src={headerLogoSrc}
|
|
alt="استاد علی عالمی"
|
|
width={140}
|
|
height={40}
|
|
priority
|
|
unoptimized={Boolean(logoDarkUrl || logoUrl)}
|
|
className={`h-9 w-auto ${headerLogoNeedsInvert ? "brightness-0 invert" : ""}`}
|
|
/>
|
|
</Link>
|
|
);
|
|
|
|
return (
|
|
<header className="sticky top-0 z-50 bg-secondary">
|
|
{/* Mobile/tablet row: hamburger on the right, logo centered, cart on the left. */}
|
|
<div className="boxed grid h-20 grid-cols-3 items-center lg:hidden">
|
|
<button
|
|
type="button"
|
|
onClick={() => setOpen((v) => !v)}
|
|
aria-label="باز کردن منو"
|
|
aria-expanded={open}
|
|
className="flex h-10 w-10 items-center justify-center justify-self-start rounded-lg border border-white/20"
|
|
>
|
|
<span className="relative block h-3.5 w-5">
|
|
<span
|
|
className={`absolute inset-x-0 top-0 h-0.5 rounded bg-white transition-transform ${open ? "translate-y-[6px] rotate-45" : ""}`}
|
|
/>
|
|
<span className={`absolute inset-x-0 top-1/2 h-0.5 -translate-y-1/2 rounded bg-white transition-opacity ${open ? "opacity-0" : ""}`} />
|
|
<span
|
|
className={`absolute inset-x-0 bottom-0 h-0.5 rounded bg-white transition-transform ${open ? "-translate-y-[6px] -rotate-45" : ""}`}
|
|
/>
|
|
</span>
|
|
</button>
|
|
|
|
<div className="justify-self-center">{Logo}</div>
|
|
|
|
<div className="justify-self-end">
|
|
<CartButton />
|
|
</div>
|
|
</div>
|
|
|
|
{/* Desktop row: logo right, nav middle, phone/user/cart left (standard RTL order). */}
|
|
<div className="boxed hidden h-20 items-center justify-between gap-4 lg:flex">
|
|
{Logo}
|
|
|
|
<nav className="flex items-center gap-1">
|
|
{NAV_LINKS.map((link) => {
|
|
const active = link.href === "/" ? pathname === "/" : pathname.startsWith(link.href);
|
|
return (
|
|
<Link key={link.href} href={link.href} className={active ? ACTIVE_LINK : INACTIVE_LINK}>
|
|
{link.label}
|
|
</Link>
|
|
);
|
|
})}
|
|
</nav>
|
|
|
|
<div className="flex items-center gap-3">
|
|
<a
|
|
href={`tel:${CONTACT.phone}`}
|
|
className="flex items-center gap-2 rounded-full bg-white px-4 py-2 text-sm font-medium text-secondary"
|
|
dir="ltr"
|
|
>
|
|
<IconPhone className="h-4 w-4 text-primary" />
|
|
{CONTACT.phoneLabel}
|
|
</a>
|
|
<UserMenu user={user} isAdmin={isAdmin} />
|
|
<CartButton />
|
|
</div>
|
|
</div>
|
|
|
|
{open && (
|
|
<div className="border-t border-white/10 bg-secondary lg:hidden">
|
|
<nav className="boxed flex flex-col gap-1 py-4">
|
|
{NAV_LINKS.map((link) => (
|
|
<Link
|
|
key={link.href}
|
|
href={link.href}
|
|
onClick={() => setOpen(false)}
|
|
className="rounded-lg px-3 py-2.5 text-sm font-medium text-white/80 hover:bg-white/5 hover:text-primary"
|
|
>
|
|
{link.label}
|
|
</Link>
|
|
))}
|
|
|
|
<div className="mt-2 flex flex-col gap-3 border-t border-white/10 pt-4">
|
|
<a href={`tel:${CONTACT.phone}`} className="mx-3 flex items-center gap-2 rounded-full bg-white px-4 py-2 text-sm font-medium text-secondary" dir="ltr">
|
|
<IconPhone className="h-4 w-4 text-primary" />
|
|
{CONTACT.phoneLabel}
|
|
</a>
|
|
<div className="px-3">
|
|
<UserMenu user={user} isAdmin={isAdmin} />
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
</div>
|
|
)}
|
|
</header>
|
|
);
|
|
}
|