menu: enhnacncements

This commit is contained in:
2025-12-15 22:46:25 +03:00
parent 346dee1392
commit a126eb8e02
4 changed files with 149 additions and 48 deletions

View File

@@ -8,7 +8,8 @@ import styles from "./AddToCartButton.module.css";
import { useAppSelector, useAppDispatch } from "redux/hooks"; import { useAppSelector, useAppDispatch } from "redux/hooks";
import { Product } from "utils/types/appTypes"; import { Product } from "utils/types/appTypes";
import NextIcon from "components/Icons/NextIcon"; import NextIcon from "components/Icons/NextIcon";
import { addItem } from "features/order/orderSlice"; import { addItem, updateQuantity, removeItem } from "features/order/orderSlice";
import ProText from "components/ProText";
export function AddToCartButton({ item }: { item: Product }) { export function AddToCartButton({ item }: { item: Product }) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -22,10 +23,12 @@ export function AddToCartButton({ item }: { item: Product }) {
const { items } = useAppSelector((state) => state.order); const { items } = useAppSelector((state) => state.order);
// Check if product is in cart // Check if product is in cart
const isInCart = const cartItemsForProduct = items.filter((i) => i.id === item.id);
items const totalQuantity = cartItemsForProduct.reduce(
.filter((i) => i.id === item.id) (total, item) => total + item.quantity,
.reduce((total, item) => total + item.quantity, 0) > 0; 0
);
const isInCart = totalQuantity > 0;
// Check if item has extras, variants, or groups // Check if item has extras, variants, or groups
const hasOptions = const hasOptions =
@@ -33,6 +36,14 @@ export function AddToCartButton({ item }: { item: Product }) {
(item.variants && item.variants.length > 0) || (item.variants && item.variants.length > 0) ||
(item.theExtrasGroups && item.theExtrasGroups.length > 0); (item.theExtrasGroups && item.theExtrasGroups.length > 0);
// Find basic cart item (no variants/extras) - the one added by quick add
const basicCartItem = cartItemsForProduct.find(
(cartItem) =>
(!cartItem.variant || cartItem.variant === "None") &&
(!cartItem.extras || cartItem.extras.length === 0) &&
(!cartItem.extrasgroupnew || cartItem.extrasgroupnew.length === 0)
);
const handleClick = () => { const handleClick = () => {
if (restaurant && !restaurant.isOpened) { if (restaurant && !restaurant.isOpened) {
message.warning(t("menu.restaurantIsClosed")); message.warning(t("menu.restaurantIsClosed"));
@@ -46,7 +57,6 @@ export function AddToCartButton({ item }: { item: Product }) {
} }
// If no options, add item directly to cart // If no options, add item directly to cart
console.log("hasOptions", hasOptions);
if (!hasOptions) { if (!hasOptions) {
dispatch( dispatch(
addItem({ addItem({
@@ -66,6 +76,83 @@ export function AddToCartButton({ item }: { item: Product }) {
} }
}; };
const handleMinusClick = () => {
if (restaurant && !restaurant.isOpened) {
message.warning(t("menu.restaurantIsClosed"));
return;
}
if (basicCartItem && basicCartItem.uniqueId) {
if (basicCartItem.quantity > 1) {
// Decrease quantity
dispatch(
updateQuantity({
id: basicCartItem.id,
uniqueId: basicCartItem.uniqueId,
quantity: basicCartItem.quantity - 1,
})
);
} else {
// Remove item if quantity is 1
dispatch(removeItem(basicCartItem.uniqueId));
}
} else if (cartItemsForProduct.length > 0) {
// If no basic item found but items exist, remove the first one
const firstItem = cartItemsForProduct[0];
if (firstItem.uniqueId) {
if (firstItem.quantity > 1) {
dispatch(
updateQuantity({
id: firstItem.id,
uniqueId: firstItem.uniqueId,
quantity: firstItem.quantity - 1,
})
);
} else {
dispatch(removeItem(firstItem.uniqueId));
}
}
}
};
const handlePlusClick = () => {
if (restaurant && !restaurant.isOpened) {
message.warning(t("menu.restaurantIsClosed"));
return;
}
if (basicCartItem && basicCartItem.uniqueId) {
// Increase quantity of existing basic item
dispatch(
updateQuantity({
id: basicCartItem.id,
uniqueId: basicCartItem.uniqueId,
quantity: basicCartItem.quantity + 1,
})
);
} else if (!hasOptions) {
// Add new basic item if no options
dispatch(
addItem({
item: {
id: Number(item.id),
name: item.name,
price: item.price,
image: item.image,
description: item.description,
variant: "None",
isHasLoyalty: item.isHasLoyalty,
no_of_stamps_give: item.no_of_stamps_give,
},
quantity: 1,
}),
);
} else {
// If has options, navigate to product page
navigate(`/${subdomain}/product/${item.id}`);
}
};
return isInCart ? ( return isInCart ? (
<> <>
<div <div
@@ -102,33 +189,49 @@ export function AddToCartButton({ item }: { item: Product }) {
<Button <Button
shape="circle" shape="circle"
iconPosition="start" iconPosition="start"
icon={<MinusOutlined title="add" style={{ color: "black" }} />} icon={<MinusOutlined title="minus" style={{ color: "black" }} />}
size="small" size="small"
onClick={handleClick} onClick={handleMinusClick}
className={styles.addButton} className={styles.addButton}
style={{ style={{
backgroundColor: "white", backgroundColor: "white",
width: 28, width: 28,
height: 28, height: 28,
position: "absolute", position: "absolute",
bottom: 8, bottom: 9,
right: 64, right: 70,
minWidth: 28, minWidth: 28,
}} }}
/> />
<ProText
style={{
position: "absolute",
bottom: 17,
right: 50,
fontSize: 14,
fontWeight: 700,
fontStyle: "Bold",
lineHeight: "100%",
letterSpacing: "0.06px",
textAlign: "center",
verticalAlign: "middle",
}}
>
{totalQuantity}
</ProText>
<Button <Button
shape="circle" shape="circle"
iconPosition="start" iconPosition="start"
icon={<PlusOutlined title="add" />} icon={<PlusOutlined title="plus" />}
size="small" size="small"
onClick={handleClick} onClick={handlePlusClick}
className={styles.addButton} className={styles.addButton}
style={{ style={{
backgroundColor: colors.primary, backgroundColor: colors.primary,
width: 28, width: 28,
height: 28, height: 28,
position: "absolute", position: "absolute",
bottom: 8, bottom: 9,
right: 10, right: 10,
minWidth: 28, minWidth: 28,
}} }}

View File

@@ -190,7 +190,7 @@ export function CategoriesList({ categories }: CategoriesListProps) {
style={{ style={{
borderRadius: 8, borderRadius: 8,
border: "none", border: "none",
backgroundColor: "none", backgroundColor: "var(--background)",
}} }}
styles={{ styles={{
body: { body: {

View File

@@ -8,7 +8,6 @@ import ImageWithFallback from "components/ImageWithFallback";
import { Product } from "utils/types/appTypes.ts"; import { Product } from "utils/types/appTypes.ts";
import useBreakPoint from "hooks/useBreakPoint.ts"; import useBreakPoint from "hooks/useBreakPoint.ts";
import { useAppSelector } from "redux/hooks.ts"; import { useAppSelector } from "redux/hooks.ts";
import { useParams, useNavigate } from "react-router-dom";
import { ProductPreviewDialog } from "pages/menu/components/ProductPreviewDialog"; import { ProductPreviewDialog } from "pages/menu/components/ProductPreviewDialog";
import { useState } from "react"; import { useState } from "react";
import { AddToCartButton } from "../AddToCartButton/AddToCartButton"; import { AddToCartButton } from "../AddToCartButton/AddToCartButton";
@@ -20,9 +19,6 @@ export default function ProductCard({ item }: Props) {
const { isRTL } = useAppSelector((state) => state.locale); const { isRTL } = useAppSelector((state) => state.locale);
const { isMobile, isTablet, isDesktop } = useBreakPoint(); const { isMobile, isTablet, isDesktop } = useBreakPoint();
const { items } = useAppSelector((state) => state.order); const { items } = useAppSelector((state) => state.order);
const { subdomain } = useParams();
const navigate = useNavigate();
// Dialog state
const [isDialogOpen, setIsDialogOpen] = useState(false); const [isDialogOpen, setIsDialogOpen] = useState(false);
// Handle dialog close // Handle dialog close
@@ -83,8 +79,8 @@ export default function ProductCard({ item }: Props) {
style={{ style={{
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
justifyContent: "space-between", justifyContent: "flex-start",
gap: item.description ? "0.5rem" : "3.5rem", gap: "0.3rem",
}} }}
> >
<ProText <ProText
@@ -101,7 +97,6 @@ export default function ProductCard({ item }: Props) {
> >
{isRTL ? item.nameOther : item.name} {isRTL ? item.nameOther : item.name}
</ProText> </ProText>
{item.description && (
<ProText <ProText
type="secondary" type="secondary"
className={styles.itemDescription} className={styles.itemDescription}
@@ -124,9 +119,14 @@ export default function ProductCard({ item }: Props) {
> >
{item.description} {item.description}
</ProText> </ProText>
)}
<div> <div
style={{
position: "absolute",
bottom: 12,
left: 12,
}}
>
{item.original_price !== item.price && ( {item.original_price !== item.price && (
<ArabicPrice <ArabicPrice
price={item.original_price} price={item.original_price}
@@ -166,9 +166,6 @@ export default function ProductCard({ item }: Props) {
</div> </div>
<div style={{ position: "relative" }}> <div style={{ position: "relative" }}>
{/* {item.isHasLoyalty && (
<StarIcon className={styles.loyaltyButton} />
)} */}
<Badge <Badge
size="default" size="default"
offset={[-3, 3]} offset={[-3, 3]}

View File

@@ -330,6 +330,7 @@ export interface CartItem {
comment?: string; comment?: string;
// Unique identifier for cart items with the same product but different variants/extras/comments // Unique identifier for cart items with the same product but different variants/extras/comments
uniqueId?: string; uniqueId?: string;
} }
export interface UserType { export interface UserType {