"use client"; import { Search, ShoppingBag, X } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { QR_ALL_CATEGORY_ID } from "@/lib/qr-menu-constants"; import { MenuItemLabels } from "@/components/menu/menu-item-labels"; import { CategoryVisual } from "@/components/menu/category-visual"; import { formatCurrency } from "@/lib/format"; import { resolveMediaUrl } from "@/lib/api/client"; import { cn } from "@/lib/utils"; import type { QrCartLine, QrPublicMenuCategory, QrPublicMenuItem } from "@/lib/api/qr-public"; import type { CafeThemePalette } from "@/lib/cafe-theme"; import { hasMenu3dView } from "@/lib/menu-3d"; import { Box } from "lucide-react"; export type QrMenuBodyProps = { menuStyle: string; colors: CafeThemePalette; categories: QrPublicMenuCategory[]; activeCategory: string; onCategoryChange: (id: string) => void; activeItems: QrPublicMenuItem[]; showAllGrouped?: boolean; searchQuery: string; onSearchChange: (value: string) => void; isSearching?: boolean; categoryNameById?: Map; cart: QrCartLine[]; onAdd: (item: QrPublicMenuItem) => void; onRemove: (itemId: string) => void; onView3d?: (item: QrPublicMenuItem) => void; totalItems: number; totalPrice: number; onOpenCart: () => void; labels: { emptyCategory: string; addToCart: string; checkout: string; searchPlaceholder: string; allCategories: string; clearSearch: string; view3d: string; }; }; export function QrGuestMenuBody({ showCartBar = true, ...props }: QrMenuBodyProps & { showCartBar?: boolean }) { const { colors } = props; const primary = colors.primary; const surface = colors.surface; const style = props.menuStyle || "cards"; const listProps = { ...props, primary, surface, colors, showCategoryLabel: props.isSearching ?? false, categoryNameById: props.categoryNameById, }; return (
{props.showAllGrouped ? ( ) : style === "grid" ? ( ) : style === "list" ? ( ) : style === "compact" ? ( ) : style === "magazine" ? ( ) : style === "classic" ? ( ) : ( )} {showCartBar ? : null}
); } function MenuSearchBar({ searchQuery, onSearchChange, primary, surface, labels, }: Pick & { surface: string; primary: string; }) { return (
onSearchChange(e.target.value)} placeholder={labels.searchPlaceholder} className="h-10 rounded-xl qr-border qr-surface ps-9 pe-9 text-sm qr-text" style={{ borderColor: `${primary}33` }} /> {searchQuery ? ( ) : null}
); } function CategoryTabs({ categories, activeCategory, onCategoryChange, primary, surface, colors, labels, }: Pick & { surface: string; primary: string; }) { return (
{categories.map((cat) => ( ))}
); } type ItemListExtras = { surface: string; primary: string; colors: CafeThemePalette; showCategoryLabel?: boolean; categoryNameById?: Map; }; function GroupedAllSections( props: Pick< QrMenuBodyProps, "categories" | "cart" | "onAdd" | "onRemove" | "onView3d" | "labels" > & ItemListExtras ) { const { categories, labels, surface, primary, colors, onView3d } = props; const hasAny = categories.some((c) => (c.items?.length ?? 0) > 0); if (!hasAny) return ; return (
{categories.map((cat) => { const items = cat.items ?? []; if (items.length === 0) return null; return (

{cat.name}

{items.map((item) => ( ))}
); })}
); } function CardItems({ activeItems, cart, onAdd, onRemove, onView3d, primary, labels, surface, colors, showCategoryLabel, categoryNameById, }: Pick< QrMenuBodyProps, "activeItems" | "cart" | "onAdd" | "onRemove" | "onView3d" | "labels" > & ItemListExtras) { return (
{activeItems.length === 0 ? ( ) : ( activeItems.map((item) => ( )) )}
); } function GridItems({ activeItems, cart, onAdd, onRemove, onView3d, primary, labels, surface, colors, showCategoryLabel, categoryNameById, }: Pick< QrMenuBodyProps, "activeItems" | "cart" | "onAdd" | "onRemove" | "onView3d" | "labels" > & ItemListExtras) { if (activeItems.length === 0) return ; return (
{activeItems.map((item) => ( ))}
); } function ListItems({ activeItems, cart, onAdd, onRemove, onView3d, primary, labels, surface, colors, compact, showCategoryLabel, categoryNameById, }: Pick< QrMenuBodyProps, "activeItems" | "cart" | "onAdd" | "onRemove" | "onView3d" | "labels" > & ItemListExtras & { compact: boolean }) { return (
{activeItems.length === 0 ? ( ) : ( activeItems.map((item) => (
{resolveMediaUrl(item.imageUrl) ? ( ) : (
)} {hasMenu3dView(item) && onView3d ? ( onView3d(item)} className="absolute -bottom-1 end-0 scale-90" /> ) : null}
{showCategoryLabel && categoryNameById?.get(item.categoryId) ? (

{categoryNameById.get(item.categoryId)}

) : null}

{formatCurrency(effectivePrice(item), "fa-IR")}

)) )}
); } function MagazineItems({ activeItems, cart, onAdd, onRemove, onView3d, primary, labels, surface, colors, showCategoryLabel, categoryNameById, }: Pick< QrMenuBodyProps, "activeItems" | "cart" | "onAdd" | "onRemove" | "onView3d" | "labels" > & ItemListExtras) { return (
{activeItems.length === 0 ? ( ) : ( activeItems.map((item) => { const img = resolveMediaUrl(item.imageUrl); return (
{img ? ( ) : (
)} {hasMenu3dView(item) && onView3d ? ( onView3d(item)} className="absolute bottom-3 start-3" /> ) : null}
{showCategoryLabel && categoryNameById?.get(item.categoryId) ? (

{categoryNameById.get(item.categoryId)}

) : null}
{formatCurrency(effectivePrice(item), "fa-IR")}
); }) )}
); } function ClassicLayout({ categories, activeCategory, onCategoryChange, activeItems, showAllGrouped, cart, onAdd, onRemove, onView3d, colors, labels, surface, }: QrMenuBodyProps & { surface: string }) { const primary = colors.primary; return (
{showAllGrouped ? ( ) : ( )}
); } function ItemRowCard({ item, cart, primary, surface, colors, onAdd, onRemove, onView3d, addLabel, view3dLabel, categoryLabel, }: { item: QrPublicMenuItem; cart: QrCartLine[]; primary: string; surface: string; colors: CafeThemePalette; onAdd: (item: QrPublicMenuItem) => void; onRemove: (itemId: string) => void; onView3d?: (item: QrPublicMenuItem) => void; addLabel: string; view3dLabel: string; categoryLabel?: string; }) { const img = resolveMediaUrl(item.imageUrl); return (
{img ? ( ) : (
)} {hasMenu3dView(item) && onView3d ? ( onView3d(item)} className="absolute bottom-1 end-1" /> ) : null}
{categoryLabel ? (

{categoryLabel}

) : null}
{item.description ? (

{item.description}

) : null}
{formatCurrency(effectivePrice(item), "fa-IR")}
); } function GridCard({ item, cart, primary, surface, colors, onAdd, onRemove, onView3d, addLabel, view3dLabel, categoryLabel, }: { item: QrPublicMenuItem; cart: QrCartLine[]; primary: string; surface: string; colors: CafeThemePalette; onAdd: (item: QrPublicMenuItem) => void; onRemove: (itemId: string) => void; onView3d?: (item: QrPublicMenuItem) => void; addLabel: string; view3dLabel: string; categoryLabel?: string; }) { const img = resolveMediaUrl(item.imageUrl); return (
{img ? ( ) : (
)} {hasMenu3dView(item) && onView3d ? ( onView3d(item)} className="absolute bottom-2 start-2" /> ) : null}
{categoryLabel ? (

{categoryLabel}

) : null}

{formatCurrency(effectivePrice(item), "fa-IR")}

); } function QtyControls({ item, cart, primary, onAdd, onRemove, addLabel, small, }: { item: QrPublicMenuItem; cart: QrCartLine[]; primary: string; onAdd: (item: QrPublicMenuItem) => void; onRemove: (itemId: string) => void; addLabel: string; small?: boolean; }) { const inCart = cart.find((c) => c.item.id === item.id); const size = small ? "size-7 text-base" : "size-8 text-lg"; if (inCart) { return (
onRemove(item.id)} /> {inCart.qty} onAdd(item)} />
); } return ( ); } function QtyBtn({ label, className, variant, color, onClick, }: { label: string; className: string; variant: "outline" | "filled"; color: string; onClick: () => void; }) { return ( ); } function View3dChip({ label, onClick, className, }: { label: string; onClick: () => void; className?: string; }) { return ( ); } export function QrFloatingCartBar( props: Pick ) { return ; } function CartBar({ totalItems, totalPrice, colors, onOpenCart, labels, floating = true, }: Pick & { floating?: boolean; }) { const primary = colors.primary; if (totalItems <= 0) return null; return (
); } function EmptyCategory({ text }: { text: string }) { return

{text}

; } function effectivePrice(item: QrPublicMenuItem): number { const discount = item.discountPercent > 0 ? item.discountPercent : 0; return Math.round(item.price * (1 - discount / 100)); }