diff --git a/src/components/OrderSummary/OrderSummary.tsx b/src/components/OrderSummary/OrderSummary.tsx
index 5896860..79f5664 100644
--- a/src/components/OrderSummary/OrderSummary.tsx
+++ b/src/components/OrderSummary/OrderSummary.tsx
@@ -3,6 +3,7 @@ import ArabicPrice from "components/ArabicPrice";
import {
selectCart,
selectCartTotal,
+ selectDiscountTotal,
selectGrandTotal,
selectHighestPricedLoyaltyItem,
selectLoyaltyValidation,
@@ -30,6 +31,7 @@ export default function OrderSummary() {
const highestLoyaltyItem = useAppSelector(selectHighestPricedLoyaltyItem);
const taxAmount = useAppSelector(selectTaxAmount);
const grandTotal = useAppSelector(selectGrandTotal);
+ const discountAmount = useAppSelector(selectDiscountTotal);
const isHasLoyaltyGift =
(restaurant?.loyalty_stamps ?? 0) -
@@ -54,13 +56,7 @@ export default function OrderSummary() {
)}
{t("cart.tax")}
diff --git a/src/features/order/orderSlice.ts b/src/features/order/orderSlice.ts
index f2506a7..1e75495 100644
--- a/src/features/order/orderSlice.ts
+++ b/src/features/order/orderSlice.ts
@@ -35,6 +35,12 @@ export interface GiftDetailsType {
isSecret: boolean;
}
+interface DiscountData {
+ value: number;
+ isGift: boolean;
+ isDiscount: boolean;
+}
+
interface CartState {
restaurant: Partial
;
items: CartItem[];
@@ -57,6 +63,7 @@ interface CartState {
useLoyaltyPoints: boolean;
loyaltyValidationError: string | null;
scheduledDate: string;
+ discount: DiscountData;
}
// localStorage keys
@@ -81,6 +88,7 @@ export const CART_STORAGE_KEYS = {
LOYALTY_VALIDATION_ERROR: "fascano_loyalty_validation_error",
RESTAURANT: "fascano_restaurant",
SCHEDULED_DATE: "fascano_scheduled_date",
+ DISCOUNT: "fascano_discount",
} as const;
// Utility functions for localStorage
@@ -97,11 +105,15 @@ const getFromLocalStorage = (key: string, defaultValue: T): T => {
};
// Generate a unique identifier for cart items based on product ID, variant, extras, and comment
-const generateUniqueId = (item: Omit): string => {
- const variantStr = item.variant || '';
- const extrasStr = item.extras ? item.extras.sort().join(',') : '';
- const extrasGroupStr = item.extrasgroup ? item.extrasgroup.sort().join(',') : '';
- const commentStr = item.comment || '';
+const generateUniqueId = (
+ item: Omit,
+): string => {
+ const variantStr = item.variant || "";
+ const extrasStr = item.extras ? item.extras.sort().join(",") : "";
+ const extrasGroupStr = item.extrasgroup
+ ? item.extrasgroup.sort().join(",")
+ : "";
+ const commentStr = item.comment || "";
return `${item.id}-${variantStr}-${extrasStr}-${extrasGroupStr}-${commentStr}`;
};
@@ -153,6 +165,11 @@ const initialState: CartState = {
),
restaurant: getFromLocalStorage(CART_STORAGE_KEYS.RESTAURANT, { taxes: [] }),
scheduledDate: getFromLocalStorage(CART_STORAGE_KEYS.SCHEDULED_DATE, ""),
+ discount: getFromLocalStorage(CART_STORAGE_KEYS.DISCOUNT, {
+ value: 0,
+ isGift: false,
+ isDiscount: false,
+ }),
};
const orderSlice = createSlice({
@@ -189,7 +206,9 @@ const orderSlice = createSlice({
if (existingItem) {
// Update quantity of existing item with same configuration
state.items = state.items.map((i) =>
- i.uniqueId === uniqueId ? { ...i, quantity: i.quantity + quantity } : i,
+ i.uniqueId === uniqueId
+ ? { ...i, quantity: i.quantity + quantity }
+ : i,
);
} else {
// Add new item with its unique identifier
@@ -220,7 +239,11 @@ const orderSlice = createSlice({
},
updateQuantity(
state,
- action: PayloadAction<{ id: number | string; uniqueId: string; quantity: number }>,
+ action: PayloadAction<{
+ id: number | string;
+ uniqueId: string;
+ quantity: number;
+ }>,
) {
const { uniqueId, quantity } = action.payload;
state.items = state.items.map((item) =>
@@ -236,7 +259,9 @@ const orderSlice = createSlice({
}
},
removeItem(state, action: PayloadAction) {
- state.items = state.items.filter((item) => item.uniqueId !== action.payload);
+ state.items = state.items.filter(
+ (item) => item.uniqueId !== action.payload,
+ );
// Validate loyalty points if enabled
if (state.useLoyaltyPoints) {
@@ -538,6 +563,17 @@ const orderSlice = createSlice({
);
}
},
+ updateDiscount(state, action: PayloadAction) {
+ state.discount = action.payload;
+
+ // Sync to localStorage
+ if (typeof window !== "undefined") {
+ localStorage.setItem(
+ CART_STORAGE_KEYS.DISCOUNT,
+ JSON.stringify(state.discount),
+ );
+ }
+ },
},
});
@@ -569,6 +605,7 @@ export const {
reset,
updateRestaurant,
updateScheduledDate,
+ updateDiscount,
} = orderSlice.actions;
// Tax calculation helper functions
@@ -586,11 +623,13 @@ const calculateTotalTax = (subtotal: number, taxes: Tax[]): number => {
// Selectors
export const selectCart = (state: RootState) => state.order;
export const selectCartItems = (state: RootState) => state.order.items;
+
export const selectCartTotal = (state: RootState) =>
state.order.items.reduce(
(total, item) => total + item.price * item.quantity,
0,
);
+
export const selectCartItemsQuantity =
(uniqueId: string) => (state: RootState) => {
const item = state.order.items.find((i) => i.uniqueId === uniqueId);
@@ -617,6 +656,13 @@ export const selectHighestPricedLoyaltyItem = (state: RootState) => {
);
};
+export const selectDiscountTotal = (state: RootState) =>
+ (state.order.discount.value / 100) * selectCartTotal(state) +
+ (state.order.useLoyaltyPoints &&
+ state.order.restaurant?.is_loyalty_enabled === 1
+ ? selectHighestPricedLoyaltyItem(state)?.price || 0
+ : 0);
+
export const selectLoyaltyValidation = (state: RootState) => {
const useLoyaltyPoints = state.order.useLoyaltyPoints;
const loyaltyItems = selectLoyaltyItems(state);
@@ -636,13 +682,13 @@ export const selectLoyaltyValidation = (state: RootState) => {
export const selectTaxes = (state: RootState) => state.order.restaurant.taxes;
export const selectTaxAmount = (state: RootState) => {
- const subtotal = selectCartTotal(state);
+ const subtotal = selectCartTotal(state) - selectDiscountTotal(state);
const taxes = selectTaxes(state);
return calculateTotalTax(subtotal, taxes || []);
};
export const selectGrandTotal = (state: RootState) => {
- const loyaltyDiscount = selectHighestPricedLoyaltyItem(state)?.price || 0;
+ const totalDiscount = selectDiscountTotal(state);
const taxAmount = selectTaxAmount(state);
const subtotal = selectCartTotal(state);
const deliveryFee =
@@ -650,11 +696,7 @@ export const selectGrandTotal = (state: RootState) => {
? Number(state.order.restaurant?.delivery_fees) || 0
: 0;
- return (
- subtotal +
- taxAmount -
- (state.order.useLoyaltyPoints && state.order.restaurant?.is_loyalty_enabled === 1 ? loyaltyDiscount : 0) +
- deliveryFee
- );};
+ return subtotal + taxAmount - totalDiscount + deliveryFee;
+};
export default orderSlice.reducer;
diff --git a/src/pages/cart/components/CouponCard.tsx b/src/pages/cart/components/CouponCard.tsx
index 08b8e44..f1c2c42 100644
--- a/src/pages/cart/components/CouponCard.tsx
+++ b/src/pages/cart/components/CouponCard.tsx
@@ -1,61 +1,76 @@
import { Button, Form, Input, message } from "antd";
-import { CouponBottomSheet } from "components/CustomBottomSheet/CouponBottomSheet.tsx";
-import { CouponDialog } from "components/CustomBottomSheet/CouponDialog.tsx";
import CouponHeartIcon from "components/Icons/cart/CouponHeart.tsx";
-import DonateIcon from "components/Icons/cart/DonateIcon.tsx";
import ProInputCard from "components/ProInputCard/ProInputCard.tsx";
-import ProText from "components/ProText.tsx";
-import { selectCart, updateCoupon } from "features/order/orderSlice.ts";
-import useBreakPoint from "hooks/useBreakPoint.ts";
+import {
+ selectCart,
+ updateCoupon,
+ updateDiscount,
+} from "features/order/orderSlice.ts";
import styles from "pages/cart/cart.module.css";
-import { useState } from "react";
import { useTranslation } from "react-i18next";
+import { useGetDiscountMutation } from "redux/api/others";
import { useAppDispatch, useAppSelector } from "redux/hooks.ts";
-import { colors } from "ThemeConstants.ts";
export default function CouponCard() {
const { t } = useTranslation();
const dispatch = useAppDispatch();
+ const { restaurant } = useAppSelector((state) => state.order);
const { coupon } = useAppSelector(selectCart);
- const { isDesktop } = useBreakPoint();
+ // const { isDesktop } = useBreakPoint();
+ const [getDiscount] = useGetDiscountMutation();
- const [isCouponOpen, setIsCouponOpen] = useState(false);
+ // const [isCouponOpen, setIsCouponOpen] = useState(false);
const handleCouponSave = (value: string) => {
- dispatch(updateCoupon(value));
- message.success(t("cart.coupon") + " " + t("updatedSuccessfully"));
+ getDiscount({
+ discountCode: value,
+ restaurantID: restaurant.restautantId || "",
+ })
+ .unwrap()
+ .then((response) => {
+ dispatch(
+ updateDiscount({
+ value: response.value,
+ isGift: response.isGift,
+ isDiscount: response.isDiscount,
+ }),
+ );
+ })
+ .catch((error) => {
+ message.error(error.data.message || t("cart.couponInvalid"));
+ });
};
- const handleCouponClose = () => {
- setIsCouponOpen(false);
- };
+ // const handleCouponClose = () => {
+ // setIsCouponOpen(false);
+ // };
return (
<>
setIsCouponOpen(true)}
- >
-
- {t("cart.viewOffers")}
-
-
-
- }
+ // titleRight={
+ // setIsCouponOpen(true)}
+ // >
+ //
+ // {t("cart.viewOffers")}
+ //
+ //
+ //
+ // }
dividerStyle={{ margin: "5px 0 0 0" }}
>
{
+ dispatch(updateCoupon(e.target.value));
+ }}
suffix={
- {isDesktop ? (
+ {/* {isDesktop ? (
- )}
+ )} */}
>
);
}
diff --git a/src/pages/cart/page.tsx b/src/pages/cart/page.tsx
index a32af65..a6fd603 100644
--- a/src/pages/cart/page.tsx
+++ b/src/pages/cart/page.tsx
@@ -10,11 +10,11 @@ import { useAppSelector } from "redux/hooks";
export default function CartPage() {
const { isDesktop } = useBreakPoint();
const [form] = Form.useForm();
- const { specialRequest } = useAppSelector(selectCart);
+ const { specialRequest, coupon } = useAppSelector(selectCart);
useEffect(() => {
- form.setFieldsValue({ specialRequest });
- }, [form, specialRequest]);
+ form.setFieldsValue({ specialRequest, coupon });
+ }, [form, specialRequest, coupon]);
// Prevent keyboard from appearing automatically on mobile
useEffect(() => {
diff --git a/src/pages/checkout/hooks/useOrder.ts b/src/pages/checkout/hooks/useOrder.ts
index fee743d..a866b98 100644
--- a/src/pages/checkout/hooks/useOrder.ts
+++ b/src/pages/checkout/hooks/useOrder.ts
@@ -35,6 +35,7 @@ export default function useOrder() {
orderType,
giftDetails,
location,
+ discount,
} = useAppSelector(selectCart);
const highestLoyaltyItem = useAppSelector(selectHighestPricedLoyaltyItem);
const { useLoyaltyPoints } = useAppSelector(selectCart);
@@ -57,8 +58,6 @@ export default function useOrder() {
const handleCreateOrder = useCallback(() => {
createOrder({
phone: mobilenumber || phone || giftDetails?.senderPhone,
- couponID: coupon,
- discountAmount: 0,
comment: specialRequest,
delivery_method: getDeliveryMethod(),
timeslot: "",
@@ -75,7 +74,9 @@ export default function useOrder() {
})),
office_no: officeDetails?.officeNo || "",
vatvalue: 0,
- discountGiftCode: "",
+ ...(discount.isDiscount ? { couponID: coupon } : {}),
+ ...(discount.isGift ? { discountGiftCode: coupon } : {}),
+ discountAmount: discount.value || 0,
paymentType: "cod",
uuid: user_uuid,
pickup_comments: "",
@@ -123,37 +124,6 @@ export default function useOrder() {
.catch((error: any) => {
console.error("Create Order failed:", error);
});
- }, [
- createOrder,
- mobilenumber,
- phone,
- giftDetails?.senderPhone,
- giftDetails?.receiverName,
- giftDetails?.receiverPhone,
- giftDetails?.message,
- giftDetails?.isSecret,
- giftDetails?.senderEmail,
- giftDetails?.senderName,
- coupon,
- specialRequest,
- tables,
- orderType,
- restaurantID,
- items,
- officeDetails?.officeNo,
- user_uuid,
- estimateTime,
- orderPrice,
- useLoyaltyPoints,
- highestLoyaltyItem,
- tip,
- location?.lat,
- location?.lng,
- location?.address,
- t,
- navigate,
- subdomain,
- dispatch,
- ]);
+ }, [createOrder, mobilenumber, phone, giftDetails?.senderPhone, giftDetails?.receiverName, giftDetails?.receiverPhone, giftDetails?.message, giftDetails?.isSecret, giftDetails?.senderEmail, giftDetails?.senderName, specialRequest, getDeliveryMethod, tables, orderType, restaurantID, items, officeDetails?.officeNo, discount.isDiscount, discount.isGift, discount.value, coupon, user_uuid, estimateTime, orderPrice, useLoyaltyPoints, highestLoyaltyItem, tip, location?.lat, location?.lng, location?.address, t, navigate, subdomain, dispatch]);
return { handleCreateOrder };
}
diff --git a/src/redux/api/others.ts b/src/redux/api/others.ts
index db7f4ff..82381ef 100644
--- a/src/redux/api/others.ts
+++ b/src/redux/api/others.ts
@@ -1,6 +1,7 @@
import {
CANCEL_ORDER_URL,
CREATE_ORDER_URL,
+ DISCOUNT_URL,
ORDER_DETAILS_URL,
ORDERS_URL,
PRODUCTS_AND_CATEGORIES_URL,
@@ -10,7 +11,7 @@ import {
import { OrderDetails } from "pages/checkout/hooks/types";
import menuParser from "pages/menu/helper";
-import { RestaurantDetails } from "utils/types/appTypes";
+import { DiscountResultType, RestaurantDetails } from "utils/types/appTypes";
import { baseApi } from "./apiSlice";
export const branchApi = baseApi.injectEndpoints({
@@ -101,6 +102,24 @@ export const branchApi = baseApi.injectEndpoints({
return response.result;
},
}),
+ getDiscount: builder.mutation<
+ DiscountResultType,
+ { discountCode: string; restaurantID: string }
+ >({
+ query: ({
+ discountCode,
+ restaurantID,
+ }: {
+ discountCode: string;
+ restaurantID: string;
+ }) => ({
+ url: `${DISCOUNT_URL}/${discountCode}/${restaurantID}`,
+ method: "GET",
+ }),
+ transformResponse: (response: any) => {
+ return response.result;
+ },
+ }),
}),
});
export const {
@@ -111,4 +130,5 @@ export const {
useGetTablesQuery,
useGetOrderDetailsQuery,
useCancelOrderMutation,
+ useGetDiscountMutation
} = branchApi;
diff --git a/src/utils/constants.ts b/src/utils/constants.ts
index 7cf4f36..e71f88f 100644
--- a/src/utils/constants.ts
+++ b/src/utils/constants.ts
@@ -103,3 +103,4 @@ export const LOGIN_URL = `${API_BASE_URL}login`;
export const SEND_OTP_URL = `${API_BASE_URL}sendOtp`;
export const CONFIRM_OTP_URL = `${API_BASE_URL}confirmOtp`;
export const PAYMENT_CONFIRMATION_URL = `https://menu.fascano.com/payment/confirmation`;
+export const DISCOUNT_URL = `${BASE_URL}getDiscount`;
\ No newline at end of file
diff --git a/src/utils/types/appTypes.ts b/src/utils/types/appTypes.ts
index cc439b0..84b9f16 100644
--- a/src/utils/types/appTypes.ts
+++ b/src/utils/types/appTypes.ts
@@ -342,13 +342,22 @@ export interface User {
export type Locale = "en" | "ar";
export type Theme = "light" | "dark";
-export interface ApiResponse {
+export interface ApiResponse {
success: boolean;
- result: any;
+ result: T;
message: string;
error: any;
}
+export interface DiscountResultType {
+ value: number
+ is_on_category: boolean
+ id: number
+ isDiscount: boolean
+ isGift: boolean
+ categories: any[]
+}
+
export interface Restaurant {
id: string;
name: string;