product: change actions button postions and general fixes

This commit is contained in:
2025-12-19 01:55:53 +03:00
parent e43675d018
commit ed23b10240
7 changed files with 308 additions and 107 deletions

View File

@@ -0,0 +1,90 @@
.quantityControls {
display: flex;
align-items: center;
border-radius: 888px;
width: fit-content;
}
.quantityLabel {
font-size: 14px;
color: var(--secondary-color);
font-weight: 500;
}
.quantityInputContainer {
display: flex;
align-items: center;
padding: 0 1px;
border-radius: 888px;
width: 140px;
height: 48px;
}
.quantityButton {
padding: 0;
width: 25px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
color: var(--secondary-color);
transition: all 0.2s ease;
}
.quantityInput {
text-align: center;
border: none;
box-shadow: none;
font-size: 16px;
font-weight: 600;
}
.removeButton {
padding: 4px 0;
height: 48px;
display: flex;
align-items: center;
gap: 4px;
width: 30px;
}
.deleteButtonContainer {
position: absolute;
top: 12px;
right: 12px;
background-color: var(--primary);
border-radius: 50%;
padding: 8px;
cursor: pointer;
transition: all 0.3s ease;
}
.deleteIcon {
font-size: 18px;
color: var(--secondary-color);
}
.cartItemActions :global(.ant-input-number-outlined) {
border: none;
width: 40px;
background-color: inherit;
text-align: center;
}
.cartItemActions :global(.ant-input-number-input) {
text-align: center !important;
}
.plusIcon {
margin-bottom: 1px;
color: #1F1C2E;
}
.minusIcon {
color: var(--secondary-foreground);
}
.deleteIcon {
position: relative;
right: 1px;
}

View File

@@ -0,0 +1,125 @@
import { MinusOutlined, PlusOutlined } from "@ant-design/icons";
import { Button, Grid, InputNumber, Popconfirm } from "antd";
import DeleteIcon from "components/Icons/DeleteIcon";
import { useTranslation } from "react-i18next";
import { useAppSelector } from "redux/hooks";
import styles from "./ActionsButtons.module.css";
import { colors } from "ThemeConstants";
const { useBreakpoint } = Grid;
export default function ActionsButtons({
quantity,
setQuantity,
max,
min,
}: {
quantity: number;
setQuantity: (quantity: number) => void;
max?: number;
min?: number;
}) {
const { t } = useTranslation();
const { xs } = useBreakpoint(); // Default to desktop
const { isRTL } = useAppSelector((state) => state.locale); // Default to LTR
const getPopconfirmOverlayStyle = () => ({
width: xs ? "280px" : "auto",
maxWidth: "320px",
".antPopconfirmMessageTitle": {
fontSize: xs ? "14px" : "16px",
paddingRight: xs ? "24px" : "0",
},
".antPopconfirmMessageContent": {
fontSize: xs ? "13px" : "14px",
marginTop: "4px",
},
".antPopconfirmButtons": {
marginTop: "12px",
".ant-btn": {
fontSize: xs ? "13px" : "14px",
height: xs ? "28px" : "32px",
padding: xs ? "0 12px" : "4px 15px",
},
},
});
return (
<div className={styles.cartItemActions}>
<div className={styles.quantityControls}>
<div className={styles.quantityInputContainer}>
{quantity > 0 ? (
<Button
shape="circle"
iconPlacement="start"
icon={<MinusOutlined title="add" className={styles.minusIcon} />}
size="small"
onClick={() => setQuantity(Math.max(1, quantity - 1))}
className={styles.quantityButton}
{...(min && { disabled: quantity === min })}
style={{
width: 48,
height: 48,
minWidth: 48,
borderColor: "#DEDEE0",
}}
/>
) : (
<Popconfirm
title={t("cart.deleteConfirmation.title")}
description={t("cart.deleteConfirmation.content")}
onConfirm={() => setQuantity(0)}
okText={t("cart.deleteConfirmation.confirm")}
cancelText={t("cart.deleteConfirmation.cancel")}
okButtonProps={{ danger: true }}
placement={isRTL ? "left" : "right"}
styles={{
root: getPopconfirmOverlayStyle(),
}}
>
<Button
shape="circle"
iconPlacement="start"
icon={<DeleteIcon />}
size="small"
className={styles.addButton}
style={{
background: "#FEF2F2",
width: 48,
height: 48,
border: "none",
minWidth: 48,
}}
/>
</Popconfirm>
)}
<InputNumber
min={min || 1}
max={max || 100}
value={quantity || 1}
onChange={(value: number | null) => setQuantity(value || 1)}
size="small"
controls={false}
className={styles.quantityInput}
name="id"
/>
<Button
shape="circle"
iconPlacement="start"
icon={<PlusOutlined title="add" className={styles.plusIcon} />}
size="small"
onClick={() => setQuantity(Math.min(100, quantity + 1))}
className={styles.quantityButton}
{...(max && { disabled: quantity >= max })}
style={{
width: 48,
height: 48,
borderColor: "#DEDEE0",
minWidth: 48,
}}
/>
</div>
</div>
</div>
);
}

