- change refresh icon & apply refreshing logic
- apply validation in action btn in menu
- preserve on customer info state upon refresh
This commit is contained in:
2026-01-04 07:00:56 +03:00
parent 13cce2f12f
commit f294138d30
13 changed files with 233 additions and 105 deletions

View File

@@ -134,9 +134,10 @@ export default function CartActionsButtons({ item }: { item: CartItem }) {
}),
)
}
disabled={item.quantity >= 99}
className={styles.addButton}
style={{
backgroundColor: colors.primary,
backgroundColor: "#FFC600",
width: 28,
height: 28,
border: "none",

View File

@@ -2,9 +2,10 @@ interface PlusIconType {
className?: string;
onClick?: () => void;
dimesion?: string
color?: string
}
const PlusIcon = ({ className, onClick, dimesion }: PlusIconType) => {
const PlusIcon = ({ className, onClick, dimesion, color }: PlusIconType) => {
return (
<svg
width={dimesion || "16"}
@@ -17,7 +18,7 @@ const PlusIcon = ({ className, onClick, dimesion }: PlusIconType) => {
>
<path
d="M7.99992 3.3335V12.6668M3.33325 8.00016H12.6666"
stroke="#FFD633"
stroke={color || "#FFD633"}
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"

View File

@@ -0,0 +1,29 @@
interface RefershIconType {
className?: string;
onClick?: () => void;
dimension?: number;
}
const RefershIcon = ({ className, onClick, dimension }: RefershIconType) => {
return (
<svg
width={dimension || "18"}
height={dimension || "18"}
viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={className}
onClick={onClick}
>
<path
d="M1.5 7.5C1.5 7.5 1.59099 6.86307 4.22703 4.22703C6.86307 1.59099 11.1369 1.59099 13.773 4.22703C14.7069 5.16099 15.31 6.30054 15.5821 7.5M1.5 7.5V3M1.5 7.5H6M16.5 10.5C16.5 10.5 16.409 11.1369 13.773 13.773C11.1369 16.409 6.86307 16.409 4.22703 13.773C3.29307 12.839 2.69002 11.6995 2.41787 10.5M16.5 10.5V15M16.5 10.5H12"
stroke="#5F6C7B"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
);
};
export default RefershIcon;

View File

@@ -3,16 +3,25 @@
flex-direction: row;
justify-content: end;
padding: 0 16px;
gap: 10px;
gap: 8px;
position: absolute;
top: 50px;
z-index: 1000;
right: 0;
}
:global(.ant-app-rtl) .languageSwitch {
left: 0;
right: auto;
}
:global(.ant-app-ltr) .languageSwitch {
right: 0;
.refreshIcon {
cursor: pointer;
position: relative;
top: 2px;
margin-right: 3px;
}
:global(.ant-app-rtl) .refreshIcon {
margin-left: 3px;
}

View File

@@ -6,6 +6,7 @@ import { useDispatch } from "react-redux";
import { useAppSelector } from "redux/hooks";
import ProText from "../ProText";
import styles from "./LanguageSwitch.module.css";
import RefershIcon from "components/Icons/RefershIcon";
export function LanguageSwitch() {
const dispatch = useDispatch();
@@ -23,11 +24,15 @@ export function LanguageSwitch() {
});
};
const refreshPage = () => {
window.location.reload();
};
return (
<div className={styles.languageSwitch}>
<GlobalOutlined
style={{
color: themeName === "dark" ? "#fff" : "#434E5C",
color: themeName === "dark" ? "#fff" : "#333333",
fontSize: 15,
marginRight: 3,
cursor: isPending ? "wait" : "pointer",
@@ -37,23 +42,19 @@ export function LanguageSwitch() {
/>
<ProText
style={{
color: themeName === "dark" ? "#fff" : "#434E5C",
color: themeName === "dark" ? "#fff" : "#333333",
fontSize: 14,
marginRight: 3,
opacity: isPending ? 0.7 : 1,
[isRTL ? "marginLeft" : "marginRight"]: 3,
}}
onClick={changeLanguage}
>
{isPending ? "..." : isRTL ? "English" : "Arabic"}
</ProText>
<ShakeOutlined
style={{
color: themeName === "dark" ? "#fff" : "#434E5C",
fontSize: 15,
marginRight: 3,
[isRTL ? "marginLeft" : "marginRight"]: 3,
}}
/>
<div onClick={refreshPage}>
<RefershIcon dimension={18} className={styles.refreshIcon} />
</div>
</div>
);
}

View File

@@ -113,6 +113,7 @@ export const CART_STORAGE_KEYS = {
HIDDEN_SERVICES: "fascano_hidden_services",
VISIBLE_SERVICES: "fascano_visible_services",
ESTIMATE_WAY: "fascano_estimate_way",
CUSTOMER_NAME: "fascano_customer_name",
} as const;
// Utility functions for localStorage
@@ -201,7 +202,7 @@ const initialState: CartState = {
estimateWay: getFromLocalStorage(CART_STORAGE_KEYS.ESTIMATE_WAY, ""),
order: getFromLocalStorage(CART_STORAGE_KEYS.ORDER, null),
splitBillAmount: 0,
customerName: "",
customerName: getFromLocalStorage(CART_STORAGE_KEYS.CUSTOMER_NAME, ""),
totalServices: 8,
hiddenServices: 0,
visibleServices: 0,
@@ -701,6 +702,12 @@ const orderSlice = createSlice({
},
updateCustomerName(state, action: PayloadAction<string>) {
state.customerName = action.payload;
if (typeof window !== "undefined") {
localStorage.setItem(
CART_STORAGE_KEYS.CUSTOMER_NAME,
JSON.stringify(state.customerName),
);
}
},
},
});

View File

@@ -1,6 +1,10 @@
import ProInputCard from "components/ProInputCard/ProInputCard.tsx";
import ProPhoneInput from "components/ProPhoneInput";
import { selectCart, updateCustomerName } from "features/order/orderSlice";
import {
selectCart,
updateCustomerName,
updatePhone,
} from "features/order/orderSlice";
import { OrderType } from "pages/checkout/hooks/types.ts";
import { useTranslation } from "react-i18next";
import { useAppDispatch, useAppSelector } from "redux/hooks";
@@ -9,9 +13,12 @@ import styles from "./CustomerInformationCard.module.css";
export default function CustomerInformationCard() {
const { t } = useTranslation();
const { orderType } = useAppSelector(selectCart);
const { orderType, customerName, phone } = useAppSelector(selectCart);
const dispatch = useAppDispatch();
const customerName = useAppSelector((state) => state.order.customerName);
const setPhone = (value: string) => {
dispatch(updatePhone(value));
};
return (
orderType !== OrderType.Gift && (
@@ -30,7 +37,7 @@ export default function CustomerInformationCard() {
}}
/>
</Form.Item>
<ProPhoneInput propName="phone" />
<ProPhoneInput propName="phone" value={phone} onChange={setPhone} />
</div>
</ProInputCard>
</>

View File

@@ -24,13 +24,13 @@ import { useEffect } from "react";
export default function CheckoutPage() {
const { t } = useTranslation();
const [form] = Form.useForm();
const { phone, order, orderType, collectionMethod, coupon } =
const { phone, order, orderType, collectionMethod, coupon, customerName } =
useAppSelector(selectCart);
const { token } = useAppSelector((state) => state.auth);
const dispatch = useAppDispatch();
useEffect(() => {
form.setFieldsValue({ coupon, collectionMethod });
}, [form, phone]);
form.setFieldsValue({ coupon, collectionMethod, phone, customerName });
}, [form, phone, coupon, collectionMethod, customerName]);
return (
<>

View File

@@ -1,25 +1,93 @@
.plusIcon {
position: relative;
top: -1px;
.quantityControls {
display: flex;
align-items: center;
background-color: var(--background);
border-radius: 888px;
width: fit-content;
}
.addButton {
position: absolute;
z-index: 1;
.quantityLabel {
font-size: 14px;
color: var(--secondary-color);
font-weight: 500;
}
.quantityInputContainer {
display: flex;
padding: 0 1px;
align-items: center;
border-radius: 888px;
width: 90px;
height: 32px;
}
.quantityButton {
padding: 0;
width: 28px !important;
height: 28px !important;
display: flex;
align-items: center;
justify-content: center;
color: var(--secondary-background);
background-color: var(--primary);
border-radius: 50%;
transition: all 0.2s ease;
}
.quantityInput {
text-align: center;
border: none;
box-shadow: none;
font-size: 16px;
font-weight: 600;
border: 0;
color: #fff;
font-size: 1rem;
padding: 0;
}
.actionRect {
fill: var(--background) !important;
.removeButton {
padding: 4px 0;
height: 32px;
display: flex;
align-items: center;
gap: 4px;
width: 30px;
}
.addButton svg rect {
fill: var(--background) !important;
.deleteButtonContainer {
position: absolute;
top: 12px;
right: 12px;
border-radius: 50%;
padding: 8px;
cursor: pointer;
transition: all 0.3s ease;
}
:global(.darkApp) .addButton rect {
fill: var(--background) !important;
.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: var(--secondary-background);
}
.minusIcon {
color: var(--secondary-foreground);
}
.deleteIcon {
position: relative;
right: 1px;
}

View File

@@ -1,5 +1,5 @@
import { MinusOutlined, PlusOutlined } from "@ant-design/icons";
import { Button, message } from "antd";
import { Button, InputNumber, message } from "antd";
import { useTranslation } from "react-i18next";
import { useNavigate, useParams } from "react-router-dom";
import { useGetRestaurantDetailsQuery } from "redux/api/others";
@@ -8,7 +8,7 @@ import { useAppSelector, useAppDispatch } from "redux/hooks";
import { Product } from "utils/types/appTypes";
import NextIcon from "components/Icons/NextIcon";
import { addItem, removeItem, updateQuantity } from "features/order/orderSlice";
import ProText from "components/ProText";
import PlusIcon from "components/Icons/PlusIcon";
export function AddToCartButton({ item }: { item: Product }) {
const { t } = useTranslation();
@@ -161,17 +161,14 @@ export function AddToCartButton({ item }: { item: Product }) {
return isInCart && !hasOptions ? (
<>
<div
className={styles.addButton}
style={{
width: 90,
height: 30,
position: "absolute",
bottom: 3,
[isRTL ? "left" : "right"]: 1,
background: "#FAFAFA",
borderRadius: 888,
className={styles.cartItemActions}
onClick={(e) => {
e.stopPropagation();
e.preventDefault;
}}
>
<div className={styles.quantityControls}>
<div className={styles.quantityInputContainer}>
<Button
shape="circle"
iconPlacement="start"
@@ -183,47 +180,48 @@ export function AddToCartButton({ item }: { item: Product }) {
backgroundColor: "white",
width: 28,
height: 28,
position: "absolute",
bottom: 1,
[isRTL ? "left" : "right"]: 60,
minWidth: 28,
border: "none",
}}
/>
<ProText
style={{
position: "absolute",
bottom: 7,
[isRTL ? "left" : "right"]: 45,
fontSize: 14,
fontWeight: 700,
fontStyle: "Bold",
lineHeight: "100%",
letterSpacing: "0.06px",
textAlign: "center",
verticalAlign: "middle",
<InputNumber
min={1}
max={99}
value={totalQuantity}
onClick={(e) => {
e.stopPropagation();
e.preventDefault;
}}
>
{totalQuantity}
</ProText>
onChange={(value: number | null) =>
dispatch(
updateQuantity({
id: item.id,
uniqueId: basicCartItem?.uniqueId || "",
quantity: value || 1,
}),
)
}
size="small"
controls={false}
className={styles.quantityInput}
name="id"
/>
<Button
shape="circle"
iconPlacement="start"
icon={<PlusOutlined title="plus" />}
icon={<PlusIcon color="#FFF" />}
size="small"
onClick={handlePlusClick}
disabled={totalQuantity >= 99}
className={styles.addButton}
style={{
backgroundColor: "#FFC600",
width: 28,
height: 28,
position: "absolute",
bottom: 1,
[isRTL ? "left" : "right"]: 2,
minWidth: 28,
}}
/>
</div>
</div>
</div>
</>
) : (
<div
@@ -246,6 +244,7 @@ export function AddToCartButton({ item }: { item: Product }) {
)
}
onClick={handleClick}
disabled={!hasOptions && totalQuantity >= 99}
className={styles.addButton}
style={{
color: "#302E3E",

View File

@@ -2,7 +2,6 @@ import styles from "pages/menu/components/MenuList/ProductCard.module.css";
import { Card, Badge } from "antd";
import ProText from "components/ProText.tsx";
import ArabicPrice from "components/ArabicPrice";
import { colors } from "ThemeConstants.ts";
import { ItemDescriptionIcons } from "components/ItemDescriptionIcons/ItemDescriptionIcons.tsx";
import ImageWithFallback from "components/ImageWithFallback";
import { Product } from "utils/types/appTypes.ts";
@@ -207,7 +206,14 @@ export default function ProductCard({ item, setIsBottomSheetOpen }: Props) {
width={91}
height={96}
/>
<div
style={{
position: "absolute",
bottom: 3,
}}
>
<AddToCartButton item={item} />
</div>
</Badge>
</div>
</div>

View File

@@ -171,7 +171,7 @@
}
.headerContainer {
margin: 5px 0px;
margin: 5px 0 0 0;
}
/* Enhanced responsive item description */