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.discount")} - +
{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={