Compare commits
4 Commits
3ab162ee5c
...
20ef4f416c
| Author | SHA1 | Date | |
|---|---|---|---|
| 20ef4f416c | |||
| c8bf8ff621 | |||
| ab265bf09a | |||
| 8563d90e8f |
@@ -268,10 +268,13 @@
|
|||||||
"checkRequiredFields": "يرجى التحقق من الحقول المطلوبة"
|
"checkRequiredFields": "يرجى التحقق من الحقول المطلوبة"
|
||||||
},
|
},
|
||||||
"checkout": {
|
"checkout": {
|
||||||
|
"addCarDetails": "إضافة تفاصيل السيارة",
|
||||||
|
"soTheRestaurantCanRecognizeYourCarWhenYouArrive": "لتتمكن المطعم من التعرف على سيارتك عند وصولك.",
|
||||||
"customerName": "اسم العميل",
|
"customerName": "اسم العميل",
|
||||||
"paymentSummary": "ملخص الدفع",
|
"paymentSummary": "ملخص الدفع",
|
||||||
"holdayGiftCard": "هدية العيد",
|
"holdayGiftCard": "هدية العيد",
|
||||||
"messageIncluded": "الرسالة مضمنة",
|
"messageIncluded": "الرسالة مضمنة",
|
||||||
|
"save":"حفظ",
|
||||||
"to": "ل",
|
"to": "ل",
|
||||||
"giftSummary": "ملخص الهدية",
|
"giftSummary": "ملخص الهدية",
|
||||||
"customerInformation": "تفاصيل العميل",
|
"customerInformation": "تفاصيل العميل",
|
||||||
@@ -505,5 +508,9 @@
|
|||||||
"add": "أضف",
|
"add": "أضف",
|
||||||
"senderNameRequired": "يجب أن يكون اسم المرسل مطلوب",
|
"senderNameRequired": "يجب أن يكون اسم المرسل مطلوب",
|
||||||
"receiverNameRequired": "يجب أن يكون اسم المستلم مطلوب"
|
"receiverNameRequired": "يجب أن يكون اسم المستلم مطلوب"
|
||||||
|
},
|
||||||
|
"car":{
|
||||||
|
"addCar":"إضافة سيارة",
|
||||||
|
"selectCar":"اختر السيارة"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -279,7 +279,10 @@
|
|||||||
"checkRequiredFields": "Please check required fields"
|
"checkRequiredFields": "Please check required fields"
|
||||||
},
|
},
|
||||||
"checkout": {
|
"checkout": {
|
||||||
|
"addCarDetails": "Add Car Details",
|
||||||
|
"soTheRestaurantCanRecognizeYourCarWhenYouArrive": "So the restaurant can recognize your car when you arrive.",
|
||||||
"customerName": "Customer Name",
|
"customerName": "Customer Name",
|
||||||
|
"save": "Save",
|
||||||
"paymentSummary": "Payment Summary",
|
"paymentSummary": "Payment Summary",
|
||||||
"orderSummary": "Order Summary",
|
"orderSummary": "Order Summary",
|
||||||
"holdayGiftCard": "Holday gift card",
|
"holdayGiftCard": "Holday gift card",
|
||||||
@@ -517,5 +520,9 @@
|
|||||||
"add": "Add",
|
"add": "Add",
|
||||||
"senderNameRequired": "Sender name is required",
|
"senderNameRequired": "Sender name is required",
|
||||||
"receiverNameRequired": "Receiver name is required"
|
"receiverNameRequired": "Receiver name is required"
|
||||||
|
},
|
||||||
|
"car": {
|
||||||
|
"addCar": "Add Car",
|
||||||
|
"selectCar": "Select Car"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ import { useEffect, useState } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { ProBottomSheet } from "../ProBottomSheet/ProBottomSheet";
|
import { ProBottomSheet } from "../ProBottomSheet/ProBottomSheet";
|
||||||
import ProRatioGroups from "../ProRatioGroups/ProRatioGroups";
|
import ProRatioGroups from "../ProRatioGroups/ProRatioGroups";
|
||||||
|
import { colors } from "ThemeConstants";
|
||||||
|
import { updateCoupon } from "features/order/orderSlice";
|
||||||
|
import { useAppDispatch } from "redux/hooks";
|
||||||
|
import { Button } from "antd";
|
||||||
|
|
||||||
interface CouponBottomSheetProps {
|
interface CouponBottomSheetProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@@ -18,13 +22,14 @@ export function CouponBottomSheet({
|
|||||||
}: CouponBottomSheetProps) {
|
}: CouponBottomSheetProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [value, setValue] = useState(initialValue);
|
const [value, setValue] = useState(initialValue);
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setValue(initialValue);
|
setValue(initialValue);
|
||||||
}, [initialValue]);
|
}, [initialValue]);
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
onSave(value);
|
onSave(value);
|
||||||
|
dispatch(updateCoupon(value));
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -38,32 +43,65 @@ export function CouponBottomSheet({
|
|||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onClose={handleCancel}
|
onClose={handleCancel}
|
||||||
title={t("cart.coupon")}
|
title={t("cart.coupon")}
|
||||||
showCloseButton={false}
|
|
||||||
initialSnap={1}
|
initialSnap={1}
|
||||||
height={350}
|
height={385}
|
||||||
snapPoints={["30vh"]}
|
snapPoints={[385]}
|
||||||
>
|
>
|
||||||
<div>
|
<div style={{ padding: "16px 0" }}>
|
||||||
<ProRatioGroups
|
<ProRatioGroups
|
||||||
options={[
|
options={[
|
||||||
{
|
{
|
||||||
label: "50% off, Min order : SDG 10,000",
|
label: "50% off, Min order : SDG 10,000",
|
||||||
value: "50",
|
value: "7CAB1",
|
||||||
price: "0"
|
price: "7CAB1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Buy one get one free, Min order : SDG 5,000",
|
label: "Buy one get one free, Min order : SDG 5,000",
|
||||||
value: "buy",
|
value: "7CAB2",
|
||||||
price: "0"
|
price: "7CAB2",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "30% off on select items, Min order : SDG 15,000",
|
label: "30% off on select items, Min order : SDG 15,000",
|
||||||
value: "30",
|
value: "7CABO",
|
||||||
price: "0"
|
price: "7CABO",
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
onRatioClick={handleSave}
|
value={value}
|
||||||
|
onRatioClick={(value) => setValue(value)}
|
||||||
|
showDivider={true}
|
||||||
|
optionsStyle={{
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: 500,
|
||||||
|
color: "#5F6C7B",
|
||||||
|
}}
|
||||||
|
valueStyle={{
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: 500,
|
||||||
|
color: colors.primary,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
gap: 12,
|
||||||
|
marginTop: 20,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
boxShadow: "none",
|
||||||
|
height: 48,
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
onClick={handleSave}
|
||||||
|
disabled={!value}
|
||||||
|
>
|
||||||
|
{t("cart.save")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ProBottomSheet>
|
</ProBottomSheet>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -17,8 +17,6 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.serviceIcon path {
|
.serviceIcon path {
|
||||||
stroke: #ea1f22;
|
stroke: #ea1f22;
|
||||||
}
|
}
|
||||||
@@ -32,3 +30,96 @@
|
|||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Make AntD checkbox look like a circular check indicator (scoped via CSS modules) */
|
||||||
|
.circleCheckbox :global(.ant-checkbox-inner) {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 50% !important;
|
||||||
|
border: 1.5px solid #d5d8da;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circleCheckbox :global(.ant-checkbox-checked .ant-checkbox-inner) {
|
||||||
|
border-radius: 50% !important;
|
||||||
|
background: transparent;
|
||||||
|
border-color: #ffb700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Replace AntD checkmark with a filled inner circle when checked (match SVG) */
|
||||||
|
.circleCheckbox :global(.ant-checkbox-inner::after) {
|
||||||
|
content: "";
|
||||||
|
border: 0 !important;
|
||||||
|
transform: none !important;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.ant-app-rtl) .circleCheckbox :global(.ant-checkbox-inner::after) {
|
||||||
|
left: auto;
|
||||||
|
right: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circleCheckbox :global(.ant-checkbox-checked .ant-checkbox-inner::after) {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
margin-left: -9px;
|
||||||
|
margin-top: -9px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #ffb700;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.ant-app-rtl)
|
||||||
|
.circleCheckbox
|
||||||
|
:global(.ant-checkbox-checked .ant-checkbox-inner::after) {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: -9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Apply same circular style to Radio buttons */
|
||||||
|
.radioCheckbox :global(.ant-radio-inner) {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 50% !important;
|
||||||
|
border: 1.5px solid #d5d8da;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radioCheckbox :global(.ant-radio-checked .ant-radio-inner) {
|
||||||
|
border-radius: 50% !important;
|
||||||
|
background: transparent;
|
||||||
|
border-color: #ffb700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radioCheckbox :global(.ant-radio-inner::after) {
|
||||||
|
content: "";
|
||||||
|
border: 0 !important;
|
||||||
|
transform: none !important;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.ant-app-rtl) .radioCheckbox :global(.ant-radio-inner::after) {
|
||||||
|
left: auto;
|
||||||
|
right: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radioCheckbox :global(.ant-radio-checked .ant-radio-inner::after) {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
margin-left: -9px;
|
||||||
|
margin-top: -9px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #ffb700;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.ant-app-rtl)
|
||||||
|
.radioCheckbox
|
||||||
|
:global(.ant-radio-checked .ant-radio-inner::after) {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: -9px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,23 +1,25 @@
|
|||||||
import { Button, Input } from "antd";
|
import { Button, Input, InputNumber } from "antd";
|
||||||
import WaiterRewardingIcon from "components/Icons/waiter/WaiterRewardingIcon";
|
import WaiterRewardingIcon from "components/Icons/waiter/WaiterRewardingIcon";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { ProBottomSheet } from "../ProBottomSheet/ProBottomSheet";
|
import { ProBottomSheet } from "../ProBottomSheet/ProBottomSheet";
|
||||||
|
import { updateTip } from "features/order/orderSlice";
|
||||||
|
import { useAppDispatch } from "redux/hooks";
|
||||||
|
import ProText from "components/ProText";
|
||||||
|
|
||||||
interface TipBottomSheetProps {
|
interface TipBottomSheetProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
initialValue: string;
|
initialValue: string;
|
||||||
onSave: (value: string) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TipBottomSheet({
|
export function TipBottomSheet({
|
||||||
isOpen,
|
isOpen,
|
||||||
onClose,
|
onClose,
|
||||||
initialValue,
|
initialValue,
|
||||||
onSave,
|
|
||||||
}: TipBottomSheetProps) {
|
}: TipBottomSheetProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
const [value, setValue] = useState(initialValue);
|
const [value, setValue] = useState(initialValue);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -25,7 +27,8 @@ export function TipBottomSheet({
|
|||||||
}, [initialValue]);
|
}, [initialValue]);
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
onSave(value);
|
const numAmount = parseFloat(value) || 0;
|
||||||
|
dispatch(updateTip(numAmount.toString()));
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -39,10 +42,9 @@ export function TipBottomSheet({
|
|||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onClose={handleCancel}
|
onClose={handleCancel}
|
||||||
title={t("cart.tip")}
|
title={t("cart.tip")}
|
||||||
showCloseButton={false}
|
|
||||||
initialSnap={1}
|
initialSnap={1}
|
||||||
height={370}
|
height={380}
|
||||||
snapPoints={[370]}
|
snapPoints={[380]}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -50,28 +52,43 @@ export function TipBottomSheet({
|
|||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
|
gap: 10,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ marginTop: 20 }}>
|
<div style={{ marginTop: 20 }}>
|
||||||
<WaiterRewardingIcon />
|
<WaiterRewardingIcon />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br />
|
<ProText
|
||||||
|
style={{
|
||||||
|
fontWeight: 400,
|
||||||
|
fontStyle: "Regular",
|
||||||
|
fontSize: 16,
|
||||||
|
lineHeight: "140%",
|
||||||
|
letterSpacing: "0%",
|
||||||
|
color: "#333333",
|
||||||
|
textAlign: "left",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("cart.amount")}
|
||||||
|
</ProText>
|
||||||
|
|
||||||
<Input
|
<InputNumber
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e) => setValue(e.target.value)}
|
onChange={(value) => setValue(value?.toString() || "")}
|
||||||
placeholder={t("cart.amount")}
|
placeholder={t("cart.amount")}
|
||||||
autoFocus={false}
|
autoFocus={false}
|
||||||
size="large"
|
size="large"
|
||||||
|
style={{ height: 48, width: "100%", marginBottom: 8 }}
|
||||||
|
min={"0"}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
style={{ width: "100%", height: 48 }}
|
style={{ width: "100%", height: 48 }}
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
|
disabled={value === "" || parseFloat(value) <= 0}
|
||||||
>
|
>
|
||||||
{t("cart.addTip")}
|
{t("cart.addTip")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -2,29 +2,27 @@ import { Button, Input, Modal } from "antd";
|
|||||||
import WaiterRewardingIcon from "components/Icons/waiter/WaiterRewardingIcon";
|
import WaiterRewardingIcon from "components/Icons/waiter/WaiterRewardingIcon";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useAppDispatch } from "redux/hooks";
|
||||||
|
import { updateTip } from "features/order/orderSlice";
|
||||||
|
|
||||||
interface TipDialogProps {
|
interface TipDialogProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
initialValue: string;
|
initialValue: string;
|
||||||
onSave: (value: string) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TipDialog({
|
export function TipDialog({ isOpen, onClose, initialValue }: TipDialogProps) {
|
||||||
isOpen,
|
|
||||||
onClose,
|
|
||||||
initialValue,
|
|
||||||
onSave,
|
|
||||||
}: TipDialogProps) {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [value, setValue] = useState(initialValue);
|
const [value, setValue] = useState(initialValue);
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setValue(initialValue);
|
setValue(initialValue);
|
||||||
}, [initialValue]);
|
}, [initialValue]);
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
onSave(value);
|
const numAmount = parseFloat(value) || 0;
|
||||||
|
dispatch(updateTip(numAmount.toString()));
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
35
src/components/Icons/CarIcon.tsx
Normal file
35
src/components/Icons/CarIcon.tsx
Normal file
File diff suppressed because one or more lines are too long
@@ -1,15 +1,15 @@
|
|||||||
interface PlusIconType {
|
interface PlusIconType {
|
||||||
className?: string;
|
className?: string;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
dimesion?: string
|
dimension?: string
|
||||||
color?: string
|
color?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const PlusIcon = ({ className, onClick, dimesion, color }: PlusIconType) => {
|
const PlusIcon = ({ className, onClick, dimension, color }: PlusIconType) => {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
width={dimesion || "16"}
|
width={dimension || "16"}
|
||||||
height={dimesion || "16"}
|
height={dimension || "16"}
|
||||||
viewBox="0 0 16 16"
|
viewBox="0 0 16 16"
|
||||||
fill="none"
|
fill="none"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Radio, RadioChangeEvent, Space } from "antd";
|
import { Divider, Radio, RadioChangeEvent, Space } from "antd";
|
||||||
import ProText from "components/ProText";
|
import ProText from "components/ProText";
|
||||||
import styles from "./ProRatioGroups.module.css";
|
import styles from "./ProRatioGroups.module.css";
|
||||||
|
|
||||||
@@ -7,6 +7,9 @@ interface ProRatioGroupsProps {
|
|||||||
onRatioClick?: (value: string) => void;
|
onRatioClick?: (value: string) => void;
|
||||||
onChange?: (e: RadioChangeEvent) => void;
|
onChange?: (e: RadioChangeEvent) => void;
|
||||||
value?: string;
|
value?: string;
|
||||||
|
optionsStyle?: React.CSSProperties;
|
||||||
|
valueStyle?: React.CSSProperties;
|
||||||
|
showDivider?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProRatioGroups = ({
|
const ProRatioGroups = ({
|
||||||
@@ -14,9 +17,13 @@ const ProRatioGroups = ({
|
|||||||
onRatioClick,
|
onRatioClick,
|
||||||
onChange,
|
onChange,
|
||||||
value,
|
value,
|
||||||
|
optionsStyle,
|
||||||
|
valueStyle,
|
||||||
|
showDivider = false,
|
||||||
...props
|
...props
|
||||||
}: ProRatioGroupsProps) => {
|
}: ProRatioGroupsProps) => {
|
||||||
const handleChange = (e: RadioChangeEvent) => {
|
const handleChange = (e: RadioChangeEvent) => {
|
||||||
|
console.log(e.target.value);
|
||||||
// If onChange is provided (from Form.Item), use it
|
// If onChange is provided (from Form.Item), use it
|
||||||
if (onChange) {
|
if (onChange) {
|
||||||
onChange(e);
|
onChange(e);
|
||||||
@@ -39,30 +46,39 @@ const ProRatioGroups = ({
|
|||||||
>
|
>
|
||||||
<Space orientation="vertical" style={{ width: "100%" }}>
|
<Space orientation="vertical" style={{ width: "100%" }}>
|
||||||
{options.map((option) => (
|
{options.map((option) => (
|
||||||
<Radio
|
<>
|
||||||
key={option.value}
|
<Radio
|
||||||
value={option.value}
|
key={option.value}
|
||||||
className={styles.circleCheckbox}
|
value={option.value}
|
||||||
>
|
className={styles.circleCheckbox}
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
width: "100%",
|
|
||||||
padding: "8px 0",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<ProText
|
<div
|
||||||
style={{
|
style={{
|
||||||
fontSize: "1rem",
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
width: "100%",
|
||||||
|
padding: "8px 0",
|
||||||
|
placeItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{option.label}
|
<ProText
|
||||||
</ProText>
|
style={{
|
||||||
<ProText style={{ fontSize: "1rem" }}>{option?.price}</ProText>
|
fontSize: "1rem",
|
||||||
</div>
|
...optionsStyle,
|
||||||
</Radio>
|
}}
|
||||||
|
>
|
||||||
|
{option.label}
|
||||||
|
</ProText>
|
||||||
|
<ProText style={{ fontSize: "1rem", ...valueStyle }}>
|
||||||
|
{option?.price}
|
||||||
|
</ProText>
|
||||||
|
</div>
|
||||||
|
</Radio>
|
||||||
|
{showDivider && options.length !== options.length - 1 && (
|
||||||
|
<Divider style={{ margin: "0 0 16px 0 " }} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
))}
|
))}
|
||||||
</Space>
|
</Space>
|
||||||
</Radio.Group>
|
</Radio.Group>
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
import { Button, Form } from "antd";
|
import { Button, Form } from "antd";
|
||||||
import { ProBottomSheet } from "components/ProBottomSheet/ProBottomSheet.tsx";
|
import { ProBottomSheet } from "components/ProBottomSheet/ProBottomSheet.tsx";
|
||||||
import { useState, useEffect } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import {
|
import { selectCart, updateGiftDetails } from "features/order/orderSlice";
|
||||||
selectCart,
|
|
||||||
selectGrandTotal,
|
|
||||||
updateGiftDetails,
|
|
||||||
updateSplitBillAmount,
|
|
||||||
} from "features/order/orderSlice";
|
|
||||||
import { useAppDispatch, useAppSelector } from "redux/hooks";
|
import { useAppDispatch, useAppSelector } from "redux/hooks";
|
||||||
import ProText from "components/ProText";
|
import ProText from "components/ProText";
|
||||||
import { ProInputNumber } from "components/Inputs/ProInputNumber";
|
import { ProInputNumber } from "components/Inputs/ProInputNumber";
|
||||||
|
|||||||
@@ -602,12 +602,16 @@
|
|||||||
top: 1px;
|
top: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.editIcon {
|
.editIconOnSide {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -7px;
|
top: -7px;
|
||||||
right: 5px;
|
right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.editIconMiddle {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
:global(.ant-app-rtl) .editIcon {
|
:global(.ant-app-rtl) .editIcon {
|
||||||
right: auto;
|
right: auto;
|
||||||
left: 5px;
|
left: 5px;
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ export default function CartMobileTabletLayout({
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<PlusIcon dimesion="18" /> {t("cart.addMore")}
|
<PlusIcon dimension="18" /> {t("cart.addMore")}
|
||||||
</Button>
|
</Button>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { Button, Form, Input, message } from "antd";
|
import { Button, Form, Input, message } from "antd";
|
||||||
|
import { CouponBottomSheet } from "components/CustomBottomSheet/CouponBottomSheet";
|
||||||
|
import { CouponDialog } from "components/CustomBottomSheet/CouponDialog";
|
||||||
import CouponHeartIcon from "components/Icons/cart/CouponHeart.tsx";
|
import CouponHeartIcon from "components/Icons/cart/CouponHeart.tsx";
|
||||||
|
import DonateIcon from "components/Icons/cart/DonateIcon";
|
||||||
import ProInputCard from "components/ProInputCard/ProInputCard.tsx";
|
import ProInputCard from "components/ProInputCard/ProInputCard.tsx";
|
||||||
import ProText from "components/ProText";
|
import ProText from "components/ProText";
|
||||||
import {
|
import {
|
||||||
@@ -7,20 +10,22 @@ import {
|
|||||||
updateCoupon,
|
updateCoupon,
|
||||||
updateDiscount,
|
updateDiscount,
|
||||||
} from "features/order/orderSlice.ts";
|
} from "features/order/orderSlice.ts";
|
||||||
|
import useBreakPoint from "hooks/useBreakPoint";
|
||||||
import styles from "pages/cart/cart.module.css";
|
import styles from "pages/cart/cart.module.css";
|
||||||
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useGetDiscountMutation } from "redux/api/others";
|
import { useGetDiscountMutation } from "redux/api/others";
|
||||||
import { useAppDispatch, useAppSelector } from "redux/hooks.ts";
|
import { useAppDispatch, useAppSelector } from "redux/hooks.ts";
|
||||||
|
import { colors } from "ThemeConstants";
|
||||||
|
|
||||||
export default function CouponCard() {
|
export default function CouponCard() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { restaurant } = useAppSelector((state) => state.order);
|
const { restaurant } = useAppSelector((state) => state.order);
|
||||||
const { coupon } = useAppSelector(selectCart);
|
const { coupon } = useAppSelector(selectCart);
|
||||||
// const { isDesktop } = useBreakPoint();
|
const { isDesktop } = useBreakPoint();
|
||||||
const [getDiscount] = useGetDiscountMutation();
|
const [getDiscount] = useGetDiscountMutation();
|
||||||
|
const [isCouponOpen, setIsCouponOpen] = useState(false);
|
||||||
// const [isCouponOpen, setIsCouponOpen] = useState(false);
|
|
||||||
|
|
||||||
const handleCouponSave = (value: string) => {
|
const handleCouponSave = (value: string) => {
|
||||||
getDiscount({
|
getDiscount({
|
||||||
@@ -43,36 +48,36 @@ export default function CouponCard() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// const handleCouponClose = () => {
|
const handleCouponClose = () => {
|
||||||
// setIsCouponOpen(false);
|
setIsCouponOpen(false);
|
||||||
// };
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ProInputCard
|
<ProInputCard
|
||||||
title={t("cart.couponCode")}
|
title={t("cart.couponCode")}
|
||||||
// titleRight={
|
titleRight={
|
||||||
// <div
|
<div
|
||||||
// style={{
|
style={{
|
||||||
// display: "flex",
|
display: "flex",
|
||||||
// flexDirection: "row",
|
flexDirection: "row",
|
||||||
// alignItems: "center",
|
alignItems: "center",
|
||||||
// gap: 10,
|
gap: 10,
|
||||||
// }}
|
}}
|
||||||
// onClick={() => setIsCouponOpen(true)}
|
onClick={() => setIsCouponOpen(true)}
|
||||||
// >
|
>
|
||||||
// <ProText
|
<ProText
|
||||||
// style={{
|
style={{
|
||||||
// color: colors.primary,
|
color: colors.primary,
|
||||||
// fontSize: 14,
|
fontSize: 14,
|
||||||
// cursor: "pointer",
|
cursor: "pointer",
|
||||||
// }}
|
}}
|
||||||
// >
|
>
|
||||||
// {t("cart.viewOffers")}
|
{t("cart.viewOffers")}
|
||||||
// </ProText>
|
</ProText>
|
||||||
// <DonateIcon />
|
<DonateIcon />
|
||||||
// </div>
|
</div>
|
||||||
// }
|
}
|
||||||
>
|
>
|
||||||
<Form.Item name="coupon">
|
<Form.Item name="coupon">
|
||||||
<Input
|
<Input
|
||||||
@@ -111,7 +116,7 @@ export default function CouponCard() {
|
|||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</ProInputCard>
|
</ProInputCard>
|
||||||
{/* {isDesktop ? (
|
{isDesktop ? (
|
||||||
<CouponDialog
|
<CouponDialog
|
||||||
isOpen={isCouponOpen}
|
isOpen={isCouponOpen}
|
||||||
onClose={handleCouponClose}
|
onClose={handleCouponClose}
|
||||||
@@ -125,7 +130,7 @@ export default function CouponCard() {
|
|||||||
initialValue={coupon}
|
initialValue={coupon}
|
||||||
onSave={handleCouponSave}
|
onSave={handleCouponSave}
|
||||||
/>
|
/>
|
||||||
)} */}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,10 @@ import { TipDialog } from "components/CustomBottomSheet/TipDialog.tsx";
|
|||||||
import DonateHandIcon from "components/Icons/cart/DonateHandIcon.tsx";
|
import DonateHandIcon from "components/Icons/cart/DonateHandIcon.tsx";
|
||||||
import EditIcon from "components/Icons/EditIcon.tsx";
|
import EditIcon from "components/Icons/EditIcon.tsx";
|
||||||
import ProText from "components/ProText.tsx";
|
import ProText from "components/ProText.tsx";
|
||||||
import ProTitle from "components/ProTitle.tsx";
|
|
||||||
import { selectCart, updateTip } from "features/order/orderSlice.ts";
|
import { selectCart, updateTip } from "features/order/orderSlice.ts";
|
||||||
import useBreakPoint from "hooks/useBreakPoint.ts";
|
import useBreakPoint from "hooks/useBreakPoint.ts";
|
||||||
import styles from "pages/cart/cart.module.css";
|
import styles from "pages/cart/cart.module.css";
|
||||||
import { useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useAppDispatch, useAppSelector } from "redux/hooks.ts";
|
import { useAppDispatch, useAppSelector } from "redux/hooks.ts";
|
||||||
|
|
||||||
@@ -18,12 +17,18 @@ export default function RewardWaiterCard() {
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { tip } = useAppSelector(selectCart);
|
const { tip } = useAppSelector(selectCart);
|
||||||
const { isDesktop } = useBreakPoint();
|
const { isDesktop } = useBreakPoint();
|
||||||
|
const [selectedTip, setSelectedTip] = useState<number | null>(null);
|
||||||
|
|
||||||
const [isTipOpen, setIsTipOpen] = useState(false);
|
const [isTipOpen, setIsTipOpen] = useState(false);
|
||||||
|
|
||||||
const handleTipSave = (value: string) => {
|
const isDefaultTip = useMemo(() => {
|
||||||
dispatch(updateTip(value));
|
const amount = parseFloat(tip);
|
||||||
message.success(t("cart.tip") + " " + t("updatedSuccessfully"));
|
return amount === 0.5 || amount === 1.0 || amount === 3.0;
|
||||||
|
}, [tip]);
|
||||||
|
|
||||||
|
const handleTipClick = (value: number) => {
|
||||||
|
setSelectedTip(value);
|
||||||
|
dispatch(updateTip(value.toString()));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTipClose = () => {
|
const handleTipClose = () => {
|
||||||
@@ -86,9 +91,9 @@ export default function RewardWaiterCard() {
|
|||||||
<Divider style={{ margin: "15px 0 15px 0" }} />
|
<Divider style={{ margin: "15px 0 15px 0" }} />
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "grid",
|
||||||
flexDirection: "row",
|
gridTemplateColumns: "repeat(3, minmax(0, 1fr))",
|
||||||
justifyContent: "space-around",
|
gap: 16,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
@@ -96,8 +101,11 @@ export default function RewardWaiterCard() {
|
|||||||
borderRadius: 100,
|
borderRadius: 100,
|
||||||
height: 32,
|
height: 32,
|
||||||
border: "none",
|
border: "none",
|
||||||
backgroundColor: "#5F6C7B0D",
|
backgroundColor:
|
||||||
|
selectedTip === 0.5 && isDefaultTip ? "#FFB7001F" : "#5F6C7B0D",
|
||||||
}}
|
}}
|
||||||
|
iconPlacement="end"
|
||||||
|
onClick={() => handleTipClick(0.5)}
|
||||||
>
|
>
|
||||||
<ArabicPrice
|
<ArabicPrice
|
||||||
price={0.5}
|
price={0.5}
|
||||||
@@ -108,7 +116,8 @@ export default function RewardWaiterCard() {
|
|||||||
lineHeight: "140%",
|
lineHeight: "140%",
|
||||||
letterSpacing: "0%",
|
letterSpacing: "0%",
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
color: "#5F6C7B",
|
color:
|
||||||
|
selectedTip === 0.5 && isDefaultTip ? "#CC9300" : "#5F6C7B",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
@@ -117,8 +126,10 @@ export default function RewardWaiterCard() {
|
|||||||
borderRadius: 100,
|
borderRadius: 100,
|
||||||
height: 32,
|
height: 32,
|
||||||
border: "none",
|
border: "none",
|
||||||
backgroundColor: "#5F6C7B0D",
|
backgroundColor:
|
||||||
|
selectedTip === 1 && isDefaultTip ? "#FFB7001F" : "#5F6C7B0D",
|
||||||
}}
|
}}
|
||||||
|
onClick={() => handleTipClick(1)}
|
||||||
>
|
>
|
||||||
<ArabicPrice
|
<ArabicPrice
|
||||||
price={1.0}
|
price={1.0}
|
||||||
@@ -129,7 +140,8 @@ export default function RewardWaiterCard() {
|
|||||||
lineHeight: "140%",
|
lineHeight: "140%",
|
||||||
letterSpacing: "0%",
|
letterSpacing: "0%",
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
color: "#5F6C7B",
|
color:
|
||||||
|
selectedTip === 1 && isDefaultTip ? "#CC9300" : "#5F6C7B",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
@@ -137,24 +149,46 @@ export default function RewardWaiterCard() {
|
|||||||
style={{
|
style={{
|
||||||
borderRadius: 100,
|
borderRadius: 100,
|
||||||
height: 32,
|
height: 32,
|
||||||
border: "none",
|
backgroundColor: tip && !isDefaultTip ? "#FFB7001F" : "inherit",
|
||||||
backgroundColor: "#FFB7001F",
|
border: tip && !isDefaultTip ? "none" : "1px solid #C0BFC4",
|
||||||
}}
|
}}
|
||||||
icon={<EditIcon className={styles.editIcon} />}
|
icon={
|
||||||
iconPlacement="end"
|
<EditIcon
|
||||||
|
className={styles.editIcon}
|
||||||
|
color={tip && !isDefaultTip ? "#CC9300" : "#3D3B4A"}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
iconPlacement="start"
|
||||||
|
onClick={() => setIsTipOpen(true)}
|
||||||
>
|
>
|
||||||
<ArabicPrice
|
{tip && !isDefaultTip ? (
|
||||||
price={3.0}
|
<ArabicPrice
|
||||||
textStyle={{
|
price={parseFloat(tip)}
|
||||||
fontWeight: 500,
|
textStyle={{
|
||||||
fontStyle: "Medium",
|
fontWeight: 500,
|
||||||
fontSize: 14,
|
fontStyle: "Medium",
|
||||||
lineHeight: "140%",
|
fontSize: 14,
|
||||||
letterSpacing: "0%",
|
lineHeight: "140%",
|
||||||
textAlign: "center",
|
letterSpacing: "0%",
|
||||||
color: "#CC9300",
|
textAlign: "center",
|
||||||
}}
|
color: "#CC9300",
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ProText
|
||||||
|
style={{
|
||||||
|
color: "#CC9300",
|
||||||
|
fontWeight: 500,
|
||||||
|
fontStyle: "Medium",
|
||||||
|
fontSize: 14,
|
||||||
|
lineHeight: "140%",
|
||||||
|
letterSpacing: "0%",
|
||||||
|
textAlign: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("cart.other")}
|
||||||
|
</ProText>
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -163,14 +197,12 @@ export default function RewardWaiterCard() {
|
|||||||
isOpen={isTipOpen}
|
isOpen={isTipOpen}
|
||||||
onClose={handleTipClose}
|
onClose={handleTipClose}
|
||||||
initialValue={tip}
|
initialValue={tip}
|
||||||
onSave={handleTipSave}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<TipBottomSheet
|
<TipBottomSheet
|
||||||
isOpen={isTipOpen}
|
isOpen={isTipOpen}
|
||||||
onClose={handleTipClose}
|
onClose={handleTipClose}
|
||||||
initialValue={tip}
|
initialValue={tip}
|
||||||
onSave={handleTipSave}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
.carCard {
|
||||||
|
gap: 20px;
|
||||||
|
opacity: 1;
|
||||||
|
border-radius: 6px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plusIcon {
|
||||||
|
position: relative;
|
||||||
|
top: 2px;
|
||||||
|
}
|
||||||
|
|||||||
112
src/pages/checkout/components/CarBottomSheet.tsx
Normal file
112
src/pages/checkout/components/CarBottomSheet.tsx
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { ProBottomSheet } from "components/ProBottomSheet/ProBottomSheet";
|
||||||
|
import { colors } from "ThemeConstants";
|
||||||
|
import { Button, Card } from "antd";
|
||||||
|
import CarRatioGroups from "./CarRatioGroups/CarRatioGroups";
|
||||||
|
import PlusIcon from "components/Icons/PlusIcon";
|
||||||
|
import styles from "../checkout.module.css";
|
||||||
|
|
||||||
|
interface CarBottomSheetProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CarBottomSheet({ isOpen, onClose }: CarBottomSheetProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [value, setValue] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
setValue(null);
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
onClose();
|
||||||
|
setValue(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProBottomSheet
|
||||||
|
isOpen={isOpen}
|
||||||
|
onClose={handleCancel}
|
||||||
|
title={t("checkout.addCarDetails")}
|
||||||
|
initialSnap={1}
|
||||||
|
height={575}
|
||||||
|
snapPoints={[575]}
|
||||||
|
>
|
||||||
|
<Card title={t("car.selectCar")} style={{ marginTop: 24 }}>
|
||||||
|
<CarRatioGroups
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
label: "",
|
||||||
|
value: "7CAB1",
|
||||||
|
price: "7CAB1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Buy one get one free, Min order : SDG 5,000",
|
||||||
|
value: "7CAB2",
|
||||||
|
price: "7CAB2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "30% off on select items, Min order : SDG 15,000",
|
||||||
|
value: "7CABO",
|
||||||
|
price: "7CABO",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
value={value || undefined}
|
||||||
|
onRatioClick={(value) => setValue(value)}
|
||||||
|
optionsStyle={{
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: 500,
|
||||||
|
color: "#5F6C7B",
|
||||||
|
}}
|
||||||
|
valueStyle={{
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: 500,
|
||||||
|
color: colors.primary,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
style={{
|
||||||
|
fontWeight: 600,
|
||||||
|
fontStyle: "SemiBold",
|
||||||
|
fontSize: 14,
|
||||||
|
lineHeight: "140%",
|
||||||
|
letterSpacing: 0,
|
||||||
|
width: "100%",
|
||||||
|
color: "#5F6C7B",
|
||||||
|
marginTop: 16,
|
||||||
|
}}
|
||||||
|
onClick={handleSave}
|
||||||
|
disabled={!value}
|
||||||
|
icon={<PlusIcon color="#5F6C7B" dimension="16" className={styles.plusIcon} />}
|
||||||
|
>
|
||||||
|
{t("car.addCar")}
|
||||||
|
</Button>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
gap: 12,
|
||||||
|
marginTop: 20,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
boxShadow: "none",
|
||||||
|
height: 48,
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
onClick={handleSave}
|
||||||
|
disabled={!value}
|
||||||
|
>
|
||||||
|
{t("checkout.save")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</ProBottomSheet>
|
||||||
|
);
|
||||||
|
}
|
||||||
78
src/pages/checkout/components/CarCard.tsx
Normal file
78
src/pages/checkout/components/CarCard.tsx
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import ProText from "components/ProText";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useAppSelector } from "redux/hooks";
|
||||||
|
import BackIcon from "components/Icons/BackIcon";
|
||||||
|
import NextIcon from "components/Icons/NextIcon";
|
||||||
|
import CarIcon from "components/Icons/CarIcon";
|
||||||
|
import { Button, Card } from "antd";
|
||||||
|
import styles from "../checkout.module.css";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { CarBottomSheet } from "./CarBottomSheet";
|
||||||
|
|
||||||
|
export function CarCard() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { isRTL } = useAppSelector((state) => state.locale);
|
||||||
|
const [isCarBottomSheetOpen, setIsCarBottomSheetOpen] = useState(false);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Card onClick={() => {
|
||||||
|
setIsCarBottomSheetOpen(true);
|
||||||
|
}}>
|
||||||
|
<div className={styles.carCard}>
|
||||||
|
<Button
|
||||||
|
shape="circle"
|
||||||
|
icon={<CarIcon />}
|
||||||
|
style={{
|
||||||
|
backgroundColor: "var(--background)",
|
||||||
|
border: "none",
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
minWidth: 32,
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
boxShadow: "0 0 10px 0 rgba(0, 0, 0, 0.1)",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: 4,
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ProText
|
||||||
|
style={{
|
||||||
|
fontWeight: 500,
|
||||||
|
fontStyle: "Medium",
|
||||||
|
fontSize: 18,
|
||||||
|
lineHeight: "140%",
|
||||||
|
color: "#333333",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("checkout.addCarDetails")}
|
||||||
|
</ProText>
|
||||||
|
|
||||||
|
<ProText
|
||||||
|
style={{
|
||||||
|
fontWeight: 400,
|
||||||
|
fontStyle: "Regular",
|
||||||
|
fontSize: 16,
|
||||||
|
lineHeight: "140%",
|
||||||
|
color: "#777580",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("checkout.soTheRestaurantCanRecognizeYourCarWhenYouArrive")}
|
||||||
|
</ProText>
|
||||||
|
</div>
|
||||||
|
{isRTL ? <BackIcon iconSize={24} /> : <NextIcon iconSize={24} />}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
<CarBottomSheet
|
||||||
|
isOpen={isCarBottomSheetOpen}
|
||||||
|
onClose={() => setIsCarBottomSheetOpen(false)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
.proRatioGroups :global(.ant-radio-wrapper:last-child) {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proRatioGroups :global(.ant-radio-label) {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* radio.module.css */
|
||||||
|
.proRatioGroups :global(.ant-radio-inner) {
|
||||||
|
width: 24px !important;
|
||||||
|
height: 24px !important;
|
||||||
|
border-radius: 30px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proRatioGroups :global(.ant-radio) {
|
||||||
|
width: 24px !important;
|
||||||
|
height: 24px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circleCheckbox {
|
||||||
|
border: 1px solid #5f6c7b1f;
|
||||||
|
height: 62px;
|
||||||
|
justify-content: space-between;
|
||||||
|
opacity: 1;
|
||||||
|
border-radius: 888px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make AntD checkbox look like a circular check indicator (scoped via CSS modules) */
|
||||||
|
.circleCheckbox :global(.ant-checkbox-inner) {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 50% !important;
|
||||||
|
border: 1.5px solid #d5d8da;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circleCheckbox :global(.ant-checkbox-checked .ant-checkbox-inner) {
|
||||||
|
border-radius: 50% !important;
|
||||||
|
background: transparent;
|
||||||
|
border-color: #ffb700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Replace AntD checkmark with a filled inner circle when checked (match SVG) */
|
||||||
|
.circleCheckbox :global(.ant-checkbox-inner::after) {
|
||||||
|
content: "";
|
||||||
|
border: 0 !important;
|
||||||
|
transform: none !important;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.ant-app-rtl) .circleCheckbox :global(.ant-checkbox-inner::after) {
|
||||||
|
left: auto;
|
||||||
|
right: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circleCheckbox :global(.ant-checkbox-checked .ant-checkbox-inner::after) {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
margin-left: -9px;
|
||||||
|
margin-top: -9px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #ffb700;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.ant-app-rtl)
|
||||||
|
.circleCheckbox
|
||||||
|
:global(.ant-checkbox-checked .ant-checkbox-inner::after) {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: -9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Apply same circular style to Radio buttons */
|
||||||
|
.circleCheckbox :global(.ant-radio-inner) {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 50% !important;
|
||||||
|
border: 1.5px solid #d5d8da;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circleCheckbox :global(.ant-radio-checked .ant-radio-inner) {
|
||||||
|
border-radius: 50% !important;
|
||||||
|
background: transparent;
|
||||||
|
border-color: #ffb700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circleCheckbox :global(.ant-radio-inner::after) {
|
||||||
|
content: "";
|
||||||
|
border: 0 !important;
|
||||||
|
transform: none !important;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.ant-app-rtl) .circleCheckbox :global(.ant-radio-inner::after) {
|
||||||
|
left: auto;
|
||||||
|
right: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circleCheckbox :global(.ant-radio-checked .ant-radio-inner::after) {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
margin-left: -9px;
|
||||||
|
margin-top: -9px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #ffb700;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.ant-app-rtl)
|
||||||
|
.circleCheckbox
|
||||||
|
:global(.ant-radio-checked .ant-radio-inner::after) {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: -9px;
|
||||||
|
}
|
||||||
132
src/pages/checkout/components/CarRatioGroups/CarRatioGroups.tsx
Normal file
132
src/pages/checkout/components/CarRatioGroups/CarRatioGroups.tsx
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
import { Button, Card, Divider, Radio, RadioChangeEvent, Space } from "antd";
|
||||||
|
import ProText from "components/ProText";
|
||||||
|
import styles from "./CarRatioGroups.module.css";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import CarIcon from "components/Icons/CarIcon";
|
||||||
|
|
||||||
|
interface CarRatioGroupsProps {
|
||||||
|
options: { label: string; value: string; price?: string }[];
|
||||||
|
onRatioClick?: (value: string) => void;
|
||||||
|
onChange?: (e: RadioChangeEvent) => void;
|
||||||
|
value?: string;
|
||||||
|
optionsStyle?: React.CSSProperties;
|
||||||
|
valueStyle?: React.CSSProperties;
|
||||||
|
showDivider?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CarRatioGroups = ({
|
||||||
|
options,
|
||||||
|
onRatioClick,
|
||||||
|
onChange,
|
||||||
|
value,
|
||||||
|
optionsStyle,
|
||||||
|
valueStyle,
|
||||||
|
showDivider = false,
|
||||||
|
...props
|
||||||
|
}: CarRatioGroupsProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const handleChange = (e: RadioChangeEvent) => {
|
||||||
|
console.log(e.target.value);
|
||||||
|
// If onChange is provided (from Form.Item), use it
|
||||||
|
if (onChange) {
|
||||||
|
onChange(e);
|
||||||
|
}
|
||||||
|
// Also call onRatioClick if provided (for backward compatibility)
|
||||||
|
if (onRatioClick) {
|
||||||
|
onRatioClick(e.target.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.proRatioGroups}>
|
||||||
|
<Radio.Group
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
value={value}
|
||||||
|
onChange={handleChange}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<Space orientation="vertical" style={{ width: "100%", gap: 16 }}>
|
||||||
|
{options.map((option) => (
|
||||||
|
<>
|
||||||
|
<Radio
|
||||||
|
key={option.value}
|
||||||
|
value={option.value}
|
||||||
|
className={styles.circleCheckbox}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{ display: "flex", flexDirection: "row", gap: 12 }}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: 4,
|
||||||
|
width: "100%",
|
||||||
|
justifyContent: "center",
|
||||||
|
textAlign: "left",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ProText
|
||||||
|
style={{
|
||||||
|
fontWeight: 500,
|
||||||
|
fontStyle: "Medium",
|
||||||
|
fontSize: 12,
|
||||||
|
lineHeight: "140%",
|
||||||
|
color: "#333333",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Optima
|
||||||
|
</ProText>
|
||||||
|
|
||||||
|
<ProText
|
||||||
|
style={{
|
||||||
|
fontWeight: 400,
|
||||||
|
fontStyle: "Regular",
|
||||||
|
fontSize: 12,
|
||||||
|
lineHeight: "140%",
|
||||||
|
color: "#777580",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
42322, blue
|
||||||
|
</ProText>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
shape="circle"
|
||||||
|
icon={<CarIcon />}
|
||||||
|
style={{
|
||||||
|
backgroundColor: "var(--background)",
|
||||||
|
border: "none",
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
minWidth: 32,
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
boxShadow: "0 0 10px 0 rgba(0, 0, 0, 0.1)",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Radio>
|
||||||
|
{showDivider && options.length !== options.length - 1 && (
|
||||||
|
<Divider style={{ margin: "0 0 16px 0 " }} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
</Space>
|
||||||
|
</Radio.Group>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CarRatioGroups;
|
||||||
@@ -20,6 +20,7 @@ import CustomerInformationCard from "./components/CustomerInformationCard";
|
|||||||
import Ads1 from "components/Ads/Ads1";
|
import Ads1 from "components/Ads/Ads1";
|
||||||
import TimeEstimateCard from "pages/cart/components/timeEstimate/TimeEstimateCard";
|
import TimeEstimateCard from "pages/cart/components/timeEstimate/TimeEstimateCard";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
import { CarCard } from "./components/CarCard";
|
||||||
|
|
||||||
export default function CheckoutPage() {
|
export default function CheckoutPage() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -46,6 +47,7 @@ export default function CheckoutPage() {
|
|||||||
<Layout.Content className={styles.checkoutContainer}>
|
<Layout.Content className={styles.checkoutContainer}>
|
||||||
{(orderType === OrderType.Pickup ||
|
{(orderType === OrderType.Pickup ||
|
||||||
orderType === OrderType.ScheduledOrder) && <TimeEstimateCard />}
|
orderType === OrderType.ScheduledOrder) && <TimeEstimateCard />}
|
||||||
|
{orderType === OrderType.Pickup && <CarCard />}
|
||||||
{orderType === OrderType.Gift && <GiftCard />}
|
{orderType === OrderType.Gift && <GiftCard />}
|
||||||
<PaymentMethods />
|
<PaymentMethods />
|
||||||
{!token && <CustomerInformationCard />}
|
{!token && <CustomerInformationCard />}
|
||||||
|
|||||||
Reference in New Issue
Block a user