diff --git a/src/assets/locals/ar.json b/src/assets/locals/ar.json
index 24fe0d8..d4dd557 100644
--- a/src/assets/locals/ar.json
+++ b/src/assets/locals/ar.json
@@ -274,7 +274,7 @@
"paymentSummary": "ملخص الدفع",
"holdayGiftCard": "هدية العيد",
"messageIncluded": "الرسالة مضمنة",
- "save":"حفظ",
+ "save": "حفظ",
"to": "ل",
"giftSummary": "ملخص الهدية",
"customerInformation": "تفاصيل العميل",
@@ -308,8 +308,8 @@
"pickupEstimate": "تقدير الاستلام",
"today": "اليوم",
"change": "تغيير",
- "pickup":"استلام",
- "setPickupTime":"تحديد وقت الاستلام"
+ "pickup": "استلام",
+ "setPickupTime": "تحديد وقت الاستلام"
},
"address": {
"title": "العنوان",
@@ -505,7 +505,7 @@
"yourName": "اسمك",
"yourPhone": "رقم هاتفك",
"keepMyNameSecret": "الاحتفاظ باسمي مخفياً",
- "receiverInformation": "تفاصيل المستلم",
+ "receiverInformation": "تفاصيل المستلم",
"costumeAmount": "مبلغ البطاقة",
"enterCustomOucherAmount": "أدخل مبلغ البطاقة المخصص",
"amount": "المبلغ",
@@ -518,13 +518,51 @@
"senderNameRequired": "يجب أن يكون اسم المرسل مطلوب",
"receiverNameRequired": "يجب أن يكون اسم المستلم مطلوب"
},
- "car":{
- "addCar":"إضافة سيارة",
- "selectCar":"اختر السيارة",
- "addCarDetails":"إضافة تفاصيل السيارة",
- "brand":"العلامة التجارية",
- "color":"اللون",
- "category":"الفئة",
- "plateNumber":"رقم السيارة"
+ "car": {
+ "addCar": "إضافة سيارة",
+ "selectCar": "اختر السيارة",
+ "addCarDetails": "إضافة تفاصيل السيارة",
+ "brand": "العلامة التجارية",
+ "color": "اللون",
+ "category": "الفئة",
+ "plateNumber": "رقم السيارة"
+ },
+ "redeem": {
+ "title": "استخدم الهدية",
+ "redeem": "استخدم",
+ "redeemDescription": "استخدم بطاقة الهدية",
+ "redeemButton": "استخدم",
+ "redeemButtonDescription": "استخدم بطاقة الهدية",
+ "showThisCodeAtTheRestaurant": "اعرض هذا الرمز في المطعم",
+ "addGiftDetails": "اضف تفاصيل الهدية",
+ "description": "اضف تفاصيل الهدية بالرسالة، ووقت التوصيل، وتفاصيل المستلم.",
+ "checkout": "الدفع",
+ "yourName": "اسمك",
+ "yourPhone": "رقم هاتفك",
+ "keepMyNameSecret": "الاحتفاظ باسمي مخفياً",
+ "receiverInformation": "تفاصيل المستلم",
+ "costumeAmount": "مبلغ البطاقة",
+ "enterCustomOucherAmount": "أدخل مبلغ البطاقة المخصص",
+ "amount": "المبلغ",
+ "eCardAmount": "مبلغ البطاقة الإلكترونية",
+ "receiverName": "اسم المستلم",
+ "edit": "تعديل",
+ "yourInformation": "تفاصيلك",
+ "minimumAmountShouldBe1OMR": "يجب أن يكون المبلغ الأدنى 1 OMR",
+ "add": "إضافة",
+ "senderNameRequired": "يجب أن يكون اسم المرسل مطلوب",
+ "receiverNameRequired": "يجب أن يكون اسم المستلم مطلوب",
+ "includesFreeItemsInThisOrder": "يشمل العناصر المجانية في هذا الطلب",
+ "redeemGiftedItems": "استخدم الهديات",
+ "pending": "قيد المعالجة",
+ "useThisCodeIfScanningNotPossible": "استخدم هذا الرمز إذا كان من المستحيل المسح",
+ "voucherWillBeAppliedAtCheckout": "سيتم تطبيق القسيمة عند الدفع",
+ "yourGiftCardBalance": "رصيد بطاقة الهدية",
+ "redeemNow": "استخدم الآن",
+ "restaurantLocation": "موقع المطعم",
+ "voucherBalance": "رصيد القسيمة",
+ "getDirections": "الحصول على الاتجاهات",
+ "giftedItems": "العناصر المهدية",
+ "viewAll": "عرض الكل"
}
}
diff --git a/src/assets/locals/en.json b/src/assets/locals/en.json
index 4928bad..ba3e5b1 100644
--- a/src/assets/locals/en.json
+++ b/src/assets/locals/en.json
@@ -179,6 +179,11 @@
"customizable": "Customizable"
},
"cart": {
+ "remainingToPay": "Remaining to Pay",
+ "remainingVoucherAmount": "Remaining Voucher Amount",
+ "voucherApplied": "Voucher Applied",
+ "giftedItems": "Gifted Items",
+ "voucherBalance": "Voucher Balance",
"addSpecialRequestOptional": "Add Special Request (Optional)",
"continueToGiftDetails": "Continue to Gift Details",
"continueToGiftDetailsDescription": "Please fill in the details of the gift recipient and sender to continue.",
@@ -539,5 +544,43 @@
"color": "Color",
"category": "Category",
"plateNumber": "Plate Number"
+ },
+ "redeem": {
+ "title": "Redeem Gift",
+ "redeem": "Redeem",
+ "redeemDescription": "Redeem your gift card",
+ "redeemButton": "Redeem",
+ "redeemButtonDescription": "Redeem your gift card",
+ "showThisCodeAtTheRestaurant": "Show this code at the restaurant",
+ "addGiftDetails": "Add Gift Details",
+ "description": "Personalize your gift with a message, delivery timing, and recipient details.",
+ "checkout": "Checkout",
+ "yourName": "Your Name",
+ "yourPhone": "Your Phone",
+ "keepMyNameSecret": "Keep my name secret",
+ "receiverInformation": "Receiver Information",
+ "costumeAmount": "Costume amount",
+ "enterCustomOucherAmount": "Enter custom oucher amount",
+ "amount": "Amount",
+ "eCardAmount": "E-Card Amount",
+ "receiverName": "Receiver Name",
+ "edit": "Edit",
+ "yourInformation": "Your Information",
+ "minimumAmountShouldBe1OMR": "Minimum amount should be 1 OMR",
+ "add": "Add",
+ "senderNameRequired": "Sender name is required",
+ "receiverNameRequired": "Receiver name is required",
+ "includesFreeItemsInThisOrder": "Includes free items in this order",
+ "redeemGiftedItems": "Redeem gifted items",
+ "pending": "Pending",
+ "useThisCodeIfScanningNotPossible": "Use this code if scanning is not possible",
+ "voucherWillBeAppliedAtCheckout": "Voucher will be applied at checkout",
+ "yourGiftCardBalance": "Your gift card balance",
+ "redeemNow": "Redeem Now",
+ "restaurantLocation": "Restaurant Location",
+ "voucherBalance": "Voucher Balance",
+ "getDirections": "Get Directions",
+ "giftedItems": "Gifted Items",
+ "viewAll": "View All"
}
}
diff --git a/src/components/Icons/CopyIcon.tsx b/src/components/Icons/CopyIcon.tsx
new file mode 100644
index 0000000..be5ba84
--- /dev/null
+++ b/src/components/Icons/CopyIcon.tsx
@@ -0,0 +1,33 @@
+interface CopyIconType {
+ className?: string;
+ onClick?: () => void;
+}
+
+const CopyIcon = ({ className, onClick }: CopyIconType) => {
+ return (
+
+ );
+};
+
+export default CopyIcon;
diff --git a/src/components/Icons/DirectionsIcon.tsx b/src/components/Icons/DirectionsIcon.tsx
new file mode 100644
index 0000000..7400fe3
--- /dev/null
+++ b/src/components/Icons/DirectionsIcon.tsx
@@ -0,0 +1,25 @@
+interface DirectionsIconType {
+ className?: string;
+ onClick?: () => void;
+}
+
+const DirectionsIcon = ({ className, onClick }: DirectionsIconType) => {
+ return (
+
+ );
+};
+
+export default DirectionsIcon;
diff --git a/src/components/Icons/GiftIcon.tsx b/src/components/Icons/GiftIcon.tsx
new file mode 100644
index 0000000..883993b
--- /dev/null
+++ b/src/components/Icons/GiftIcon.tsx
@@ -0,0 +1,45 @@
+interface GiftIconType {
+ className?: string;
+ onClick?: () => void;
+}
+
+const GiftIcon = ({ className, onClick }: GiftIconType) => {
+ return (
+
+ );
+};
+
+export default GiftIcon;
diff --git a/src/components/OrderSummary/OrderSummary.tsx b/src/components/OrderSummary/OrderSummary.tsx
index e8c1021..dd0f362 100644
--- a/src/components/OrderSummary/OrderSummary.tsx
+++ b/src/components/OrderSummary/OrderSummary.tsx
@@ -25,7 +25,7 @@ export default function OrderSummary() {
const { useLoyaltyPoints, splitBillAmount } = useAppSelector(selectCart);
const { subdomain } = useParams();
const { data: restaurant } = useGetRestaurantDetailsQuery(subdomain);
- const { orderType } = useAppSelector(selectCart);
+ const { orderType, tip } = useAppSelector(selectCart);
const dispatch = useAppDispatch();
const subtotal = useAppSelector(selectCartTotal);
const loyaltyValidation = useAppSelector(selectLoyaltyValidation);
@@ -82,25 +82,62 @@ export default function OrderSummary() {
/>
)}
-
-
- {t("cart.discount")}
-
-
-
-
-
- {t("cart.tax")}
-
-
-
- {splitBillAmount > 0 && (
+ {orderType !== OrderType.Redeem && (
+
+
+ {t("cart.discount")}
+
+
+
+ )}
+ {orderType !== OrderType.Redeem && (
+
+
+ {t("cart.tip")}
+
+
+
+ )}
+ {orderType === OrderType.Redeem && (
+
+
+ {t("cart.giftedItems")}
+
+
+
+ )}
+ {orderType === OrderType.Redeem && (
+
+
+ {t("cart.voucherApplied")}
+
+
+
+ )}
+ {orderType !== OrderType.Redeem && (
+
+
+ {t("cart.tax")}
+
+
+
+ )}
+ {orderType !== OrderType.Redeem && splitBillAmount > 0 && (
{t("splitBill.splitBillAmount")}
@@ -124,7 +161,9 @@ export default function OrderSummary() {
color: "#333333",
}}
>
- {t("cart.totalAmount")}
+ {orderType === OrderType.Redeem
+ ? t("cart.remainingToPay")
+ : t("cart.totalAmount")}
- {isHasLoyaltyGift && restaurant?.is_loyalty_enabled === 1 && (
- <>
- {
- dispatch(updateUseLoyaltyPoints(value.target.checked));
- }}
- style={{ marginTop: 8 }}
- >
- {t("cart.useLoyaltyPoints")}
-
- >
- )}
-
- {isHasLoyaltyGift && loyaltyValidation.errorMessage && (
-
- {t(loyaltyValidation.errorMessage)}
-
- )}
+ {isHasLoyaltyGift &&
+ restaurant?.is_loyalty_enabled === 1 &&
+ orderType !== OrderType.Redeem && (
+ <>
+ {
+ dispatch(updateUseLoyaltyPoints(value.target.checked));
+ }}
+ style={{ marginTop: 8 }}
+ >
+ {t("cart.useLoyaltyPoints")}
+
+ >
+ )}
{isHasLoyaltyGift &&
+ loyaltyValidation.errorMessage &&
+ orderType !== OrderType.Redeem && (
+
+ {t(loyaltyValidation.errorMessage)}
+
+ )}
+
+ {isHasLoyaltyGift &&
+ orderType !== OrderType.Redeem &&
useLoyaltyPoints &&
highestLoyaltyItem &&
restaurant?.is_loyalty_enabled === 1 && (
diff --git a/src/features/order/orderSlice.ts b/src/features/order/orderSlice.ts
index 548f6aa..3e5fd58 100644
--- a/src/features/order/orderSlice.ts
+++ b/src/features/order/orderSlice.ts
@@ -873,7 +873,8 @@ export const selectGrandTotal = (state: RootState) => {
taxAmount -
totalDiscount +
deliveryFee -
- state.order.splitBillAmount
+ state.order.splitBillAmount -
+ Number(state.order.tip)
);
};
diff --git a/src/index.css b/src/index.css
index 469213d..1ee4509 100644
--- a/src/index.css
+++ b/src/index.css
@@ -37,6 +37,9 @@
0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
--tw-ring-offset-shadow: 0 0 rgba(0, 0, 0, 0);
--tw-ring-shadow: 0 0 rgba(0, 0, 0, 0);
+
+ --greylight-hover: rgba(234, 234, 234, 1);
+ --variable-collection-PP: rgba(255, 183, 0, 1);
}
/* Dark theme variables */
diff --git a/src/pages/cart/components/CartMobileTabletLayout.tsx b/src/pages/cart/components/CartMobileTabletLayout.tsx
index 8c5c90b..8337cf6 100644
--- a/src/pages/cart/components/CartMobileTabletLayout.tsx
+++ b/src/pages/cart/components/CartMobileTabletLayout.tsx
@@ -23,6 +23,7 @@ import { useTranslation } from "react-i18next";
import { Variant } from "utils/types/appTypes";
import DeleteIcon from "components/Icons/DeleteIcon";
import PlusIcon from "components/Icons/PlusIcon";
+import { GiftItemsCard } from "pages/redeem/components/GiftItemsCard";
interface CartMobileTabletLayoutProps {
form: FormInstance;
@@ -66,7 +67,9 @@ export default function CartMobileTabletLayout({
>
{/* Table Number */}
{(orderType === OrderType.DineIn ||
- orderType === OrderType.ToOffice) && }
+ orderType === OrderType.ToOffice) && }
+
+ {orderType === OrderType.Redeem && }
diff --git a/src/pages/cart/components/RewardWaiterCard.tsx b/src/pages/cart/components/RewardWaiterCard.tsx
index df40896..a653d75 100644
--- a/src/pages/cart/components/RewardWaiterCard.tsx
+++ b/src/pages/cart/components/RewardWaiterCard.tsx
@@ -17,7 +17,7 @@ export default function RewardWaiterCard() {
const dispatch = useAppDispatch();
const { tip } = useAppSelector(selectCart);
const { isDesktop } = useBreakPoint();
- const [selectedTip, setSelectedTip] = useState(null);
+ const [selectedTip, setSelectedTip] = useState(parseFloat(tip));
const [isTipOpen, setIsTipOpen] = useState(false);
diff --git a/src/pages/checkout/hooks/types.ts b/src/pages/checkout/hooks/types.ts
index 8876b27..25f5f18 100644
--- a/src/pages/checkout/hooks/types.ts
+++ b/src/pages/checkout/hooks/types.ts
@@ -246,5 +246,6 @@ export enum OrderType {
ToRoom = "room",
ToOffice = "office",
Booking = "booking",
- Pay = "pay"
+ Pay = "pay",
+ Redeem = "redeem"
}
diff --git a/src/pages/checkout/page.tsx b/src/pages/checkout/page.tsx
index db097c6..379cb13 100644
--- a/src/pages/checkout/page.tsx
+++ b/src/pages/checkout/page.tsx
@@ -22,17 +22,25 @@ import { useEffect } from "react";
import { CarCard } from "./components/CarCard";
import { CollectWay } from "./components/CollectWay/CollectWay";
import PickupTimeCard from "./components/pickupEstimate/TimeEstimateCard";
+import VoucherSummary from "pages/redeem/components/VoucherSummary/VoucherSummary";
export default function CheckoutPage() {
const { t } = useTranslation();
const [form] = Form.useForm();
- const { phone, order, orderType, collectionMethod, coupon, customerName } =
- useAppSelector(selectCart);
+ const {
+ phone,
+ order,
+ orderType,
+ collectionMethod,
+ coupon,
+ customerName,
+ tip,
+ } = useAppSelector(selectCart);
const { token } = useAppSelector((state) => state.auth);
const dispatch = useAppDispatch();
useEffect(() => {
- form.setFieldsValue({ coupon, collectionMethod, phone, customerName });
- }, [form, phone, coupon, collectionMethod, customerName]);
+ form.setFieldsValue({ coupon, collectionMethod, phone, customerName, tip });
+ }, [form, phone, coupon, collectionMethod, customerName, tip]);
return (
<>
@@ -71,12 +79,13 @@ export default function CheckoutPage() {
value={order?.officeNumber}
/>
)}
+ {orderType === OrderType.Redeem && }
{/* {orderType === OrderType.Gift && } */}
{/*
*/}
{/* */}
{/* */}
-
+ {orderType !== OrderType.Redeem && }
{/* Collection Method */}
{orderType === OrderType.Pickup && (
@@ -114,7 +123,7 @@ export default function CheckoutPage() {
)}
{/* Reward Your Waiter */}
-
+ {orderType !== OrderType.Redeem && }
diff --git a/src/pages/menu/menu.module.css b/src/pages/menu/menu.module.css
index d3a0465..218b970 100644
--- a/src/pages/menu/menu.module.css
+++ b/src/pages/menu/menu.module.css
@@ -483,9 +483,6 @@
position: absolute;
z-index: 999;
top: 70px;
- opacity: 0.9;
- background: #aaa8a833;
- backdrop-filter: blur(40px);
}
.backButtonContainer {
@@ -567,7 +564,7 @@
.ratingScore {
position: relative;
- top:6px;
+ top: 6px;
font-family: Roboto;
font-weight: 600;
font-style: SemiBold;
@@ -802,3 +799,48 @@
display: none !important;
}
}
+
+.frame {
+ align-items: flex-start;
+ background-color: #aaa7a733;
+ border-radius: 60px;
+ display: inline-flex;
+ flex-direction: column;
+ gap: 10px;
+ padding: 8px 10px;
+ position: relative;
+}
+
+.div {
+ align-items: center;
+ align-self: stretch;
+ display: flex;
+ flex: 0 0 auto;
+ gap: 4px;
+ position: relative;
+}
+
+.pickup {
+ color: #ffffff;
+ font-family: "Roboto-Medium", Helvetica;
+ font-size: 14px;
+ font-weight: 500;
+ letter-spacing: 0;
+ line-height: normal;
+ margin-top: -1px;
+ position: relative;
+ white-space: nowrap;
+ width: fit-content;
+}
+
+.elementMin {
+ color: var(--greylight-hover);
+ font-family: "Roboto-Regular", Helvetica;
+ font-size: 12px;
+ font-weight: 400;
+ letter-spacing: 0;
+ line-height: normal;
+ position: relative;
+ white-space: nowrap;
+ width: fit-content;
+}
diff --git a/src/pages/menu/page.tsx b/src/pages/menu/page.tsx
index 90aa3cf..07726ea 100644
--- a/src/pages/menu/page.tsx
+++ b/src/pages/menu/page.tsx
@@ -96,21 +96,31 @@ function MenuPage() {
-
diff --git a/src/pages/order/components/OrderDetails.tsx b/src/pages/order/components/OrderDetails.tsx
index 7b48191..6b73be3 100644
--- a/src/pages/order/components/OrderDetails.tsx
+++ b/src/pages/order/components/OrderDetails.tsx
@@ -3,11 +3,9 @@ import { useGetOrderDetailsQuery } from "redux/api/others";
import { useAppSelector } from "redux/hooks";
import styles from "./OrderDetails.module.css";
import ProHeader from "components/ProHeader/ProHeader";
-import { useState } from "react";
import { useTranslation } from "react-i18next";
import ProText from "components/ProText";
import useBreakPoint from "hooks/useBreakPoint";
-import ArabicPrice from "components/ArabicPrice";
import ImageWithFallback from "components/ImageWithFallback";
import { useParams } from "react-router-dom";
@@ -16,6 +14,7 @@ export default function OrderDetails() {
const { t } = useTranslation();
const { isRTL } = useAppSelector((state) => state.locale);
const { isMobile, isTablet } = useBreakPoint();
+
const { data: orderDetails } = useGetOrderDetailsQuery(
{
orderID: orderId || "",
@@ -25,6 +24,7 @@ export default function OrderDetails() {
skip: !orderId,
},
);
+
const getMenuItemImageStyle = () => {
if (isMobile) {
return {
@@ -86,7 +86,6 @@ export default function OrderDetails() {
- {item.qty}
+
+ {item.qty}{" "}
+
diff --git a/src/pages/redeem/components/GiftItemsCard.module.css b/src/pages/redeem/components/GiftItemsCard.module.css
new file mode 100644
index 0000000..b3387e0
--- /dev/null
+++ b/src/pages/redeem/components/GiftItemsCard.module.css
@@ -0,0 +1,77 @@
+.floatingContainer {
+ position: relative;
+ display: inline-block;
+ height: 150px;
+}
+
+.floatingPresent {
+ position: relative;
+ z-index: 2;
+ animation: float 3s ease-in-out infinite;
+}
+
+.floatingShadow {
+ position: absolute;
+ bottom: 10px;
+ left: 50%;
+ width: 80px;
+ height: 20px;
+ background-color: rgba(0, 0, 0, 0.2);
+ border-radius: 50%;
+ filter: blur(6px);
+ z-index: 1;
+ transform-origin: center;
+ transform: translateX(-50%);
+ animation: shadowPulse 3s ease-in-out infinite;
+}
+
+@keyframes float {
+ 0%,
+ 100% {
+ transform: translateY(0px);
+ }
+ 50% {
+ transform: translateY(-15px);
+ }
+}
+
+@keyframes shadowPulse {
+ 0%,
+ 100% {
+ transform: translateX(-50%) scale(1);
+ opacity: 0.3;
+ }
+ 50% {
+ transform: translateX(-50%) scale(0.6);
+ opacity: 0.15;
+ }
+}
+
+.orderNotes {
+ gap: 20px;
+ opacity: 1;
+ border-radius: 6px;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.menuItemImage {
+ object-fit: cover;
+ border-radius: 8px;
+ transition: transform 0.3s ease;
+ width: 90px;
+ height: 80px;
+ margin-bottom: 6px;
+}
+
+.backIcon {
+ position: relative;
+ top: 1px;
+}
+
+.nextIcon {
+ position: relative;
+ top: 1px;
+}
diff --git a/src/pages/redeem/components/GiftItemsCard.tsx b/src/pages/redeem/components/GiftItemsCard.tsx
new file mode 100644
index 0000000..044dd2f
--- /dev/null
+++ b/src/pages/redeem/components/GiftItemsCard.tsx
@@ -0,0 +1,328 @@
+import { Button, Divider, Space, Switch, Tag, Skeleton } from "antd";
+import ProInputCard from "components/ProInputCard/ProInputCard";
+import ProText from "components/ProText";
+import { useTranslation } from "react-i18next";
+import styles from "./GiftItemsCard.module.css";
+import { useParams } from "react-router-dom";
+import { useGetOrderDetailsQuery } from "redux/api/others";
+import ImageWithFallback from "components/ImageWithFallback";
+import { useAppSelector } from "redux/hooks";
+import useBreakPoint from "hooks/useBreakPoint";
+import GiftIcon from "components/Icons/GiftIcon";
+import NextIcon from "components/Icons/NextIcon";
+import BackIcon from "components/Icons/BackIcon";
+
+export function GiftItemsCard({ isCart = false }: { isCart?: boolean }) {
+ const { t } = useTranslation();
+
+ const { voucherId } = useParams();
+ const { data: orderDetails, isLoading } = useGetOrderDetailsQuery(
+ {
+ orderID: voucherId || "5711385",
+ restaurantID: localStorage.getItem("restaurantID") || "",
+ },
+ // {
+ // skip: !voucherId,
+ // },
+ );
+ const { isRTL } = useAppSelector((state) => state.locale);
+ const { isMobile, isTablet } = useBreakPoint();
+ const getMenuItemImageStyle = () => {
+ if (isMobile) {
+ return {
+ width: 72,
+ height: 72,
+ };
+ }
+ return {
+ width: 120,
+ height: 120,
+ };
+ };
+
+ return (
+ <>
+
+ {!isCart && (
+
+ {t("redeem.pending")}
+
+ )}
+ >
+ }
+ >
+ {!isCart && (
+ <>
+
+
+
+ {t("redeem.redeemGiftedItems")}
+
+
+
+ {t("redeem.includesFreeItemsInThisOrder")}
+
+
+
+
+
+
+ >
+ )}
+
+ {isLoading ? (
+ // Skeleton loading state
+ <>
+ {[1, 2].map((skeletonIndex) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {skeletonIndex !== 3 && (
+
+ )}
+
+ ))}
+ >
+ ) : (
+ orderDetails?.orderItems?.map((item: any, index: number) => (
+
+
+
+
+
+ {item.name}
+
+ {/* {isRTL
+ ? (item.variant as Variant)?.optionsAR?.[0]?.value
+ : (item.variant as Variant)?.options?.[0]?.value} */}
+
+
+
+
+ {item.name}
+
+
+
+
+
+
+
+
+
+
+
+ {index !== orderDetails?.orderItems?.length - 1 && (
+
+ )}
+
+ )) || null
+ )}
+
+ {!isCart && (
+ <>
+
+
+
+ ) : (
+
+ )
+ }
+ iconPlacement={isRTL ? "start" : "end"}
+ >
+ {t("redeem.viewAll")}
+
+ >
+ )}
+
+ >
+ );
+}
diff --git a/src/pages/redeem/components/LocationCard.module.css b/src/pages/redeem/components/LocationCard.module.css
new file mode 100644
index 0000000..2ac5ad2
--- /dev/null
+++ b/src/pages/redeem/components/LocationCard.module.css
@@ -0,0 +1,58 @@
+.floatingContainer {
+ position: relative;
+ display: inline-block;
+ height: 150px;
+}
+
+.floatingPresent {
+ position: relative;
+ z-index: 2;
+ animation: float 3s ease-in-out infinite;
+}
+
+.floatingShadow {
+ position: absolute;
+ bottom: 10px;
+ left: 50%;
+ width: 80px;
+ height: 20px;
+ background-color: rgba(0, 0, 0, 0.2);
+ border-radius: 50%;
+ filter: blur(6px);
+ z-index: 1;
+ transform-origin: center;
+ transform: translateX(-50%);
+ animation: shadowPulse 3s ease-in-out infinite;
+}
+
+@keyframes float {
+ 0%,
+ 100% {
+ transform: translateY(0px);
+ }
+ 50% {
+ transform: translateY(-15px);
+ }
+}
+
+@keyframes shadowPulse {
+ 0%,
+ 100% {
+ transform: translateX(-50%) scale(1);
+ opacity: 0.3;
+ }
+ 50% {
+ transform: translateX(-50%) scale(0.6);
+ opacity: 0.15;
+ }
+}
+
+.orderNotes {
+ gap: 20px;
+ opacity: 1;
+ border-radius: 6px;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+}
diff --git a/src/pages/redeem/components/LocationCard.tsx b/src/pages/redeem/components/LocationCard.tsx
new file mode 100644
index 0000000..a4234bb
--- /dev/null
+++ b/src/pages/redeem/components/LocationCard.tsx
@@ -0,0 +1,94 @@
+import { Divider, Tag } from "antd";
+import ProInputCard from "components/ProInputCard/ProInputCard";
+import ProText from "components/ProText";
+import { selectCart } from "features/order/orderSlice";
+import { useTranslation } from "react-i18next";
+import { useAppSelector } from "redux/hooks";
+import styles from "./LocationCard.module.css";
+import { GoogleMap } from "components/CustomBottomSheet/GoogleMap";
+import DirectionsIcon from "components/Icons/DirectionsIcon";
+
+export function LocationCard() {
+ const { t } = useTranslation();
+ const { restaurant } = useAppSelector(selectCart);
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ {restaurant.restautantName}
+
+
+
+ {restaurant.address}
+
+
+
+
+
+ {t("redeem.getDirections")}
+
+
+
+
+ >
+ );
+}
diff --git a/src/pages/redeem/components/VoucherBalanceCard.module.css b/src/pages/redeem/components/VoucherBalanceCard.module.css
new file mode 100644
index 0000000..2ac5ad2
--- /dev/null
+++ b/src/pages/redeem/components/VoucherBalanceCard.module.css
@@ -0,0 +1,58 @@
+.floatingContainer {
+ position: relative;
+ display: inline-block;
+ height: 150px;
+}
+
+.floatingPresent {
+ position: relative;
+ z-index: 2;
+ animation: float 3s ease-in-out infinite;
+}
+
+.floatingShadow {
+ position: absolute;
+ bottom: 10px;
+ left: 50%;
+ width: 80px;
+ height: 20px;
+ background-color: rgba(0, 0, 0, 0.2);
+ border-radius: 50%;
+ filter: blur(6px);
+ z-index: 1;
+ transform-origin: center;
+ transform: translateX(-50%);
+ animation: shadowPulse 3s ease-in-out infinite;
+}
+
+@keyframes float {
+ 0%,
+ 100% {
+ transform: translateY(0px);
+ }
+ 50% {
+ transform: translateY(-15px);
+ }
+}
+
+@keyframes shadowPulse {
+ 0%,
+ 100% {
+ transform: translateX(-50%) scale(1);
+ opacity: 0.3;
+ }
+ 50% {
+ transform: translateX(-50%) scale(0.6);
+ opacity: 0.15;
+ }
+}
+
+.orderNotes {
+ gap: 20px;
+ opacity: 1;
+ border-radius: 6px;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+}
diff --git a/src/pages/redeem/components/VoucherBalanceCard.tsx b/src/pages/redeem/components/VoucherBalanceCard.tsx
new file mode 100644
index 0000000..65cc299
--- /dev/null
+++ b/src/pages/redeem/components/VoucherBalanceCard.tsx
@@ -0,0 +1,124 @@
+import { Divider, Switch, Tag } from "antd";
+import ProInputCard from "components/ProInputCard/ProInputCard";
+import ProText from "components/ProText";
+import { selectCart } from "features/order/orderSlice";
+import { useTranslation } from "react-i18next";
+import { useAppSelector } from "redux/hooks";
+import styles from "./VoucherBalanceCard.module.css";
+import { useNavigate, useParams } from "react-router-dom";
+import CardAmountIcon from "components/Icons/CardAmountIcon";
+import ArabicPrice from "components/ArabicPrice";
+
+export function VoucherBalanceCard() {
+ const { t } = useTranslation();
+ const { giftDetails } = useAppSelector(selectCart);
+
+ const navigate = useNavigate();
+ const { subdomain } = useParams();
+
+ return (
+ <>
+
+
+ {t("redeem.pending")}
+
+ >
+ }
+ >
+
+
+
+
+
+
+ {t("redeem.yourGiftCardBalance")}
+
+
+
+
+
+
+
+
+
+
+ {
+ navigate(`/${subdomain}/cart`);
+ }}
+ >
+
+ {t("redeem.voucherWillBeAppliedAtCheckout")}
+
+
+
+ >
+ );
+}
diff --git a/src/pages/redeem/components/VoucherSummary/VoucherSummary.module.css b/src/pages/redeem/components/VoucherSummary/VoucherSummary.module.css
new file mode 100644
index 0000000..e5b006f
--- /dev/null
+++ b/src/pages/redeem/components/VoucherSummary/VoucherSummary.module.css
@@ -0,0 +1,101 @@
+.voucherSummary :global(.ant-card-body) {
+ padding: 16px !important;
+}
+
+.voucherSummary {
+ transition: all 0.3s ease;
+}
+
+.summaryRow {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-size: 16px;
+}
+
+.summaryDivider {
+ margin: 0 !important;
+}
+
+.totalRow {
+ font-weight: bold;
+ font-size: 16px;
+}
+
+.desktopOrderSummary {
+ background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
+ border: 1px solid rgba(0, 0, 0, 0.08);
+}
+
+.desktopSummaryRow {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 12px 0;
+ font-size: 16px;
+}
+
+.desktopTotalRow {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 16px 0;
+ margin-top: 16px;
+}
+
+/* Enhanced responsive focus states */
+.voucherSummary:focus {
+ outline: 2px solid var(--primary);
+ outline-offset: 2px;
+}
+
+/* Enhanced responsive animations */
+@keyframes fadeInUp {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+/* ===== MEDIA QUERIES ===== */
+
+/* Tablet styles (769px - 1024px) */
+@media (min-width: 769px) and (max-width: 1024px) {
+ .summaryRow {
+ font-size: 16px;
+ }
+
+ .summaryDivider {
+ margin: 10px 0 !important;
+ }
+
+ .totalRow {
+ font-size: 18px;
+ }
+}
+
+/* Desktop styles (1025px+) */
+@media (min-width: 1025px) {
+ .summaryRow {
+ font-size: 18px;
+ }
+
+ .summaryDivider {
+ margin: 10px 0 !important;
+ }
+
+ .totalRow {
+ font-size: 20px;
+ }
+}
+
+/* Focus states for larger screens */
+@media (min-width: 768px) {
+ .voucherSummary:focus {
+ outline-offset: 4px;
+ }
+}
diff --git a/src/pages/redeem/components/VoucherSummary/VoucherSummary.tsx b/src/pages/redeem/components/VoucherSummary/VoucherSummary.tsx
new file mode 100644
index 0000000..4585e56
--- /dev/null
+++ b/src/pages/redeem/components/VoucherSummary/VoucherSummary.tsx
@@ -0,0 +1,90 @@
+import { Card, Checkbox, Divider, Space } from "antd";
+import ArabicPrice from "components/ArabicPrice";
+import {
+ selectCart,
+ selectCartTotal,
+ selectGrandTotal,
+} from "features/order/orderSlice";
+import { OrderType } from "pages/checkout/hooks/types";
+import { useTranslation } from "react-i18next";
+import { useParams } from "react-router-dom";
+import { useGetRestaurantDetailsQuery } from "redux/api/others";
+import { useAppSelector } from "redux/hooks";
+import ProText from "components/ProText";
+import ProTitle from "components/ProTitle";
+import styles from "./VoucherSummary.module.css";
+import { CSSProperties } from "react";
+
+export default function VoucherSummary() {
+ const { t } = useTranslation();
+ const { subdomain } = useParams();
+ const { data: restaurant } = useGetRestaurantDetailsQuery(subdomain);
+ const subtotal = useAppSelector(selectCartTotal);
+ const grandTotal = useAppSelector(selectGrandTotal);
+
+ const titlesStyle: CSSProperties = {
+ fontWeight: 400,
+ fontStyle: "Regular",
+ fontSize: 12,
+ lineHeight: "140%",
+ letterSpacing: "0%",
+ textAlign: "center",
+ };
+
+ return (
+ <>
+
+
+ {t("cart.voucherSummary")}
+
+
+
+
+
+ {t("cart.voucherBalance")}
+
+
+
+
+
+ {t("cart.voucherApplied")}
+
+
+
+
+
+
+ {t("cart.remainingVoucherAmount")}
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/pages/redeem/page.tsx b/src/pages/redeem/page.tsx
index 8fe0e1b..ca179b5 100644
--- a/src/pages/redeem/page.tsx
+++ b/src/pages/redeem/page.tsx
@@ -1,16 +1,7 @@
-import { Button, Card, Divider, Image } from "antd";
-import Ads2 from "components/Ads/Ads2";
-import { CancelOrderBottomSheet } from "components/CustomBottomSheet/CancelOrderBottomSheet";
-import LocationIcon from "components/Icons/LocationIcon";
-import InvoiceIcon from "components/Icons/order/InvoiceIcon";
-import TimeIcon from "components/Icons/order/TimeIcon";
-import OrderDishIcon from "components/Icons/OrderDishIcon";
-import PaymentDetails from "components/PaymentDetails/PaymentDetails";
+import { Button, Card, Image, Layout, Skeleton } from "antd";
+
import ProHeader from "components/ProHeader/ProHeader";
-import ProInputCard from "components/ProInputCard/ProInputCard";
import ProText from "components/ProText";
-import ProTitle from "components/ProTitle";
-import dayjs from "dayjs";
import { useEffect, useRef } from "react";
import { useTranslation } from "react-i18next";
import { useNavigate, useParams } from "react-router-dom";
@@ -18,31 +9,30 @@ import {
useGetOrderDetailsQuery,
useGetRestaurantDetailsQuery,
} from "redux/api/others";
-import { useAppSelector } from "redux/hooks";
import styles from "./redeem.module.css";
-import BackIcon from "components/Icons/BackIcon";
-import NextIcon from "components/Icons/NextIcon";
-import { RateBottomSheet } from "components/CustomBottomSheet/RateBottomSheet";
-import Stepper from "pages/order/components/Stepper";
+import QRIcon from "components/Icons/QRIcon";
+import CopyIcon from "components/Icons/CopyIcon";
+import { useAppSelector } from "redux/hooks";
+import { LocationCard } from "./components/LocationCard.tsx";
+import { GiftItemsCard } from "./components/GiftItemsCard.tsx";
+import { VoucherBalanceCard } from "./components/VoucherBalanceCard.tsx";
+import { OrderType } from "pages/checkout/hooks/types.ts";
export default function RedeemPage() {
const { t } = useTranslation();
- const { orderId } = useParams();
- const { isRTL } = useAppSelector((state) => state.locale);
+ const { voucherId } = useParams();
const { restaurant } = useAppSelector((state) => state.order);
- const navigate = useNavigate();
const hasRefetchedRef = useRef(false);
+ const navigate = useNavigate();
+ const { subdomain } = useParams();
- const { data: orderDetails } = useGetOrderDetailsQuery(
+ const { data: orderDetails, isLoading } = useGetOrderDetailsQuery(
{
- orderID: orderId || "",
+ orderID: voucherId || "",
restaurantID: localStorage.getItem("restaurantID") || "",
},
{
- skip: !orderId,
- // return it t0 60000 after finish testing
- pollingInterval: 10000,
- refetchOnMountOrArgChange: true,
+ skip: !voucherId,
},
);
@@ -58,7 +48,7 @@ export default function RedeemPage() {
// Reset refetch flag when orderId changes
useEffect(() => {
hasRefetchedRef.current = false;
- }, [orderId]);
+ }, [voucherId]);
// Refetch restaurant details when order status has alias "closed"
useEffect(() => {
@@ -74,190 +64,225 @@ export default function RedeemPage() {
}
}, [orderDetails?.status, restaurantSubdomain, refetchRestaurantDetails]);
+ const handleCheckout = () => {
+ navigate(`/${subdomain}/menu?orderType=${OrderType.Redeem}`);
+ };
+
return (
<>
- {t("order.title")}
-
-
+
+ {t("redeem.title")}
+
-
-
-
- {t("order.yourOrderFromFascanoRestaurant")}
-
-
-
- {" "}
- {isRTL ? orderDetails?.restaurantAR : orderDetails?.restaurant}
-
-
+ {t("redeem.addGiftDetails")}
+
+
+ {t("redeem.description")}
+
-
+ {isLoading || !orderDetails?.restaurant_iimage ? (
+
+ ) : (
+
+ )}
-
-
+
+ {t("redeem.description")}
+
+
- {t("order.inProgressOrder")} (1)
-
-
-
-
- #{orderDetails?.order.id}
-
-
-
- ordered :- Today -{" "}
- {dayjs(orderDetails?.status[0]?.pivot?.created_at).format(
- "h:mm A",
- )}
-
-
-
-
-
-
+ {t("redeem.addGiftDetails")}
+
-
-
-
-
-
- {t("order.yourOrderFrom")}
-
-
-
- {" "}
- {t("order.muscat")}
-
-
- }
- >
-
- {orderDetails?.orderItems.map((item, index) => (
-
- ))}
-
-
-
-
-
- navigate(`/${restaurant?.subdomain}`)}
- >
-
-
-
-
- {isRTL ? restaurant?.nameAR : restaurant?.restautantName}
-
+ {t("redeem.showThisCodeAtTheRestaurant")}
+
+
+
+
}
+ iconPlacement="end"
+ >
+ GFT - 92KD - 7X84
+
+
+ {t("redeem.useThisCodeIfScanningNotPossible")}
+
+
+
- {isRTL ? (
-
- ) : (
-
- )}
+
+
+ Active - Expires in 12 days
+
-
- {/*
*/}
+
+
+
+
+
+
-
-
+
+
+
+
>
);
}
diff --git a/src/pages/redeem/redeem.module.css b/src/pages/redeem/redeem.module.css
index 78b105e..d715277 100644
--- a/src/pages/redeem/redeem.module.css
+++ b/src/pages/redeem/redeem.module.css
@@ -1,247 +1,81 @@
-.orderSummary :global(.ant-card-body) {
- padding: 16px !important;
-}
-
-.profileImage {
- border-radius: 50%;
- width: 50px;
- height: 50px;
-}
-
-/* Enhanced responsive order summary */
-@media (min-width: 769px) and (max-width: 1024px) {
- .orderSummary {
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
- }
-}
-
- .fascanoIcon {
- position: relative;
- top: 3px;
-}
-
-.locationIcon {
- position: relative;
- top: 3px;
-}
-
-.orderDishIcon {
+.carouselContainer {
display: flex;
- justify-content: center;
align-items: center;
+ justify-content: center;
+ gap: 16px;
width: 100%;
}
-.orderCard :global(.ant-card-body) {
+.cardWrapper {
display: flex;
- flex-direction: column;
- justify-content: flex-start;
-}
-
-.orderCard :global(.ant-card-body) > *:not(:last-child) {
- margin-bottom: 3.5rem;
-}
-
-.orderSummary {
- transition: all 0.3s ease;
-}
-
-.invoiceIcon {
- position: relative;
- top: 3px;
-}
-
-.timeIcon {
- position: relative;
- top: 3px;
-}
-
-/* Enhanced responsive order summary */
-@media (min-width: 769px) and (max-width: 1024px) {
- .orderSummary {
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
- }
-}
-
-.summaryRow {
- display: flex;
- justify-content: space-between;
align-items: center;
- font-size: 16px;
+ justify-content: center;
}
-/* Enhanced responsive summary rows */
-@media (min-width: 769px) and (max-width: 1024px) {
- .summaryRow {
- padding: 12px 0;
- font-size: 16px;
- }
+.cardImage {
+ width: 205px;
+ height: 134px;
+ object-fit: cover;
+ border-radius: 8px;
}
-@media (min-width: 1025px) {
- .summaryRow {
- padding: 16px 0;
- font-size: 18px;
- }
-}
-
-.summaryDivider {
- margin: 8px 0 !important;
-}
-
-/* Enhanced responsive summary divider */
-@media (min-width: 769px) and (max-width: 1024px) {
- .summaryDivider {
- margin: 20px 0 !important;
- }
-}
-
-@media (min-width: 1025px) {
- .summaryDivider {
- margin: 24px 0 !important;
- }
-}
-
-.totalRow {
- font-weight: bold;
- font-size: 16px;
-}
-
-/* Enhanced responsive total row */
-@media (min-width: 769px) and (max-width: 1024px) {
- .totalRow {
- font-size: 18px;
- padding-top: 20px;
- margin-top: 12px;
- }
-}
-
-@media (min-width: 1025px) {
- .totalRow {
- font-size: 20px;
- padding-top: 24px;
- margin-top: 16px;
- }
-}
-
-.desktopOrderSummary {
- background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
- border: 1px solid rgba(0, 0, 0, 0.08);
-}
-
-.desktopSummaryRow {
+.arrowButton {
display: flex;
- justify-content: space-between;
align-items: center;
- padding: 12px 0;
- font-size: 16px;
+ justify-content: center;
+ min-width: 40px;
+ height: 40px;
+ padding: 0;
+ border: none;
+ background: transparent;
+ cursor: pointer;
}
-.desktopTotalRow {
+.arrowButton:hover {
+ background: rgba(0, 0, 0, 0.04);
+ border-radius: 50%;
+}
+
+/* CheckoutButton Styles */
+.checkoutButtonContainer {
+ width: 100%;
+ padding: 16px 16px 0;
+ position: sticky;
+ bottom: 0;
+ left: 0;
+ height: 80px;
display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 16px 0;
- margin-top: 16px;
+ flex-direction: row;
+ justify-content: space-around;
+ gap: 1rem;
+ background-color: var(--secondary-background);
+ box-shadow: none;
+ z-index: 999;
}
-[data-theme="dark"] .orderSummary {
- background-color: #181818 !important;
- border-color: #363636 !important;
+/* Dark theme styles for checkout */
+:global(.darkApp) .checkoutButtonContainer {
+ background-color: #000000 !important;
}
-[data-theme="dark"] .orderSummary:hover {
- background-color: #363636 !important;
- border-color: #424242 !important;
-}
-
-[data-theme="dark"] .summaryRow {
- color: #b0b0b0;
-}
-
-[data-theme="dark"] .totalRow {
- color: #ffffff;
- border-top-color: #424242;
-}
-
-/* Enhanced responsive animations */
-@media (prefers-reduced-motion: no-preference) {
- .orderSummary {
- animation: fadeInUp 0.8s ease-out;
- }
-}
-
-@keyframes fadeInUp {
- from {
- opacity: 0;
- transform: translateY(20px);
- }
- to {
- opacity: 1;
- transform: translateY(0);
- }
-}
-
-/* Enhanced responsive focus states */
-.orderSummary:focus {
- outline: 2px solid var(--primary);
- outline-offset: 2px;
-}
-
-@media (min-width: 768px) {
- .orderSummary:focus {
- outline-offset: 4px;
- }
-}
-
-/* Enhanced responsive print styles */
-@media print {
- .orderSummary {
- box-shadow: none !important;
- border: 1px solid #ccc !important;
- }
-}
-
-/* Enhanced responsive hover effects */
-@media (hover: hover) {
- .orderSummary:hover {
- transform: translateY(-2px);
- }
-
- .menuItemImage:hover {
- transform: scale(1.05);
- }
-
- [data-theme="dark"] .orderSummary:hover {
- box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
- }
-}
-
-.backToHomePage {
+.checkoutButton {
width: 100%;
height: 48px;
+ margin-bottom: 16px;
+ box-shadow: none;
+}
+
+.copyIcon {
+ cursor: pointer;
+ position: relative;
+ top: 3px;
+}
+
+.redeemContainer {
display: flex;
- justify-content: flex-start;
- padding: 12px 18px !important;
- row-gap: 10px;
- transition: all 0.3s ease;
- border-radius: 50px;
+ flex-direction: column;
+ padding: 16px;
+ gap: 16px;
+ overflow: auto;
+ scrollbar-width: none;
}
-
-.backToHomePage :global(.ant-card-body) {
- padding: 0px !important;
- text-align: start;
- width: 100%;
-}
-
-.nextIcon {
- width: 24px;
- height: 24px;
-}
-
-.backIcon {
- width: 24px;
- height: 24px;
-}
-
-
diff --git a/src/routes/routes.tsx b/src/routes/routes.tsx
index 793c892..533dbf4 100644
--- a/src/routes/routes.tsx
+++ b/src/routes/routes.tsx
@@ -15,7 +15,6 @@ import OrderDetails from "pages/order/components/OrderDetails";
import OrderPage from "pages/order/page";
import OrdersPage from "pages/orders/page";
import OtpPage from "pages/otp/page";
-import PayPage from "pages/pay/page";
import ProductDetailPage from "pages/product/page";
import RedeemPage from "pages/redeem/page";
import RestaurantPage from "pages/restaurant/page";