Implements winofyfrontenddoc (1).md §3-4: unified guest-home/authenticated-home
screen (header with login chip vs cart badge, pill search bar, dismissible
location-reminder banner, category chips, store cards, interleaved seller
promo card, sticky bottom nav), plus the full post-OTP location-onboarding
sub-flow (geolocation permission -> error/retry -> manual neighborhood
search, unified idle/results/empty states per the doc's own recommendation).
Location state persists in a plain (non-httpOnly) cookie readable both
client- and server-side, since it drives SSR store queries (neighborhood
or lat/lng) as well as client interactions.
Moved cart/checkout/store/product/orders/addresses under a new (shop) route
group so the new home-shell header doesn't double up with the existing
simple nav header on inner pages.
Real bug caught via live staging data: Neighborhood.city is a nested
{uuid, name, slug} object, not a bare uuid string as typed -- broke both
the address form's city filter and the new neighborhood search. Fixed and
verified against real API responses, not just assumed from the guide.
40 lines
1.4 KiB
TypeScript
40 lines
1.4 KiB
TypeScript
import Link from "next/link";
|
|
import { Home, Search, ShoppingBag, User } from "lucide-react";
|
|
import { cn } from "@/lib/utils/cn";
|
|
|
|
type Tab = "profile" | "orders" | "search" | "home";
|
|
|
|
const TABS: { key: Tab; href: string; label: string; icon: typeof Home }[] = [
|
|
{ key: "profile", href: "/profile", label: "پروفایل", icon: User },
|
|
{ key: "orders", href: "/orders", label: "سفارشها", icon: ShoppingBag },
|
|
{ key: "search", href: "/", label: "جستجو", icon: Search },
|
|
{ key: "home", href: "/", label: "خانه", icon: Home },
|
|
];
|
|
|
|
/** winofyfrontenddoc (1).md §3.6 — sticky bottom nav, 4 equal-width tabs. */
|
|
export function BottomNav({ active }: { active: Tab }) {
|
|
return (
|
|
<nav
|
|
className="fixed inset-x-0 bottom-0 z-10 mx-auto flex w-full max-w-[390px] items-stretch border-t border-gray-300 bg-white"
|
|
style={{ paddingBottom: "env(safe-area-inset-bottom)" }}
|
|
>
|
|
{TABS.map((tab) => {
|
|
const Icon = tab.icon;
|
|
const isActive = tab.key === active;
|
|
return (
|
|
<Link
|
|
key={tab.key}
|
|
href={tab.href}
|
|
className={cn(
|
|
"flex flex-1 flex-col items-center gap-1 py-3 text-label",
|
|
isActive ? "text-primary-action" : "text-gray-500",
|
|
)}
|
|
>
|
|
<Icon className="h-5 w-5" />
|
|
<span>{tab.label}</span>
|
|
</Link>
|
|
);
|
|
})}
|
|
</nav>
|
|
);
|
|
}
|