apply loyalty discount logic
This commit is contained in:
@@ -228,7 +228,10 @@
|
|||||||
"pleaseSelectEstimateTime": "يرجى اختيار وقت التقديم",
|
"pleaseSelectEstimateTime": "يرجى اختيار وقت التقديم",
|
||||||
"pleaseSelectTable": "يرجى اختيار رقم الطاولة",
|
"pleaseSelectTable": "يرجى اختيار رقم الطاولة",
|
||||||
"pleaseSelectCollectionMethod": "يرجى اختيار طريقة الاستلام",
|
"pleaseSelectCollectionMethod": "يرجى اختيار طريقة الاستلام",
|
||||||
"useLoyaltyPoints": "استخدام نقاط الولاء"
|
"useLoyaltyPoints": "استخدام نقاط الولاء",
|
||||||
|
"noLoyaltyItemsInCart": "لا توجد عناصر ولاء في سلة المشتريات",
|
||||||
|
"pleaseAddLoyaltyItems": "يرجى إضافة عناصر ولاء إلى سلة المشتريات لاستخدام نقاط الولاء",
|
||||||
|
"loyaltyDiscountApplied": "تم تطبيق خصم الولاء: {{itemName}} (خصم {{amount}})"
|
||||||
},
|
},
|
||||||
"checkout": {
|
"checkout": {
|
||||||
"title": "الدفع",
|
"title": "الدفع",
|
||||||
|
|||||||
@@ -238,7 +238,10 @@
|
|||||||
"pleaseSelectEstimateTime": "Please select estimate time",
|
"pleaseSelectEstimateTime": "Please select estimate time",
|
||||||
"pleaseSelectTable": "Please select table",
|
"pleaseSelectTable": "Please select table",
|
||||||
"pleaseSelectCollectionMethod": "Please select collection method",
|
"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": {
|
"checkout": {
|
||||||
"title": "Checkout",
|
"title": "Checkout",
|
||||||
|
|||||||
@@ -3,9 +3,13 @@ import ArabicPrice from "components/ArabicPrice";
|
|||||||
import {
|
import {
|
||||||
selectCart,
|
selectCart,
|
||||||
selectCartTotal,
|
selectCartTotal,
|
||||||
|
selectCartTotalWithLoyaltyDiscount,
|
||||||
|
selectHighestPricedLoyaltyItem,
|
||||||
|
selectLoyaltyValidation,
|
||||||
updateUseLoyaltyPoints,
|
updateUseLoyaltyPoints,
|
||||||
} from "features/order/orderSlice";
|
} from "features/order/orderSlice";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useGetRestaurantDetailsQuery } from "redux/api/others";
|
||||||
import { useAppDispatch, useAppSelector } from "redux/hooks";
|
import { useAppDispatch, useAppSelector } from "redux/hooks";
|
||||||
import ProText from "../ProText";
|
import ProText from "../ProText";
|
||||||
import ProTitle from "../ProTitle";
|
import ProTitle from "../ProTitle";
|
||||||
@@ -16,8 +20,21 @@ export default function OrderSummary() {
|
|||||||
const { useLoyaltyPoints } = useAppSelector(selectCart);
|
const { useLoyaltyPoints } = useAppSelector(selectCart);
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const subtotal = useAppSelector(selectCartTotal);
|
const subtotal = useAppSelector(selectCartTotal);
|
||||||
const tax = subtotal * 0.1; // 10% tax
|
const subtotalWithLoyaltyDiscount = useAppSelector(
|
||||||
const total = subtotal + tax;
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -31,7 +48,7 @@ export default function OrderSummary() {
|
|||||||
</div>
|
</div>
|
||||||
<div className={styles.summaryRow}>
|
<div className={styles.summaryRow}>
|
||||||
<ProText type="secondary">{t("cart.discount")}</ProText>
|
<ProText type="secondary">{t("cart.discount")}</ProText>
|
||||||
<ArabicPrice price={0} />
|
<ArabicPrice price={loyaltyDiscountAmount} />
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.summaryRow}>
|
<div className={styles.summaryRow}>
|
||||||
<ProText type="secondary">{t("cart.riderTip")}</ProText>
|
<ProText type="secondary">{t("cart.riderTip")}</ProText>
|
||||||
@@ -45,6 +62,9 @@ export default function OrderSummary() {
|
|||||||
<ArabicPrice price={total} strong />
|
<ArabicPrice price={total} strong />
|
||||||
</div>
|
</div>
|
||||||
</Space>
|
</Space>
|
||||||
|
|
||||||
|
{isHasLoyaltyGift && (
|
||||||
|
<>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@@ -55,6 +75,23 @@ export default function OrderSummary() {
|
|||||||
>
|
>
|
||||||
{t("cart.useLoyaltyPoints")}
|
{t("cart.useLoyaltyPoints")}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isHasLoyaltyGift && loyaltyValidation.errorMessage && (
|
||||||
|
<div style={{ marginTop: 8, color: "red", fontSize: "12px" }}>
|
||||||
|
{t(loyaltyValidation.errorMessage)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isHasLoyaltyGift && useLoyaltyPoints && highestLoyaltyItem && (
|
||||||
|
<div style={{ marginTop: 8, color: "green", fontSize: "12px" }}>
|
||||||
|
{t("cart.loyaltyDiscountApplied", {
|
||||||
|
itemName: highestLoyaltyItem.name,
|
||||||
|
amount: Math.round(loyaltyDiscountAmount).toFixed(2),
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -53,32 +53,34 @@ interface CartState {
|
|||||||
paymentMethod: string;
|
paymentMethod: string;
|
||||||
orderType: string;
|
orderType: string;
|
||||||
useLoyaltyPoints: boolean;
|
useLoyaltyPoints: boolean;
|
||||||
|
loyaltyValidationError: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// localStorage keys
|
// localStorage keys
|
||||||
export const CART_STORAGE_KEYS = {
|
export const CART_STORAGE_KEYS = {
|
||||||
ITEMS: 'fascano_cart_items',
|
ITEMS: "fascano_cart_items",
|
||||||
SPECIAL_REQUEST: 'fascano_special_request',
|
SPECIAL_REQUEST: "fascano_special_request",
|
||||||
COUPON: 'fascano_coupon',
|
COUPON: "fascano_coupon",
|
||||||
TIP: 'fascano_tip',
|
TIP: "fascano_tip",
|
||||||
TABLES: 'fascano_tables',
|
TABLES: "fascano_tables",
|
||||||
LOCATION: 'fascano_location',
|
LOCATION: "fascano_location",
|
||||||
ROOM_DETAILS: 'fascano_room_details',
|
ROOM_DETAILS: "fascano_room_details",
|
||||||
OFFICE_DETAILS: 'fascano_office_details',
|
OFFICE_DETAILS: "fascano_office_details",
|
||||||
GIFT_DETAILS: 'fascano_gift_details',
|
GIFT_DETAILS: "fascano_gift_details",
|
||||||
ESTIMATE_TIME: 'fascano_estimate_time',
|
ESTIMATE_TIME: "fascano_estimate_time",
|
||||||
ESTIMATE_TIME_DATE: 'fascano_estimate_time_date',
|
ESTIMATE_TIME_DATE: "fascano_estimate_time_date",
|
||||||
ESTIMATE_TIME_TIME: 'fascano_estimate_time_time',
|
ESTIMATE_TIME_TIME: "fascano_estimate_time_time",
|
||||||
COLLECTION_METHOD: 'fascano_collection_method',
|
COLLECTION_METHOD: "fascano_collection_method",
|
||||||
PHONE: 'fascano_phone',
|
PHONE: "fascano_phone",
|
||||||
PAYMENT_METHOD: 'fascano_payment_method',
|
PAYMENT_METHOD: "fascano_payment_method",
|
||||||
ORDER_TYPE: 'fascano_order_type',
|
ORDER_TYPE: "fascano_order_type",
|
||||||
USE_LOYALTY_POINTS: 'fascano_use_loyalty_points',
|
USE_LOYALTY_POINTS: "fascano_use_loyalty_points",
|
||||||
|
LOYALTY_VALIDATION_ERROR: "fascano_loyalty_validation_error",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// Utility functions for localStorage
|
// Utility functions for localStorage
|
||||||
const getFromLocalStorage = <T>(key: string, defaultValue: T): T => {
|
const getFromLocalStorage = <T>(key: string, defaultValue: T): T => {
|
||||||
if (typeof window === 'undefined') return defaultValue;
|
if (typeof window === "undefined") return defaultValue;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const item = localStorage.getItem(key);
|
const item = localStorage.getItem(key);
|
||||||
@@ -100,14 +102,37 @@ const initialState: CartState = {
|
|||||||
coupon: getFromLocalStorage(CART_STORAGE_KEYS.COUPON, ""),
|
coupon: getFromLocalStorage(CART_STORAGE_KEYS.COUPON, ""),
|
||||||
tip: getFromLocalStorage(CART_STORAGE_KEYS.TIP, ""),
|
tip: getFromLocalStorage(CART_STORAGE_KEYS.TIP, ""),
|
||||||
tables: getFromLocalStorage(CART_STORAGE_KEYS.TABLES, []),
|
tables: getFromLocalStorage(CART_STORAGE_KEYS.TABLES, []),
|
||||||
estimateTime: new Date(getFromLocalStorage(CART_STORAGE_KEYS.ESTIMATE_TIME, new Date().toISOString())),
|
estimateTime: new Date(
|
||||||
estimateTimeDate: new Date(getFromLocalStorage(CART_STORAGE_KEYS.ESTIMATE_TIME_DATE, new Date().toISOString())),
|
getFromLocalStorage(
|
||||||
estimateTimeTime: getFromLocalStorage(CART_STORAGE_KEYS.ESTIMATE_TIME_TIME, ""),
|
CART_STORAGE_KEYS.ESTIMATE_TIME,
|
||||||
collectionMethod: getFromLocalStorage(CART_STORAGE_KEYS.COLLECTION_METHOD, ""),
|
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, ""),
|
phone: getFromLocalStorage(CART_STORAGE_KEYS.PHONE, ""),
|
||||||
paymentMethod: getFromLocalStorage(CART_STORAGE_KEYS.PAYMENT_METHOD, ""),
|
paymentMethod: getFromLocalStorage(CART_STORAGE_KEYS.PAYMENT_METHOD, ""),
|
||||||
orderType: getFromLocalStorage(CART_STORAGE_KEYS.ORDER_TYPE, ""),
|
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({
|
const orderSlice = createSlice({
|
||||||
@@ -117,40 +142,86 @@ const orderSlice = createSlice({
|
|||||||
reset() {
|
reset() {
|
||||||
return initialState;
|
return initialState;
|
||||||
},
|
},
|
||||||
addItem(state, action: PayloadAction<{ item: Omit<CartItem, "quantity">; quantity: number }>) {
|
addItem(
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{
|
||||||
|
item: Omit<CartItem, "quantity">;
|
||||||
|
quantity: number;
|
||||||
|
}>,
|
||||||
|
) {
|
||||||
const { item, quantity } = action.payload;
|
const { item, quantity } = action.payload;
|
||||||
const existingItem = state.items.find((i) => i.id === item.id);
|
const existingItem = state.items.find((i) => i.id === item.id);
|
||||||
|
|
||||||
if (existingItem) {
|
if (existingItem) {
|
||||||
state.items = state.items.map((i) =>
|
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 {
|
} else {
|
||||||
state.items = [...state.items, { ...item, quantity }];
|
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
|
// Sync to localStorage
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== "undefined") {
|
||||||
localStorage.setItem(CART_STORAGE_KEYS.ITEMS, JSON.stringify(state.items));
|
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;
|
const { id, quantity } = action.payload;
|
||||||
state.items = state.items.map((item) =>
|
state.items = state.items.map((item) =>
|
||||||
item.id === id ? { ...item, quantity } : item
|
item.id === id ? { ...item, quantity } : item,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Sync to localStorage
|
// Sync to localStorage
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== "undefined") {
|
||||||
localStorage.setItem(CART_STORAGE_KEYS.ITEMS, JSON.stringify(state.items));
|
localStorage.setItem(
|
||||||
|
CART_STORAGE_KEYS.ITEMS,
|
||||||
|
JSON.stringify(state.items),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
removeItem(state, action: PayloadAction<number | string>) {
|
removeItem(state, action: PayloadAction<number | string>) {
|
||||||
state.items = state.items.filter((item) => item.id !== action.payload);
|
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
|
// Sync to localStorage
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== "undefined") {
|
||||||
localStorage.setItem(CART_STORAGE_KEYS.ITEMS, JSON.stringify(state.items));
|
localStorage.setItem(
|
||||||
|
CART_STORAGE_KEYS.ITEMS,
|
||||||
|
JSON.stringify(state.items),
|
||||||
|
);
|
||||||
|
localStorage.setItem(
|
||||||
|
CART_STORAGE_KEYS.LOYALTY_VALIDATION_ERROR,
|
||||||
|
JSON.stringify(state.loyaltyValidationError),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
clearCart(state) {
|
clearCart(state) {
|
||||||
@@ -169,9 +240,12 @@ const orderSlice = createSlice({
|
|||||||
state.estimateTimeTime = "";
|
state.estimateTimeTime = "";
|
||||||
state.collectionMethod = "";
|
state.collectionMethod = "";
|
||||||
state.paymentMethod = "";
|
state.paymentMethod = "";
|
||||||
|
state.loyaltyValidationError = null;
|
||||||
// Clear all cart data from localStorage
|
// Clear all cart data from localStorage
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== "undefined") {
|
||||||
Object.values(CART_STORAGE_KEYS).filter(key => key !== CART_STORAGE_KEYS.ORDER_TYPE).forEach(key => {
|
Object.values(CART_STORAGE_KEYS)
|
||||||
|
.filter((key) => key !== CART_STORAGE_KEYS.ORDER_TYPE)
|
||||||
|
.forEach((key) => {
|
||||||
localStorage.removeItem(key);
|
localStorage.removeItem(key);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -180,15 +254,18 @@ const orderSlice = createSlice({
|
|||||||
state.specialRequest = action.payload;
|
state.specialRequest = action.payload;
|
||||||
|
|
||||||
// Sync to localStorage
|
// Sync to localStorage
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== "undefined") {
|
||||||
localStorage.setItem(CART_STORAGE_KEYS.SPECIAL_REQUEST, JSON.stringify(state.specialRequest));
|
localStorage.setItem(
|
||||||
|
CART_STORAGE_KEYS.SPECIAL_REQUEST,
|
||||||
|
JSON.stringify(state.specialRequest),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
clearSpecialRequest(state) {
|
clearSpecialRequest(state) {
|
||||||
state.specialRequest = "";
|
state.specialRequest = "";
|
||||||
|
|
||||||
// Sync to localStorage
|
// Sync to localStorage
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== "undefined") {
|
||||||
localStorage.removeItem(CART_STORAGE_KEYS.SPECIAL_REQUEST);
|
localStorage.removeItem(CART_STORAGE_KEYS.SPECIAL_REQUEST);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -196,15 +273,18 @@ const orderSlice = createSlice({
|
|||||||
state.coupon = action.payload;
|
state.coupon = action.payload;
|
||||||
|
|
||||||
// Sync to localStorage
|
// Sync to localStorage
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== "undefined") {
|
||||||
localStorage.setItem(CART_STORAGE_KEYS.COUPON, JSON.stringify(state.coupon));
|
localStorage.setItem(
|
||||||
|
CART_STORAGE_KEYS.COUPON,
|
||||||
|
JSON.stringify(state.coupon),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateTip(state, action: PayloadAction<string>) {
|
updateTip(state, action: PayloadAction<string>) {
|
||||||
state.tip = action.payload;
|
state.tip = action.payload;
|
||||||
|
|
||||||
// Sync to localStorage
|
// Sync to localStorage
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== "undefined") {
|
||||||
localStorage.setItem(CART_STORAGE_KEYS.TIP, JSON.stringify(state.tip));
|
localStorage.setItem(CART_STORAGE_KEYS.TIP, JSON.stringify(state.tip));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -212,15 +292,18 @@ const orderSlice = createSlice({
|
|||||||
state.tables = action.payload;
|
state.tables = action.payload;
|
||||||
|
|
||||||
// Sync to localStorage
|
// Sync to localStorage
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== "undefined") {
|
||||||
localStorage.setItem(CART_STORAGE_KEYS.TABLES, JSON.stringify(state.tables));
|
localStorage.setItem(
|
||||||
|
CART_STORAGE_KEYS.TABLES,
|
||||||
|
JSON.stringify(state.tables),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
removeTable(state) {
|
removeTable(state) {
|
||||||
state.tables = [];
|
state.tables = [];
|
||||||
|
|
||||||
// Sync to localStorage
|
// Sync to localStorage
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== "undefined") {
|
||||||
localStorage.removeItem(CART_STORAGE_KEYS.TABLES);
|
localStorage.removeItem(CART_STORAGE_KEYS.TABLES);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -231,15 +314,18 @@ const orderSlice = createSlice({
|
|||||||
state.location = action.payload;
|
state.location = action.payload;
|
||||||
|
|
||||||
// Sync to localStorage
|
// Sync to localStorage
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== "undefined") {
|
||||||
localStorage.setItem(CART_STORAGE_KEYS.LOCATION, JSON.stringify(state.location));
|
localStorage.setItem(
|
||||||
|
CART_STORAGE_KEYS.LOCATION,
|
||||||
|
JSON.stringify(state.location),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
clearLocation(state) {
|
clearLocation(state) {
|
||||||
state.location = null;
|
state.location = null;
|
||||||
|
|
||||||
// Sync to localStorage
|
// Sync to localStorage
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== "undefined") {
|
||||||
localStorage.removeItem(CART_STORAGE_KEYS.LOCATION);
|
localStorage.removeItem(CART_STORAGE_KEYS.LOCATION);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -247,76 +333,162 @@ const orderSlice = createSlice({
|
|||||||
state.roomDetails = action.payload;
|
state.roomDetails = action.payload;
|
||||||
|
|
||||||
// Sync to localStorage
|
// Sync to localStorage
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== "undefined") {
|
||||||
localStorage.setItem(CART_STORAGE_KEYS.ROOM_DETAILS, JSON.stringify(state.roomDetails));
|
localStorage.setItem(
|
||||||
|
CART_STORAGE_KEYS.ROOM_DETAILS,
|
||||||
|
JSON.stringify(state.roomDetails),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateOfficeDetails(state, action: PayloadAction<OfficeDetailsType | null>) {
|
updateOfficeDetails(
|
||||||
|
state,
|
||||||
|
action: PayloadAction<OfficeDetailsType | null>,
|
||||||
|
) {
|
||||||
state.officeDetails = action.payload;
|
state.officeDetails = action.payload;
|
||||||
|
|
||||||
// Sync to localStorage
|
// Sync to localStorage
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== "undefined") {
|
||||||
localStorage.setItem(CART_STORAGE_KEYS.OFFICE_DETAILS, JSON.stringify(state.officeDetails));
|
localStorage.setItem(
|
||||||
|
CART_STORAGE_KEYS.OFFICE_DETAILS,
|
||||||
|
JSON.stringify(state.officeDetails),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateGiftDetails(state, action: PayloadAction<GiftDetailsType | null>) {
|
updateGiftDetails(state, action: PayloadAction<GiftDetailsType | null>) {
|
||||||
state.giftDetails = action.payload;
|
state.giftDetails = action.payload;
|
||||||
|
|
||||||
// Sync to localStorage
|
// Sync to localStorage
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== "undefined") {
|
||||||
localStorage.setItem(CART_STORAGE_KEYS.GIFT_DETAILS, JSON.stringify(state.giftDetails));
|
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.estimateTime = action.payload.date;
|
||||||
state.estimateTimeDate = action.payload.date;
|
state.estimateTimeDate = action.payload.date;
|
||||||
state.estimateTimeTime = action.payload.time;
|
state.estimateTimeTime = action.payload.time;
|
||||||
|
|
||||||
// Sync to localStorage
|
// Sync to localStorage
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== "undefined") {
|
||||||
localStorage.setItem(CART_STORAGE_KEYS.ESTIMATE_TIME, JSON.stringify(state.estimateTime.toISOString()));
|
localStorage.setItem(
|
||||||
localStorage.setItem(CART_STORAGE_KEYS.ESTIMATE_TIME_DATE, JSON.stringify(state.estimateTimeDate.toISOString()));
|
CART_STORAGE_KEYS.ESTIMATE_TIME,
|
||||||
localStorage.setItem(CART_STORAGE_KEYS.ESTIMATE_TIME_TIME, JSON.stringify(state.estimateTimeTime));
|
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<string>) {
|
updateCollectionMethod(state, action: PayloadAction<string>) {
|
||||||
state.collectionMethod = action.payload;
|
state.collectionMethod = action.payload;
|
||||||
|
|
||||||
// Sync to localStorage
|
// Sync to localStorage
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== "undefined") {
|
||||||
localStorage.setItem(CART_STORAGE_KEYS.COLLECTION_METHOD, JSON.stringify(state.collectionMethod));
|
localStorage.setItem(
|
||||||
|
CART_STORAGE_KEYS.COLLECTION_METHOD,
|
||||||
|
JSON.stringify(state.collectionMethod),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updatePhone(state, action: PayloadAction<string>) {
|
updatePhone(state, action: PayloadAction<string>) {
|
||||||
state.phone = action.payload;
|
state.phone = action.payload;
|
||||||
|
|
||||||
// Sync to localStorage
|
// Sync to localStorage
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== "undefined") {
|
||||||
localStorage.setItem(CART_STORAGE_KEYS.PHONE, JSON.stringify(state.phone));
|
localStorage.setItem(
|
||||||
|
CART_STORAGE_KEYS.PHONE,
|
||||||
|
JSON.stringify(state.phone),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updatePaymentMethod(state, action: PayloadAction<string>) {
|
updatePaymentMethod(state, action: PayloadAction<string>) {
|
||||||
state.paymentMethod = action.payload;
|
state.paymentMethod = action.payload;
|
||||||
|
|
||||||
// Sync to localStorage
|
// Sync to localStorage
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== "undefined") {
|
||||||
localStorage.setItem(CART_STORAGE_KEYS.PAYMENT_METHOD, JSON.stringify(state.paymentMethod));
|
localStorage.setItem(
|
||||||
|
CART_STORAGE_KEYS.PAYMENT_METHOD,
|
||||||
|
JSON.stringify(state.paymentMethod),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateOrderType(state, action: PayloadAction<string>) {
|
updateOrderType(state, action: PayloadAction<string>) {
|
||||||
state.orderType = action.payload;
|
state.orderType = action.payload;
|
||||||
|
|
||||||
// Sync to localStorage
|
// Sync to localStorage
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== "undefined") {
|
||||||
localStorage.setItem(CART_STORAGE_KEYS.ORDER_TYPE, JSON.stringify(state.orderType));
|
localStorage.setItem(
|
||||||
|
CART_STORAGE_KEYS.ORDER_TYPE,
|
||||||
|
JSON.stringify(state.orderType),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateUseLoyaltyPoints(state, action: PayloadAction<boolean>) {
|
updateUseLoyaltyPoints(state, action: PayloadAction<boolean>) {
|
||||||
state.useLoyaltyPoints = action.payload;
|
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
|
// Sync to localStorage
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== "undefined") {
|
||||||
localStorage.setItem(CART_STORAGE_KEYS.USE_LOYALTY_POINTS, JSON.stringify(state.useLoyaltyPoints));
|
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,6 +517,8 @@ export const {
|
|||||||
updatePaymentMethod,
|
updatePaymentMethod,
|
||||||
updateOrderType,
|
updateOrderType,
|
||||||
updateUseLoyaltyPoints,
|
updateUseLoyaltyPoints,
|
||||||
|
validateLoyaltyPoints,
|
||||||
|
clearLoyaltyValidationError,
|
||||||
reset,
|
reset,
|
||||||
} = orderSlice.actions;
|
} = orderSlice.actions;
|
||||||
|
|
||||||
@@ -352,10 +526,54 @@ export const {
|
|||||||
export const selectCart = (state: RootState) => state.order;
|
export const selectCart = (state: RootState) => state.order;
|
||||||
export const selectCartItems = (state: RootState) => state.order.items;
|
export const selectCartItems = (state: RootState) => state.order.items;
|
||||||
export const selectCartTotal = (state: RootState) =>
|
export const selectCartTotal = (state: RootState) =>
|
||||||
state.order.items.reduce((total, item) => total + item.price * item.quantity, 0);
|
state.order.items.reduce(
|
||||||
export const selectCartItemsQuantity = (id: number | string) => (state: RootState) => {
|
(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);
|
const item = state.order.items.find((i) => i.id === id);
|
||||||
return item ? item.quantity : 0;
|
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;
|
export default orderSlice.reducer;
|
||||||
|
|||||||
@@ -109,6 +109,8 @@ export default function YouMightAlsoLike() {
|
|||||||
image: item.image,
|
image: item.image,
|
||||||
description: item.description,
|
description: item.description,
|
||||||
variant: "None",
|
variant: "None",
|
||||||
|
isHasLoyalty: item.isHasLoyalty,
|
||||||
|
no_of_stamps_give: item.no_of_stamps_give,
|
||||||
},
|
},
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import { message } from "antd";
|
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 { useCallback } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
@@ -29,6 +34,10 @@ export default function useOrder() {
|
|||||||
orderType,
|
orderType,
|
||||||
giftDetails,
|
giftDetails,
|
||||||
} = useAppSelector(selectCart);
|
} = useAppSelector(selectCart);
|
||||||
|
const highestLoyaltyItem = useAppSelector(selectHighestPricedLoyaltyItem);
|
||||||
|
const { useLoyaltyPoints } = useAppSelector(selectCart);
|
||||||
|
|
||||||
|
const orderPrice = useAppSelector(selectCartTotalWithLoyaltyDiscount);
|
||||||
|
|
||||||
const [createOrder] = useCreateOrderMutation();
|
const [createOrder] = useCreateOrderMutation();
|
||||||
|
|
||||||
@@ -59,10 +68,8 @@ export default function useOrder() {
|
|||||||
pickup_comments: "",
|
pickup_comments: "",
|
||||||
pickup_time: estimateTime,
|
pickup_time: estimateTime,
|
||||||
delivery_pickup_interval: "",
|
delivery_pickup_interval: "",
|
||||||
orderPrice: items.reduce(
|
orderPrice: orderPrice,
|
||||||
(acc, item) => acc + item.price * item.quantity,
|
use_loylaty: useLoyaltyPoints && highestLoyaltyItem ? 1 : 0,
|
||||||
0,
|
|
||||||
),
|
|
||||||
useWallet: 0,
|
useWallet: 0,
|
||||||
tip,
|
tip,
|
||||||
...(orderType === "gift"
|
...(orderType === "gift"
|
||||||
@@ -74,7 +81,7 @@ export default function useOrder() {
|
|||||||
senderEmail: giftDetails?.senderEmail,
|
senderEmail: giftDetails?.senderEmail,
|
||||||
senderPhone: giftDetails?.senderPhone,
|
senderPhone: giftDetails?.senderPhone,
|
||||||
senderName: giftDetails?.senderName,
|
senderName: giftDetails?.senderName,
|
||||||
dineType: orderType
|
dineType: orderType,
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
})
|
})
|
||||||
@@ -120,6 +127,7 @@ export default function useOrder() {
|
|||||||
user_uuid,
|
user_uuid,
|
||||||
estimateTime,
|
estimateTime,
|
||||||
tip,
|
tip,
|
||||||
|
orderPrice,
|
||||||
t,
|
t,
|
||||||
navigate,
|
navigate,
|
||||||
dispatch,
|
dispatch,
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ export default function ProductFooter({
|
|||||||
variant: variantId,
|
variant: variantId,
|
||||||
extras: selectedExtras,
|
extras: selectedExtras,
|
||||||
extrasgroup: selectedGroups,
|
extrasgroup: selectedGroups,
|
||||||
|
isHasLoyalty: product?.isHasLoyalty,
|
||||||
|
no_of_stamps_give: product?.no_of_stamps_give,
|
||||||
},
|
},
|
||||||
quantity: quantity,
|
quantity: quantity,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -331,6 +331,8 @@ export interface CartItem {
|
|||||||
variant?: string;
|
variant?: string;
|
||||||
extras?: string[];
|
extras?: string[];
|
||||||
extrasgroup?: string[];
|
extrasgroup?: string[];
|
||||||
|
isHasLoyalty?: boolean;
|
||||||
|
no_of_stamps_give?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
|
|||||||
Reference in New Issue
Block a user