View File

@@ -1,12 +1,7 @@
import {
LeftOutlined,
RightOutlined,
ShoppingCartOutlined,
} from "@ant-design/icons";
import { Button, Form, Input, message, Row } from "antd";
import { ShoppingCartOutlined } from "@ant-design/icons";
import { Button, Form, message, Row } from "antd";
import { addItem } from "features/order/orderSlice";
import useBreakPoint from "hooks/useBreakPoint";
import { BottomSheet } from "pages/cart/components/specialRequest/BottomSheet.tsx";
import { useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { useAppDispatch, useAppSelector } from "redux/hooks";
@@ -15,6 +10,9 @@ import { Extra, Product, Variant } from "utils/types/appTypes";
import styles from "../product.module.css";
import { useGetRestaurantDetailsQuery } from "redux/api/others";
import { useParams } from "react-router-dom";
import TextArea from "antd/es/input/TextArea";
import ProText from "components/ProText";
import ActionsButtons from "./ActionsButtons/ActionsButtons";
export default function ProductFooter({
product,
@@ -24,6 +22,7 @@ export default function ProductFooter({
selectedGroups,
quantity,
onClose,
setQuantity,
}: {
product: Product;
isValid?: boolean;
@@ -32,11 +31,11 @@ export default function ProductFooter({
selectedGroups: Record<number, string[]>;
quantity: number;
onClose?: () => void;
setQuantity: (quantity: number) => void;
}) {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const { themeName } = useAppSelector((state) => state.theme);
const [isSpecialRequestOpen, setIsSpecialRequestOpen] = useState(false);
const { isMobile, isDesktop } = useBreakPoint();
const { isRTL } = useAppSelector((state) => state.locale);
const [specialRequest, setSpecialRequest] = useState("");
@@ -122,14 +121,6 @@ export default function ProductFooter({
}
};
const handleSpecialRequestSave = (value: string) => {
setSpecialRequest(value);
};
const handleSpecialRequestClose = () => {
setIsSpecialRequestOpen(false);
};
return (
<>
<Row
@@ -149,7 +140,7 @@ export default function ProductFooter({
[isRTL ? "right" : "left"]: 0,
width: hasCustomizationOptions ? "50%" : "100%",
}),
height: "135px",
height: "195px",
}}
>
<div
@@ -160,84 +151,79 @@ export default function ProductFooter({
gap: "12px",
}}
>
<ProText>{t("cart.specialRequest")}</ProText>
<Form.Item style={{ position: "relative", top: -5, width: "100%" }}>
<TextArea
value={specialRequest}
rows={2}
placeholder={t("cart.specialRequest")}
size="large"
autoFocus={false}
className={styles.inputField}
onChange={(e) => setSpecialRequest(e.target.value)}
/>
</Form.Item>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
gap: "12px",
width: "100%",
}}
>
<Form.Item style={{ position: "relative", top: -5, width: "100%" }}>
<Input
value={specialRequest}
placeholder={t("cart.specialRequest")}
size="large"
autoFocus={false}
className={styles.inputField}
onChange={(e) => setSpecialRequest(e.target.value)}
suffix={
<div
className={styles.editButton}
onClick={() => setIsSpecialRequestOpen(true)}
>
<u>{t("cart.editNote")}</u>{" "}
{isRTL ? <LeftOutlined /> : <RightOutlined />}
</div>
}
<ActionsButtons
quantity={quantity}
setQuantity={setQuantity}
max={100}
min={1}
/>
</Form.Item>
</div>
<div
style={{
display: "flex",
gap: "12px",
width: "100%",
}}
>
<Button
type="primary"
icon={<ShoppingCartOutlined />}
onClick={handleAddToCart}
disabled={!isValid}
style={{
flex: 1,
height: "48px",
fontSize: isMobile ? "1rem" : "16px",
transition: "all 0.3s ease",
width: "100%",
borderRadius: 888,
boxShadow: "none",
backgroundColor: isValid
? colors.primary
: "rgba(233, 233, 233, 1)",
color: isValid ? "#FFF" : "#999",
cursor: isValid ? "pointer" : "not-allowed",
}}
onMouseEnter={(e) => {
if (!isMobile && isValid) {
e.currentTarget.style.transform = "translateY(-2px)";
}
}}
onMouseLeave={(e) => {
if (!isMobile) {
e.currentTarget.style.transform = "translateY(0)";
}
}}
>
{isValid ? t("menu.addToCart") : t("menu.selectRequiredOptions")}
</Button>
<Button
type="primary"
icon={<ShoppingCartOutlined />}
onClick={handleAddToCart}
disabled={!isValid}
style={{
flex: 1,
height: 48,
fontSize: isMobile ? "1rem" : "16px",
transition: "all 0.3s ease",
width: "100%",
borderRadius: 888,
boxShadow: "none",
backgroundColor: isValid
? colors.primary
: "rgba(233, 233, 233, 1)",
color: isValid ? "#FFF" : "#999",
cursor: isValid ? "pointer" : "not-allowed",
}}
onMouseEnter={(e) => {
if (!isMobile && isValid) {
e.currentTarget.style.transform = "translateY(-2px)";
}
}}
onMouseLeave={(e) => {
if (!isMobile) {
e.currentTarget.style.transform = "translateY(0)";
}
}}
>
{isValid
? t("menu.addToCart")
: t("menu.selectRequiredOptions")}
</Button>
</div>
</div>
</Row>
{!isDesktop && isSpecialRequestOpen && (
{/* {!isDesktop && isSpecialRequestOpen && (
<BottomSheet
isOpen={isSpecialRequestOpen}
onClose={handleSpecialRequestClose}
initialValue={specialRequest}
onSave={handleSpecialRequestSave}
/>
)}
)} */}
</>
);
}

View File

@@ -110,7 +110,7 @@ export default function Variants({
<>
{variantsList?.length > 0 && variantLevels.length > 0 && (
<>
{!isDesktop && <Divider style={{ margin: "0" }} />}
{!isDesktop && <Divider style={{ margin: "0 0 10px 0" }} />}
<div>
<div
style={{

View File

@@ -1,4 +1,3 @@
import ActionsButtons from "components/ActionsButtons/ActionsButtons";
import ImageWithFallback from "components/ImageWithFallback";
import { ItemDescriptionIcons } from "components/ItemDescriptionIcons/ItemDescriptionIcons";
import ProText from "components/ProText";
@@ -177,7 +176,7 @@ export default function ProductDetailPage({
return (
<div
style={{
height: "80vh",
height: "75vh",
overflow: "auto",
scrollbarWidth: "none",
}}
@@ -221,12 +220,16 @@ export default function ProductDetailPage({
<div className={styles.productHeader}>
<div className={styles.productDetails}>
<ProText
strong
style={{ fontSize: isDesktop ? "1.5rem" : "1.25rem" }}
style={{
fontWeight: 400,
fontStyle: "Regular",
fontSize: "14px",
lineHeight: "140%",
letterSpacing: "0%",
}}
>
{isRTL ? product?.nameOther : product?.name}
</ProText>
<br />
{product?.description && (
<ProText
type="secondary"
@@ -239,24 +242,19 @@ export default function ProductDetailPage({
textOverflow: "ellipsis",
wordWrap: "break-word",
overflowWrap: "break-word",
lineHeight: "1.4",
maxHeight: "2.8em",
fontWeight: "500",
letterSpacing: "0.01em",
fontSize: "1rem",
fontWeight: 400,
fontStyle: "Regular",
fontSize: "12px",
lineHeight: "140%",
letterSpacing: "0%",
marginTop: 10,
}}
>
{isRTL ? product?.descriptionAR : product?.description}
</ProText>
)}
<ArabicPrice
price={product?.price}
style={{
fontSize: isDesktop ? "1.2rem" : "1rem",
color: colors.primary,
marginTop: "12px",
}}
/>
<div
style={{
display: "flex",
@@ -271,14 +269,13 @@ export default function ProductDetailPage({
className={styles.itemDescriptionIcons}
/>
</div>
</div>
<div className={styles.quantitySection}>
<ActionsButtons
quantity={quantity}
setQuantity={(quantity) => setQuantity(quantity)}
max={100}
min={1}
<ArabicPrice
price={product?.price}
style={{
fontSize: isDesktop ? "1.2rem" : "1rem",
color: colors.primary,
marginTop: "12px",
}}
/>
</div>
</div>
@@ -293,6 +290,7 @@ export default function ProductDetailPage({
selectedGroups={selectedExtrasByGroup}
quantity={quantity}
onClose={onClose}
setQuantity={(quantity: number) => setQuantity(quantity)}
/>
)}
</div>
@@ -301,7 +299,7 @@ export default function ProductDetailPage({
{hasCustomizationOptions && (
<div className={isDesktop ? styles.rightColumn : styles.fullWidth}>
<Space
direction="vertical"
orientation="vertical"
size="middle"
style={{ width: "100%", padding: "0 1rem" }}
>
@@ -341,6 +339,7 @@ export default function ProductDetailPage({
selectedExtras={selectedExtras}
selectedGroups={selectedExtrasByGroup}
quantity={quantity}
setQuantity={(quantity: number) => setQuantity(quantity)}
/>
)}
</div>

View File

@@ -232,7 +232,7 @@
align-items: center;
justify-content: flex-end;
position: relative;
top: 5px
top: 5px;
}
/* Dark mode desktop styles */
@@ -320,8 +320,9 @@
}
.inputField {
height: 50px;
height: 100px;
width: 100% !important;
border-radius: 6px;
}
.editButton {
@@ -329,4 +330,3 @@
font-size: 14px;
cursor: pointer;
}