loyalty & reward: initial commit

This commit is contained in:
2026-01-13 06:24:25 +03:00
parent 69bc9d4dee
commit a06147dfa4
14 changed files with 873 additions and 25 deletions

View File

@@ -77,7 +77,7 @@
"office": "المكتب",
"booking": "الحجز",
"scheduledOrder": "الطلب المجدول",
"loyaltyAndReward": "المكافأة واللايفور"
"rewardsAndLoyalty": "المكافأة واللايفور"
},
"home": {
"title": "العنوان",
@@ -567,5 +567,30 @@
"viewAll": "عرض الكل",
"voucherCodeCopied": "تم نسخ رمز القسيمة",
"copyFailed": "فشل نسخ رمز القسيمة"
},
"rewardsAndLoyalty": {
"title": "المكافأة واللايفور",
"description": "المكافأة واللايفور",
"rewardsAndLoyalty": "المكافأة واللايفور",
"rewardsAndLoyaltyDescription": "المكافأة واللايفور",
"rewardsAndLoyaltyButton": "المكافأة واللايفور",
"rewardsAndLoyaltyButtonDescription": "المكافأة واللايفور",
"completedPurchases": "المشتريات المكتملة",
"totalPurchased": "المشتريات الكلية",
"saved": "المحفوظ",
"almosthere": "قريب جدا!",
"youreJustXCupsAwayFromYourNextReward": "أنت فقط {{cups}} أكواب بعيد عن المكافأة التالية!",
"youCanRedeemDuringTheCheckout": "يمكنك استخدام المكافأة أثناء الدفع",
"youCurrentlyHave": "لديك",
"freeItems": "عناصر مجانية",
"redeemNow": "استخدم الآن",
"yourAvailableRewards": "المكافأات المتاحة",
"loyaltyHistory": "سجل الولاء",
"loyaltyHistoryDescription": "سجل الولاء",
"earnedPoints": "حصل على {{points}} نقطة",
"order": "الطلب",
"yourOrderFrom": "طلبك من {{restaurantName}}",
"earned": "حصل على",
"xPoints": "{{points}} نقطة"
}
}

View File

@@ -77,7 +77,7 @@
"restaurantCover": "Restaurant Cover",
"restaurantLogo": "Restaurant Logo",
"scheduledOrder": "Scheduled Order",
"loyaltyAndReward": "Rewards And Loyalty"
"rewardsAndLoyalty": "Rewards And Loyalty"
},
"home": {
"title": "title",
@@ -587,5 +587,30 @@
"copyFailed": "Failed to copy voucher code",
"hiX": "Hi {{name}}!",
"youHaveReceivedAGiftCarFromX": "You have received a gift car from {{name}}!"
},
"rewardsAndLoyalty": {
"title": "Rewards And Loyalty",
"description": "Rewards And Loyalty",
"rewardsAndLoyalty": "Rewards And Loyalty",
"rewardsAndLoyaltyDescription": "Rewards And Loyalty",
"rewardsAndLoyaltyButton": "Rewards And Loyalty",
"rewardsAndLoyaltyButtonDescription": "Rewards And Loyalty",
"completedPurchases": "Completed Purchases",
"totalPurchased": "Total Purchased",
"saved": "Saved",
"almosthere": "Almost here!",
"youreJustXCupsAwayFromYourNextReward": "You're just {{cups}} cups away from your next reward!",
"youCanRedeemDuringTheCheckout": "You can redeem during the checkout",
"youCurrentlyHave": "You currently have",
"freeItems": "free items",
"redeemNow": "Redeem Now",
"yourAvailableRewards": "Your available rewards",
"loyaltyHistory": "Loyalty History",
"loyaltyHistoryDescription": "Loyalty History",
"earnedPoints": "Earned {{points}} points",
"order": "Order",
"yourOrderFrom": "Your order from {{restaurantName}}",
"earned": "Earned",
"xPoints": "{{points}} points"
}
}

View File

