loyalty & reward: initial commit
This commit is contained in:
472
src/pages/rewardsAndLoyalty/page.tsx
Normal file
472
src/pages/rewardsAndLoyalty/page.tsx
Normal 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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
45
src/pages/rewardsAndLoyalty/rewardsAndLoyalty.module.css
Normal file
45
src/pages/rewardsAndLoyalty/rewardsAndLoyalty.module.css
Normal 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%;
|
||||
}
|
||||
94
src/pages/rewardsAndLoyalty/types.ts
Normal file
94
src/pages/rewardsAndLoyalty/types.ts
Normal 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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user