diff --git a/index.html b/index.html index 7fb58a2..34c12ae 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,7 @@ - + Fascano | Restaurant diff --git a/src/assets/locals/ar.json b/src/assets/locals/ar.json index 3b0ad26..f64b36b 100644 --- a/src/assets/locals/ar.json +++ b/src/assets/locals/ar.json @@ -459,5 +459,21 @@ "balance": "هدية الرصيد", "itemsAndBalance": "العناصر والرصيد", "selectGiftType": "اختر نوع الهدية" + }, + "eGiftCards": { + "title": "بطاقات الهدية", + "pickCardForYourGift": "اختر بطاقة لهديتك", + "chooseDesignToMatchTheOccasion": "اختر تصميم ليتناسب مع المناسبة. يمكنك إضافة ملاحظة لاحقاً." + }, + "cardDetails": { + "title": "تفاصيل البطاقة", + "addGiftDetails": "أضف تفاصيل الهدية", + "description": "اضف تفاصيل الهدية بالرسالة، ووقت التوصيل، وتفاصيل المستلم.", + "checkout": "الدفع", + "yourName": "اسمك", + "yourPhone": "رقم هاتفك", + "keepMyNameSecret": "الاحتفاظ باسمي مخفياً", + "receiverInformation": "تفاصيل المستلم", + "costumeAmount": "مبلغ البطاقة" } } diff --git a/src/assets/locals/en.json b/src/assets/locals/en.json index 9f56a00..625a074 100644 --- a/src/assets/locals/en.json +++ b/src/assets/locals/en.json @@ -471,5 +471,21 @@ "balance": "Gift Balance", "itemsAndBalance": "Items and Balance", "selectGiftType": "Select Gift Type" + }, + "eGiftCards": { + "title": "Gift Cards", + "pickCardForYourGift": "Pick a card for your gift", + "chooseDesignToMatchTheOccasion": "Choose a design to match the occasion. You can add a message next." + }, + "cardDetails": { + "title": "Card Details", + "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" } } diff --git a/src/components/Icons/EditIcon.tsx b/src/components/Icons/EditIcon.tsx index 6bd9304..17b8270 100644 --- a/src/components/Icons/EditIcon.tsx +++ b/src/components/Icons/EditIcon.tsx @@ -1,9 +1,10 @@ interface EditIconType { className?: string; onClick?: () => void; + color?: string; } -const EditIcon = ({ className, onClick }: EditIconType) => { +const EditIcon = ({ className, onClick, color }: EditIconType) => { return ( { diff --git a/src/features/order/orderSlice.ts b/src/features/order/orderSlice.ts index c9ab7f9..13e6272 100644 --- a/src/features/order/orderSlice.ts +++ b/src/features/order/orderSlice.ts @@ -77,7 +77,6 @@ interface CartState { visibleServices: number; fee: number; giftType: string; - } // localStorage keys @@ -475,8 +474,14 @@ const orderSlice = createSlice({ ); } }, - updateGiftDetails(state, action: PayloadAction) { - state.giftDetails = action.payload; + updateGiftDetails( + state, + action: PayloadAction | null>, + ) { + state.giftDetails = { + ...state.giftDetails, + ...action.payload, + } as GiftDetailsType; // Sync to localStorage if (typeof window !== "undefined") { diff --git a/src/pages/CardDetails/CardDetails.module.css b/src/pages/CardDetails/CardDetails.module.css new file mode 100644 index 0000000..b56afac --- /dev/null +++ b/src/pages/CardDetails/CardDetails.module.css @@ -0,0 +1,76 @@ +.checkoutContainer { + display: flex; + flex-direction: column; + padding: 16px; + gap: 16px; + overflow: auto; + scrollbar-width: none; +} + +.carouselContainer { + display: flex; + align-items: center; + justify-content: center; + gap: 16px; + width: 100%; + margin-bottom: 24px; +} + +.cardWrapper { + display: flex; + align-items: center; + justify-content: center; +} + +.cardImage { + width: 205px; + height: 134px; + object-fit: cover; + border-radius: 8px; +} + +.arrowButton { + display: flex; + align-items: center; + justify-content: center; + min-width: 40px; + height: 40px; + padding: 0; + border: none; + background: transparent; + cursor: pointer; +} + +.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; + flex-direction: row; + justify-content: space-around; + gap: 1rem; + background-color: var(--secondary-background); + box-shadow: none; + z-index: 999; +} + +/* Dark theme styles for checkout */ +:global(.darkApp) .checkoutButtonContainer { + background-color: #000000 !important; +} + +.checkoutButton { + width: 100%; + height: 48px; + margin-bottom: 16px; + box-shadow: none; +} \ No newline at end of file diff --git a/src/pages/CardDetails/CardDetails.tsx b/src/pages/CardDetails/CardDetails.tsx new file mode 100644 index 0000000..4e08db6 --- /dev/null +++ b/src/pages/CardDetails/CardDetails.tsx @@ -0,0 +1,153 @@ +import { Layout, Image, Button, Form, Skeleton } from "antd"; +import ProHeader from "components/ProHeader/ProHeader"; +import { useTranslation } from "react-i18next"; +import styles from "./CardDetails.module.css"; +import ProText from "components/ProText"; +import { useGetEGiftCardsQuery } from "redux/api/others"; +import { selectCart } from "features/order/orderSlice"; +import { useAppSelector } from "redux/hooks"; +import { useState, useEffect, useCallback } from "react"; +import BackIcon from "components/Icons/BackIcon"; +import NextIcon from "components/Icons/NextIcon"; +import cardStyles from "./CardDetails.module.css"; +import ReceivernformationCard from "./components/ReceivernformationCard/ReceivernformationCard"; +import SenderformationCard from "./components/SenderformationCard/SenderformationCard"; +import TimeEstimateCard from "pages/cart/components/timeEstimate/TimeEstimateCard"; +import { useNavigate, useParams } from "react-router-dom"; +import GiftAmountCard from "./components/GiftAmountCard/GiftAmountCard"; + +export default function CardDetailsPage() { + const { t } = useTranslation(); + const { data: cards, isLoading } = useGetEGiftCardsQuery(); + const { giftDetails } = useAppSelector(selectCart); + const { isRTL } = useAppSelector((state) => state.locale); + const [currentIndex, setCurrentIndex] = useState(0); + const { subdomain } = useParams(); + const navigate = useNavigate(); + + // Find the initial index based on selected cardId from gift details + useEffect(() => { + if (cards && giftDetails?.cardId) { + const index = cards.findIndex( + (card) => card.id.toString() === giftDetails.cardId, + ); + if (index !== -1) { + setCurrentIndex(index); + } + } + }, [cards, giftDetails?.cardId]); + + const handlePrevious = () => { + if (cards && cards.length > 0) { + setCurrentIndex((prev) => (prev === 0 ? cards.length - 1 : prev - 1)); + } + }; + + const handleNext = () => { + if (cards && cards.length > 0) { + setCurrentIndex((prev) => (prev === cards.length - 1 ? 0 : prev + 1)); + } + }; + + const currentCard = cards && cards.length > 0 ? cards[currentIndex] : null; + + const handleCheckout = useCallback(() => { + navigate(`/${subdomain}/checkout`); + }, [subdomain]); + + + return ( + + {t("cardDetails.title")} + +
+ + {t("cardDetails.addGiftDetails")} + + + {t("cardDetails.description")} + +
+ {isLoading || !currentCard ? ( +
+ +
+ +
+ +
+ ) : ( +
+
+ )} +
+ + + + + + + + + + + ); +} diff --git a/src/pages/CardDetails/components/ECardList.tsx b/src/pages/CardDetails/components/ECardList.tsx new file mode 100644 index 0000000..25db966 --- /dev/null +++ b/src/pages/CardDetails/components/ECardList.tsx @@ -0,0 +1,81 @@ +import { Card, Divider, Image } from "antd"; +import { EGiftCard } from "../type"; +import { useGetEGiftCardsQuery } from "redux/api/others"; +import LoadingSpinner from "components/LoadingSpinner/LoadingSpinner"; +import ProText from "components/ProText"; +import { useTranslation } from "react-i18next"; +import { updateGiftDetails } from "features/order/orderSlice"; +import { useAppDispatch } from "redux/hooks"; +import { useNavigate, useParams } from "react-router-dom"; + +export default function ECardList() { + const dispatch = useAppDispatch(); + const navigate = useNavigate(); + const { subdomain } = useParams(); + const { data: eGiftCards, isLoading } = useGetEGiftCardsQuery(); + const { t } = useTranslation(); + console.log(eGiftCards); + const handleCardClick = (id: number) => { + dispatch(updateGiftDetails({ cardId: id.toString() })); + navigate(`/${subdomain}/card-details`); + }; + + if (isLoading) { + return ; + } + + return ( + +
+ + {t("eGiftCards.pickCardForYourGift")} + +
+ + {t("eGiftCards.chooseDesignToMatchTheOccasion")} + + +
+ {eGiftCards?.map((card: EGiftCard) => ( + {card.image} handleCardClick(card.id)} + style={{ + width: "100%", + height: "100%", + objectFit: "cover", + borderRadius: 8, + }} + /> + ))} +
+
+ ); +} diff --git a/src/pages/CardDetails/components/GiftAmountCard/GiftAmountCard.module.css b/src/pages/CardDetails/components/GiftAmountCard/GiftAmountCard.module.css new file mode 100644 index 0000000..9850d2c --- /dev/null +++ b/src/pages/CardDetails/components/GiftAmountCard/GiftAmountCard.module.css @@ -0,0 +1,18 @@ +.customerInformationCard { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 16px; + width: 100%; +} + +.customerInformationCard > * { + min-width: 0; + width: 100%; +} + +/* Reduce gap on small screens to prevent overflow */ +@media (max-width: 480px) { + .customerInformationCard { + gap: 8px; + } +} diff --git a/src/pages/CardDetails/components/GiftAmountCard/GiftAmountCard.tsx b/src/pages/CardDetails/components/GiftAmountCard/GiftAmountCard.tsx new file mode 100644 index 0000000..ab05c55 --- /dev/null +++ b/src/pages/CardDetails/components/GiftAmountCard/GiftAmountCard.tsx @@ -0,0 +1,106 @@ +import ProInputCard from "components/ProInputCard/ProInputCard.tsx"; +import { useTranslation } from "react-i18next"; +import { Button } from "antd"; +import styles from "./GiftAmountCard.module.css"; +import ArabicPrice from "components/ArabicPrice"; +import ProText from "components/ProText"; +import EditIcon from "components/Icons/EditIcon"; + +export default function GiftAmountCard() { + const { t } = useTranslation(); + + return ( + +
+ + + +
+ +
+ ); +} diff --git a/src/pages/CardDetails/components/ReceivernformationCard/ReceivernformationCard.module.css b/src/pages/CardDetails/components/ReceivernformationCard/ReceivernformationCard.module.css new file mode 100644 index 0000000..fb5dd7d --- /dev/null +++ b/src/pages/CardDetails/components/ReceivernformationCard/ReceivernformationCard.module.css @@ -0,0 +1,5 @@ +.customerInformationCard { + display: flex; + flex-direction: column; + gap: 16px; +} diff --git a/src/pages/CardDetails/components/ReceivernformationCard/ReceivernformationCard.tsx b/src/pages/CardDetails/components/ReceivernformationCard/ReceivernformationCard.tsx new file mode 100644 index 0000000..56cdd14 --- /dev/null +++ b/src/pages/CardDetails/components/ReceivernformationCard/ReceivernformationCard.tsx @@ -0,0 +1,56 @@ +import ProInputCard from "components/ProInputCard/ProInputCard.tsx"; +import ProPhoneInput from "components/ProPhoneInput"; +import { selectCart, updateGiftDetails } from "features/order/orderSlice"; +import { useTranslation } from "react-i18next"; +import { useAppDispatch, useAppSelector } from "redux/hooks"; +import { Form, Input } from "antd"; +import styles from "./ReceivernformationCard.module.css"; +import TextArea from "antd/es/input/TextArea"; + +export default function ReceivernformationCard() { + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + const { giftDetails } = useAppSelector(selectCart); + + return ( + +
+ + { + dispatch(updateGiftDetails({ receiverName: e.target.value })); + }} + /> + + { + dispatch(updateGiftDetails({ receiverPhone: e })); + }} + /> + +