@@ -17,44 +17,44 @@ const BranchesIcon = ({ className, onClick }: BranchesIconType) => {
<path
d="M5.40234 3.18555C5.54014 2.593 6.04569 1.00063 7.49875 1.00063C8.95667 1.00063 9.4607 2.60361 9.59656 3.19141"
stroke="#333333"
stroke-miterlimit="10"
stroke-linecap="round"
stroke-linejoin="round"
strokeMiterlimit="10"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M12.7224 15H2.27935C1.70007 15 1.23047 14.5305 1.23047 13.9512V4.23958C1.23047 3.66035 1.70007 3.19075 2.27935 3.19075H12.7224C13.3017 3.19075 13.7713 3.66035 13.7713 4.23958V13.9512C13.7713 14.5305 13.3017 15 12.7224 15Z"
stroke="#333333"
stroke-miterlimit="10"
stroke-linecap="round"
stroke-linejoin="round"
strokeMiterlimit="10"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M10.4466 8.39983C10.7649 8.92011 10.9482 9.53128 10.9482 10.1851V10.6016H4.05078V10.1851C4.05078 8.2877 5.59476 6.74955 7.4995 6.74955C8.09085 6.74955 8.64745 6.8978 9.13388 7.15906"
stroke="#333333"
stroke-miterlimit="10"
stroke-linecap="round"
stroke-linejoin="round"
strokeMiterlimit="10"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M2.90234 10.6016H12.0989"
stroke="#333333"
stroke-miterlimit="10"
stroke-linecap="round"
stroke-linejoin="round"
strokeMiterlimit="10"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M7.5 6.75V5.94196"
stroke="#333333"
stroke-miterlimit="10"
stroke-linecap="round"
stroke-linejoin="round"
strokeMiterlimit="10"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M10.6209 12.0625H4.37929C4.18146 12.0625 4.0038 11.9419 3.93143 11.7585L3.47656 10.605H11.5236L11.0687 11.7585C10.9964 11.9419 10.8187 12.0625 10.6209 12.0625Z"
stroke="#333333"
stroke-miterlimit="10"
stroke-linecap="round"
stroke-linejoin="round"
strokeMiterlimit="10"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);

View File

@@ -0,0 +1,47 @@
interface CoinsIconType {
className?: string;
onClick?: () => void;
}
const CoinsIcon = ({ className, onClick }: CoinsIconType) => {
return (
<svg
width="18"
height="18"
viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={className}
onClick={onClick}
>
<path
d="M15.8555 9.85547V12.4269C15.8555 13.5412 13.1692 14.9983 9.85547 14.9983C6.54175 14.9983 3.85547 13.5412 3.85547 12.4269V10.284"
stroke="#FFC600"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M4.10938 10.5083C4.8508 11.4923 7.14366 12.4154 9.85737 12.4154C13.1711 12.4154 15.8574 11.0388 15.8574 9.85598C15.8574 9.1917 15.0114 8.46398 13.6837 7.95312"
stroke="#FFC600"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M13.2852 5.57031V8.14174C13.2852 9.25603 10.5989 10.7132 7.28516 10.7132C3.97144 10.7132 1.28516 9.25603 1.28516 8.14174V5.57031"
stroke="#FFC600"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M7.28516 8.12914C10.5989 8.12914 13.2852 6.75257 13.2852 5.56971C13.2852 4.38686 10.5989 3 7.28516 3C3.97144 3 1.28516 4.386 1.28516 5.56971C1.28516 6.75257 3.97144 8.12914 7.28516 8.12914Z"
stroke="#FFC600"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
);
};
export default CoinsIcon;

View File

