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

@@ -169,6 +169,7 @@
gap: 1rem; gap: 1rem;
background-color: var(--secondary-background); background-color: var(--secondary-background);
box-shadow: none; box-shadow: none;
z-index: 999;
} }
.splitBillButton { .splitBillButton {

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

View File

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

View File

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

View File

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