diff --git a/src/assets/locals/ar.json b/src/assets/locals/ar.json
index 53d06f7..2683add 100644
--- a/src/assets/locals/ar.json
+++ b/src/assets/locals/ar.json
@@ -412,6 +412,13 @@
"payAsCustomAmount": "دفع كمبلغ مخصص",
"payAsSplitAmount": "دفع كمبلغ مقسم",
"divideTheBillEqually": "تقسيم الفاتورة الى حسب العدد",
- "payForYourItems": "دفع للطلبات الخاصة بي"
+ "payForYourItems": "دفع للطلبات الخاصة بي",
+ "enterCustomAmount": "أدخل المبلغ المخصص",
+ "amountPlaceholder": "0.00",
+ "divisionPreview": "معاينة التقسيم",
+ "yourAmount": "مبلغك",
+ "selectedTotal": "المجموع المحدد",
+ "splitBillAmount": "مبلغ تقسيم الفاتورة",
+ "removeSplitWay": "إزالة طريقة التقسيم"
}
}
diff --git a/src/assets/locals/en.json b/src/assets/locals/en.json
index 10dd2bb..ddd8c9e 100644
--- a/src/assets/locals/en.json
+++ b/src/assets/locals/en.json
@@ -424,6 +424,13 @@
"payAsCustomAmount": "Pay as Custom Amount",
"payAsSplitAmount": "Pay as Split Amount",
"divideTheBillEqually": "Divide the Bill Equally",
- "payForYourItems": "Pay for Your Items"
+ "payForYourItems": "Pay for Your Items",
+ "enterCustomAmount": "Enter Custom Amount",
+ "amountPlaceholder": "0.00",
+ "divisionPreview": "Division Preview",
+ "yourAmount": "Your Amount",
+ "selectedTotal": "Selected Total",
+ "splitBillAmount": "Split Bill Amount",
+ "removeSplitWay": "Remove Split Way"
}
}
diff --git a/src/components/OrderSummary/OrderSummary.tsx b/src/components/OrderSummary/OrderSummary.tsx
index 3a319c6..c123338 100644
--- a/src/components/OrderSummary/OrderSummary.tsx
+++ b/src/components/OrderSummary/OrderSummary.tsx
@@ -21,7 +21,7 @@ import styles from "./OrderSummary.module.css";
export default function OrderSummary() {
const { t } = useTranslation();
- const { useLoyaltyPoints } = useAppSelector(selectCart);
+ const { useLoyaltyPoints, splitBillAmount } = useAppSelector(selectCart);
const { subdomain } = useParams();
const { data: restaurant } = useGetRestaurantDetailsQuery(subdomain);
const { orderType } = useAppSelector(selectCart);
@@ -62,6 +62,14 @@ export default function OrderSummary() {
{t("cart.tax")}
+ {splitBillAmount > 0 && (
+
+
+ {t("splitBill.splitBillAmount")}
+
+
+
+ )}
diff --git a/src/features/order/orderSlice.ts b/src/features/order/orderSlice.ts
index 020f327..3b7f52c 100644
--- a/src/features/order/orderSlice.ts
+++ b/src/features/order/orderSlice.ts
@@ -69,6 +69,7 @@ interface CartState {
pickupTime: string;
pickupType: string;
order: any;
+ splitBillAmount: number;
}
// localStorage keys
@@ -185,6 +186,7 @@ const initialState: CartState = {
pickupTime: getFromLocalStorage(CART_STORAGE_KEYS.PICKUP_TIME, ""),
pickupType: getFromLocalStorage(CART_STORAGE_KEYS.PICKUP_TYPE, ""),
order: getFromLocalStorage(CART_STORAGE_KEYS.ORDER, null),
+ splitBillAmount: 0,
};
const orderSlice = createSlice({
@@ -632,6 +634,9 @@ const orderSlice = createSlice({
localStorage.setItem(CART_STORAGE_KEYS.ORDER, JSON.stringify(state.order));
}
},
+ updateSplitBillAmount(state, action: PayloadAction) {
+ state.splitBillAmount = action.payload;
+ },
},
});
@@ -669,6 +674,7 @@ export const {
updatePickupTime,
updatePickUpType,
updateOrder,
+ updateSplitBillAmount,
} = orderSlice.actions;
// Tax calculation helper functions
@@ -775,7 +781,7 @@ export const selectGrandTotal = (state: RootState) => {
? Number(state.order.restaurant?.delivery_fees) || 0
: 0;
- return subtotal + taxAmount - totalDiscount + deliveryFee;
+ return subtotal + taxAmount - totalDiscount + deliveryFee - state.order.splitBillAmount;
};
export default orderSlice.reducer;
diff --git a/src/pages/pay/components/PayButton.tsx b/src/pages/pay/components/PayButton.tsx
index 73385e5..c8e5c3e 100644
--- a/src/pages/pay/components/PayButton.tsx
+++ b/src/pages/pay/components/PayButton.tsx
@@ -1,25 +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 useOrder from "pages/checkout/hooks/useOrder";
import { useCallback, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
-import { useAppSelector } from "redux/hooks";
+import { useAppDispatch, useAppSelector } from "redux/hooks";
import styles from "../../address/address.module.css";
+import { CustomAmountChoiceBottomSheet } from "./splitBill/CustomAmountChoiceBottomSheet";
+import { EqualltyChoiceBottomSheet } from "./splitBill/EqualltyChoiceBottomSheet";
+import { PayForYourItemsChoiceBottomSheet } from "./splitBill/PayForYourItemsChoiceBottomSheet";
import { SplitBillChoiceBottomSheet } from "./splitBill/SplitBillChoiceBottomSheet";
+type SplitWay = "customAmount" | "equality" | "payForItems" | null;
+
export default function PayButton({ form }: { form: FormInstance }) {
const { t } = useTranslation();
+ const dispatch = useAppDispatch();
const { orderType } = useAppSelector(selectCart);
const { handleCreateOrder } = useOrder();
+ 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(() => {
- setIsSplitBillChoiceBottomSheetOpen(true);
- }, []);
+ 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 {
@@ -43,7 +77,7 @@ export default function PayButton({ form }: { form: FormInstance }) {
className={styles.splitBillButton}
onClick={handleSplitBillClick}
>
- {t("checkout.splitBill")}
+ {getSplitButtonTitle}
)}
@@ -60,6 +94,25 @@ export default function PayButton({ form }: { form: FormInstance }) {
setIsSplitBillChoiceBottomSheetOpen(false)}
+ onSelectSplitWay={setSelectedSplitWay}
+ />
+
+ setIsCustomAmountOpen(false)}
+ onRemoveSplitWay={handleRemoveSplitWay}
+ />
+
+ setIsEqualityOpen(false)}
+ onRemoveSplitWay={handleRemoveSplitWay}
+ />
+
+ setIsPayForItemsOpen(false)}
+ onRemoveSplitWay={handleRemoveSplitWay}
/>
>
);
diff --git a/src/pages/pay/components/splitBill/CustomAmountChoiceBottomSheet.tsx b/src/pages/pay/components/splitBill/CustomAmountChoiceBottomSheet.tsx
new file mode 100644
index 0000000..abbe736
--- /dev/null
+++ b/src/pages/pay/components/splitBill/CustomAmountChoiceBottomSheet.tsx
@@ -0,0 +1,112 @@
+import { Button, Form, InputNumber } from "antd";
+import { ProBottomSheet } from "components/ProBottomSheet/ProBottomSheet.tsx";
+import { useState, useEffect } from "react";
+import { useTranslation } from "react-i18next";
+
+import {
+ selectCart,
+ selectGrandTotal,
+ updateSplitBillAmount,
+} from "features/order/orderSlice";
+import { useAppDispatch, useAppSelector } from "redux/hooks";
+import ProInputCard from "components/ProInputCard/ProInputCard";
+
+interface SplitBillChoiceBottomSheetProps {
+ isOpen: boolean;
+ onClose: () => void;
+ onSave?: (value: string) => void;
+ onRemoveSplitWay?: () => void;
+}
+
+export function CustomAmountChoiceBottomSheet({
+ isOpen,
+ onClose,
+ onRemoveSplitWay,
+}: SplitBillChoiceBottomSheetProps) {
+ const { t } = useTranslation();
+ const dispatch = useAppDispatch();
+ const { splitBillAmount } = useAppSelector(selectCart);
+ const grandTotal = useAppSelector(selectGrandTotal);
+ const [amount, setAmount] = useState(
+ splitBillAmount > 0 ? splitBillAmount.toString() : "",
+ );
+
+ useEffect(() => {
+ if (isOpen && splitBillAmount > 0) {
+ setAmount(splitBillAmount.toString());
+ }
+ }, [isOpen, splitBillAmount]);
+
+ const handleSave = () => {
+ const numAmount = parseFloat(amount) || 0;
+ dispatch(updateSplitBillAmount(numAmount));
+ onClose();
+ };
+
+ const handleRemoveSplitWay = () => {
+ setAmount("");
+ dispatch(updateSplitBillAmount(0));
+ onRemoveSplitWay?.();
+ onClose();
+ };
+
+ return (
+
+
+
+
+
+ setAmount(value?.toString() || "")}
+ placeholder={t("splitBill.amount")}
+ max={grandTotal.toString()}
+ min={"0"}
+ style={{ width: "100%", fontSize:"1rem" }}
+ />
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/pay/components/splitBill/EqualltyChoiceBottomSheet.tsx b/src/pages/pay/components/splitBill/EqualltyChoiceBottomSheet.tsx
new file mode 100644
index 0000000..e196c01
--- /dev/null
+++ b/src/pages/pay/components/splitBill/EqualltyChoiceBottomSheet.tsx
@@ -0,0 +1,310 @@
+import { Button } from "antd";
+import { ProBottomSheet } from "components/ProBottomSheet/ProBottomSheet.tsx";
+import { useEffect, useMemo } from "react";
+import { useTranslation } from "react-i18next";
+
+import PeopleIcon from "components/Icons/PeopleIcon";
+import ProInputCard from "components/ProInputCard/ProInputCard";
+import ProText from "components/ProText";
+import ProTitle from "components/ProTitle";
+import {
+ selectCart,
+ selectGrandTotal,
+ updateSplitBillAmount,
+} from "features/order/orderSlice";
+import { useAppDispatch, useAppSelector } from "redux/hooks";
+import { ProGray1 } from "ThemeConstants";
+import PayForActions from "../../../split-bill/components/PayForActions";
+import TotalPeopleActions from "../../../split-bill/components/TotalPeopleActions";
+
+interface SplitBillChoiceBottomSheetProps {
+ isOpen: boolean;
+ onClose: () => void;
+ onSave?: (value: string) => void;
+ onRemoveSplitWay?: () => void;
+}
+
+interface SplitBillTmp {
+ totalPeople?: number;
+ payFor?: number;
+}
+
+export function EqualltyChoiceBottomSheet({
+ isOpen,
+ onClose,
+ onRemoveSplitWay,
+}: SplitBillChoiceBottomSheetProps) {
+ const { t } = useTranslation();
+ const dispatch = useAppDispatch();
+ const { tmp } = useAppSelector(selectCart);
+ const grandTotal = useAppSelector(selectGrandTotal);
+
+ const splitBillTmp = tmp as SplitBillTmp;
+ const totalPeople = splitBillTmp?.totalPeople || 1;
+ const payFor = splitBillTmp?.payFor || 1;
+
+ // Calculate split amount
+ const splitAmount = useMemo(() => {
+ if (totalPeople > 0) {
+ return (grandTotal / totalPeople) * payFor;
+ }
+ return 0;
+ }, [grandTotal, totalPeople, payFor]);
+
+ // Update splitBillAmount when values change
+ useEffect(() => {
+ if (isOpen && splitAmount > 0) {
+ dispatch(updateSplitBillAmount(splitAmount));
+ }
+ }, [isOpen, splitAmount, dispatch]);
+
+ const handleSave = () => {
+ dispatch(updateSplitBillAmount(splitAmount));
+ onClose();
+ };
+
+ const handleRemoveSplitWay = () => {
+ dispatch(updateSplitBillAmount(0));
+ onRemoveSplitWay?.();
+ onClose();
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ {t("checkout.totalPeople")}
+
+
+
+
+
+
+
+
+
+
+ {t("checkout.payFor")}
+
+
+
+
+
+
+
+
+ {/* Spinner Visualization - Blank Spin Wheel */}
+ {totalPeople > 0 && (
+
+
+ {t("splitBill.divisionPreview")}
+
+
+
+ {/* Center circle to make it look like a blank wheel */}
+
+ {payFor}
+
+
+
+ {t("splitBill.yourAmount")}: {splitAmount.toFixed(2)}
+
+
+ )}
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/pay/components/splitBill/PayForYourItemsChoiceBottomSheet.tsx b/src/pages/pay/components/splitBill/PayForYourItemsChoiceBottomSheet.tsx
new file mode 100644
index 0000000..0af7772
--- /dev/null
+++ b/src/pages/pay/components/splitBill/PayForYourItemsChoiceBottomSheet.tsx
@@ -0,0 +1,211 @@
+import { Button, Card, Image } from "antd";
+import { ProBottomSheet } from "components/ProBottomSheet/ProBottomSheet.tsx";
+import { useEffect, useState } from "react";
+import { useTranslation } from "react-i18next";
+
+import ArabicPrice from "components/ArabicPrice";
+import ProText from "components/ProText";
+import { selectCart, updateSplitBillAmount } from "features/order/orderSlice";
+import { useAppDispatch, useAppSelector } from "redux/hooks";
+
+interface SplitBillChoiceBottomSheetProps {
+ isOpen: boolean;
+ onClose: () => void;
+ onSave?: (value: string) => void;
+ onRemoveSplitWay?: () => void;
+}
+
+export function PayForYourItemsChoiceBottomSheet({
+ isOpen,
+ onClose,
+ onRemoveSplitWay,
+}: SplitBillChoiceBottomSheetProps) {
+ const { t } = useTranslation();
+ const dispatch = useAppDispatch();
+ const { items } = useAppSelector(selectCart);
+ const [selectedItems, setSelectedItems] = useState>(new Set());
+
+ // Calculate total for selected items
+ const selectedTotal = items
+ .filter((item) => selectedItems.has(item.uniqueId || item.id.toString()))
+ .reduce((sum, item) => sum + item.price * item.quantity, 0);
+
+ useEffect(() => {
+ if (isOpen) {
+ setSelectedItems(new Set());
+ dispatch(updateSplitBillAmount(0));
+ }
+ }, [isOpen, dispatch]);
+
+ const handleItemToggle = (uniqueId: string) => {
+ const newSelected = new Set(selectedItems);
+ if (newSelected.has(uniqueId)) {
+ newSelected.delete(uniqueId);
+ } else {
+ newSelected.add(uniqueId);
+ }
+ setSelectedItems(newSelected);
+
+ // Calculate and update split bill amount
+ const newTotal = items
+ .filter((item) => newSelected.has(item.uniqueId || item.id.toString()))
+ .reduce((sum, item) => sum + item.price * item.quantity, 0);
+ dispatch(updateSplitBillAmount(newTotal));
+ };
+
+ const handleSave = () => {
+ dispatch(updateSplitBillAmount(selectedTotal));
+ onClose();
+ };
+
+ const handleRemoveSplitWay = () => {
+ setSelectedItems(new Set());
+ dispatch(updateSplitBillAmount(0));
+ onRemoveSplitWay?.();
+ onClose();
+ };
+
+ 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}
+
+
+ {t("cart.quantity")}: {item.quantity}
+
+
+
+
+
+
+ );
+ })
+ )}
+
+ {selectedTotal > 0 && (
+
+
+
+ {t("splitBill.selectedTotal")}:
+
+
+
+
+ )}
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/pay/components/splitBill/SplitBillChoiceBottomSheet.tsx b/src/pages/pay/components/splitBill/SplitBillChoiceBottomSheet.tsx
index 347652c..fafb501 100644
--- a/src/pages/pay/components/splitBill/SplitBillChoiceBottomSheet.tsx
+++ b/src/pages/pay/components/splitBill/SplitBillChoiceBottomSheet.tsx
@@ -1,46 +1,60 @@
import { Card } from "antd";
import { ProBottomSheet } from "components/ProBottomSheet/ProBottomSheet.tsx";
+import { useState } from "react";
import { useTranslation } from "react-i18next";
import BackIcon from "components/Icons/BackIcon";
import NextIcon from "components/Icons/NextIcon";
import ProTitle from "components/ProTitle";
-import { useNavigate, useParams } from "react-router-dom";
-import { useGetRestaurantDetailsQuery } from "redux/api/others";
import { useAppSelector } from "redux/hooks";
+import { CustomAmountChoiceBottomSheet } from "./CustomAmountChoiceBottomSheet";
+import { EqualltyChoiceBottomSheet } from "./EqualltyChoiceBottomSheet";
+import { PayForYourItemsChoiceBottomSheet } from "./PayForYourItemsChoiceBottomSheet";
import styles from "./SplitBill.module.css";
interface SplitBillChoiceBottomSheetProps {
isOpen: boolean;
onClose: () => void;
onSave?: (value: string) => void;
+ onSelectSplitWay?: (way: "customAmount" | "equality" | "payForItems") => void;
}
export function SplitBillChoiceBottomSheet({
isOpen,
onClose,
- // onSave,
+ onSelectSplitWay,
}: SplitBillChoiceBottomSheetProps) {
const { t } = useTranslation();
- // const [value, setValue] = useState(initialValue);
const { isRTL } = useAppSelector((state) => state.locale);
- const { subdomain } = useParams();
- const navigate = useNavigate();
- const { data: restaurant } = useGetRestaurantDetailsQuery(subdomain, {
- skip: !subdomain,
- });
- // const handleSave = () => {
- // onSave(value);
- // onClose();
- // };
+ const [isCustomAmountOpen, setIsCustomAmountOpen] = useState(false);
+ const [isEqualityOpen, setIsEqualityOpen] = useState(false);
+ const [isPayForItemsOpen, setIsPayForItemsOpen] = useState(false);
const handleCancel = () => {
- // setValue(initialValue);
onClose();
};
+ const handleCustomAmountClick = () => {
+ onSelectSplitWay?.("customAmount");
+ onClose();
+ setIsCustomAmountOpen(true);
+ };
+
+ const handleEqualityClick = () => {
+ onSelectSplitWay?.("equality");
+ onClose();
+ setIsEqualityOpen(true);
+ };
+
+ const handlePayForItemsClick = () => {
+ onSelectSplitWay?.("payForItems");
+ onClose();
+ setIsPayForItemsOpen(true);
+ };
+
return (
+ <>
navigate(`/${restaurant?.subdomain}`)}
+ onClick={handleCustomAmountClick}
>
navigate(`/${restaurant?.subdomain}`)}
+ onClick={handleEqualityClick}
>
navigate(`/${restaurant?.subdomain}`)}
+ onClick={handlePayForItemsClick}
>
+
+
setIsCustomAmountOpen(false)}
+ />
+
+ setIsEqualityOpen(false)}
+ />
+
+ setIsPayForItemsOpen(false)}
+ />
+ >
);
}