@@ -0,0 +1,25 @@
interface CupIconType {
className?: string;
onClick?: () => void;
}
const CupIcon = ({ className, onClick }: CupIconType) => {
return (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={className}
onClick={onClick}
>
<path
d="M4.16797 2.5H16.668C17.11 2.5 17.5339 2.67559 17.8465 2.98816C18.159 3.30072 18.3346 3.72464 18.3346 4.16667V6.66667C18.3346 7.10869 18.159 7.53262 17.8465 7.84518C17.5339 8.15774 17.11 8.33333 16.668 8.33333H15.0013V10.8333C15.0013 11.7174 14.6501 12.5652 14.025 13.1904C13.3999 13.8155 12.552 14.1667 11.668 14.1667H6.66797C5.78391 14.1667 4.93607 13.8155 4.31095 13.1904C3.68582 12.5652 3.33464 11.7174 3.33464 10.8333V3.33333C3.33464 3.11232 3.42243 2.90036 3.57871 2.74408C3.73499 2.5878 3.94695 2.5 4.16797 2.5ZM15.0013 4.16667V6.66667H16.668V4.16667H15.0013ZM1.66797 15.8333H16.668V17.5H1.66797V15.8333Z"
fill="#FFB700"
/>
</svg>
);
};
export default CupIcon;

View File

@@ -0,0 +1,51 @@
interface PopularIconType {
className?: string;
onClick?: () => void;
}
const PopularIcon = ({ className, onClick }: PopularIconType) => {
return (
<svg
fill="#FFC600"
height="30"
width="30"
version="1.1"
id="Icons"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
viewBox="0 0 32 32"
xmlSpace="preserve"
className={className}
onClick={onClick}
>
<g>
<path
d="M12,17c0.8-4.2,1.9-5.3,6.1-6.1c0.5-0.1,0.8-0.5,0.8-1s-0.3-0.9-0.8-1C13.9,8.1,12.8,7,12,2.8C11.9,2.3,11.5,2,11,2
c-0.5,0-0.9,0.3-1,0.8C9.2,7,8.1,8.1,3.9,8.9C3.5,9,3.1,9.4,3.1,9.9s0.3,0.9,0.8,1c4.2,0.8,5.3,1.9,6.1,6.1c0.1,0.5,0.5,0.8,1,0.8
S11.9,17.4,12,17z"
/>
<path
d="M22,24c-2.8-0.6-3.4-1.2-4-4c-0.1-0.5-0.5-0.8-1-0.8s-0.9,0.3-1,0.8c-0.6,2.8-1.2,3.4-4,4c-0.5,0.1-0.8,0.5-0.8,1
s0.3,0.9,0.8,1c2.8,0.6,3.4,1.2,4,4c0.1,0.5,0.5,0.8,1,0.8s0.9-0.3,1-0.8c0.6-2.8,1.2-3.4,4-4c0.5-0.1,0.8-0.5,0.8-1
S22.4,24.1,22,24z"
/>
<path
d="M29.2,14c-2.2-0.4-2.7-0.9-3.1-3.1c-0.1-0.5-0.5-0.8-1-0.8c-0.5,0-0.9,0.3-1,0.8c-0.4,2.2-0.9,2.7-3.1,3.1
c-0.5,0.1-0.8,0.5-0.8,1s0.3,0.9,0.8,1c2.2,0.4,2.7,0.9,3.1,3.1c0.1,0.5,0.5,0.8,1,0.8c0.5,0,0.9-0.3,1-0.8
c0.4-2.2,0.9-2.7,3.1-3.1c0.5-0.1,0.8-0.5,0.8-1S29.7,14.1,29.2,14z"
/>
<path
d="M5.7,22.3C5.4,22,5,21.9,4.6,22.1c-0.1,0-0.2,0.1-0.3,0.2c-0.1,0.1-0.2,0.2-0.2,0.3C4,22.7,4,22.9,4,23s0,0.3,0.1,0.4
c0.1,0.1,0.1,0.2,0.2,0.3c0.1,0.1,0.2,0.2,0.3,0.2C4.7,24,4.9,24,5,24c0.1,0,0.3,0,0.4-0.1s0.2-0.1,0.3-0.2
c0.1-0.1,0.2-0.2,0.2-0.3C6,23.3,6,23.1,6,23s0-0.3-0.1-0.4C5.9,22.5,5.8,22.4,5.7,22.3z"
/>
<path
d="M28,7c0.3,0,0.5-0.1,0.7-0.3C28.9,6.5,29,6.3,29,6s-0.1-0.5-0.3-0.7c-0.1-0.1-0.2-0.2-0.3-0.2c-0.2-0.1-0.5-0.1-0.8,0
c-0.1,0-0.2,0.1-0.3,0.2C27.1,5.5,27,5.7,27,6c0,0.3,0.1,0.5,0.3,0.7C27.5,6.9,27.7,7,28,7z"
/>
</g>
</svg>
);
};
export default PopularIcon;

