diff --git a/src/assets/locals/ar.json b/src/assets/locals/ar.json index d8af1f4..2fb8d6b 100644 --- a/src/assets/locals/ar.json +++ b/src/assets/locals/ar.json @@ -387,7 +387,8 @@ "pleaseLoginToAllowRating": "يرجى تسجيل الدخول لتمكين التقييم", "remainingTime": "الوقت المتبقي", "sec": "ثانية", - "min": "دقيقة" + "min": "دقيقة", + "inviteToBill": "دع الجميع يدفعوا معك" }, "orderTypes": { "dine-in": "في المطعم", diff --git a/src/assets/locals/en.json b/src/assets/locals/en.json index da9cbd3..ab8ef87 100644 --- a/src/assets/locals/en.json +++ b/src/assets/locals/en.json @@ -398,7 +398,8 @@ "pleaseLoginToAllowRating": "Please login to allow rating", "remainingTime": "Remaining Time", "sec": "Sec", - "min": "Min" + "min": "Min", + "inviteToBill": "Invite to Bill" }, "orderTypes": { "dine-in": "Dine In", diff --git a/src/components/CustomBottomSheet/RateBottomSheet.tsx b/src/components/CustomBottomSheet/RateBottomSheet.tsx index 045a05f..1a1dfac 100644 --- a/src/components/CustomBottomSheet/RateBottomSheet.tsx +++ b/src/components/CustomBottomSheet/RateBottomSheet.tsx @@ -1,7 +1,5 @@ // import { useGlobals } from "../../hooks/useGlobals"; import { Button, Card, message } from "antd"; -import BackIcon from "components/Icons/BackIcon"; -import NextIcon from "components/Icons/NextIcon"; import RateIcon from "components/Icons/order/RateIcon"; import { useState } from "react"; import { useTranslation } from "react-i18next"; diff --git a/src/pages/checkout/components/BriefMenuCard.tsx b/src/pages/checkout/components/BriefMenuCard.tsx index 60d91d7..d9bdf92 100644 --- a/src/pages/checkout/components/BriefMenuCard.tsx +++ b/src/pages/checkout/components/BriefMenuCard.tsx @@ -6,13 +6,24 @@ import { useTranslation } from "react-i18next"; import { useAppSelector } from "redux/hooks"; import { selectCart } from "features/order/orderSlice"; import { useNavigate, useParams } from "react-router-dom"; +import { useGetOrderDetailsQuery } from "redux/api/others"; export default function BriefMenuCard() { const { t } = useTranslation(); const { items } = useAppSelector(selectCart); - const totalItems = items.length; - const { subdomain } = useParams(); + const { subdomain, orderId } = useParams(); const navigate = useNavigate(); + const { data: orderDetails } = useGetOrderDetailsQuery( + { + orderID: orderId || "", + restaurantID: localStorage.getItem("restaurantID") || "", + }, + { + skip: !orderId, + // return it t0 60000 after finish testing + }, + ); + const totalItems = items.length || orderDetails?.orderItems.length; return ( <> diff --git a/src/pages/checkout/components/CheckoutButton.tsx b/src/pages/checkout/components/CheckoutButton.tsx index 35616d6..7d86431 100644 --- a/src/pages/checkout/components/CheckoutButton.tsx +++ b/src/pages/checkout/components/CheckoutButton.tsx @@ -1,23 +1,59 @@ import { Button, FormInstance, Layout } from "antd"; -import { selectCart } from "features/order/orderSlice"; +import { selectCart, updateSplitBillAmount } from "features/order/orderSlice"; import { OrderType } from "pages/checkout/hooks/types.ts"; -import { useCallback, useMemo } from "react"; +import { useCallback, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; -import { useNavigate, useParams } from "react-router-dom"; -import { useAppSelector } from "redux/hooks"; +import { useAppDispatch, useAppSelector } from "redux/hooks"; import styles from "../../address/address.module.css"; import useOrder from "../hooks/useOrder"; +import { EqualltyChoiceBottomSheet } from "pages/pay/components/splitBill/EqualltyChoiceBottomSheet"; +import { SplitBillChoiceBottomSheet } from "pages/pay/components/splitBill/SplitBillChoiceBottomSheet"; +import { CustomAmountChoiceBottomSheet } from "pages/pay/components/splitBill/CustomAmountChoiceBottomSheet"; +import { PayForYourItemsChoiceBottomSheet } from "pages/pay/components/splitBill/PayForYourItemsChoiceBottomSheet"; + +type SplitWay = "customAmount" | "equality" | "payForItems" | null; export default function CheckoutButton({ form }: { form: FormInstance }) { + const dispatch = useAppDispatch(); const { t } = useTranslation(); const { orderType } = useAppSelector(selectCart); - const navigate = useNavigate(); const { handleCreateOrder } = useOrder(); - const { subdomain } = useParams(); + const [selectedSplitWay, setSelectedSplitWay] = useState(null); + const [ + isSplitBillChoiceBottomSheetOpen, + setIsSplitBillChoiceBottomSheetOpen, + ] = useState(false); + const [isCustomAmountOpen, setIsCustomAmountOpen] = useState(false); + const [isEqualityOpen, setIsEqualityOpen] = useState(false); + const [isPayForItemsOpen, setIsPayForItemsOpen] = useState(false); const handleSplitBillClick = useCallback(() => { - navigate(`/${subdomain}/split-bill`); - }, [navigate, subdomain]); + if (selectedSplitWay === "customAmount") { + setIsCustomAmountOpen(true); + } else if (selectedSplitWay === "equality") { + setIsEqualityOpen(true); + } else if (selectedSplitWay === "payForItems") { + setIsPayForItemsOpen(true); + } else { + setIsSplitBillChoiceBottomSheetOpen(true); + } + }, [selectedSplitWay]); + + const handleRemoveSplitWay = useCallback(() => { + setSelectedSplitWay(null); + dispatch(updateSplitBillAmount(0)); + }, [dispatch]); + + const getSplitButtonTitle = useMemo(() => { + if (selectedSplitWay === "customAmount") { + return t("splitBill.payAsCustomAmount"); + } else if (selectedSplitWay === "equality") { + return t("splitBill.divideTheBillEqually"); + } else if (selectedSplitWay === "payForItems") { + return t("splitBill.payForYourItems"); + } + return t("checkout.splitBill"); + }, [selectedSplitWay, t]); const handlePlaceOrderClick = useCallback(async () => { try { @@ -34,24 +70,56 @@ export default function CheckoutButton({ form }: { form: FormInstance }) { ); return ( - - {shouldShowSplitBill && ( - - )} + <> + + {shouldShowSplitBill && ( + + )} - - + + + + setIsSplitBillChoiceBottomSheetOpen(false)} + onSelectSplitWay={setSelectedSplitWay} + /> + + { + setIsCustomAmountOpen(false); + }} + onRemoveSplitWay={handleRemoveSplitWay} + /> + + { + setIsEqualityOpen(false); + }} + onRemoveSplitWay={handleRemoveSplitWay} + /> + + { + setIsPayForItemsOpen(false); + }} + onRemoveSplitWay={handleRemoveSplitWay} + /> + ); } diff --git a/src/pages/order/order.module.css b/src/pages/order/order.module.css index 78b105e..fc8ef94 100644 --- a/src/pages/order/order.module.css +++ b/src/pages/order/order.module.css @@ -244,4 +244,19 @@ height: 24px; } +.inviteToBillCard { + width: 100%; + height: 48px; + display: flex; + justify-content: flex-start; + padding: 12px 18px !important; + row-gap: 10px; + transition: all 0.3s ease; + border-radius: 50px; +} +.inviteToBillCard :global(.ant-card-body) { + padding: 0px !important; + text-align: start; + width: 100%; +} diff --git a/src/pages/order/page.tsx b/src/pages/order/page.tsx index e6e3ebe..f71d571 100644 --- a/src/pages/order/page.tsx +++ b/src/pages/order/page.tsx @@ -7,7 +7,6 @@ import TimeIcon from "components/Icons/order/TimeIcon"; import OrderDishIcon from "components/Icons/OrderDishIcon"; import PaymentDetails from "components/PaymentDetails/PaymentDetails"; import ProHeader from "components/ProHeader/ProHeader"; -import ProInputCard from "components/ProInputCard/ProInputCard"; import ProText from "components/ProText"; import ProTitle from "components/ProTitle"; import dayjs from "dayjs"; @@ -24,6 +23,8 @@ import styles from "./order.module.css"; import BackIcon from "components/Icons/BackIcon"; import NextIcon from "components/Icons/NextIcon"; import { RateBottomSheet } from "components/CustomBottomSheet/RateBottomSheet"; +import BriefMenuCard from "pages/checkout/components/BriefMenuCard"; +import { QRBottomSheet } from "pages/pay/components/splitBill/QRBottomSheet"; export default function OrderPage() { const { t } = useTranslation(); @@ -32,6 +33,7 @@ export default function OrderPage() { const { restaurant } = useAppSelector((state) => state.order); const navigate = useNavigate(); const hasRefetchedRef = useRef(false); + const [isOpen, setIsOpen] = useState(false); const { data: orderDetails } = useGetOrderDetailsQuery( { @@ -346,7 +348,7 @@ export default function OrderPage() { - @@ -393,7 +395,9 @@ export default function OrderPage() { ))} - + */} + + @@ -439,6 +443,34 @@ export default function OrderPage() { + setIsOpen(true)} + > +
+
+ + {t("order.inviteToBill")} + +
+
+
+ + setIsOpen(false)} /> + diff --git a/src/pages/pay/components/splitBill/CustomAmountChoiceBottomSheet.tsx b/src/pages/pay/components/splitBill/CustomAmountChoiceBottomSheet.tsx index 14fed93..d313909 100644 --- a/src/pages/pay/components/splitBill/CustomAmountChoiceBottomSheet.tsx +++ b/src/pages/pay/components/splitBill/CustomAmountChoiceBottomSheet.tsx @@ -12,7 +12,6 @@ import { useAppDispatch, useAppSelector } from "redux/hooks"; import ProText from "components/ProText"; import ArabicPrice from "components/ArabicPrice"; import styles from "./SplitBill.module.css"; -import { QRBottomSheet } from "./QRBottomSheet"; interface SplitBillChoiceBottomSheetProps { isOpen: boolean; @@ -32,7 +31,6 @@ export function CustomAmountChoiceBottomSheet({ const [amount, setAmount] = useState( splitBillAmount > 0 ? splitBillAmount.toString() : "", ); - const [isQROpen, setIsQROpen] = useState(false); useEffect(() => { if (isOpen && splitBillAmount > 0) { setAmount(splitBillAmount.toString()); @@ -42,7 +40,6 @@ export function CustomAmountChoiceBottomSheet({ const handleSave = () => { const numAmount = parseFloat(amount) || 0; dispatch(updateSplitBillAmount(numAmount)); - setIsQROpen(true); onClose(); }; @@ -62,7 +59,6 @@ export function CustomAmountChoiceBottomSheet({ const previewRemaining = originalTotalBill - currentAmount; return ( - <> - setIsQROpen(false)} /> - ); } diff --git a/src/pages/pay/components/splitBill/EqualltyChoiceBottomSheet.tsx b/src/pages/pay/components/splitBill/EqualltyChoiceBottomSheet.tsx index 45ce118..8081d65 100644 --- a/src/pages/pay/components/splitBill/EqualltyChoiceBottomSheet.tsx +++ b/src/pages/pay/components/splitBill/EqualltyChoiceBottomSheet.tsx @@ -15,7 +15,6 @@ import { ProGray1 } from "ThemeConstants"; import PayForActions from "../../../split-bill/components/PayForActions"; import TotalPeopleActions from "../../../split-bill/components/TotalPeopleActions"; import styles from "./SplitBill.module.css"; -import { QRBottomSheet } from "./QRBottomSheet"; interface SplitBillChoiceBottomSheetProps { isOpen: boolean; @@ -38,7 +37,6 @@ export function EqualltyChoiceBottomSheet({ const dispatch = useAppDispatch(); const { tmp, splitBillAmount } = useAppSelector(selectCart); const grandTotal = useAppSelector(selectGrandTotal); - const [isQROpen, setIsQROpen] = useState(false); const splitBillTmp = tmp as SplitBillTmp; const totalPeople = splitBillTmp?.totalPeople || 2; const payFor = splitBillTmp?.payFor || 1; @@ -56,7 +54,6 @@ export function EqualltyChoiceBottomSheet({ const handleSave = () => { dispatch(updateSplitBillAmount(splitAmount)); - setIsQROpen(true); onClose(); }; @@ -67,284 +64,281 @@ export function EqualltyChoiceBottomSheet({ }; return ( - <> - +
+ {/* Spinner Visualization - Blank Spin Wheel */} + {totalPeople > 0 && ( +
+
+ + {Array.from({ length: totalPeople }).map((_, index) => { + const anglePerSlice = 360 / totalPeople; + const startAngle = index * anglePerSlice; + const endAngle = (index + 1) * anglePerSlice; + const isSelected = index < payFor; + + // Convert angles to radians + const startAngleRad = (startAngle * Math.PI) / 180; + const endAngleRad = (endAngle * Math.PI) / 180; + + // Calculate path for pie slice (fit 200x200 viewBox) + const radius = 90; + const centerX = 100; + const centerY = 100; + + const x1 = centerX + radius * Math.cos(startAngleRad); + const y1 = centerY + radius * Math.sin(startAngleRad); + const x2 = centerX + radius * Math.cos(endAngleRad); + const y2 = centerY + radius * Math.sin(endAngleRad); + + const largeArcFlag = anglePerSlice > 180 ? 1 : 0; + + const pathData = [ + `M ${centerX} ${centerY}`, + `L ${x1} ${y1}`, + `A ${radius} ${radius} 0 ${largeArcFlag} 1 ${x2} ${y2}`, + "Z", + ].join(" "); + + return ( + + ); + })} + + {/* Center circle with total bill amount */} +
+
+ +
+ + {t("splitBill.totalBill")} + +
+
+
+ )} +
- {/* Spinner Visualization - Blank Spin Wheel */} - {totalPeople > 0 && ( -
-
- - {Array.from({ length: totalPeople }).map((_, index) => { - const anglePerSlice = 360 / totalPeople; - const startAngle = index * anglePerSlice; - const endAngle = (index + 1) * anglePerSlice; - const isSelected = index < payFor; - - // Convert angles to radians - const startAngleRad = (startAngle * Math.PI) / 180; - const endAngleRad = (endAngle * Math.PI) / 180; - - // Calculate path for pie slice (fit 200x200 viewBox) - const radius = 90; - const centerX = 100; - const centerY = 100; - - const x1 = centerX + radius * Math.cos(startAngleRad); - const y1 = centerY + radius * Math.sin(startAngleRad); - const x2 = centerX + radius * Math.cos(endAngleRad); - const y2 = centerY + radius * Math.sin(endAngleRad); - - const largeArcFlag = anglePerSlice > 180 ? 1 : 0; - - const pathData = [ - `M ${centerX} ${centerY}`, - `L ${x1} ${y1}`, - `A ${radius} ${radius} 0 ${largeArcFlag} 1 ${x2} ${y2}`, - "Z", - ].join(" "); - - return ( - - ); - })} - - {/* Center circle with total bill amount */} -
-
- -
- - {t("splitBill.totalBill")} - -
-
-
- )} -
-
- - {t("checkout.totalPeople")} - + {t("checkout.totalPeople")} + - + - - {t("checkout.totalPeople")} - -
- -
- - {t("checkout.payFor")} - - - - - - {t("checkout.payFor")} - -
+ {t("checkout.totalPeople")} +
-
- - {t("splitBill.yourShare")} - - -
-
- -
- - + {t("checkout.payFor")} +
- - setIsQROpen(false)} /> - + +
+
+ + {t("splitBill.yourShare")} + + +
+
+ +
+ + +
+
+
); } diff --git a/src/pages/pay/components/splitBill/PayForYourItemsChoiceBottomSheet.tsx b/src/pages/pay/components/splitBill/PayForYourItemsChoiceBottomSheet.tsx index 57fb0ca..a7a2d9c 100644 --- a/src/pages/pay/components/splitBill/PayForYourItemsChoiceBottomSheet.tsx +++ b/src/pages/pay/components/splitBill/PayForYourItemsChoiceBottomSheet.tsx @@ -8,7 +8,6 @@ import ProText from "components/ProText"; import { selectCart, updateSplitBillAmount } from "features/order/orderSlice"; import { useAppDispatch, useAppSelector } from "redux/hooks"; import styles from "./SplitBill.module.css"; -import { QRBottomSheet } from "./QRBottomSheet"; interface SplitBillChoiceBottomSheetProps { isOpen: boolean; @@ -26,7 +25,6 @@ export function PayForYourItemsChoiceBottomSheet({ const dispatch = useAppDispatch(); const { items } = useAppSelector(selectCart); const [selectedItems, setSelectedItems] = useState>(new Set()); - const [isQROpen, setIsQROpen] = useState(false); // Calculate total for selected items const selectedTotal = items .filter((item) => selectedItems.has(item.uniqueId || item.id.toString())) @@ -57,7 +55,6 @@ export function PayForYourItemsChoiceBottomSheet({ const handleSave = () => { dispatch(updateSplitBillAmount(selectedTotal)); - setIsQROpen(true); onClose(); }; @@ -69,162 +66,159 @@ export function PayForYourItemsChoiceBottomSheet({ }; return ( - <> - +
+ {items.length === 0 ? ( + + {t("cart.emptyCart")} + + ) : ( + items.map((item) => { + const itemId = item.uniqueId || item.id.toString(); + const isSelected = selectedItems.has(itemId); + const itemTotal = item.price * item.quantity; + + return ( + <> + handleItemToggle(itemId)} + > +
+ {item.name} +
+ + {item.name} + + +
+
e.stopPropagation()} + > + handleItemToggle(itemId)} + /> +
+
+
+ {item !== items[items.length - 1] && ( + + )} + + ); + }) + )} +
+
- {items.length === 0 ? ( - - {t("cart.emptyCart")} - - ) : ( - items.map((item) => { - const itemId = item.uniqueId || item.id.toString(); - const isSelected = selectedItems.has(itemId); - const itemTotal = item.price * item.quantity; - - return ( - <> - handleItemToggle(itemId)} - > -
- {item.name} -
- - {item.name} - - -
-
e.stopPropagation()} - > - handleItemToggle(itemId)} - /> -
-
-
- {item !== items[items.length - 1] && ( - - )} - - ); - }) - )} + + {t("splitBill.selectedTotal")}: + +
-
+
+
-
+ - -
- - setIsQROpen(false)} /> - + {t("cart.save")} + +
+ ); } diff --git a/src/pages/pay/components/splitBill/SplitBillChoiceBottomSheet.tsx b/src/pages/pay/components/splitBill/SplitBillChoiceBottomSheet.tsx index fafb501..9ea17c4 100644 --- a/src/pages/pay/components/splitBill/SplitBillChoiceBottomSheet.tsx +++ b/src/pages/pay/components/splitBill/SplitBillChoiceBottomSheet.tsx @@ -55,126 +55,123 @@ export function SplitBillChoiceBottomSheet({ return ( <> - -
- -
- - {t("splitBill.payAsCustomAmount")} - + + {t("splitBill.payAsCustomAmount")} + - {isRTL ? ( - - ) : ( - - )} -
-
+ {isRTL ? ( + + ) : ( + + )} +
+ - -
- +
- {t("splitBill.divideTheBillEqually")} - + + {t("splitBill.divideTheBillEqually")} + - {isRTL ? ( - - ) : ( - - )} -
- + {isRTL ? ( + + ) : ( + + )} +
+
- -
- - {t("splitBill.payForYourItems")} - + + {t("splitBill.payForYourItems")} + - {isRTL ? ( - - ) : ( - - )} -
-
-
-
+ {isRTL ? ( + + ) : ( + + )} + + + + - setIsCustomAmountOpen(false)} - /> + setIsCustomAmountOpen(false)} + /> - setIsEqualityOpen(false)} - /> + setIsEqualityOpen(false)} + /> - setIsPayForItemsOpen(false)} - /> + setIsPayForItemsOpen(false)} + /> ); } diff --git a/src/pages/restaurant/RestaurantServices.tsx b/src/pages/restaurant/RestaurantServices.tsx index bbf9d4f..d806bfc 100644 --- a/src/pages/restaurant/RestaurantServices.tsx +++ b/src/pages/restaurant/RestaurantServices.tsx @@ -158,21 +158,21 @@ export default function RestaurantServices() { }, ]) || []), - ...((true && [ - { - id: OrderType.Pay, - title: t("common.pay"), - description: t("home.services.pay"), - icon: ( - - ), - color: "bg-orange-50 text-orange-600", - href: `/${subdomain}/pay`, - }, - ]) || - []), + // ...((true && [ + // { + // id: OrderType.Pay, + // title: t("common.pay"), + // description: t("home.services.pay"), + // icon: ( + // + // ), + // color: "bg-orange-50 text-orange-600", + // href: `/${subdomain}/pay`, + // }, + // ]) || + // []), ]; // Determine grid class based on number of services