badge and show ai button
Build and Deploy Docker Images / build_and_deploy (push) Has been cancelled
Build and Deploy Docker Images / build_and_deploy (push) Has been cancelled
This commit is contained in:
@@ -46,6 +46,7 @@ export interface Restaurant {
|
||||
domain: string;
|
||||
score: Score;
|
||||
plan: "base" | "premium";
|
||||
enableAiChat?: boolean;
|
||||
bgType?: "pattern" | "custom" | "color";
|
||||
bgBlur?: number;
|
||||
bgOpacity?: number;
|
||||
|
||||
@@ -161,7 +161,7 @@ function AiPage() {
|
||||
key={labelKey}
|
||||
type="button"
|
||||
disabled={isPending}
|
||||
onClick={() => setInput(t(labelKey))}
|
||||
onClick={() => sendMessage(t(labelKey))}
|
||||
className={glassSurfaceFlat("inline-flex items-center gap-1.5 rounded-lg px-3 py-1.5 transition-[filter] active:brightness-95 disabled:opacity-50")}
|
||||
>
|
||||
<Icon size={15} className="stroke-foreground" variant="Bold" />
|
||||
|
||||
@@ -51,9 +51,7 @@ const MenuItem = ({ food }: MenuItemProps) => {
|
||||
if (!content) return "";
|
||||
|
||||
if (Array.isArray(content)) {
|
||||
return content
|
||||
.filter((item) => item && typeof item === "string")
|
||||
.join("، ");
|
||||
return content.filter((item) => item && typeof item === "string").join("، ");
|
||||
}
|
||||
|
||||
return "";
|
||||
@@ -74,15 +72,9 @@ const MenuItem = ({ food }: MenuItemProps) => {
|
||||
return basePrice;
|
||||
}, [food.price, food.discount]);
|
||||
|
||||
const formattedPrice = useMemo(
|
||||
() => (finalPrice ? finalPrice.toLocaleString("fa-IR") : "0"),
|
||||
[finalPrice],
|
||||
);
|
||||
const formattedPrice = useMemo(() => (finalPrice ? finalPrice.toLocaleString("fa-IR") : "0"), [finalPrice]);
|
||||
|
||||
const formattedOriginalPrice = useMemo(
|
||||
() => (food.price ? food.price.toLocaleString("fa-IR") : "0"),
|
||||
[food.price],
|
||||
);
|
||||
const formattedOriginalPrice = useMemo(() => (food.price ? food.price.toLocaleString("fa-IR") : "0"), [food.price]);
|
||||
|
||||
const hasDiscount = useMemo(() => (food.discount || 0) > 0, [food.discount]);
|
||||
|
||||
@@ -102,63 +94,34 @@ const MenuItem = ({ food }: MenuItemProps) => {
|
||||
return (
|
||||
<div className="flex gap-4 w-full h-full items-center">
|
||||
<Link href={foodDetailUrl} className="cursor-pointer">
|
||||
<img
|
||||
className="rounded-xl bg-[#F2F2F9] min-w-28 w-28 max-h-28 object-cover"
|
||||
src={imageSrc}
|
||||
height={112}
|
||||
width={112}
|
||||
alt={foodName}
|
||||
loading="lazy"
|
||||
onError={() => setImageSrc(fallbackImage)}
|
||||
/>
|
||||
<img className="rounded-xl bg-[#F2F2F9] min-w-28 w-28 max-h-28 object-cover" src={imageSrc} height={112} width={112} alt={foodName} loading="lazy" onError={() => setImageSrc(fallbackImage)} />
|
||||
</Link>
|
||||
<div className="w-full inline-flex flex-col justify-between min-w-0">
|
||||
<Link href={foodDetailUrl} className="cursor-pointer min-w-0">
|
||||
<div className="min-w-0">
|
||||
<div className="text-sm2 font-normal text-black dark:text-white wrap-break-word">
|
||||
{foodName}
|
||||
</div>
|
||||
{(foodContent || foodDescription) && (
|
||||
<div className="text-[#7F7F7F] line-clamp-2 text-xs leading-5 font-normal mt-2 wrap-break-word overflow-hidden">
|
||||
{foodContent || foodDescription}
|
||||
</div>
|
||||
)}
|
||||
<div className="text-sm2 font-normal text-black dark:text-white wrap-break-word">{foodName}</div>
|
||||
{(foodContent || foodDescription) && <div className="text-[#7F7F7F] line-clamp-2 text-xs leading-5 font-normal mt-2 wrap-break-word overflow-hidden">{foodContent || foodDescription}</div>}
|
||||
</div>
|
||||
</Link>
|
||||
<div className="inline-flex mt-2 gap-2 justify-between w-full items-start">
|
||||
<div
|
||||
className="w-full flex flex-col gap-1 items-end"
|
||||
dir="ltr"
|
||||
>
|
||||
<div className="w-full flex flex-col gap-1 items-end" dir="ltr">
|
||||
{hasDiscount ? (
|
||||
<>
|
||||
<span className="text-xs text-disabled-text line-through">
|
||||
{formattedOriginalPrice} T
|
||||
</span>
|
||||
<span className="text-sm font-medium text-black dark:text-white">
|
||||
{formattedPrice} T
|
||||
</span>
|
||||
<span className="text-xs text-disabled-text line-through">{formattedOriginalPrice} T</span>
|
||||
<span className="text-sm font-medium text-black dark:text-white">{formattedPrice} T</span>
|
||||
</>
|
||||
) : (
|
||||
<span className="text-sm text-right">
|
||||
{formattedPrice} T
|
||||
</span>
|
||||
<span className="text-sm text-right">{formattedPrice} T</span>
|
||||
)}
|
||||
</div>
|
||||
<motion.div
|
||||
whileTap={isAvailable ? { scale: 1.05 } : undefined}
|
||||
className="bg-background pattern-secondary-bg active:drop-shadow-xs max-w-[115px] rounded-md w-full h-8 inline-flex p-1 justify-between items-center gap-2 relative overflow-hidden"
|
||||
className="bg-background pattern-secondary-bg active:drop-shadow-xs max-w-28.75 rounded-md w-full h-8 inline-flex p-1 justify-between items-center gap-2 relative overflow-hidden"
|
||||
>
|
||||
{quantity <= 0 ? (
|
||||
<button
|
||||
onClick={handleAddToCart}
|
||||
disabled={!isAvailable}
|
||||
className="inline-flex w-full justify-center items-center gap-2 disabled:pointer-events-none"
|
||||
>
|
||||
<button onClick={handleAddToCart} disabled={!isAvailable} className="inline-flex w-full justify-center items-center gap-2 disabled:pointer-events-none">
|
||||
{isAvailable && <PlusIcon />}
|
||||
<div className="text-sm2 pt-0.5 font-normal text-foreground">
|
||||
{isAvailable ? "افزودن" : "عدم موجودی"}
|
||||
</div>
|
||||
<div className="text-sm2 pt-0.5 font-normal text-foreground">{isAvailable ? "افزودن" : "عدم موجودی"}</div>
|
||||
</button>
|
||||
) : (
|
||||
<>
|
||||
@@ -169,19 +132,10 @@ const MenuItem = ({ food }: MenuItemProps) => {
|
||||
>
|
||||
<PlusIcon className="text-foreground" />
|
||||
</button>
|
||||
<motion.div
|
||||
key={quantity}
|
||||
initial={{ scale: 1 }}
|
||||
animate={{ scale: [1.2, 0.95, 1] }}
|
||||
transition={{ duration: 0.3 }}
|
||||
className="text-sm2 pt-0.5 font-semibold"
|
||||
>
|
||||
<motion.div key={quantity} initial={{ scale: 1 }} animate={{ scale: [1.2, 0.95, 1] }} transition={{ duration: 0.3 }} className="text-sm2 pt-0.5 font-semibold">
|
||||
{quantity}
|
||||
</motion.div>
|
||||
<button
|
||||
onClick={handleRemoveFromCart}
|
||||
className="bg-container hover:bg-container/60 active:bg-container/30 rounded-sm p-2 transition-colors"
|
||||
>
|
||||
<button onClick={handleRemoveFromCart} className="bg-container hover:bg-container/60 active:bg-container/30 rounded-sm p-2 transition-colors">
|
||||
<MinusIcon className="text-foreground" />
|
||||
</button>
|
||||
</>
|
||||
|
||||
@@ -44,6 +44,7 @@ type MenuItemType = {
|
||||
href: string | undefined;
|
||||
title: string;
|
||||
icon: Icon;
|
||||
badge?: string;
|
||||
auth?: boolean;
|
||||
guestOnly?: boolean;
|
||||
premiumOnly?: boolean;
|
||||
@@ -57,7 +58,7 @@ const menuItems: Array<Array<MenuItemType>> = [
|
||||
{ href: "order/history", title: "Orders", icon: CalendarSearch, premiumOnly: true },
|
||||
{ href: "transactions", title: "Transactions", icon: Receipt1, premiumOnly: true },
|
||||
{ href: "game", title: "Games", icon: Game },
|
||||
{ href: "ai", title: "AI", icon: MessageProgramming },
|
||||
{ href: "ai", title: "AI", icon: MessageProgramming, badge: "AIBadge" },
|
||||
{ href: "?share", title: "ShareWithFriends", icon: Like1 },
|
||||
{ href: "report", title: "ReportProblem", icon: NoteBoardIcon },
|
||||
{ href: "?installpwa", title: "InstallApp", icon: DirectboxReceive },
|
||||
@@ -251,17 +252,19 @@ function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeS
|
||||
if (item.guestOnly && isSuccess) return false;
|
||||
if (item.href === "?installpwa" && isPWAInstalled) return false;
|
||||
if (item.premiumOnly && !isPremium) return false;
|
||||
if (item.href === "ai" && !aboutData?.data?.enableAiChat) return false;
|
||||
return true;
|
||||
})
|
||||
.map(({ icon: Icon, ...item }, index) => {
|
||||
const href = `/${name}/${item.href}`;
|
||||
const isActive = pathname === href;
|
||||
return (
|
||||
<li key={index} className="mt-4 h-6">
|
||||
<li key={index} className={clsx("mt-4", item.badge ? "h-8" : "h-6")}>
|
||||
<SideMenuItem
|
||||
className={clsx(isActive && "text-primary! border-primary! dark:text-neutral-200! dark:border-neutral-200!", "border-r-4!")}
|
||||
href={href ?? ""}
|
||||
title={tMenu(item.title)}
|
||||
badge={item.badge ? tMenu(item.badge) : undefined}
|
||||
onClick={typeof item.href === "string" ? hrefOnClicks.find((i) => i.href === item.href)?.handler : undefined}
|
||||
icon={
|
||||
<Icon
|
||||
|
||||
@@ -1,29 +1,36 @@
|
||||
'use client';
|
||||
"use client";
|
||||
|
||||
import Link, { LinkProps } from 'next/link'
|
||||
import React from 'react'
|
||||
import Link, { LinkProps } from "next/link";
|
||||
import React from "react";
|
||||
|
||||
type Props = {
|
||||
icon: React.ReactElement
|
||||
title: string
|
||||
} & LinkProps & React.AnchorHTMLAttributes<HTMLAnchorElement>
|
||||
icon: React.ReactElement;
|
||||
title: string;
|
||||
badge?: string;
|
||||
} & LinkProps &
|
||||
React.AnchorHTMLAttributes<HTMLAnchorElement>;
|
||||
|
||||
export default function SideMenuItem({ href, title, icon, className, children, ...restProps }: Props) {
|
||||
return (
|
||||
<Link
|
||||
{...restProps}
|
||||
href={href}
|
||||
className={`
|
||||
export default function SideMenuItem({ href, title, icon, badge, className, children, ...restProps }: Props) {
|
||||
return (
|
||||
<Link
|
||||
{...restProps}
|
||||
href={href}
|
||||
className={`
|
||||
inline-flex gap-3 ps-6 xl:ps-9 h-full w-full items-center overflow-hidden text-nowrap
|
||||
text-xs font-light text-disabled-text border-transparent
|
||||
data-active:border-s-6 data-active:border-primary data-active:text-primary
|
||||
dark:data-active:border-foreground dark:data-active:text-foreground
|
||||
${className}
|
||||
`}
|
||||
>
|
||||
{children}
|
||||
<span className="shrink-0">{icon}</span>
|
||||
<span className='mt-0.5'>{title}</span>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
>
|
||||
{children}
|
||||
<span className="shrink-0">{icon}</span>
|
||||
<span className="flex relative flex-col items-start gap-0.5">
|
||||
{badge && (
|
||||
<span className="rounded-full -top-2 right-9 absolute bg-primary/15 px-1.5 py-0.5 text-[8px] font-medium leading-none text-primary dark:bg-white/15 dark:text-neutral-200">{badge}</span>
|
||||
)}
|
||||
<span className="mt-0.5">{title}</span>
|
||||
</span>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"Transactions": "لیست تراکنش ها",
|
||||
"Games": "بازی و سرگرمی ها",
|
||||
"AI": "چی بخورم؟",
|
||||
"AIBadge": "هوش مصنوعی",
|
||||
"ShareWithFriends": "معرفی به دوستان",
|
||||
"ReportProblem": "گزارش اشکال",
|
||||
"InstallApp": "نصب اپلیکیشن",
|
||||
|
||||
Reference in New Issue
Block a user