Initial commit: Ali Alemi storefront (Next.js, Meshkee API integration)

Farsi RTL Next.js 16 storefront for استاد علی عالمی, cloned from the
original Meshkee web-builder site and rebuilt with Tailwind 4, live
Meshkee API integration (products, store items, portfolios, blog,
workshops, comments, cart, customer auth), and standalone production
output for the shared-VM deploy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
amirhosein.ashourloo
2026-09-16 09:43:10 +03:30
co-authored by Claude Sonnet 5
commit 8f102a8250
106 changed files with 11605 additions and 0 deletions
+110
View File
@@ -0,0 +1,110 @@
"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";
export function Header({ user, isAdmin }: { user: CustomerUser | null; isAdmin: boolean }) {
const pathname = usePathname();
const [open, setOpen] = useState(false);
return (
<header className="sticky top-0 z-50 bg-secondary">
<div className="boxed flex h-20 items-center justify-between gap-4">
<Link href="/" className="shrink-0" onClick={() => setOpen(false)}>
<Image
src="/images/logo.png"
alt="استاد علی عالمی"
width={140}
height={40}
priority
className="h-9 w-auto brightness-0 invert"
/>
</Link>
<nav className="hidden items-center gap-5 xl:flex">
{NAV_LINKS.map((link) => {
const active = link.href === "/" ? pathname === "/" : pathname.startsWith(link.href);
return (
<Link
key={link.href}
href={link.href}
className={`whitespace-nowrap text-sm font-medium transition-colors hover:text-primary ${
active ? "text-primary" : "text-white/80"
}`}
>
{link.label}
</Link>
);
})}
</nav>
<div className="hidden items-center gap-3 lg:flex">
<a
href={`tel:${CONTACT.phone}`}
className="flex items-center gap-2 text-sm font-medium text-white/80 transition-colors hover:text-primary"
dir="ltr"
>
<IconPhone className="h-4 w-4 text-primary" />
{CONTACT.phoneLabel}
</a>
<CartButton light />
<UserMenu user={user} isAdmin={isAdmin} light />
</div>
<div className="flex items-center gap-2 lg:hidden">
<CartButton light />
<button
type="button"
onClick={() => setOpen((v) => !v)}
aria-label="باز کردن منو"
aria-expanded={open}
className="flex h-10 w-10 items-center justify-center 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>
</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="flex items-center gap-2 px-3 text-sm text-white/80" dir="ltr">
<IconPhone className="h-4 w-4 text-primary" />
{CONTACT.phoneLabel}
</a>
<div className="px-3">
<UserMenu user={user} isAdmin={isAdmin} light />
</div>
</div>
</nav>
</div>
)}
</header>
);
}