checkout: add card deyails & card amount BT
This commit is contained in:
@@ -15,6 +15,7 @@ import SenderformationCard from "./components/SenderformationCard/Senderformatio
|
||||
import TimeEstimateCard from "pages/cart/components/timeEstimate/TimeEstimateCard";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import GiftAmountCard from "./components/GiftAmountCard/GiftAmountCard";
|
||||
import { GiftAmountBottomSheet } from "./components/GiftAmountBottomSheet";
|
||||
|
||||
export default function CardDetailsPage() {
|
||||
const { t } = useTranslation();
|
||||
@@ -22,6 +23,7 @@ export default function CardDetailsPage() {
|
||||
const { giftDetails } = useAppSelector(selectCart);
|
||||
const { isRTL } = useAppSelector((state) => state.locale);
|
||||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
const [isGiftAmountBottomSheetOpen, setIsGiftAmountBottomSheetOpen] = useState(false);
|
||||
const { subdomain } = useParams();
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -29,7 +31,7 @@ export default function CardDetailsPage() {
|
||||
useEffect(() => {
|
||||
if (cards && giftDetails?.cardId) {
|
||||
const index = cards.findIndex(
|
||||
(card) => card.id.toString() === giftDetails.cardId,
|
||||
(card) => card?.id?.toString() === giftDetails.cardId,
|
||||
);
|
||||
if (index !== -1) {
|
||||
setCurrentIndex(index);
|
||||
@@ -56,6 +58,7 @@ export default function CardDetailsPage() {
|
||||
}, [subdomain]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Layout>
|
||||
<ProHeader>{t("cardDetails.title")}</ProHeader>
|
||||
<Layout.Content className={styles.checkoutContainer}>
|
||||
@@ -131,7 +134,7 @@ export default function CardDetailsPage() {
|
||||
layout="vertical"
|
||||
style={{ display: "flex", flexDirection: "column", gap: 16 }}
|
||||
>
|
||||
{giftDetails?.giftType !== "items" && <GiftAmountCard />}
|
||||
{giftDetails?.giftType !== "items" && <GiftAmountCard onOpen={() => setIsGiftAmountBottomSheetOpen(true)} />}
|
||||
<ReceivernformationCard />
|
||||
<SenderformationCard />
|
||||
<TimeEstimateCard />
|
||||
@@ -148,5 +151,7 @@ export default function CardDetailsPage() {
|
||||
</Button>
|
||||
</Layout.Footer>
|
||||
</Layout>
|
||||
<GiftAmountBottomSheet isOpen={isGiftAmountBottomSheetOpen} onClose={() => setIsGiftAmountBottomSheetOpen(false)} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
109
src/pages/CardDetails/components/GiftAmountBottomSheet.tsx
Normal file
109
src/pages/CardDetails/components/GiftAmountBottomSheet.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
import { Button, Form } from "antd";
|
||||
import { ProBottomSheet } from "components/ProBottomSheet/ProBottomSheet.tsx";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import {
|
||||
selectCart,
|
||||
selectGrandTotal,
|
||||
updateGiftDetails,
|
||||
updateSplitBillAmount,
|
||||
} from "features/order/orderSlice";
|
||||
import { useAppDispatch, useAppSelector } from "redux/hooks";
|
||||
import ProText from "components/ProText";
|
||||
import { ProInputNumber } from "components/Inputs/ProInputNumber";
|
||||
|
||||
interface SplitBillChoiceBottomSheetProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export function GiftAmountBottomSheet({
|
||||
isOpen,
|
||||
onClose,
|
||||
}: SplitBillChoiceBottomSheetProps) {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const { giftDetails, } = useAppSelector(selectCart);
|
||||
const [amount, setAmount] = useState<string>(
|
||||
giftDetails?.amount && giftDetails?.amount > 0 ? giftDetails?.amount?.toString() : "",
|
||||
);
|
||||
|
||||
const handleSave = () => {
|
||||
const numAmount = parseFloat(amount) || 0;
|
||||
dispatch(updateGiftDetails({amount: numAmount}));
|
||||
onClose();
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<ProBottomSheet
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
title={t("cardDetails.customAmount")}
|
||||
showCloseButton={true}
|
||||
initialSnap={1}
|
||||
height={295}
|
||||
snapPoints={[295]}
|
||||
contentStyle={{
|
||||
padding: 0,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
padding: "20px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: 20,
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
|
||||
<ProText
|
||||
style={{
|
||||
fontWeight: 400,
|
||||
fontStyle: "Regular",
|
||||
fontSize: 16,
|
||||
lineHeight: "140%",
|
||||
letterSpacing: "0%",
|
||||
color:"#333333"
|
||||
}}
|
||||
>
|
||||
{t("cardDetails.enterCustomOucherAmount")}
|
||||
</ProText>
|
||||
<Form.Item name="amount">
|
||||
<ProInputNumber
|
||||
value={amount}
|
||||
onChange={(value) => {
|
||||
setAmount(value?.toString() || "");
|
||||
dispatch(updateSplitBillAmount(Number(value) || 0));
|
||||
}}
|
||||
placeholder={t("cardDetails.amount")}
|
||||
min={0}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
gap: 12,
|
||||
margin: 20,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
type="primary"
|
||||
style={{
|
||||
flex: 1,
|
||||
boxShadow: "none",
|
||||
height: 48,
|
||||
}}
|
||||
onClick={handleSave}
|
||||
disabled={!amount || parseFloat(amount) <= 0}
|
||||
>
|
||||
{t("cardDetails.add")}
|
||||
</Button>
|
||||
</div>
|
||||
</ProBottomSheet>
|
||||
);
|
||||
}
|
||||
@@ -6,11 +6,11 @@ import ArabicPrice from "components/ArabicPrice";
|
||||
import ProText from "components/ProText";
|
||||
import EditIcon from "components/Icons/EditIcon";
|
||||
|
||||
export default function GiftAmountCard() {
|
||||
export default function GiftAmountCard({ onOpen }: { onOpen: () => void }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<ProInputCard title={t("cardDetails.eGiftCardAmount")}>
|
||||
<ProInputCard title={t("cardDetails.eCardAmount")}>
|
||||
<div className={styles.customerInformationCard}>
|
||||
<Button
|
||||
style={{
|
||||
@@ -87,6 +87,7 @@ export default function GiftAmountCard() {
|
||||
}}
|
||||
icon={<EditIcon className={styles.editIcon} color="#FFF" />}
|
||||
iconPlacement="start"
|
||||
onClick={onOpen}
|
||||
>
|
||||
<ProText
|
||||
style={{
|
||||
|
||||
@@ -18,7 +18,6 @@ import CarPlateCard from "pages/cart/components/CarPlateCard.tsx";
|
||||
import CartFooter from "pages/cart/components/cartFooter/CartFooter.tsx";
|
||||
import SpecialRequestCard from "pages/cart/components/specialRequest/SpecialRequestCard.tsx";
|
||||
import TableNumberCard from "pages/cart/components/TableNumberCard.tsx";
|
||||
import TimeEstimateCard from "pages/cart/components/timeEstimate/TimeEstimateCard.tsx";
|
||||
import { OrderType } from "pages/checkout/hooks/types";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Variant } from "utils/types/appTypes";
|
||||
|
||||
@@ -1,115 +1,181 @@
|
||||
import { Checkbox, Form, Input } from "antd";
|
||||
import { Divider, Image } from "antd";
|
||||
import ProInputCard from "components/ProInputCard/ProInputCard";
|
||||
import ProPhoneInput from "components/ProPhoneInput";
|
||||
import ProText from "components/ProText";
|
||||
import {
|
||||
selectCart,
|
||||
updateGiftDetails,
|
||||
updateOrder,
|
||||
} from "features/order/orderSlice";
|
||||
import { selectCart } from "features/order/orderSlice";
|
||||
import { EGiftCard } from "pages/CardDetails/type";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useAppDispatch, useAppSelector } from "redux/hooks";
|
||||
|
||||
const { TextArea } = Input;
|
||||
import { useGetEGiftCardsQuery } from "redux/api/others";
|
||||
import { useAppSelector } from "redux/hooks";
|
||||
import styles from "./GiftDetails.module.css";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import InvoiceIcon from "components/Icons/order/InvoiceIcon";
|
||||
import BackIcon from "components/Icons/BackIcon";
|
||||
import NextIcon from "components/Icons/NextIcon";
|
||||
import { useMemo } from "react";
|
||||
import CardAmountIcon from "components/Icons/CardAmountIcon";
|
||||
import ArabicPrice from "components/ArabicPrice";
|
||||
|
||||
export function GiftCard() {
|
||||
const { t } = useTranslation();
|
||||
const { order } = useAppSelector(selectCart);
|
||||
const dispatch = useAppDispatch();
|
||||
const { giftDetails, items } = useAppSelector(selectCart);
|
||||
const { data: cards } = useGetEGiftCardsQuery();
|
||||
const currentCard = cards?.find(
|
||||
(card: EGiftCard) => card.id == giftDetails?.cardId,
|
||||
);
|
||||
const navigate = useNavigate();
|
||||
const { subdomain } = useParams();
|
||||
const { isRTL } = useAppSelector((state) => state.locale);
|
||||
const totalItems = useMemo(() => {
|
||||
return items.length || 0;
|
||||
}, [items]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProInputCard title={t("address.giftDetails")}>
|
||||
<ProPhoneInput
|
||||
propName="receiverPhone"
|
||||
label={t("address.receiverPhone")}
|
||||
value={order?.receiverPhone}
|
||||
onChange={(e) => {
|
||||
dispatch(updateGiftDetails({ receiverPhone: e }));
|
||||
}}
|
||||
/>
|
||||
<div className={styles.orderNotes}>
|
||||
<Image src={currentCard?.imageURL} height={42} width={64} style={{height: 42, width: 64}} />
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 4, width: "100%" }}>
|
||||
<ProText
|
||||
style={{
|
||||
fontWeight: 500,
|
||||
fontStyle: "Medium",
|
||||
fontSize: 14,
|
||||
lineHeight: "140%",
|
||||
letterSpacing: "0%",
|
||||
color: "#333333",
|
||||
}}
|
||||
>
|
||||
{t("checkout.giftSummary")}
|
||||
</ProText>
|
||||
<ProText
|
||||
style={{
|
||||
fontWeight: 400,
|
||||
fontStyle: "Regular",
|
||||
fontSize: 12,
|
||||
lineHeight: "140%",
|
||||
letterSpacing: "0%",
|
||||
color: "#5F6C7B",
|
||||
}}
|
||||
>
|
||||
{t("checkout.holdayGiftCard")} - {t("checkout.messageIncluded")}
|
||||
</ProText>
|
||||
<div style={{ display: "flex", flexDirection: "row", gap: 4 }}>
|
||||
<ProText
|
||||
style={{
|
||||
fontWeight: 400,
|
||||
fontStyle: "Regular",
|
||||
fontSize: 12,
|
||||
lineHeight: "140%",
|
||||
letterSpacing: "0%",
|
||||
color: "#040F35",
|
||||
}}
|
||||
>
|
||||
{t("checkout.to")} :
|
||||
</ProText>
|
||||
<ProText
|
||||
style={{
|
||||
fontWeight: 400,
|
||||
fontStyle: "Regular",
|
||||
fontSize: 12,
|
||||
lineHeight: "140%",
|
||||
letterSpacing: "0%",
|
||||
color: "#5F6C7B",
|
||||
}}
|
||||
>
|
||||
{giftDetails?.receiverName}
|
||||
</ProText>
|
||||
</div>
|
||||
</div>
|
||||
{isRTL ? <BackIcon iconSize={24} /> : <NextIcon iconSize={24} />}
|
||||
</div>
|
||||
<Divider style={{ margin: "10px 0" }} />
|
||||
|
||||
<Form.Item name="message" label={t("address.message")}>
|
||||
<TextArea
|
||||
placeholder={t("address.message")}
|
||||
size="large"
|
||||
{giftDetails?.giftType === "items" && (
|
||||
<div
|
||||
style={{
|
||||
fontSize: 14,
|
||||
borderRadius: 10,
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
rows={2}
|
||||
autoFocus={false}
|
||||
value={order?.message}
|
||||
onChange={(e) => {
|
||||
dispatch(updateGiftDetails({ message: e.target.value }));
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="senderName"
|
||||
label={t("address.senderName")}
|
||||
rules={[{ required: true, message: "" }]}
|
||||
colon={false}
|
||||
>
|
||||
<Input
|
||||
placeholder={t("address.senderName")}
|
||||
size="large"
|
||||
style={{
|
||||
fontSize: 14,
|
||||
height: 50,
|
||||
}}
|
||||
autoFocus={false}
|
||||
value={order?.senderName}
|
||||
onChange={(e) => {
|
||||
dispatch(updateGiftDetails({ senderName: e.target.value }));
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<ProPhoneInput
|
||||
propName="senderPhone"
|
||||
label={t("address.senderPhone")}
|
||||
value={order?.senderPhone}
|
||||
onChange={(e) => {
|
||||
dispatch(updateGiftDetails({ senderPhone: e }));
|
||||
}}
|
||||
/>
|
||||
|
||||
<Form.Item
|
||||
name="senderEmail"
|
||||
label={t("address.senderEmail")}
|
||||
rules={[{ required: true, message: "" }]}
|
||||
colon={false}
|
||||
>
|
||||
<Input
|
||||
placeholder={t("address.senderEmail")}
|
||||
size="large"
|
||||
style={{
|
||||
fontSize: 14,
|
||||
height: 50,
|
||||
}}
|
||||
autoFocus={false}
|
||||
value={order?.senderEmail}
|
||||
onChange={(e) => {
|
||||
dispatch(updateGiftDetails({ senderEmail: e.target.value }));
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="isSecret" colon={false}>
|
||||
<Checkbox
|
||||
style={{
|
||||
fontSize: 14,
|
||||
}}
|
||||
autoFocus={false}
|
||||
checked={order?.isSecret}
|
||||
onChange={(e) => {
|
||||
dispatch(updateGiftDetails({ isSecret: e.target.checked }));
|
||||
onClick={() => {
|
||||
navigate(`/${subdomain}/cart`);
|
||||
}}
|
||||
>
|
||||
<ProText>{t("address.keepMyNameSecret")}</ProText>
|
||||
</Checkbox>
|
||||
</Form.Item>
|
||||
<ProText
|
||||
style={{
|
||||
fontWeight: 400,
|
||||
fontStyle: "Regular",
|
||||
fontSize: 12,
|
||||
lineHeight: "140%",
|
||||
letterSpacing: "0%",
|
||||
color: "#777580",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
[isRTL ? "marginLeft" : "marginRight"]: 5,
|
||||
position: "relative",
|
||||
top: 3.5,
|
||||
}}
|
||||
>
|
||||
<InvoiceIcon />
|
||||
</span>
|
||||
{t("checkout.viewOrder")} ( {totalItems} {t("cart.items")} )
|
||||
</ProText>
|
||||
{isRTL ? <BackIcon /> : <NextIcon />}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{giftDetails?.giftType === "vouchers" && (
|
||||
<div className={styles.orderNotes}>
|
||||
<div
|
||||
style={{
|
||||
height: 42,
|
||||
width: 64,
|
||||
backgroundColor: "var(--background)",
|
||||
borderRadius: 8,
|
||||
}}
|
||||
>
|
||||
<CardAmountIcon />
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: 4,
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<ProText
|
||||
style={{
|
||||
fontWeight: 500,
|
||||
fontStyle: "Medium",
|
||||
fontSize: 14,
|
||||
lineHeight: "140%",
|
||||
letterSpacing: "0%",
|
||||
color: "#777580",
|
||||
}}
|
||||
>
|
||||
{t("checkout.giftSummary")}
|
||||
</ProText>
|
||||
|
||||
<ProText
|
||||
style={{
|
||||
fontWeight: 500,
|
||||
fontStyle: "Medium",
|
||||
fontSize: 14,
|
||||
lineHeight: "140%",
|
||||
letterSpacing: "0%",
|
||||
color: "#333333",
|
||||
}}
|
||||
>
|
||||
<ArabicPrice price={giftDetails?.amount || 0} />
|
||||
</ProText>
|
||||
</div>
|
||||
{isRTL ? <BackIcon iconSize={24} /> : <NextIcon iconSize={24} />}
|
||||
</div>
|
||||
)}
|
||||
</ProInputCard>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -46,3 +46,13 @@
|
||||
opacity: 0.15;
|
||||
}
|
||||
}
|
||||
|
||||
.orderNotes {
|
||||
gap: 20px;
|
||||
opacity: 1;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ export default function CheckoutPage() {
|
||||
<Layout.Content className={styles.checkoutContainer}>
|
||||
{(orderType === OrderType.Pickup ||
|
||||
orderType === OrderType.ScheduledOrder) && <TimeEstimateCard />}
|
||||
|
||||
{orderType === OrderType.Gift && <GiftCard />}
|
||||
<PaymentMethods />
|
||||
{!token && <CustomerInformationCard />}
|
||||
<AddressSummary />
|
||||
|
||||
@@ -5,7 +5,6 @@ import LogoContainerIcon from "components/Icons/LogoContainerIcon";
|
||||
import ImageWithFallback from "components/ImageWithFallback";
|
||||
import LoyaltyCard from "components/LoyaltyCard/LoyaltyCard";
|
||||
import ProText from "components/ProText";
|
||||
import ProTitle from "components/ProTitle";
|
||||
import { useScrollHandler } from "contexts/ScrollHandlerContext";
|
||||
import useBreakPoint from "hooks/useBreakPoint";
|
||||
import { useRestaurant } from "hooks/useRestaurant";
|
||||
|
||||
Reference in New Issue
Block a user