View File

@@ -0,0 +1,33 @@
interface RaiseIconType {
className?: string;
onClick?: () => void;
}
const RaiseIcon = ({ className, onClick }: RaiseIconType) => {
return (
<svg
width="18"
height="18"
viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={className}
onClick={onClick}
>
<path
d="M2.25 12.75L6.75 8.25L9.75 11.25L15.75 5.25"
stroke="#FFC600"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M12.75 5.25H15.75V8.25"
stroke="#FFC600"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
};
export default RaiseIcon;

View File

@@ -66,13 +66,16 @@ export default function useHeaderMenu() {
},
},
{
key: "loyalty",
key: "rewardsAndLoyalty",
icon: <LoyaltyAndRewardIcon />,
label: (
<Link to={`/${subdomain}/loyalty-and-rewards`}>
{t("common.loyaltyAndReward")}
<Link to={`/${subdomain}/rewards-and-loyalty`}>
{t("common.rewardsAndLoyalty")}
</Link>
),
onClick: () => {
navigate(`/${subdomain}/rewards-and-loyalty`);
},
},
{
key: "branches",

View File

@@ -0,0 +1,472 @@
import { Button, Card, Flex, Layout, Progress, Timeline, Tooltip } from "antd";
import ProHeader from "components/ProHeader/ProHeader";
import ProText from "components/ProText";
import { useTranslation } from "react-i18next";
import { useNavigate, useParams } from "react-router-dom";
import styles from "./rewardsAndLoyalty.module.css";
import { OrderType } from "pages/checkout/hooks/types.ts";
import { useAppSelector } from "redux/hooks";
import { selectCart } from "features/order/orderSlice";
import CoinsIcon from "components/Icons/CoinsIcon";
import ArabicPrice from "components/ArabicPrice";
import RaiseIcon from "components/Icons/RaiseIcon";
import CupIcon from "components/Icons/CupIcon";
import ProInputCard from "components/ProInputCard/ProInputCard";
import { useGetLoyaltyHistoryQuery } from "redux/api/others";
import dayjs from "dayjs";
import PopularIcon from "components/Icons/PopularIcon";
export default function RewardsAndLoyalityPage() {
const { t } = useTranslation();
const navigate = useNavigate();
const { subdomain } = useParams();
const { restaurant } = useAppSelector(selectCart);
const loyaltyStamps = restaurant?.loyalty_stamps ?? 0;
const customerLoyaltyPoints = restaurant?.customer_loyalty_points ?? 0;
const { data: loyaltyHistory } = useGetLoyaltyHistoryQuery();
console.log(loyaltyHistory);
// Calculate percentage: (customer_loyalty_points / loyalty_stamps) * 100
const progressPercent =
loyaltyStamps > 0
? Math.min((customerLoyaltyPoints / loyaltyStamps) * 100, 100)
: 0;
const handleCheckout = () => {
navigate(`/${subdomain}/menu?orderType=${OrderType.Redeem}`);
};
return (
<>
<Layout>
<ProHeader>{t("rewardsAndLoyalty.title")}</ProHeader>
<Layout.Content className={styles.redeemContainer}>
<Flex gap="small" wrap justify="center">
<Tooltip title="3 done / 3 in progress / 4 to do">
<div style={{ margin: "47px 47px 20px 47px" }}>
<Progress
percent={70}
format={() => (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
gap: 4,
}}
>
<ProText
style={{
fontWeight: 600,
fontStyle: "SemiBold",
fontSize: 48,
lineHeight: "100%",
letterSpacing: "0%",
textAlign: "center",
}}
>
{Number(customerLoyaltyPoints / loyaltyStamps).toFixed(
0,
)}
</ProText>
<ProText
style={{
fontWeight: 400,
fontStyle: "Regular",
fontSize: 16,
lineHeight: "140%",
letterSpacing: "0%",
textAlign: "center",
color: "#E8B400",
marginTop: 4,
}}
>
{t("rewardsAndLoyalty.completedPurchases")}
</ProText>
</div>
)}
strokeColor="#FFB700"
size={250}
type="circle"
/>
</div>
</Tooltip>
</Flex>
<Flex
gap="small"
wrap={false}
justify="center"
style={{ width: "100%" }}
>
<div className={styles.orderNotes}>
<div
style={{
height: 32,
width: 32,
minHeight: 32,
minWidth: 32,
backgroundColor: "var(--background)",
borderRadius: "50%",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<CoinsIcon />
</div>
<div
style={{
display: "flex",
flexDirection: "column",
gap: 4,
width: "100%",
}}
>
<ProText
style={{
fontWeight: 400,
fontStyle: "Regular",
fontSize: 12,
lineHeight: "140%",
letterSpacing: "0%",
color: "#777580",
}}
>
{t("rewardsAndLoyalty.saved")}
</ProText>
<ProText
style={{
fontWeight: 500,
fontStyle: "Medium",
fontSize: 14,
lineHeight: "140%",
letterSpacing: "0%",
color: "#333333",
}}
>
<ArabicPrice price={customerLoyaltyPoints || 0} />
</ProText>
</div>
</div>
<div className={styles.orderNotes}>
<div
style={{
height: 32,
width: 32,
minHeight: 32,
minWidth: 32,
backgroundColor: "var(--background)",
borderRadius: "50%",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<RaiseIcon />
</div>
<div
style={{
display: "flex",
flexDirection: "column",
gap: 4,
width: "100%",
}}
>
<ProText
style={{
fontWeight: 400,
fontStyle: "Regular",
fontSize: 12,
lineHeight: "140%",
letterSpacing: "0%",
color: "#777580",
}}
>
{t("rewardsAndLoyalty.totalPurchased")}
</ProText>
<ProText
style={{
fontWeight: 500,
fontStyle: "Medium",
fontSize: 14,
lineHeight: "140%",
letterSpacing: "0%",
color: "#333333",
}}
>
{loyaltyStamps}
</ProText>
</div>
</div>
</Flex>
<div className={styles.nextRewards}>
<div
style={{
height: 42,
width: 42,
minHeight: 42,
minWidth: 42,
backgroundColor: "#FFF5D4",
borderRadius: "50%",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<CupIcon />
</div>
<div
style={{
display: "flex",
flexDirection: "column",
gap: 4,
width: "100%",
}}
>
<ProText
style={{
fontWeight: 500,
fontStyle: "Medium",
fontSize: 16,
lineHeight: "140%",
letterSpacing: "0%",
}}
>
{t("rewardsAndLoyalty.almosthere")}
</ProText>
<ProText
style={{
fontWeight: 400,
fontStyle: "Regular",
fontSize: 14,
lineHeight: "140%",
letterSpacing: "0%",
color: "#777580",
}}
>
{t("rewardsAndLoyalty.youreJustXCupsAwayFromYourNextReward", {
cups: loyaltyStamps - (customerLoyaltyPoints % loyaltyStamps),
})}
</ProText>
</div>
</div>
<ProInputCard title={t("rewardsAndLoyalty.yourAvailableRewards")}>
<div
style={{
display: "flex",
flexDirection: "column",
gap: 10,
alignItems: "center",
placeItems: "center",
}}
>
<div
style={{
height: 42,
width: 42,
minHeight: 42,
minWidth: 42,
backgroundColor: "#FFF5D4",
borderRadius: "50%",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<CupIcon />
</div>
<ProText
style={{
fontWeight: 600,
fontStyle: "SemiBold",
fontSize: 18,
lineHeight: "140%",
letterSpacing: "0%",
textAlign: "center",
}}
>
{t("rewardsAndLoyalty.youCurrentlyHave")}
</ProText>
<ProText
style={{
fontWeight: 600,
fontStyle: "SemiBold",
fontSize: 20,
lineHeight: "140%",
letterSpacing: "0%",
textAlign: "center",
color: "#E8B400",
}}
>
<span style={{ padding: "0 4px" }}>
{Math.floor(customerLoyaltyPoints / loyaltyStamps)}
</span>
{t("rewardsAndLoyalty.freeItems")}
</ProText>
<ProText
style={{
fontWeight: 400,
fontStyle: "Regular",
fontSize: 12,
lineHeight: "140%",
letterSpacing: "0%",
textAlign: "center",
color: "#777580",
}}
>
{t("rewardsAndLoyalty.youCanRedeemDuringTheCheckout")}
</ProText>
<Button
type="primary"
shape="round"
className={styles.checkoutButton}
onClick={handleCheckout}
style={{
width: "100%",
height: 40,
borderRadius: 888,
backgroundColor: "#FFB700",
color: "white",
}}
>
{t("rewardsAndLoyalty.redeemNow")}
</Button>
</div>
</ProInputCard>
<ProInputCard title={t("rewardsAndLoyalty.loyaltyHistory")}>
<Timeline
items={
loyaltyHistory && loyaltyHistory.length > 0
? loyaltyHistory.map((item: any, index: number) => ({
content: (
<div key={index}>
{/* <ProText
style={{
fontWeight: 500,
fontStyle: "Medium",
fontSize: 14,
lineHeight: "140%",
letterSpacing: "0%",
color: "#333333",
}}
>
{item.restaurant_name ||
item.name ||
item.restaurantName ||
t("rewardsAndLoyalty.order")}
</ProText> */}
{(item.created_at ||
item.date ||
item.order_date ||
true) && (
<ProText
style={{
fontWeight: 600,
fontStyle: "SemiBold",
fontSize: 16,
lineHeight: "140%",
letterSpacing: "0%",
color: "#777580",
}}
>
{dayjs(
item.created_at || item.date || item.order_date,
).format("DD MMMM YYYY")}
</ProText>
)}
<div className={styles.loyaltyHistoryItem}>
<div
style={{
height: 30,
width: 20,
minHeight: 30,
minWidth: 20,
borderRadius: "50%",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<PopularIcon />
</div>
<div
style={{
display: "flex",
flexDirection: "column",
gap: 4,
width: "100%",
}}
>
<ProText
style={{
fontWeight: 500,
fontStyle: "Medium",
fontSize: 16,
lineHeight: "140%",
letterSpacing: "0%",
}}
>
{t("rewardsAndLoyalty.earned")}{" "}
<ProText
style={{
fontWeight: 500,
fontStyle: "Medium",
fontSize: 16,
lineHeight: "140%",
letterSpacing: "0%",
color: "#E8B400",
}}
>
{t("rewardsAndLoyalty.xPoints", {
points:
item.total_points ||
item.points ||
item.loyalty_stamps,
})}
</ProText>
</ProText>
<ProText
style={{
fontWeight: 400,
fontStyle: "Regular",
fontSize: 14,
lineHeight: "140%",
letterSpacing: "0%",
color: "#777580",
}}
>
{t("rewardsAndLoyalty.yourOrderFrom", {
restaurantName:
item.restaurant_name ||
item.name ||
item.restaurantName,
})}
</ProText>
</div>
</div>
</div>
),
}))
: []
}
/>
</ProInputCard>
</Layout.Content>
</Layout>
</>
);
}

View File

@@ -0,0 +1,45 @@
.redeemContainer {
display: flex;
flex-direction: column;
padding: 16px;
gap: 16px;
overflow: auto;
scrollbar-width: none;
}
.orderNotes {
gap: 20px;
border-radius: 16px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
background-color: var(--secondary-background);
padding: 16px;
width: 100%;
}
.nextRewards {
gap: 20px;
border-radius: 16px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
background-color: #fefbf2;
padding: 16px;
width: 100%;
border: 1px solid #ffedb0;
}
.loyaltyHistoryItem {
gap: 20px;
border-radius: 16px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
background-color: var(--secondary-background);
padding: 16px;
width: 100%;
}

View File

@@ -0,0 +1,94 @@
export interface RedeemResponse {
gift: Gift
working_hours: WorkingHours
notes: Notes
is_restaurant_closed: boolean
}
export interface Gift {
id: number
voucher_amount: string
amount: string
restorant_id: number
voucher_code: string
used_amount: string
show_sender_info: number
remaining_amount: string
is_used: number
sender_phone: string
gift_type: string
sender_name: string
recipient_phone: string
recipient_name: string
message: any
created_at: string
card_url: string
gr_url: string
restaurant: string
global_currency: string
lat: string
lng: string
local_currency: string
restaurant_iimage: string
order_id: number
items: Item[]
itemsImagePrefix: string
itemsImagePrefixOld: string
}
export interface Item {
id: number
is_loyalty_used: number
no_of_stamps_give: number
isHasLoyalty: boolean
is_vat_disabled: number
name: string
price: number
qty: number
variant_price: string
image: string
imageName: string
variantName: string
variantLocalName: string
extras: any[]
descriptionEN: string
descriptionAR: string
itemline: string
itemlineAR: string
itemlineAREN: string
extrasgroups: any[]
extragroupnew: any[]
itemComment: string
variant: any
itemExtras: any[]
AvaiilableVariantExtras: any[]
isPrinted: number
category_id: number
pos_order_id: string
updated_at: string
created_at: string
old_qty: number
new_qty: number
last_printed_qty: number
deleted_qty: number
discount_value: any
discount_type_id: any
original_price: number
pricing_method: string
is_already_paid: number
hash_item: string
}
export interface WorkingHours {
opening_time: string
closing_time: string
opening_time_2: any
closing_time_2: any
is_open: boolean
}
export interface Notes {
en: string
ar: string
}

View File

@@ -11,6 +11,7 @@ import {
USER_DETAILS_URL,
EGIFT_CARDS_URL,
REDEEM_DETAILS_URL,
LOYALTY_HISTORY_URL,
} from "utils/constants";
import { OrderDetails } from "pages/checkout/hooks/types";
@@ -182,6 +183,25 @@ export const branchApi = baseApi.injectEndpoints({
return response.result;
},
}),
getLoyaltyHistory: builder.query<
{
restaurant_id: number;
restaurant_name: string;
restaurant_icon: string;
total_points: string;
loyalty_stamp_image: string;
loyalty_stamps: number;
}[],
void
>({
query: () => ({
url: LOYALTY_HISTORY_URL,
method: "GET",
}),
transformResponse: (response: any) => {
return response.result.data.orders;
},
}),
}),
});
export const {
@@ -197,4 +217,5 @@ export const {
useGetUserDetailsQuery,
useGetEGiftCardsQuery,
useGetRedeemDetailsQuery,
useGetLoyaltyHistoryQuery,
} = branchApi;

View File

@@ -18,6 +18,7 @@ import OtpPage from "pages/otp/page";
import ProductDetailPage from "pages/product/page";
import RedeemPage from "pages/redeem/page";
import RestaurantPage from "pages/restaurant/page";
import RewardsAndLoyalityPage from "pages/rewardsAndLoyalty/page";
import SearchPage from "pages/search/page";
import SplitBillPage from "pages/split-bill/page";
import React, { ReactNode, Suspense, useEffect } from "react";
@@ -170,6 +171,11 @@ export const router = createHashRouter([
element: <PageWrapper children={<RedeemPage />} />,
errorElement: <ErrorPage />,
},
{
path: "rewards-and-loyalty",
element: <PageWrapper children={<RewardsAndLoyalityPage />} />,
errorElement: <ErrorPage />,
},
],
},
{

View File

@@ -110,3 +110,4 @@ export const PAYMENT_CONFIRMATION_URL = `https://menu.fascano.com/payment/confir
export const DISCOUNT_URL = `${BASE_URL}getDiscount`;
export const EGIFT_CARDS_URL = `${BASE_URL}gift/cards`;
export const REDEEM_DETAILS_URL = `${BASE_URL}gift/getGiftOrderByVoucherCode`;
export const LOYALTY_HISTORY_URL = `${BASE_URL}loyaltyHistory`;