diff --git a/src/assets/locals/ar.json b/src/assets/locals/ar.json
index fedf940..4693aaf 100644
--- a/src/assets/locals/ar.json
+++ b/src/assets/locals/ar.json
@@ -228,7 +228,10 @@
"pleaseSelectEstimateTime": "يرجى اختيار وقت التقديم",
"pleaseSelectTable": "يرجى اختيار رقم الطاولة",
"pleaseSelectCollectionMethod": "يرجى اختيار طريقة الاستلام",
- "useLoyaltyPoints": "استخدام نقاط الولاء"
+ "useLoyaltyPoints": "استخدام نقاط الولاء",
+ "noLoyaltyItemsInCart": "لا توجد عناصر ولاء في سلة المشتريات",
+ "pleaseAddLoyaltyItems": "يرجى إضافة عناصر ولاء إلى سلة المشتريات لاستخدام نقاط الولاء",
+ "loyaltyDiscountApplied": "تم تطبيق خصم الولاء: {{itemName}} (خصم {{amount}})"
},
"checkout": {
"title": "الدفع",
diff --git a/src/assets/locals/en.json b/src/assets/locals/en.json
index 5c6fbbe..41835e2 100644
--- a/src/assets/locals/en.json
+++ b/src/assets/locals/en.json
@@ -238,7 +238,10 @@
"pleaseSelectEstimateTime": "Please select estimate time",
"pleaseSelectTable": "Please select table",
"pleaseSelectCollectionMethod": "Please select collection method",
- "useLoyaltyPoints": "Use Loyalty Points"
+ "useLoyaltyPoints": "Use Loyalty Points",
+ "noLoyaltyItemsInCart": "No loyalty items found in your cart",
+ "pleaseAddLoyaltyItems": "Please add loyalty items to your cart to use loyalty points",
+ "loyaltyDiscountApplied": "Loyalty discount applied: {{itemName}} ({{amount}} off)"
},
"checkout": {
"title": "Checkout",
diff --git a/src/components/OrderSummary/OrderSummary.tsx b/src/components/OrderSummary/OrderSummary.tsx
index ebb8039..15e3ff2 100644
--- a/src/components/OrderSummary/OrderSummary.tsx
+++ b/src/components/OrderSummary/OrderSummary.tsx
@@ -3,9 +3,13 @@ import ArabicPrice from "components/ArabicPrice";
import {
selectCart,
selectCartTotal,
+ selectCartTotalWithLoyaltyDiscount,
+ selectHighestPricedLoyaltyItem,
+ selectLoyaltyValidation,
updateUseLoyaltyPoints,
} from "features/order/orderSlice";
import { useTranslation } from "react-i18next";
+import { useGetRestaurantDetailsQuery } from "redux/api/others";
import { useAppDispatch, useAppSelector } from "redux/hooks";
import ProText from "../ProText";
import ProTitle from "../ProTitle";
@@ -16,8 +20,21 @@ export default function OrderSummary() {
const { useLoyaltyPoints } = useAppSelector(selectCart);
const dispatch = useAppDispatch();
const subtotal = useAppSelector(selectCartTotal);
- const tax = subtotal * 0.1; // 10% tax
- const total = subtotal + tax;
+ const subtotalWithLoyaltyDiscount = useAppSelector(
+ selectCartTotalWithLoyaltyDiscount,
+ );
+ const loyaltyValidation = useAppSelector(selectLoyaltyValidation);
+ const highestLoyaltyItem = useAppSelector(selectHighestPricedLoyaltyItem);
+
+ const tax = subtotalWithLoyaltyDiscount * 0.1; // 10% tax on discounted amount
+ const total = subtotalWithLoyaltyDiscount + tax;
+ const loyaltyDiscountAmount = subtotal - subtotalWithLoyaltyDiscount;
+
+ const { data: restaurant } = useGetRestaurantDetailsQuery("595");
+ const isHasLoyaltyGift =
+ (restaurant?.loyalty_stamps ?? 0) -
+ (restaurant?.customer_loyalty_points ?? 0) ===
+ 0;
return (
<>
@@ -31,7 +48,7 @@ export default function OrderSummary() {
{t("cart.riderTip")}
@@ -45,16 +62,36 @@ export default function OrderSummary() {
-
-
- {
- dispatch(updateUseLoyaltyPoints(value.target.checked));
- }}
- >
- {t("cart.useLoyaltyPoints")}
-
+
+ {isHasLoyaltyGift && (
+ <>
+
+
+ {
+ dispatch(updateUseLoyaltyPoints(value.target.checked));
+ }}
+ >
+ {t("cart.useLoyaltyPoints")}
+
+ >
+ )}
+
+ {isHasLoyaltyGift && loyaltyValidation.errorMessage && (
+
+ {t(loyaltyValidation.errorMessage)}
+
+ )}
+
+ {isHasLoyaltyGift && useLoyaltyPoints && highestLoyaltyItem && (
+
+ {t("cart.loyaltyDiscountApplied", {
+ itemName: highestLoyaltyItem.name,
+ amount: Math.round(loyaltyDiscountAmount).toFixed(2),
+ })}
+
+ )}
>
);
diff --git a/src/features/order/orderSlice.ts b/src/features/order/orderSlice.ts
index 3bc2492..2d93772 100644
--- a/src/features/order/orderSlice.ts
+++ b/src/features/order/orderSlice.ts
@@ -53,33 +53,35 @@ interface CartState {
paymentMethod: string;
orderType: string;
useLoyaltyPoints: boolean;
+ loyaltyValidationError: string | null;
}
// localStorage keys
export const CART_STORAGE_KEYS = {
- ITEMS: 'fascano_cart_items',
- SPECIAL_REQUEST: 'fascano_special_request',
- COUPON: 'fascano_coupon',
- TIP: 'fascano_tip',
- TABLES: 'fascano_tables',
- LOCATION: 'fascano_location',
- ROOM_DETAILS: 'fascano_room_details',
- OFFICE_DETAILS: 'fascano_office_details',
- GIFT_DETAILS: 'fascano_gift_details',
- ESTIMATE_TIME: 'fascano_estimate_time',
- ESTIMATE_TIME_DATE: 'fascano_estimate_time_date',
- ESTIMATE_TIME_TIME: 'fascano_estimate_time_time',
- COLLECTION_METHOD: 'fascano_collection_method',
- PHONE: 'fascano_phone',
- PAYMENT_METHOD: 'fascano_payment_method',
- ORDER_TYPE: 'fascano_order_type',
- USE_LOYALTY_POINTS: 'fascano_use_loyalty_points',
+ ITEMS: "fascano_cart_items",
+ SPECIAL_REQUEST: "fascano_special_request",
+ COUPON: "fascano_coupon",
+ TIP: "fascano_tip",
+ TABLES: "fascano_tables",
+ LOCATION: "fascano_location",
+ ROOM_DETAILS: "fascano_room_details",
+ OFFICE_DETAILS: "fascano_office_details",
+ GIFT_DETAILS: "fascano_gift_details",
+ ESTIMATE_TIME: "fascano_estimate_time",
+ ESTIMATE_TIME_DATE: "fascano_estimate_time_date",
+ ESTIMATE_TIME_TIME: "fascano_estimate_time_time",
+ COLLECTION_METHOD: "fascano_collection_method",
+ PHONE: "fascano_phone",
+ PAYMENT_METHOD: "fascano_payment_method",
+ ORDER_TYPE: "fascano_order_type",
+ USE_LOYALTY_POINTS: "fascano_use_loyalty_points",
+ LOYALTY_VALIDATION_ERROR: "fascano_loyalty_validation_error",
} as const;
// Utility functions for localStorage
const getFromLocalStorage = (key: string, defaultValue: T): T => {
- if (typeof window === 'undefined') return defaultValue;
-
+ if (typeof window === "undefined") return defaultValue;
+
try {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : defaultValue;
@@ -100,14 +102,37 @@ const initialState: CartState = {
coupon: getFromLocalStorage(CART_STORAGE_KEYS.COUPON, ""),
tip: getFromLocalStorage(CART_STORAGE_KEYS.TIP, ""),
tables: getFromLocalStorage(CART_STORAGE_KEYS.TABLES, []),
- estimateTime: new Date(getFromLocalStorage(CART_STORAGE_KEYS.ESTIMATE_TIME, new Date().toISOString())),
- estimateTimeDate: new Date(getFromLocalStorage(CART_STORAGE_KEYS.ESTIMATE_TIME_DATE, new Date().toISOString())),
- estimateTimeTime: getFromLocalStorage(CART_STORAGE_KEYS.ESTIMATE_TIME_TIME, ""),
- collectionMethod: getFromLocalStorage(CART_STORAGE_KEYS.COLLECTION_METHOD, ""),
+ estimateTime: new Date(
+ getFromLocalStorage(
+ CART_STORAGE_KEYS.ESTIMATE_TIME,
+ new Date().toISOString(),
+ ),
+ ),
+ estimateTimeDate: new Date(
+ getFromLocalStorage(
+ CART_STORAGE_KEYS.ESTIMATE_TIME_DATE,
+ new Date().toISOString(),
+ ),
+ ),
+ estimateTimeTime: getFromLocalStorage(
+ CART_STORAGE_KEYS.ESTIMATE_TIME_TIME,
+ "",
+ ),
+ collectionMethod: getFromLocalStorage(
+ CART_STORAGE_KEYS.COLLECTION_METHOD,
+ "",
+ ),
phone: getFromLocalStorage(CART_STORAGE_KEYS.PHONE, ""),
paymentMethod: getFromLocalStorage(CART_STORAGE_KEYS.PAYMENT_METHOD, ""),
orderType: getFromLocalStorage(CART_STORAGE_KEYS.ORDER_TYPE, ""),
- useLoyaltyPoints: getFromLocalStorage(CART_STORAGE_KEYS.USE_LOYALTY_POINTS, false),
+ useLoyaltyPoints: getFromLocalStorage(
+ CART_STORAGE_KEYS.USE_LOYALTY_POINTS,
+ false,
+ ),
+ loyaltyValidationError: getFromLocalStorage(
+ CART_STORAGE_KEYS.LOYALTY_VALIDATION_ERROR,
+ null,
+ ),
};
const orderSlice = createSlice({
@@ -117,40 +142,86 @@ const orderSlice = createSlice({
reset() {
return initialState;
},
- addItem(state, action: PayloadAction<{ item: Omit; quantity: number }>) {
+ addItem(
+ state,
+ action: PayloadAction<{
+ item: Omit;
+ quantity: number;
+ }>,
+ ) {
const { item, quantity } = action.payload;
const existingItem = state.items.find((i) => i.id === item.id);
if (existingItem) {
state.items = state.items.map((i) =>
- i.id === item.id ? { ...i, quantity: i.quantity + quantity } : i
+ i.id === item.id ? { ...i, quantity: i.quantity + quantity } : i,
);
} else {
state.items = [...state.items, { ...item, quantity }];
}
-
+
+ // Validate loyalty points if enabled
+ if (state.useLoyaltyPoints) {
+ const loyaltyItems = state.items.filter((item) => item.isHasLoyalty);
+ if (loyaltyItems.length === 0) {
+ state.loyaltyValidationError = "cart.noLoyaltyItemsInCart";
+ } else {
+ state.loyaltyValidationError = null;
+ }
+ }
+
// Sync to localStorage
- if (typeof window !== 'undefined') {
- localStorage.setItem(CART_STORAGE_KEYS.ITEMS, JSON.stringify(state.items));
+ if (typeof window !== "undefined") {
+ localStorage.setItem(
+ CART_STORAGE_KEYS.ITEMS,
+ JSON.stringify(state.items),
+ );
+ localStorage.setItem(
+ CART_STORAGE_KEYS.LOYALTY_VALIDATION_ERROR,
+ JSON.stringify(state.loyaltyValidationError),
+ );
}
},
- updateQuantity(state, action: PayloadAction<{ id: number | string; quantity: number }>) {
+ updateQuantity(
+ state,
+ action: PayloadAction<{ id: number | string; quantity: number }>,
+ ) {
const { id, quantity } = action.payload;
state.items = state.items.map((item) =>
- item.id === id ? { ...item, quantity } : item
+ item.id === id ? { ...item, quantity } : item,
);
-
+
// Sync to localStorage
- if (typeof window !== 'undefined') {
- localStorage.setItem(CART_STORAGE_KEYS.ITEMS, JSON.stringify(state.items));
+ if (typeof window !== "undefined") {
+ localStorage.setItem(
+ CART_STORAGE_KEYS.ITEMS,
+ JSON.stringify(state.items),
+ );
}
},
removeItem(state, action: PayloadAction) {
state.items = state.items.filter((item) => item.id !== action.payload);
-
+
+ // Validate loyalty points if enabled
+ if (state.useLoyaltyPoints) {
+ const loyaltyItems = state.items.filter((item) => item.isHasLoyalty);
+ if (loyaltyItems.length === 0) {
+ state.loyaltyValidationError = "cart.noLoyaltyItemsInCart";
+ } else {
+ state.loyaltyValidationError = null;
+ }
+ }
+
// Sync to localStorage
- if (typeof window !== 'undefined') {
- localStorage.setItem(CART_STORAGE_KEYS.ITEMS, JSON.stringify(state.items));
+ if (typeof window !== "undefined") {
+ localStorage.setItem(
+ CART_STORAGE_KEYS.ITEMS,
+ JSON.stringify(state.items),
+ );
+ localStorage.setItem(
+ CART_STORAGE_KEYS.LOYALTY_VALIDATION_ERROR,
+ JSON.stringify(state.loyaltyValidationError),
+ );
}
},
clearCart(state) {
@@ -169,58 +240,70 @@ const orderSlice = createSlice({
state.estimateTimeTime = "";
state.collectionMethod = "";
state.paymentMethod = "";
+ state.loyaltyValidationError = null;
// Clear all cart data from localStorage
- if (typeof window !== 'undefined') {
- Object.values(CART_STORAGE_KEYS).filter(key => key !== CART_STORAGE_KEYS.ORDER_TYPE).forEach(key => {
- localStorage.removeItem(key);
- });
+ if (typeof window !== "undefined") {
+ Object.values(CART_STORAGE_KEYS)
+ .filter((key) => key !== CART_STORAGE_KEYS.ORDER_TYPE)
+ .forEach((key) => {
+ localStorage.removeItem(key);
+ });
}
},
updateSpecialRequest(state, action: PayloadAction) {
state.specialRequest = action.payload;
-
+
// Sync to localStorage
- if (typeof window !== 'undefined') {
- localStorage.setItem(CART_STORAGE_KEYS.SPECIAL_REQUEST, JSON.stringify(state.specialRequest));
+ if (typeof window !== "undefined") {
+ localStorage.setItem(
+ CART_STORAGE_KEYS.SPECIAL_REQUEST,
+ JSON.stringify(state.specialRequest),
+ );
}
},
clearSpecialRequest(state) {
state.specialRequest = "";
-
+
// Sync to localStorage
- if (typeof window !== 'undefined') {
+ if (typeof window !== "undefined") {
localStorage.removeItem(CART_STORAGE_KEYS.SPECIAL_REQUEST);
}
},
updateCoupon(state, action: PayloadAction) {
state.coupon = action.payload;
-
+
// Sync to localStorage
- if (typeof window !== 'undefined') {
- localStorage.setItem(CART_STORAGE_KEYS.COUPON, JSON.stringify(state.coupon));
+ if (typeof window !== "undefined") {
+ localStorage.setItem(
+ CART_STORAGE_KEYS.COUPON,
+ JSON.stringify(state.coupon),
+ );
}
},
updateTip(state, action: PayloadAction) {
state.tip = action.payload;
-
+
// Sync to localStorage
- if (typeof window !== 'undefined') {
+ if (typeof window !== "undefined") {
localStorage.setItem(CART_STORAGE_KEYS.TIP, JSON.stringify(state.tip));
}
},
updateTables(state, action: PayloadAction) {
state.tables = action.payload;
-
+
// Sync to localStorage
- if (typeof window !== 'undefined') {
- localStorage.setItem(CART_STORAGE_KEYS.TABLES, JSON.stringify(state.tables));
+ if (typeof window !== "undefined") {
+ localStorage.setItem(
+ CART_STORAGE_KEYS.TABLES,
+ JSON.stringify(state.tables),
+ );
}
},
removeTable(state) {
state.tables = [];
-
+
// Sync to localStorage
- if (typeof window !== 'undefined') {
+ if (typeof window !== "undefined") {
localStorage.removeItem(CART_STORAGE_KEYS.TABLES);
}
},
@@ -229,94 +312,183 @@ const orderSlice = createSlice({
},
updateLocation(state, action: PayloadAction) {
state.location = action.payload;
-
+
// Sync to localStorage
- if (typeof window !== 'undefined') {
- localStorage.setItem(CART_STORAGE_KEYS.LOCATION, JSON.stringify(state.location));
+ if (typeof window !== "undefined") {
+ localStorage.setItem(
+ CART_STORAGE_KEYS.LOCATION,
+ JSON.stringify(state.location),
+ );
}
},
clearLocation(state) {
state.location = null;
-
+
// Sync to localStorage
- if (typeof window !== 'undefined') {
+ if (typeof window !== "undefined") {
localStorage.removeItem(CART_STORAGE_KEYS.LOCATION);
}
},
updateRoomDetails(state, action: PayloadAction) {
state.roomDetails = action.payload;
-
+
// Sync to localStorage
- if (typeof window !== 'undefined') {
- localStorage.setItem(CART_STORAGE_KEYS.ROOM_DETAILS, JSON.stringify(state.roomDetails));
+ if (typeof window !== "undefined") {
+ localStorage.setItem(
+ CART_STORAGE_KEYS.ROOM_DETAILS,
+ JSON.stringify(state.roomDetails),
+ );
}
},
- updateOfficeDetails(state, action: PayloadAction) {
+ updateOfficeDetails(
+ state,
+ action: PayloadAction,
+ ) {
state.officeDetails = action.payload;
-
+
// Sync to localStorage
- if (typeof window !== 'undefined') {
- localStorage.setItem(CART_STORAGE_KEYS.OFFICE_DETAILS, JSON.stringify(state.officeDetails));
+ if (typeof window !== "undefined") {
+ localStorage.setItem(
+ CART_STORAGE_KEYS.OFFICE_DETAILS,
+ JSON.stringify(state.officeDetails),
+ );
}
},
updateGiftDetails(state, action: PayloadAction) {
state.giftDetails = action.payload;
-
+
// Sync to localStorage
- if (typeof window !== 'undefined') {
- localStorage.setItem(CART_STORAGE_KEYS.GIFT_DETAILS, JSON.stringify(state.giftDetails));
+ if (typeof window !== "undefined") {
+ localStorage.setItem(
+ CART_STORAGE_KEYS.GIFT_DETAILS,
+ JSON.stringify(state.giftDetails),
+ );
}
},
- updateEstimateTime(state, action: PayloadAction<{ date: Date; time: string }>) {
+ updateEstimateTime(
+ state,
+ action: PayloadAction<{ date: Date; time: string }>,
+ ) {
state.estimateTime = action.payload.date;
state.estimateTimeDate = action.payload.date;
state.estimateTimeTime = action.payload.time;
-
+
// Sync to localStorage
- if (typeof window !== 'undefined') {
- localStorage.setItem(CART_STORAGE_KEYS.ESTIMATE_TIME, JSON.stringify(state.estimateTime.toISOString()));
- localStorage.setItem(CART_STORAGE_KEYS.ESTIMATE_TIME_DATE, JSON.stringify(state.estimateTimeDate.toISOString()));
- localStorage.setItem(CART_STORAGE_KEYS.ESTIMATE_TIME_TIME, JSON.stringify(state.estimateTimeTime));
+ if (typeof window !== "undefined") {
+ localStorage.setItem(
+ CART_STORAGE_KEYS.ESTIMATE_TIME,
+ JSON.stringify(state.estimateTime.toISOString()),
+ );
+ localStorage.setItem(
+ CART_STORAGE_KEYS.ESTIMATE_TIME_DATE,
+ JSON.stringify(state.estimateTimeDate.toISOString()),
+ );
+ localStorage.setItem(
+ CART_STORAGE_KEYS.ESTIMATE_TIME_TIME,
+ JSON.stringify(state.estimateTimeTime),
+ );
}
},
updateCollectionMethod(state, action: PayloadAction) {
state.collectionMethod = action.payload;
-
+
// Sync to localStorage
- if (typeof window !== 'undefined') {
- localStorage.setItem(CART_STORAGE_KEYS.COLLECTION_METHOD, JSON.stringify(state.collectionMethod));
+ if (typeof window !== "undefined") {
+ localStorage.setItem(
+ CART_STORAGE_KEYS.COLLECTION_METHOD,
+ JSON.stringify(state.collectionMethod),
+ );
}
},
updatePhone(state, action: PayloadAction) {
state.phone = action.payload;
-
+
// Sync to localStorage
- if (typeof window !== 'undefined') {
- localStorage.setItem(CART_STORAGE_KEYS.PHONE, JSON.stringify(state.phone));
+ if (typeof window !== "undefined") {
+ localStorage.setItem(
+ CART_STORAGE_KEYS.PHONE,
+ JSON.stringify(state.phone),
+ );
}
},
updatePaymentMethod(state, action: PayloadAction) {
state.paymentMethod = action.payload;
-
+
// Sync to localStorage
- if (typeof window !== 'undefined') {
- localStorage.setItem(CART_STORAGE_KEYS.PAYMENT_METHOD, JSON.stringify(state.paymentMethod));
+ if (typeof window !== "undefined") {
+ localStorage.setItem(
+ CART_STORAGE_KEYS.PAYMENT_METHOD,
+ JSON.stringify(state.paymentMethod),
+ );
}
},
updateOrderType(state, action: PayloadAction) {
state.orderType = action.payload;
-
+
// Sync to localStorage
- if (typeof window !== 'undefined') {
- localStorage.setItem(CART_STORAGE_KEYS.ORDER_TYPE, JSON.stringify(state.orderType));
+ if (typeof window !== "undefined") {
+ localStorage.setItem(
+ CART_STORAGE_KEYS.ORDER_TYPE,
+ JSON.stringify(state.orderType),
+ );
}
},
updateUseLoyaltyPoints(state, action: PayloadAction) {
state.useLoyaltyPoints = action.payload;
-
+
+ // Validate loyalty points usage
+ if (action.payload) {
+ const loyaltyItems = state.items.filter((item) => item.isHasLoyalty);
+ if (loyaltyItems.length === 0) {
+ state.loyaltyValidationError = "cart.noLoyaltyItemsInCart";
+ } else {
+ state.loyaltyValidationError = null;
+ }
+ } else {
+ state.loyaltyValidationError = null;
+ }
+
// Sync to localStorage
- if (typeof window !== 'undefined') {
- localStorage.setItem(CART_STORAGE_KEYS.USE_LOYALTY_POINTS, JSON.stringify(state.useLoyaltyPoints));
+ if (typeof window !== "undefined") {
+ localStorage.setItem(
+ CART_STORAGE_KEYS.USE_LOYALTY_POINTS,
+ JSON.stringify(state.useLoyaltyPoints),
+ );
+ localStorage.setItem(
+ CART_STORAGE_KEYS.LOYALTY_VALIDATION_ERROR,
+ JSON.stringify(state.loyaltyValidationError),
+ );
+ }
+ },
+ validateLoyaltyPoints(state) {
+ if (state.useLoyaltyPoints) {
+ const loyaltyItems = state.items.filter((item) => item.isHasLoyalty);
+ if (loyaltyItems.length === 0) {
+ state.loyaltyValidationError = "cart.noLoyaltyItemsInCart";
+ } else {
+ state.loyaltyValidationError = null;
+ }
+ } else {
+ state.loyaltyValidationError = null;
+ }
+
+ // Sync to localStorage
+ if (typeof window !== "undefined") {
+ localStorage.setItem(
+ CART_STORAGE_KEYS.LOYALTY_VALIDATION_ERROR,
+ JSON.stringify(state.loyaltyValidationError),
+ );
+ }
+ },
+ clearLoyaltyValidationError(state) {
+ state.loyaltyValidationError = null;
+
+ // Sync to localStorage
+ if (typeof window !== "undefined") {
+ localStorage.setItem(
+ CART_STORAGE_KEYS.LOYALTY_VALIDATION_ERROR,
+ JSON.stringify(state.loyaltyValidationError),
+ );
}
},
},
@@ -345,17 +517,63 @@ export const {
updatePaymentMethod,
updateOrderType,
updateUseLoyaltyPoints,
+ validateLoyaltyPoints,
+ clearLoyaltyValidationError,
reset,
} = orderSlice.actions;
// 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 = (id: number | string) => (state: RootState) => {
- const item = state.order.items.find((i) => i.id === id);
- return item ? item.quantity : 0;
+export const selectCartTotal = (state: RootState) =>
+ state.order.items.reduce(
+ (total, item) => total + item.price * item.quantity,
+ 0,
+ );
+export const selectCartItemsQuantity =
+ (id: number | string) => (state: RootState) => {
+ const item = state.order.items.find((i) => i.id === id);
+ return item ? item.quantity : 0;
+ };
+
+// Loyalty selectors
+export const selectLoyaltyItems = (state: RootState) =>
+ state.order.items.filter((item) => item.isHasLoyalty);
+
+export const selectHighestPricedLoyaltyItem = (state: RootState) => {
+ const loyaltyItems = selectLoyaltyItems(state);
+ if (loyaltyItems.length === 0) return null;
+
+ return loyaltyItems.reduce((highest, current) =>
+ current.price > highest.price ? current : highest,
+ );
+};
+
+export const selectCartTotalWithLoyaltyDiscount = (state: RootState) => {
+ const total = selectCartTotal(state);
+ const useLoyaltyPoints = state.order.useLoyaltyPoints;
+ const highestLoyaltyItem = selectHighestPricedLoyaltyItem(state);
+
+ if (useLoyaltyPoints && highestLoyaltyItem) {
+ return total - highestLoyaltyItem.price;
+ }
+
+ return total;
+};
+
+export const selectLoyaltyValidation = (state: RootState) => {
+ const useLoyaltyPoints = state.order.useLoyaltyPoints;
+ const loyaltyItems = selectLoyaltyItems(state);
+
+ return {
+ canUseLoyaltyPoints: loyaltyItems.length > 0,
+ hasLoyaltyItems: loyaltyItems.length > 0,
+ loyaltyItemsCount: loyaltyItems.length,
+ errorMessage:
+ useLoyaltyPoints && loyaltyItems.length === 0
+ ? "cart.noLoyaltyItemsInCart"
+ : null,
+ };
};
export default orderSlice.reducer;
diff --git a/src/pages/cart/components/youMayLike/YouMightAlsoLike.tsx b/src/pages/cart/components/youMayLike/YouMightAlsoLike.tsx
index 3ab7bb3..9a84863 100644
--- a/src/pages/cart/components/youMayLike/YouMightAlsoLike.tsx
+++ b/src/pages/cart/components/youMayLike/YouMightAlsoLike.tsx
@@ -109,6 +109,8 @@ export default function YouMightAlsoLike() {
image: item.image,
description: item.description,
variant: "None",
+ isHasLoyalty: item.isHasLoyalty,
+ no_of_stamps_give: item.no_of_stamps_give,
},
quantity: 1,
}),
diff --git a/src/pages/checkout/hooks/useOrder.ts b/src/pages/checkout/hooks/useOrder.ts
index 14260d7..e152323 100644
--- a/src/pages/checkout/hooks/useOrder.ts
+++ b/src/pages/checkout/hooks/useOrder.ts
@@ -1,5 +1,10 @@
import { message } from "antd";
-import { clearCart, selectCart } from "features/order/orderSlice";
+import {
+ clearCart,
+ selectCart,
+ selectCartTotalWithLoyaltyDiscount,
+ selectHighestPricedLoyaltyItem,
+} from "features/order/orderSlice";
import { useCallback } from "react";
import { useTranslation } from "react-i18next";
import { useNavigate, useParams } from "react-router-dom";
@@ -29,6 +34,10 @@ export default function useOrder() {
orderType,
giftDetails,
} = useAppSelector(selectCart);
+ const highestLoyaltyItem = useAppSelector(selectHighestPricedLoyaltyItem);
+ const { useLoyaltyPoints } = useAppSelector(selectCart);
+
+ const orderPrice = useAppSelector(selectCartTotalWithLoyaltyDiscount);
const [createOrder] = useCreateOrderMutation();
@@ -59,10 +68,8 @@ export default function useOrder() {
pickup_comments: "",
pickup_time: estimateTime,
delivery_pickup_interval: "",
- orderPrice: items.reduce(
- (acc, item) => acc + item.price * item.quantity,
- 0,
- ),
+ orderPrice: orderPrice,
+ use_loylaty: useLoyaltyPoints && highestLoyaltyItem ? 1 : 0,
useWallet: 0,
tip,
...(orderType === "gift"
@@ -74,7 +81,7 @@ export default function useOrder() {
senderEmail: giftDetails?.senderEmail,
senderPhone: giftDetails?.senderPhone,
senderName: giftDetails?.senderName,
- dineType: orderType
+ dineType: orderType,
}
: {}),
})
@@ -120,6 +127,7 @@ export default function useOrder() {
user_uuid,
estimateTime,
tip,
+ orderPrice,
t,
navigate,
dispatch,
diff --git a/src/pages/product/components/ProductFooter.tsx b/src/pages/product/components/ProductFooter.tsx
index e0f28a3..0c69fa7 100644
--- a/src/pages/product/components/ProductFooter.tsx
+++ b/src/pages/product/components/ProductFooter.tsx
@@ -69,6 +69,8 @@ export default function ProductFooter({
variant: variantId,
extras: selectedExtras,
extrasgroup: selectedGroups,
+ isHasLoyalty: product?.isHasLoyalty,
+ no_of_stamps_give: product?.no_of_stamps_give,
},
quantity: quantity,
}),
diff --git a/src/utils/types/appTypes.ts b/src/utils/types/appTypes.ts
index f2ff901..7fac6e8 100644
--- a/src/utils/types/appTypes.ts
+++ b/src/utils/types/appTypes.ts
@@ -331,6 +331,8 @@ export interface CartItem {
variant?: string;
extras?: string[];
extrasgroup?: string[];
+ isHasLoyalty?: boolean;
+ no_of_stamps_give?: number;
}
export interface User {