enhance E-Amount card

This commit is contained in:
2026-01-01 14:52:40 +03:00
parent 5e209d5d78
commit 527f4686de
6 changed files with 206 additions and 113 deletions

View File

@@ -491,6 +491,8 @@
"eCardAmount": "مبلغ البطاقة الإلكترونية", "eCardAmount": "مبلغ البطاقة الإلكترونية",
"receiverName": "اسم المستلم", "receiverName": "اسم المستلم",
"edit": "تعديل", "edit": "تعديل",
"yourInformation": "تفاصيلك" "yourInformation": "تفاصيلك",
"minimumAmountShouldBe1OMR": "يجب أن يكون المبلغ الأدنى 1 OMR",
"add": "أضف"
} }
} }

View File

@@ -503,6 +503,8 @@
"eCardAmount": "E-Card Amount", "eCardAmount": "E-Card Amount",
"receiverName": "Receiver Name", "receiverName": "Receiver Name",
"edit": "Edit", "edit": "Edit",
"yourInformation": "Your Information" "yourInformation": "Your Information",
"minimumAmountShouldBe1OMR": "Minimum amount should be 1 OMR",
"add": "Add"
} }
} }

View File

@@ -23,7 +23,8 @@ export default function CardDetailsPage() {
const { giftDetails } = useAppSelector(selectCart); const { giftDetails } = useAppSelector(selectCart);
const { isRTL } = useAppSelector((state) => state.locale); const { isRTL } = useAppSelector((state) => state.locale);
const [currentIndex, setCurrentIndex] = useState(0); const [currentIndex, setCurrentIndex] = useState(0);
const [isGiftAmountBottomSheetOpen, setIsGiftAmountBottomSheetOpen] = useState(false); const [isGiftAmountBottomSheetOpen, setIsGiftAmountBottomSheetOpen] =
useState(false);
const { subdomain } = useParams(); const { subdomain } = useParams();
const navigate = useNavigate(); const navigate = useNavigate();
@@ -31,7 +32,7 @@ export default function CardDetailsPage() {
useEffect(() => { useEffect(() => {
if (cards && giftDetails?.cardId) { if (cards && giftDetails?.cardId) {
const index = cards.findIndex( const index = cards.findIndex(
(card) => card?.id?.toString() === giftDetails.cardId, (card) => card?.id?.toString() === giftDetails.cardId.toString(),
); );
if (index !== -1) { if (index !== -1) {
setCurrentIndex(index); setCurrentIndex(index);
@@ -59,99 +60,110 @@ export default function CardDetailsPage() {
return ( return (
<> <>
<Layout> <Layout>
<ProHeader>{t("cardDetails.title")}</ProHeader> <ProHeader>{t("cardDetails.title")}</ProHeader>
<Layout.Content className={styles.checkoutContainer}> <Layout.Content className={styles.checkoutContainer}>
<div <div
style={{ style={{
textAlign: "center", textAlign: "center",
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
margin: "50px 28px 35px 28px", margin: "50px 28px 35px 28px",
gap: 8, gap: 8,
}} }}
> >
<ProText style={{ fontSize: 16, fontWeight: 600, color: "#333333" }}> <ProText
{t("cardDetails.addGiftDetails")} style={{ fontSize: 16, fontWeight: 600, color: "#333333" }}
</ProText> >
<ProText style={{ fontSize: 14, fontWeight: 400, color: "#95949C" }}> {t("cardDetails.addGiftDetails")}
{t("cardDetails.description")} </ProText>
</ProText> <ProText
</div> style={{ fontSize: 14, fontWeight: 400, color: "#95949C" }}
{isLoading || !currentCard ? ( >
<div className={cardStyles.carouselContainer}> {t("cardDetails.description")}
<Skeleton.Avatar </ProText>
active </div>
size={40} {isLoading || !currentCard ? (
shape="circle" <div className={cardStyles.carouselContainer}>
style={{ flexShrink: 0 }} <Skeleton.Avatar
/>
<div className={cardStyles.cardWrapper}>
<Skeleton.Image
active active
style={{ size={40}
width: 205, shape="circle"
height: 134, style={{ flexShrink: 0 }}
borderRadius: 8, />
}} <div className={cardStyles.cardWrapper}>
<Skeleton.Image
active
style={{
width: 205,
height: 134,
borderRadius: 8,
}}
/>
</div>
<Skeleton.Avatar
active
size={40}
shape="circle"
style={{ flexShrink: 0 }}
/> />
</div> </div>
<Skeleton.Avatar ) : (
active <div className={cardStyles.carouselContainer}>
size={40} <Button
shape="circle" type="text"
style={{ flexShrink: 0 }} className={cardStyles.arrowButton}
/> onClick={isRTL ? handleNext : handlePrevious}
</div> icon={<BackIcon iconSize={24} />}
) : ( />
<div className={cardStyles.carouselContainer}> <div className={cardStyles.cardWrapper}>
<Button <Image
type="text" src={currentCard.imageURL}
className={cardStyles.arrowButton} alt={currentCard.image}
onClick={isRTL ? handleNext : handlePrevious} width={205}
icon={<BackIcon iconSize={24} />} height={134}
/> className={cardStyles.cardImage}
<div className={cardStyles.cardWrapper}> />
<Image </div>
src={currentCard.imageURL} <Button
alt={currentCard.image} type="text"
width={205} className={cardStyles.arrowButton}
height={134} onClick={isRTL ? handlePrevious : handleNext}
className={cardStyles.cardImage} icon={<NextIcon iconSize={24} />}
/> />
</div> </div>
<Button )}
type="text" <Form
className={cardStyles.arrowButton} layout="vertical"
onClick={isRTL ? handlePrevious : handleNext} style={{ display: "flex", flexDirection: "column", gap: 16 }}
icon={<NextIcon iconSize={24} />} >
/> {giftDetails?.giftType !== "items" && (
</div> <GiftAmountCard
)} onOpen={() => setIsGiftAmountBottomSheetOpen(true)}
<Form />
layout="vertical" )}
style={{ display: "flex", flexDirection: "column", gap: 16 }} <ReceivernformationCard />
> <SenderformationCard />
{giftDetails?.giftType !== "items" && <GiftAmountCard onOpen={() => setIsGiftAmountBottomSheetOpen(true)} />} <TimeEstimateCard />
<ReceivernformationCard /> </Form>
<SenderformationCard /> </Layout.Content>
<TimeEstimateCard /> <Layout.Footer className={styles.checkoutButtonContainer}>
</Form> <Button
</Layout.Content> type="primary"
<Layout.Footer className={styles.checkoutButtonContainer}> shape="round"
<Button className={styles.checkoutButton}
type="primary" onClick={handleCheckout}
shape="round" >
className={styles.checkoutButton} {t("cardDetails.checkout")}
onClick={handleCheckout} </Button>
> </Layout.Footer>
{t("cardDetails.checkout")} </Layout>
</Button> <GiftAmountBottomSheet
</Layout.Footer> isOpen={isGiftAmountBottomSheetOpen}
</Layout> onClose={() => setIsGiftAmountBottomSheetOpen(false)}
<GiftAmountBottomSheet isOpen={isGiftAmountBottomSheetOpen} onClose={() => setIsGiftAmountBottomSheetOpen(false)} /> />
</> </>
); );
} }

View File

@@ -24,18 +24,19 @@ export function GiftAmountBottomSheet({
}: SplitBillChoiceBottomSheetProps) { }: SplitBillChoiceBottomSheetProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { giftDetails, } = useAppSelector(selectCart); const { giftDetails } = useAppSelector(selectCart);
const [amount, setAmount] = useState<string>( const [amount, setAmount] = useState<string>(
giftDetails?.amount && giftDetails?.amount > 0 ? giftDetails?.amount?.toString() : "", giftDetails?.amount && giftDetails?.amount > 0
? giftDetails?.amount?.toString()
: "",
); );
const handleSave = () => { const handleSave = () => {
const numAmount = parseFloat(amount) || 0; const numAmount = parseFloat(amount) || 0;
dispatch(updateGiftDetails({amount: numAmount})); dispatch(updateGiftDetails({ amount: numAmount }));
onClose(); onClose();
}; };
return ( return (
<ProBottomSheet <ProBottomSheet
isOpen={isOpen} isOpen={isOpen}
@@ -51,13 +52,12 @@ export function GiftAmountBottomSheet({
> >
<div <div
style={{ style={{
padding: "20px", padding: "20px 20px 0 20px",
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
gap: 20,
}} }}
> >
<div style={{ display: "flex", flexDirection: "column", gap: 5 }}> <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
<ProText <ProText
style={{ style={{
fontWeight: 400, fontWeight: 400,
@@ -65,7 +65,7 @@ export function GiftAmountBottomSheet({
fontSize: 16, fontSize: 16,
lineHeight: "140%", lineHeight: "140%",
letterSpacing: "0%", letterSpacing: "0%",
color:"#333333" color: "#333333",
}} }}
> >
{t("cardDetails.enterCustomOucherAmount")} {t("cardDetails.enterCustomOucherAmount")}
@@ -81,6 +81,18 @@ export function GiftAmountBottomSheet({
min={0} min={0}
/> />
</Form.Item> </Form.Item>
<ProText
style={{
fontWeight: 500,
fontStyle: "Medium",
fontSize: 12,
lineHeight: "140%",
letterSpacing: "0%",
color: "#B1B0B6",
}}
>
{t("cardDetails.minimumAmountShouldBe1OMR")}
</ProText>
</div> </div>
</div> </div>

View File

@@ -16,3 +16,9 @@
gap: 8px; gap: 8px;
} }
} }
.editIcon {
position: absolute;
top: -7px;
right: 5px;
}

View File

@@ -5,23 +5,52 @@ import styles from "./GiftAmountCard.module.css";
import ArabicPrice from "components/ArabicPrice"; import ArabicPrice from "components/ArabicPrice";
import ProText from "components/ProText"; import ProText from "components/ProText";
import EditIcon from "components/Icons/EditIcon"; import EditIcon from "components/Icons/EditIcon";
import { useAppDispatch, useAppSelector } from "redux/hooks";
import { updateGiftDetails } from "features/order/orderSlice";
import { useState, useEffect, useMemo } from "react";
export default function GiftAmountCard({ onOpen }: { onOpen: () => void }) { export default function GiftAmountCard({ onOpen }: { onOpen: () => void }) {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch();
const { giftDetails } = useAppSelector((state) => state.order);
const [selectedAmount, setSelectedAmount] = useState<number | null>(null);
useEffect(() => {
const amount = giftDetails?.amount;
if (amount === 10 || amount === 20 || amount === 30) {
setSelectedAmount(amount);
} else {
setSelectedAmount(null);
}
}, [giftDetails?.amount]);
const handleAmountClick = (amount: number) => {
setSelectedAmount(amount);
dispatch(updateGiftDetails({ amount }));
};
const isDefaultAmount = useMemo(() => {
const amount = giftDetails?.amount;
return amount === 10 || amount === 20 || amount === 30;
}, [giftDetails?.amount]);
return ( return (
<ProInputCard title={t("cardDetails.eCardAmount")}> <ProInputCard title={t("cardDetails.eCardAmount")}>
<div className={styles.customerInformationCard}> <div className={styles.customerInformationCard}>
<Button <Button
onClick={() => handleAmountClick(10)}
style={{ style={{
borderRadius: 100, borderRadius: 100,
height: 40, height: 40,
border: "none", border: "none",
backgroundColor: "#5F6C7B0D", backgroundColor:
selectedAmount === 10 && isDefaultAmount
? "#FFB7001F"
: "#5F6C7B0D",
}} }}
> >
<ArabicPrice <ArabicPrice
price={10.00} price={10.0}
textStyle={{ textStyle={{
fontWeight: 500, fontWeight: 500,
fontStyle: "Medium", fontStyle: "Medium",
@@ -29,20 +58,27 @@ export default function GiftAmountCard({ onOpen }: { onOpen: () => void }) {
lineHeight: "140%", lineHeight: "140%",
letterSpacing: "0%", letterSpacing: "0%",
textAlign: "center", textAlign: "center",
color: "#5F6C7B", color:
selectedAmount === 10 && isDefaultAmount
? "#CC9300"
: "#5F6C7B",
}} }}
/> />
</Button> </Button>
<Button <Button
onClick={() => handleAmountClick(20)}
style={{ style={{
borderRadius: 100, borderRadius: 100,
height: 40, height: 40,
border: "none", border: "none",
backgroundColor: "#5F6C7B0D", backgroundColor:
selectedAmount === 20 && isDefaultAmount
? "#FFB7001F"
: "#5F6C7B0D",
}} }}
> >
<ArabicPrice <ArabicPrice
price={20.00} price={20.0}
textStyle={{ textStyle={{
fontWeight: 500, fontWeight: 500,
fontStyle: "Medium", fontStyle: "Medium",
@@ -50,20 +86,27 @@ export default function GiftAmountCard({ onOpen }: { onOpen: () => void }) {
lineHeight: "140%", lineHeight: "140%",
letterSpacing: "0%", letterSpacing: "0%",
textAlign: "center", textAlign: "center",
color: "#5F6C7B", color:
selectedAmount === 20 && isDefaultAmount
? "#CC9300"
: "#5F6C7B",
}} }}
/> />
</Button> </Button>
<Button <Button
onClick={() => handleAmountClick(30)}
style={{ style={{
borderRadius: 100, borderRadius: 100,
height: 40, height: 40,
border: "none", border: "none",
backgroundColor: "#5F6C7B0D", backgroundColor:
selectedAmount === 30 && isDefaultAmount
? "#FFB7001F"
: "#5F6C7B0D",
}} }}
> >
<ArabicPrice <ArabicPrice
price={30.00} price={30.0}
textStyle={{ textStyle={{
fontWeight: 500, fontWeight: 500,
fontStyle: "Medium", fontStyle: "Medium",
@@ -71,7 +114,10 @@ export default function GiftAmountCard({ onOpen }: { onOpen: () => void }) {
lineHeight: "140%", lineHeight: "140%",
letterSpacing: "0%", letterSpacing: "0%",
textAlign: "center", textAlign: "center",
color: "#5F6C7B", color:
selectedAmount === 30 && isDefaultAmount
? "#CC9300"
: "#5F6C7B",
}} }}
/> />
</Button> </Button>
@@ -83,23 +129,36 @@ export default function GiftAmountCard({ onOpen }: { onOpen: () => void }) {
width: "100%", width: "100%",
height: 40, height: 40,
border: "none", border: "none",
backgroundColor: "#030014", backgroundColor:
giftDetails?.amount && !isDefaultAmount ? "#FFB7001F" : "#030014",
}} }}
icon={<EditIcon className={styles.editIcon} color="#FFF" />} icon={
giftDetails?.amount && !isDefaultAmount ? (
<EditIcon
className={styles.editIcon}
color={
giftDetails?.amount && !isDefaultAmount ? "#CC9300" : "#FFF"
}
/>
) : undefined
}
iconPlacement="start" iconPlacement="start"
onClick={onOpen} onClick={onOpen}
> >
<ProText <ProText
style={{ style={{
color: "#FFF", color: giftDetails?.amount && !isDefaultAmount ? "#CC9300" : "#FFF",
fontWeight: 500, fontWeight: 500,
fontStyle: "Medium", fontStyle: "Medium",
fontSize: 14, fontSize: 14,
lineHeight: "140%", lineHeight: "140%",
letterSpacing: "0%", letterSpacing: "0%",
textAlign: "center",
}} }}
> >
{t("cardDetails.costumeAmount")} {giftDetails?.amount && !isDefaultAmount
? giftDetails?.amount
: t("cardDetails.costumeAmount")}
</ProText> </ProText>
</Button> </Button>
</ProInputCard> </ProInputCard>