// File: src/actions/getAddresses.ts import axios from "axios"; import { Branch, Emirate } from "../store/userStore"; export async function getAddresses() { try { const response = await axios.get("/user/addresses"); const data = response.data; return data.data as Address[]; } catch (error) { console.error(error); } } export interface Address { id: number; name: string; receiverName: string | null; address: string; mobile: string | null; streetName: string; apartmentNum: string; lat: number; lng: number; buildingNumber: string; addressType: "home" | "work" | "other"; notes: string | null; isGift: boolean; isDefault: boolean; region: { id: number; name: string; branch: Branch; }; emirate: Emirate; branch: Branch; street_name: string; building_number: string; } // File: src/actions/getCart.ts import axios from "axios"; import { Cart } from "../types/cart"; export async function getCart() { try { const response = await axios.get("/cart"); const data = response.data; return data.data as Cart; } catch (error) { console.error(error); } } // File: src/actions/getCategory.ts import axios from "axios"; import { Category } from "../types/home-page"; export async function getCategory({ categoryId }: { categoryId: string }) { try { const response = await axios.get(`/categories/${categoryId}`); const data = response.data; return data.data as Category; } catch (error) { console.error(error); } } // File: src/actions/getFavourite.ts import axios from "axios"; import Product from "../types/product"; export async function getFavourite() { try { const response = await axios.get(`/user/favourites/products`); const data = response.data; return data.data as { id: number; products: Product }[]; } catch (error) { console.error(error); } } // File: src/actions/getHomePage.ts import axios from "axios"; import HomePage from "../types/home-page"; export async function getHomePage() { try { const response = await axios.get("/home"); const data = response.data; console.log(data.data); return data.data as HomePage; } catch (error) { console.error(error); } } // File: src/actions/getProduct.ts import axios from "axios"; import Product from "../types/product"; export async function getProduct({ productId }: { productId: string }) { try { const response = await axios.get(`/product/${productId}`); const data = response.data; return data.data as Product; } catch (error) { console.error(error); } } // File: src/actions/getSearchProducts.ts import axios from "axios"; import Product from "../types/product"; export async function getSearchProducts({ q }: { q: string }) { try { const response = await axios.post(`/products`, { q, }); const data = response.data; return data.data as Product[]; } catch (error) { console.error(error); } } // File: src/actions/getShop.ts import axios from "axios"; import { Shop } from "../types/shop"; export async function getShop({ shopId }: { shopId: string }) { try { const response = await axios.get(`/shop/${shopId}`); const data = response.data; return data.data as Shop; } catch (error) { console.error(error); } } // File: src/assets/DriverSvg.tsx import { cn } from "../lib/utils"; export const DriverSvg = ({ className }: { className?: string }) => ( ); // File: src/assets/header-logo.tsx export default function HeaderLogo() { return ( ); } // File: src/assets/icons.tsx export const Medal = () => ( ); export const Favorite = () => ( ); export const SavedAddresses = () => ( ); export const BankCard = () => ( ); export const DiscountCoupon = () => ( ); export const Chevron = () => ( ); export const Logout = () => ( ); export const Earth = () => ( ); export const Share = () => ( ); export const RetiredMilitary = () => ( ); export const Star = () => ( ); export const Orders = () => ( ); export const Camera = () => ( ); export const Edit = () => ( ); export const Delete = () => ( ); export const Warning = () => ( ); export const StoreIcon = () => ( ); export const EmailIcon = () => ( ); export const UAEAvailableIcon = () => ( ); export const CompanyProfileIcon = () => ( ); export const MobileNumberIcon = () => ( ); export const CompanyTypeIcon = () => ( company-type ); export const CommercialImageIcon = () => ( ); export const CloseIcon = () => ( ); export const ChevronDownIcon = () => ( ); export const Buthcering = () => ( ); export const DeliveryCar = () => ( ); // File: src/assets/logo.tsx import { cn } from "../lib/utils"; export default function Logo({ className }: { className?: string }) { return ( ); } // File: src/assets/no-data-icon.tsx import { cn } from "../lib/utils"; export default function NoDataIcon({ className }: { className?: string }) { return ( ); } // File: src/components/accordion.tsx import { useState } from "react"; import { cn } from "../lib/utils"; export default function Accordion({ title, children, }: { title: string; children: React.ReactNode; }) { const [isOpen, setIsOpen] = useState(true); return (
{isOpen && (
{children}
)}
); } // File: src/components/account/add-new-address.tsx import { useTranslation } from "react-i18next"; import useUserStore from "../../store/userStore"; import useModalsStore from "../../store/modalsStore"; import { cn } from "../../lib/utils"; import { useState } from "react"; import axios from "axios"; import SelectCountryCode from "../modals/select-country-code"; import { toast } from "react-toastify"; import LoadingSpinner from "../loading-spinner"; import { SubmitHandler, useForm } from "react-hook-form"; import { z } from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; import { useLocation, useNavigate, useSearchParams } from "react-router-dom"; export default function AddNewAddress() { const { t } = useTranslation(); const region = useUserStore((state) => state.region); const emirate = useUserStore((state) => state.emirate); const setMapModalOpen = useModalsStore((state) => state.setMapModalOpen); const latLng = useUserStore((state) => state.latLng); const { register, handleSubmit, formState: { errors }, } = useForm({ resolver: zodResolver(AddressSchema), }); const [addressType, setAddressType] = useState("home"); const [countryCode, setCountryCode] = useState("971"); const [isCountryCodeSelecting, setIsCountryCodeSelecting] = useState(false); const [loading, setLoading] = useState(false); const [searchParams, setSearchParams] = useSearchParams(); const navigate = useNavigate(); const onSubmit: SubmitHandler = async (data) => { try { setLoading(true); const formData = new FormData(); formData.append("name", data.name); formData.append("address", emirate?.name!); formData.append("street_name", data.streetName); formData.append("apartment_num", data.apartmentNumber); formData.append("region_id", region?.id.toString()!); formData.append("lat", latLng?.lat.toString()!); formData.append("lng", latLng?.lng.toString()!); formData.append("country_code", countryCode); formData.append("mobile", data.mobile); formData.append("building_number", data.buildingNumber); formData.append("additional_number", data.additionalNumber); formData.append("address_type", addressType); await axios.post("/user/address/add", formData); navigate(searchParams.get("redirectTo") || "/account/addresses"); toast.success(t("saved-access-success")); } catch { toast.error(t("something-went-wrong")); } setLoading(false); }; const location = useLocation(); return (

{t("add-new-address")}

{errors.name && {errors.name.message && `${t(errors.name.message)} ${t("is-required")}`}}
{emirate?.name}
{region?.name}
{errors.streetName && {errors.streetName.message && `${t(errors.streetName.message)} ${t("is-required")}`}}
{errors.mobile && {errors.mobile.message && `${t(errors.mobile.message)} ${t("is-required")}`}}
{errors.buildingNumber && ( {errors.buildingNumber.message && `${t(errors.buildingNumber.message)} ${t("is-required")}`} )}
{errors.additionalNumber && ( {errors.additionalNumber.message && `${t(errors.additionalNumber.message)} ${t("is-required")}`} )}
{errors.apartmentNumber && ( {errors.apartmentNumber.message && `${t(errors.apartmentNumber.message)} ${t("is-required")}`} )}
{t("address-type")}
{isCountryCodeSelecting && (
)}
); } const AddressSchema = z.object({ name: z.string().min(1, "name"), mobile: z.string().min(1, "mobile"), buildingNumber: z.string().min(1, "building-number"), streetName: z.string().min(1, "street-name"), apartmentNumber: z.string().min(1, "apartment-number"), additionalNumber: z.string().min(1, "additional-number"), }); type AddressSchemaType = z.infer; // File: src/components/account/addresses.tsx import { useTranslation } from "react-i18next"; import useModalsStore from "../../store/modalsStore"; import { useNavigate } from "react-router-dom"; import { useQuery } from "@tanstack/react-query"; import axios from "axios"; import LoadingPage from "../../loading-page"; import { Branch, Emirate } from "../../store/userStore"; import { useState } from "react"; import Logo from "../../assets/logo"; import LoadingScreen from "../loading-screen"; import { getAddresses } from "../../actions/getAddresses"; export default function Addresses() { const { t } = useTranslation(); const setMapModalOpen = useModalsStore((state) => state.setMapModalOpen); const navigate = useNavigate(); const { data, isFetching, refetch } = useQuery({ queryKey: ["global", "addresses"], queryFn: getAddresses, }); const setModalContent = useModalsStore((state) => state.setModalContent); const setModalOpen = useModalsStore((state) => state.setModalOpen); const modalContent = useModalsStore((state) => state.modalContent); if (isFetching) return ; if (!data) return null; async function deleteAddress(id: number) { setModalContent({ ...modalContent, loading: true, }); try { const formData = new FormData(); formData.append("address_id", id.toString()); await axios.post("/user/address/delete", formData); await refetch(); } catch (error) { console.error(error); } setModalContent({ ...modalContent, loading: false, }); setModalOpen(false); } return (

{t("saved-addresses")}

{data.map((address) => (
{t("deliver-to")}
{/* */}
{address.emirate.name} - {address.region.name} -{t("street-name")} {address.street_name} - {t("building-number")}{" "} {address.building_number}
))}
{" "}
); } // File: src/components/account/change-language.tsx import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Language } from "../../types/user"; export default function ChangeLanguage() { const { t, i18n } = useTranslation(); const [language, setLanguage] = useState(i18n.language as Language); const handleUpdate = () => { i18n.changeLanguage(language); window.location.reload(); }; return (

{t("language")}

{t("account.change-language")}
); } // File: src/components/account/change-password.tsx import axios from "axios"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { toast } from "react-toastify"; import LoadingSpinner from "../loading-spinner"; export default function ChangePassword() { const { t } = useTranslation(); const [password, setPassword] = useState(""); const [newPassword, setNewPassword] = useState(""); const [confirmNewPassword, setConfirmNewPassword] = useState(""); const [showPassword, setShowPassword] = useState(false); const [showNewPassword, setShowNewPassword] = useState(false); const [showConfirmNewPassword, setShowConfirmNewPassword] = useState(false); const [loading, setLoading] = useState(false); const handleUpdate = async () => { setLoading(true); if (newPassword !== confirmNewPassword) { setLoading(false); toast.error(t("modals.errors.confirm-password")); return; } try { const formData = new FormData(); formData.append("old_password", password); formData.append("password", newPassword); await axios.post("/user/update_password", formData); toast.success(t("messages.password-reset-success")); } catch { toast.error(t("something-went-wrong")); } setLoading(false); }; return (

{t("account.change-password")}

setPassword(e.target.value)} />
setNewPassword(e.target.value)} />
setConfirmNewPassword(e.target.value)} />
); } // File: src/components/account/edit-account.tsx import { useTranslation } from "react-i18next"; import SelectCountryCode from "../modals/select-country-code"; import { useEffect, useRef, useState } from "react"; import { Link } from "react-router-dom"; import useUserStore from "../../store/userStore"; import axios from "axios"; import { toast } from "react-toastify"; import LoadingSpinner from "../loading-spinner"; import { md5 } from "js-md5"; export default function EditAccount() { const { t } = useTranslation(); const user = useUserStore((state) => state.user); const [isCountryCodeSelecting, setIsCountryCodeSelecting] = useState(false); const [first_name, setFirstName] = useState(""); const [last_name, setLastName] = useState(""); const [email, setEmail] = useState(""); const [mobile, setMobile] = useState(""); const [countryCode, setCountryCode] = useState(""); const [loading, setLoading] = useState(false); const fileInputRef = useRef(null); const [selectedImage, setSelectedImage] = useState(null); const handleFileInput = (event: React.ChangeEvent) => { const imageFile = event.target.files?.[0]; if (!imageFile) return; const imageUrl = URL.createObjectURL(imageFile); setSelectedImage(imageUrl); }; const triggerFileInput = () => { const fileInput = fileInputRef.current; if (fileInput) { fileInput.value = ""; fileInput.click(); } }; useEffect(() => { setFirstName(user?.first_name || ""); setLastName(user?.last_name || ""); setEmail(user?.email || ""); setMobile(user?.mobile || ""); setCountryCode(user?.country_code || ""); }, [user]); const handleUpdate = async () => { setLoading(true); try { const formData = new FormData(); first_name && formData.append("first_name", first_name); last_name && formData.append("last_name", last_name); email && formData.append("email", email); // mobile && formData.append("mobile", mobile); countryCode && formData.append("country_code", countryCode); // fileInputRef.current?.files?.[0] && // formData.append("image", fileInputRef.current?.files?.[0]); await axios.post("/user/update", formData, { headers: { "Content-Type": "multipart/form-data", "otp-token": md5(mobile), }, }); toast.success(t("account.success-update")); } catch { toast.error(t("something-went-wrong")); } setLoading(false); }; if (!user) return null; return (

{t("account.edit-profile")}

{user.first_name}
{t("account.profile-info")}
setFirstName(e.target.value)} />
setLastName(e.target.value)} />
setEmail(e.target.value)} />
setMobile(e.target.value)} />
{t("account.change-password")}
{isCountryCodeSelecting && (
)}
); } // File: src/components/account/favourite.tsx import { useQuery } from "@tanstack/react-query"; import { useTranslation } from "react-i18next"; import ProductCard from "../home/product-card"; import { getFavourite } from "../../actions/getFavourite"; import LoadingScreen from "../loading-screen"; export default function Favourite() { const { t } = useTranslation(); const { data, isFetching } = useQuery({ queryKey: ["global", "favourite"], queryFn: getFavourite, }); if (isFetching) return ; if (!data) return null; return (

{t("account.favourite")}

{data.map((fav) => fav.products ? ( ) : null, )}
); } // File: src/components/account/my-points.tsx import { useState } from "react"; import { useTranslation } from "react-i18next"; export default function MyPoints() { const { t } = useTranslation(); const [points, setPoints] = useState(""); return (

{t("account.my-points")}

{t("account.my-points")} 1000
50 {t("account.points")} = 1 {t("AED")}
{t("account.use-points")} {t("account.use-points-desc")} ={" "} {t("account.minimum-points")}
{t("account.points-card")}
setPoints(e.target.value)} />
); } // File: src/components/account/orders.tsx import { useTranslation } from "react-i18next"; import NoDataIcon from "../../assets/no-data-icon"; export default function Orders() { const { t } = useTranslation(); return (

{t("account.orders")}

); } // File: src/components/account/retired-military.tsx import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Language } from "../../types/user"; export default function RetiredMilitary() { const { t, i18n } = useTranslation(); const [language, setLanguage] = useState( i18n.language as Language, ); const handleUpdate = () => { i18n.changeLanguage(language); window.location.reload(); }; return (

{t("account.retired-military-association")}

{t("account.enter-membership-number")}
); } // File: src/components/account/share-app.tsx import { useTranslation } from "react-i18next"; import SelectCountryCode from "../modals/select-country-code"; import { useState } from "react"; import useUserStore from "../../store/userStore"; export default function ShareApp() { const { t } = useTranslation(); const country_code = useUserStore((state) => state.user?.country_code); const [isCountryCodeSelecting, setIsCountryCodeSelecting] = useState(false); const [countryCode, setCountryCode] = useState(country_code || 971); const [mobile, setMobile] = useState(""); return (

{t("account.favourite")}

{t("account.invite-friends")}
{t("account.add-phone")}
setMobile(e.target.value)} />
{isCountryCodeSelecting && (
)}
); } // File: src/components/account/special-customer.tsx import { useState } from "react"; import { useTranslation } from "react-i18next"; import Logo from "../../assets/logo"; import { cn } from "../../lib/utils"; export default function SpecialCustomer() { const { t } = useTranslation(); const [isSubscribeModalOpen, setIsSubscribeModalOpen] = useState(false); const [packageType, setPackageType] = useState<1 | 2 | 3>(1); return (

{t("account.special-deals")}

{t("account.special-customer-desc")}

{PROS.map((pro) => (
{pro.icon}

{t(`account.${pro.title}`)}

{t(`account.${pro.desc}`)}

))}
{isSubscribeModalOpen && (
)}
); } const PROS = [ { title: "special-discounts", desc: "special-discounts-desc", icon: ( ), }, { title: "express-delivery", desc: "express-delivery-desc", icon: ( ), }, { title: "special-services", desc: "special-services-desc", icon: ( ), }, { title: "direct-contact", desc: "direct-contact-desc", icon: ( ), }, { title: "more-points", desc: "more-points-desc", icon: ( ), }, ]; const SelecPackage = ({ setIsSubscribeModalOpen, packageType, setPackageType, }: { setIsSubscribeModalOpen: (value: boolean) => void; packageType: 1 | 2 | 3; setPackageType: (value: 1 | 2 | 3) => void; }) => { const { t } = useTranslation(); return (
{t("account.select-package")}
); }; // File: src/components/attribute-card.tsx import { cn } from "../lib/utils"; export default function AttributeCard({ className, image, title, subTitle, subSubTitle, video, }: { className?: string; image: string; title: string; subTitle: string; video?: string; subSubTitle?: string; }) { return (
{title} {video && ( )}
{title} {subSubTitle && ( {subSubTitle} )} {subTitle}
); } // File: src/components/cart/driver-tip.tsx import { useTranslation } from "react-i18next"; import { DriverSvg } from "../../assets/DriverSvg"; import { cn, toArabic } from "../../lib/utils"; import useSlaughterStore from "../../store/slaughterStore"; import useCartStore, { CartType } from "../../store/cartStore"; import useDonationsStore from "../../store/donations"; import useGiftsStore from "../../store/giftsStore"; export default function DriverTip({ type = "all" }: { type?: CartType }) { const cartDriverTip = useCartStore((state) => state.driverTip); const setCartDriverTip = useCartStore((state) => state.setDriverTip); const slaughterDriverTip = useSlaughterStore((state) => state.driverTip); const setSlaughterDriverTip = useSlaughterStore( (state) => state.setDriverTip, ); const donatorDriverTip = useDonationsStore((state) => state.driverTip); const setDonatorDriverTip = useDonationsStore( (state) => state.setDriverTip, ); const giftDriverTip = useGiftsStore((state) => state.driverTip); const setGiftDriverTip = useGiftsStore((state) => state.setDriverTip); const driverTip = type === "all" ? cartDriverTip : type === "sacrifices" ? slaughterDriverTip : type === "donations" ? donatorDriverTip : giftDriverTip; const setDriverTip = type === "all" ? setCartDriverTip : type === "sacrifices" ? setSlaughterDriverTip : type === "donations" ? setDonatorDriverTip : setGiftDriverTip; const { t } = useTranslation(); return (
{t("driver-tip")} {t("driver-tip-desc")}
); } // File: src/components/cart/notes.tsx import { useTranslation } from "react-i18next"; import useSlaughterStore from "../../store/slaughterStore"; import useCartStore, { CartType } from "../../store/cartStore"; import useDonationsStore from "../../store/donations"; import useGiftsStore from "../../store/giftsStore"; export default function Notes({ type = "all" }: { type?: CartType }) { const cartNotes = useCartStore((state) => state.notes); const setCartNotes = useCartStore((state) => state.setNotes); const slaughterNotes = useSlaughterStore((state) => state.notes); const setSlaughterNotes = useSlaughterStore((state) => state.setNotes); const donatorNotes = useDonationsStore((state) => state.notes); const setDonatorNotes = useDonationsStore((state) => state.setNotes); const giftNotes = useGiftsStore((state) => state.notes); const setGiftNotes = useGiftsStore((state) => state.setNotes); const notes = type === "all" ? cartNotes : type === "sacrifices" ? slaughterNotes : type === "donations" ? donatorNotes : giftNotes; const setNotes = type === "all" ? setCartNotes : type === "sacrifices" ? setSlaughterNotes : type === "donations" ? setDonatorNotes : setGiftNotes; const { t } = useTranslation(); return (
{t("notes")}
); } // File: src/components/cart/payment-method.tsx import { useTranslation } from "react-i18next"; import useSlaughterStore from "../../store/slaughterStore"; import useCartStore, { CartType } from "../../store/cartStore"; import useDonationsStore from "../../store/donations"; import useGiftsStore from "../../store/giftsStore"; export default function PaymentMethod({ type, paymentMethods }: { paymentMethods: string[]; type?: CartType }) { const cartPaymentMethod = useCartStore((state) => state.paymentMethod); const setCartPaymentMethod = useCartStore((state) => state.setPaymentMethod); const slaughterPaymentMethod = useSlaughterStore((state) => state.paymentMethod); const setSlaughterPaymentMethod = useSlaughterStore((state) => state.setPaymentMethod); const donatorPaymentMethod = useDonationsStore((state) => state.paymentMethod); const setDonatorPaymentMethod = useDonationsStore((state) => state.setPaymentMethod); const giftPaymentMethod = useGiftsStore((state) => state.paymentMethod); const setGiftPaymentMethod = useGiftsStore((state) => state.setPaymentMethod); const paymentMethod = type === "all" ? cartPaymentMethod : type === "sacrifices" ? slaughterPaymentMethod : type === "donations" ? donatorPaymentMethod : giftPaymentMethod; const setPaymentMethod = type === "all" ? setCartPaymentMethod : type === "sacrifices" ? setSlaughterPaymentMethod : type === "donations" ? setDonatorPaymentMethod : setGiftPaymentMethod; const { t } = useTranslation(); return (
{t("payment-method")}
{paymentMethods.map((method, idx) => { if (method === "2") { return (
{idx !== paymentMethods.length - 1 &&
}
); } else if (method === "1") { return (
{idx !== paymentMethods.length - 1 &&
}
); } else if (method === "6") { return (
{idx !== paymentMethods.length - 1 &&
}
); } else return null; })}
); } // File: src/components/cart/quantity-price.tsx import axios from "axios"; import { useState } from "react"; import { useTranslation } from "react-i18next"; export default function QuantityPrice({ quantity: initialQuantity, price, cart_detail_id, setTotalPrice, }: { quantity: number; price: number; cart_detail_id: number; setTotalPrice: React.Dispatch>; }) { const [quantity, setQuantity] = useState(initialQuantity); const { t } = useTranslation(); const updateProduct = async (quantity: number) => { try { const formData = new FormData(); formData.append("cart_detail_id", cart_detail_id.toString()); formData.append("quantity", quantity.toString()); await axios.post("/cart/edit", formData); } catch (error) { console.error(error); } }; return (
+{quantity * price} {t("AED")}
{quantity}
); } // File: src/components/cart/redeem-points.tsx import { useTranslation } from "react-i18next"; import useUserStore from "../../store/userStore"; import { useRef, useState } from "react"; import { cn } from "../../lib/utils"; import useSlaughterStore from "../../store/slaughterStore"; import useCartStore, { CartType } from "../../store/cartStore"; import useDonationsStore from "../../store/donations"; import useGiftsStore from "../../store/giftsStore"; export default function RedeemPoints({ type = "all" }: { type?: CartType }) { const cartPoints = useCartStore((state) => state.points); const setCartPoints = useCartStore((state) => state.setPoints); const slaughterPoints = useSlaughterStore((state) => state.points); const setSlaughterPoints = useSlaughterStore((state) => state.setPoints); const donatorPoints = useDonationsStore((state) => state.points); const setDonatorPoints = useDonationsStore((state) => state.setPoints); const giftPoints = useGiftsStore((state) => state.points); const setGiftPoints = useGiftsStore((state) => state.setPoints); const points = type === "all" ? cartPoints : type === "sacrifices" ? slaughterPoints : type === "donations" ? donatorPoints : giftPoints; const setPoints = type === "all" ? setCartPoints : type === "sacrifices" ? setSlaughterPoints : type === "donations" ? setDonatorPoints : setGiftPoints; const userPoints = useUserStore((state) => state.user?.points); const { t } = useTranslation(); const [error, setError] = useState(false); const ref = useRef(null); const [pointsValue, setPointsValue] = useState(points); const handleUsePoints = () => { setError(false); if (!pointsValue) { ref.current?.focus(); return; } if (!userPoints || +pointsValue > +userPoints) { setError(true); return; } if (points) { setPoints(""); setPointsValue(""); } else { setPoints(pointsValue); } }; return (
setPointsValue(e.target.value)} />
{error && {t("not-enough-balance")}}
); } // File: src/components/cart/request-review-all/checkout.tsx import { CartItem } from "../../../types/cart"; import ChooseDeliverySlot from "./choose-delivery-slot"; import { useTranslation } from "react-i18next"; import AddToFavouriteButton from "../../home/add-to-favourite-button"; import ChooseSlaughterDay from "../request-review-sacrifices/choose-slaughter-day"; export default function Checkout({ cartItem, isSlaughter = false }: { cartItem: CartItem; isSlaughter?: boolean }) { const { t } = useTranslation(); return (
{cartItem.department.name} {cartItem.details.map((detail) => (
{detail.product.name}
{detail.product.name} {detail.category.name}
+{+detail.product.price * detail.quantity} {t("AED")}
{+detail.quantity}
))} {!isSlaughter && } {isSlaughter && }
); } // File: src/components/cart/request-review-all/choose-address.tsx import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { Address, getAddresses } from "../../../actions/getAddresses"; import { useNavigate } from "react-router-dom"; import useModalsStore from "../../../store/modalsStore"; import useCartStore, { CartType } from "../../../store/cartStore"; import useUserStore from "../../../store/userStore"; import useSlaughterStore from "../../../store/slaughterStore"; export default function ChooseAddress({ type = "all" }: { type?: CartType }) { const { t } = useTranslation(); const [addresses, setAddresses] = useState([]); const navigate = useNavigate(); const setMapModalOpen = useModalsStore((state) => state.setMapModalOpen); const [address, setAddress] = useState
(null); const [isChoosingAddress, setIsChoosingAddress] = useState(false); const mainAddress = useUserStore((state) => state.mainAddress); const setSlaughterAddressId = useSlaughterStore( (state) => state.setAddressId, ); const deliveryPoint = useSlaughterStore((state) => state.deliveryPoint); const setCartAddressId = useCartStore((state) => state.setAddressId); const setAddressId = type === "all" ? setCartAddressId : setSlaughterAddressId; async function fetchAddresses() { try { const _addresses = await getAddresses(); setAddresses(_addresses ?? []); } catch (error) { console.error(error); } } useEffect(() => { if (deliveryPoint && type === "sacrifices") { setAddressId(""); setAddress(null); } }, [deliveryPoint]); useEffect(() => { if (mainAddress) { setAddress(mainAddress); setAddressId(mainAddress.id.toString()); setIsChoosingAddress(false); } fetchAddresses(); }, []); return isChoosingAddress ? ( <>
{addresses.map((add) => ( ))}
) : address ? ( ) : ( ); } // File: src/components/cart/request-review-all/choose-delivery-slot.tsx import { eachDayOfInterval, endOfDay, format, startOfDay } from "date-fns"; import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import LoadingSpinner from "../../loading-spinner"; import { cn } from "../../../lib/utils"; import axios from "axios"; import { DriverSvg } from "../../../assets/DriverSvg"; import useCartStore, { CollectionItem } from "../../../store/cartStore"; export type DeliveryTime = { available: boolean; from: string; to: string }; export type ShippingMethod = { id: number; name: string; price: number }; export default function ChooseDeliverySlot({ cart_id }: { cart_id: string }) { const { t } = useTranslation(); const [isChoosingDelivery, setIsChoosingDelivery] = useState(false); const [dateFrom, setDateFrom] = useState(); const [dateTo, setDateTo] = useState(); const [shippingMethods, setShippingMethods] = useState([]); const [deliveryTimes, setDeliveryTimes] = useState([]); const [notAvailableDates, setNotAvailableDates] = useState([]); const [loading, setLoading] = useState(false); const [shippingMethod, setShippingMethod] = useState(null); const [confirmSlot, setConfirmSlot] = useState(false); const [deliveryDay, setDeliveryDay] = useState(null); const [deliveryTime, setDeliveryTime] = useState(null); const fetchDeliverySlots = async () => { setLoading(true); try { const formData = new FormData(); formData.append("cart_id", cart_id); shippingMethod && formData.append("shipping_method_id", shippingMethod.id.toString()); if (deliveryDay) formData.append("date", deliveryDay); const response = await axios.post("/checkout/settings", formData); const _data = response.data.data; setDateFrom(new Date(_data.date_from)); setDateTo(new Date(_data.date_to)); setShippingMethods(_data.shipping_methods); setNotAvailableDates(_data.not_available_dates); !shippingMethod && setShippingMethod(_data.shipping_methods[0]); setDeliveryTimes(_data.delivery_times); } catch (e) { console.log(e); } setLoading(false); }; useEffect(() => { fetchDeliverySlots(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const addToCollection = useCartStore((state) => state.addToCollection); const setDeliveryPrice = useCartStore((state) => state.setDeliveryPrice); const deliveryPrice = useCartStore((state) => state.deliveryPrice); const addItemsToCollection = () => { const newItem: CollectionItem = { cart_id, time_from: deliveryTime?.from!, time_to: deliveryTime?.to!, date: deliveryDay!, deliveryDay: format(new Date(deliveryDay!), "EEEE"), }; addToCollection(newItem); setDeliveryPrice(deliveryPrice + shippingMethod!.price); }; return ( <> {isChoosingDelivery && (
{t("delivery-selection")}
{shippingMethods.map((_method, idx) => ( ))}
{getDatesBetween(dateFrom!, dateTo!).map((_date, idx) => ( ))}
{deliveryDay && (
{deliveryTimes.map((_time, idx) => ( ))}
)} {deliveryTime && (
)}
)} ); } function getDatesBetween(startDate: Date, endDate: Date) { const start = startOfDay(startDate); const end = endOfDay(endDate); const dates = []; for (const date of eachDayOfInterval({ start, end })) { dates.push(date); } return dates; } // File: src/components/cart/request-review-sacrifices/choose-delivery-point.tsx import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { Address, getAddresses } from "../../../actions/getAddresses"; import { useNavigate } from "react-router-dom"; import useModalsStore from "../../../store/modalsStore"; import useCartStore from "../../../store/cartStore"; import useUserStore, { Emirate } from "../../../store/userStore"; import useSlaughterStore from "../../../store/slaughterStore"; import axios from "axios"; export type DeliveryPoint = { id: string; name: string; address: string; lat: number; lng: number; emirate_id: Emirate; region_id: number | null; }; export default function ChooseDeiveryPoint() { const { t } = useTranslation(); const [deliveryPoints, setDeliveryPoints] = useState([]); const setDeliveryPoint = useSlaughterStore( (state) => state.setDeliveryPoint, ); const deliveryPoint = useSlaughterStore((state) => state.deliveryPoint); const [isChoosingDeliveryPoint, setIsChoosingDeliveryPoint] = useState(false); const addressId = useSlaughterStore((state) => state.address_id); const emirate = useUserStore((state) => state.emirate); async function fetchDeliveryPoints() { try { const formData = new FormData(); formData.append("emirate_id", emirate?.id.toString() || ""); const response = await axios.post("delivery_points", formData); setDeliveryPoints(response.data.data); } catch (error) { console.error(error); } } useEffect(() => { if (addressId) { setDeliveryPoint(null); } }, [addressId]); useEffect(() => { fetchDeliveryPoints(); }, []); return isChoosingDeliveryPoint ? ( <>
{deliveryPoints.map((point) => ( ))}
) : deliveryPoint ? ( ) : ( ); } // File: src/components/cart/request-review-sacrifices/choose-slaughter-day.tsx import { useState } from "react"; import { DriverSvg } from "../../../assets/DriverSvg"; import { useTranslation } from "react-i18next"; import { Buthcering, Chevron, DeliveryCar } from "../../../assets/icons"; import SelectionModal from "../selection-modal"; import useSlaughterStore from "../../../store/slaughterStore"; export default function ChooseSlaughterDay({ cart_id }: { cart_id: string }) { const { t } = useTranslation(); const { slaughterDay, deliveryDay, setSlaughterDay, setDeliveryDay } = useSlaughterStore((state) => state); const [isChoosingSlaughterDay, setIsChoosingSlaughterDay] = useState(false); const [isChoosingDeliveryDay, setIsChoosingDeliveryDay] = useState(false); return (
{t("select-slaughter-day")} {t("select-delivery-day")}
); } // File: src/components/cart/selection-modal.tsx import { useTranslation } from "react-i18next"; import Modal from "../custom-modal"; import { CloseIcon } from "../../assets/icons"; export default function SelectionModal({ isOpen, setIsOpen, selectionTitle, selectedDay, setSelectedDay, optionsCount = 4, confirmText, }: { isOpen: boolean; setIsOpen: (isOpen: boolean) => void; selectionTitle: string; selectedDay: number | null; setSelectedDay: (day: number | null) => void; optionsCount?: number; confirmText: string; }) { const { t } = useTranslation(); return (
{selectionTitle}
{Array.from( { length: optionsCount }, (_, index) => index + 1, ).map((day) => ( ))}
{selectedDay && (
)}
); } // File: src/components/cart/use-coupon.tsx import { useTranslation } from "react-i18next"; import { useEffect, useRef, useState } from "react"; import { cn } from "../../lib/utils"; import axios from "axios"; import useSlaughterStore from "../../store/slaughterStore"; import useCartStore, { CartType } from "../../store/cartStore"; import useDonationsStore from "../../store/donations"; import useGiftsStore from "../../store/giftsStore"; export default function UseCoupon({ type = "all" }: { type?: CartType }) { const cartCoupon = useCartStore((state) => state.coupon); const setCartCoupon = useCartStore((state) => state.setCoupon); const slaughterCoupon = useSlaughterStore((state) => state.coupon); const setSlaughterCoupon = useSlaughterStore((state) => state.setCoupon); const donatorCoupon = useDonationsStore((state) => state.coupon); const setDonatorCoupon = useDonationsStore((state) => state.setCoupon); const giftCoupon = useGiftsStore((state) => state.coupon); const setGiftCoupon = useGiftsStore((state) => state.setCoupon); const coupon = type === "all" ? cartCoupon : type === "sacrifices" ? slaughterCoupon : type === "donations" ? donatorCoupon : giftCoupon; const setCoupon = type === "all" ? setCartCoupon : type === "sacrifices" ? setSlaughterCoupon : type === "donations" ? setDonatorCoupon : setGiftCoupon; const { t } = useTranslation(); const [error, setError] = useState(false); const ref = useRef(null); const [usedCoupon, setUsedCoupon] = useState(coupon?.code || ""); const [validCoupons, setValidCoupons] = useState([]); const handleUseCoupon = () => { setError(false); if (!usedCoupon) { ref.current?.focus(); return; } if (!validCoupons.find((c) => c.code === usedCoupon)) { setError(true); return; } if (coupon) { setCoupon(null); setUsedCoupon(""); } else setCoupon(validCoupons.find((c) => c.code === usedCoupon) || null); }; async function fetchCoupons() { try { const response = await axios.post("/promocodes"); setValidCoupons(response.data.data); } catch (error) { console.error(error); } } useEffect(() => { fetchCoupons(); }, []); return (
{t("enter-coupon")}
setUsedCoupon(e.target.value)} />
{error && {t("invalid-coupon")}}
); } export type CouponType = { id: number; code: string; percentage: null | number; value: null | number; description: string; expire_at: null; categories: []; }; // File: src/components/cart/use-wallet.tsx import { useTranslation } from "react-i18next"; import useUserStore from "../../store/userStore"; import { useRef, useState } from "react"; import { cn } from "../../lib/utils"; import useSlaughterStore from "../../store/slaughterStore"; import useCartStore, { CartType } from "../../store/cartStore"; import useDonationsStore from "../../store/donations"; import useGiftsStore from "../../store/giftsStore"; export default function UseWallet({ type = "all" }: { type?: CartType }) { const cartBalance = useCartStore((state) => state.balance); const setCartBalance = useCartStore((state) => state.setBalance); const slaughterBalance = useSlaughterStore((state) => state.balance); const setSlaughterBalance = useSlaughterStore((state) => state.setBalance); const donatorBalance = useDonationsStore((state) => state.balance); const setDonatorBalance = useDonationsStore((state) => state.setBalance); const giftBalance = useGiftsStore((state) => state.balance); const setGiftBalance = useGiftsStore((state) => state.setBalance); const balance = type === "all" ? cartBalance : type === "sacrifices" ? slaughterBalance : type === "donations" ? donatorBalance : giftBalance; const setBalance = type === "all" ? setCartBalance : type === "sacrifices" ? setSlaughterBalance : type === "donations" ? setDonatorBalance : setGiftBalance; const { t } = useTranslation(); const userBalance = useUserStore((state) => state.user?.balance); const [error, setError] = useState(false); const ref = useRef(null); const [usedValue, setUsedBalance] = useState(balance); const handleUseWallet = () => { setError(false); if (!usedValue) { ref.current?.focus(); return; } if (!userBalance || +usedValue > +userBalance) { setError(true); return; } console.log("balance", balance); console.log("usedValue", usedValue); if (balance) { setBalance(""); setUsedBalance(""); } else setBalance(usedValue); }; return (
setUsedBalance(e.target.value)} />
{error && ( {t("not-enough-balance")} )}
); } // File: src/components/custom-modal.tsx import { useEffect, useRef } from "react"; export default function CustomModal({ isOpen, children, setIsOpen, }: { isOpen: boolean; children: React.ReactNode; setIsOpen: (isOpen: boolean) => void; }) { const modalRef = useRef(null); const closeModal = (e: MouseEvent) => { if (modalRef.current && !modalRef.current.contains(e.target as Node)) { setIsOpen(false); } }; useEffect(() => { document.addEventListener("mousedown", closeModal); return () => { document.removeEventListener("mousedown", closeModal); }; }, []); if (!isOpen) return null; return (
{children}
); } // File: src/components/footer/footer.tsx import { useTranslation } from "react-i18next"; import HeaderLogo from "../../assets/header-logo"; export default function Footer() { const { t } = useTranslation(); return ( ); } // File: src/components/forms/forgot-password.tsx import { useTranslation } from "react-i18next"; import Logo from "../../assets/logo"; import { useState } from "react"; import LoadingSpinner from "../loading-spinner"; import { RESET_PASSWORD_URL } from "../../lib/urls"; import { md5Encrypt } from "../../lib/utils"; import { toast } from "react-toastify"; import axios from "axios"; import useModalsStore from "../../store/modalsStore"; export default function ForgotPassword({ mobile, forget_token, }: { mobile: string; forget_token: string; }) { const { t } = useTranslation(); const { setOtpModalOpen } = useModalsStore(); const [password, setPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState(""); const [visiblePassword, setVisiblePassword] = useState(false); const [visibleConfirmPassword, setVisibleConfirmPassword] = useState(false); const [error, setError] = useState<1 | 2 | 3 | null>(null); const [loading, setLoading] = useState(false); const handleResetPassword = async () => { setLoading(true); setError(null); if (!password) { setError(1); return; } if (!confirmPassword) { setError(2); return; } if (password !== confirmPassword) { setError(3); return; } const formData = new FormData(); formData.append("mobile", mobile); formData.append("new_password", password); formData.append("forget_token", forget_token); try { await axios.post("/user/forget_password/reset_password", formData, { headers: { "otp-token": md5Encrypt(mobile), }, }); toast.success(t("messages.password-reset-success")); setOtpModalOpen(false); } catch (error) { console.error(error); } setLoading(false); }; return (
{t("modals.login.reset-password")} {t("modals.login.reset-password-msg")}
setPassword(e.target.value)} placeholder={t("modals.password")} className="flex-1 font-light text-foreground placeholder:text-[rgba(212,212,212,1)] focus:outline-none" />
{error === 1 && (

{t("modals.password")}{" "} {t("modals.errors.is-required-h")}

)}
setConfirmPassword(e.target.value)} placeholder={t("modals.confirm-password")} className="flex-1 font-light text-foreground placeholder:text-[rgba(212,212,212,1)] focus:outline-none" />
{error === 2 && (

{t("modals.confirm-password")}{" "} {t("modals.errors.is-required")}

)}
{error === 3 && (

{t("modals.errors.confirm-password")}

)}
); } // File: src/components/forms/login-form.tsx import { useState } from "react"; import Logo from "../../assets/logo"; import { cn, md5Encrypt } from "../../lib/utils"; import { Country } from "../../types/user"; import { LOGIN_URL } from "../../lib/urls"; import useUserStore from "../../store/userStore"; import LoadingSpinner from "../loading-spinner"; import { toast } from "react-toastify"; import SocialLogin from "./social-login"; import { useTranslation } from "react-i18next"; import useModalsStore from "../../store/modalsStore"; import SelectCountryCode from "../modals/select-country-code"; import Cookies from "js-cookie"; import axios from "axios"; export const LoginForm = () => { const { isLoginModalOpen, setLoginModalOpen, setRegisterModalOpen, setForgotPassword, setOtpModalOpen } = useModalsStore(); const [isCountrySelecting, setIsCountrySelecting] = useState(false); const [isCountryCodeSelecting, setIsCountryCodeSelecting] = useState(false); const [cou, setCou] = useState<{ name: string; code: Country; icon: string; }>({ name: "United Arab Emirates", code: "uae", icon: "/zabehatyIcons/uae.png", }); const { t } = useTranslation(); const [mobile, setMobile] = useState(""); const [countryCode, setCountryCode] = useState("971"); const [password, setPassword] = useState(""); const [visiblePassword, setVisiblePassword] = useState(false); const [error, setError] = useState<1 | 2 | null>(null); const [loading, setLoading] = useState(false); const setUser = useUserStore((state) => state.setUser); const handleLogin = async () => { setError(null); setLoading(true); if (!mobile) { setError(1); setLoading(false); return; } if (!password) { setError(2); setLoading(false); return; } const formData = new FormData(); formData.append("mobile", mobile); formData.append("password", password); formData.append("country_code", countryCode); formData.append("device_type", "web"); try { const response = await axios.post("/user/login", formData, { headers: { "otp-token": md5Encrypt(mobile), }, }); const data = response.data; setUser(data.data); toast.success(t("messages.login-success")); localStorage.setItem("user", JSON.stringify(data.data)); Cookies.set("token", data.data.token); axios.defaults.headers.common.authorization = `Bearer ${data.data.token}`; setLoading(false); setLoginModalOpen(false); } catch (err: any) { setLoading(false); toast.error(t("messages.wrong-mobile-password")); } }; const setMapModalOpen = useModalsStore((state) => state.setMapModalOpen); return ( <>
{!isCountrySelecting && !isCountryCodeSelecting && ( <>
{t("modals.login.title")}
setMobile(e.target.value)} placeholder={t("modals.mobile")} className="flex-1 font-light text-foreground placeholder:text-[rgba(212,212,212,1)] focus:outline-none" />
{error === 1 && (

{t("modals.mobile")} {t("modals.errors.is-required")}

)}
setPassword(e.target.value)} placeholder={t("modals.password")} className="flex-1 font-light text-foreground placeholder:text-[rgba(212,212,212,1)] focus:outline-none" />
{error === 2 && (

{t("modals.password")} {t("modals.errors.is-required-h")}

)}
{t("modals.login.dont-have-account")}{" "}
{t("modals.login-with")}
)} {isCountrySelecting && ( <> {t("modals.select-country")} )} {isCountryCodeSelecting && ( )}
); }; // File: src/components/forms/otp-verification.tsx import { useEffect, useState } from "react"; import { cn, md5Encrypt } from "../../lib/utils"; import Logo from "../../assets/logo"; import { useTranslation } from "react-i18next"; import LoadingSpinner from "../loading-spinner"; import OtpInput from "react-otp-input"; import { toast } from "react-toastify"; import useModalsStore from "../../store/modalsStore"; import ForgotPassword from "./forgot-password"; import SelectCountryCode from "../modals/select-country-code"; import axios from "axios"; export default function OtpVerification() { const { isOtpModalOpen, setOtpModalOpen, isForgotPassword } = useModalsStore(); const [step, setStep] = useState<1 | 2 | 3 | 4>(1); const { t } = useTranslation(); const [forgetToken, setForgetToken] = useState(""); const [isCountryCodeSelecting, setIsCountryCodeSelecting] = useState(false); const [otpMethod, setOtpMethod] = useState<"sms" | "whatsapp">("sms"); const [otpCode, setOtpCode] = useState(""); const [mobile, setMobile] = useState(""); const [countryCode, setCountryCode] = useState("971"); const [error, setError] = useState<1 | null>(null); const [loading, setLoading] = useState(false); const [minutes, setMinutes] = useState(3); const [seconds, setSeconds] = useState(0); useEffect(() => { if (step === 3) { const interval = setInterval(() => { if (seconds === 0) { if (minutes === 0) { clearInterval(interval); setStep(2); } else { setMinutes((prev) => prev - 1); setSeconds(59); } } else { setSeconds((prev) => prev - 1); } }, 1000); return () => clearInterval(interval); } }, [minutes, seconds, step]); async function sendCode() { try { const formData = new FormData(); formData.append("mobile", mobile); formData.append("country_code", countryCode); formData.append("channel", otpMethod); await axios.post("/user/forget_password/send_code", formData, { headers: { "otp-token": md5Encrypt(mobile), }, }); setStep(3); } catch (e) { console.log(e); } } const handleOtpVerification = async () => { setError(null); setLoading(true); if (!mobile) { setError(1); setLoading(false); return; } const formData = new FormData(); switch (step) { case 1: setStep(2); setLoading(false); break; case 2: sendCode(); setLoading(false); break; case 3: try { formData.append("code", otpCode); formData.append("mobile", mobile); const response = await axios.post( "/user/forget_password/check_code", formData, { headers: { "otp-token": md5Encrypt(mobile), }, }, ); const data = response.data.data; console.log(data); setForgetToken(data.forget_password_token); toast.success(t("messages.otp-verified")); if (!isForgotPassword) { setStep(1); setOtpModalOpen(false); } else { setStep(4); } } catch (e) { console.log(e); toast.error(t("messages.incorrect-otp")); } setLoading(false); break; case 4: // setStep(1); // setOtpModalOpen(false); break; } }; const handlePaste: React.ClipboardEventHandler = (event) => { const data = event.clipboardData.getData("text"); console.log(data); }; return ( <>
{!isCountryCodeSelecting && step === 1 && ( <>
{t("modals.otp-verification")} {t("modals.otp-msg")}
setMobile(e.target.value)} placeholder={t("modals.mobile")} className="flex-1 font-light text-foreground placeholder:text-[rgba(212,212,212,1)] focus:outline-none" />
{error === 1 && (

{t("modals.mobile")}{" "} {t("modals.errors.is-required")}

)}
)} {!isCountryCodeSelecting && step === 2 && ( <> {t("modals.send-cod-via")} )} {!isCountryCodeSelecting && step === 3 && ( <>
{t("modals.otp-verification")} {t("modals.otp-verification-msg")}
( )} shouldAutoFocus skipDefaultStyles />
0{minutes}: {seconds < 10 ? `0${seconds}` : seconds}
)} {!isCountryCodeSelecting && step === 4 && ( )} {isCountryCodeSelecting && ( )}
); } // File: src/components/forms/register-form.tsx import { useState } from "react"; import Logo from "../../assets/logo"; import { cn, md5Encrypt } from "../../lib/utils"; import { Country } from "../../types/user"; import SocialLogin from "./social-login"; import { REGISTER_URL } from "../../lib/urls"; import useUserStore from "../../store/userStore"; import { toast } from "react-toastify"; import LoadingSpinner from "../loading-spinner"; import { useTranslation } from "react-i18next"; import useModalsStore from "../../store/modalsStore"; import SelectCountryCode from "../modals/select-country-code"; import Cookies from "js-cookie"; import axios from "axios"; export const RegisterForm = () => { const { isRegisterModalOpen, setLoginModalOpen, setRegisterModalOpen, setOtpModalOpen, setForgotPassword } = useModalsStore(); const [isCountrySelecting, setIsCountrySelecting] = useState(false); const [isCountryCodeSelecting, setIsCountryCodeSelecting] = useState(false); const [cou, setCou] = useState<{ name: string; code: Country; icon: string; }>({ name: "United Arab Emirates", code: "uae", icon: "/zabehatyIcons/uae.png", }); const { t } = useTranslation(); const [firstName, setFirstName] = useState(""); const [lastName, setLastName] = useState(""); const [mobile, setMobile] = useState(""); const [countryCode, setCountryCode] = useState("971"); const [password, setPassword] = useState(""); const [visiblePassword, setVisiblePassword] = useState(false); const [error, setError] = useState<1 | 2 | 3 | 4 | null>(null); const [loading, setLoading] = useState(false); const setUser = useUserStore((state) => state.setUser); const handleRegister = async () => { setLoading(true); setError(null); if (!firstName) { setError(1); setLoading(false); return; } if (!lastName) { setError(2); setLoading(false); return; } if (!mobile) { setError(3); setLoading(false); return; } if (!password) { setError(4); setLoading(false); return; } const formData = new FormData(); formData.append("first_name", firstName); formData.append("last_name", lastName); formData.append("mobile", mobile); formData.append("password", password); formData.append("country_code", countryCode); formData.append("device_type", "web"); try { const response = await axios.post("/user/register", formData, { headers: { "otp-token": md5Encrypt(mobile), }, }); const data = response.data; setUser(data.data); toast.success(t("messages.register-success")); Cookies.set("token", data.data.token); localStorage.setItem("user", JSON.stringify(data.data)); axios.defaults.headers.common.authorization = `Bearer ${data.data.token}`; setRegisterModalOpen(false); setForgotPassword(false); setOtpModalOpen(true); setLoading(false); } catch (err: any) { setLoading(false); toast.error(t("messages.mobile-exists")); } }; return ( <>
{!isCountrySelecting && !isCountryCodeSelecting && ( <>
{t("modals.register.title")}
setFirstName(e.target.value)} placeholder={t("modals.first-name")} className="flex-1 font-light text-foreground placeholder:text-[rgba(212,212,212,1)] focus:outline-none" />
{error === 1 && (

{t("modals.first-name")} {t("modals.errors.is-required")}

)}
setLastName(e.target.value)} placeholder={t("modals.last-name")} className="flex-1 font-light text-foreground placeholder:text-[rgba(212,212,212,1)] focus:outline-none" />
{error === 2 && (

{t("modals.last-name")} {t("modals.errors.is-required")}

)}
setMobile(e.target.value)} placeholder={t("modals.mobile")} className="flex-1 font-light text-foreground placeholder:text-[rgba(212,212,212,1)] focus:outline-none" />
{error === 3 && (

{t("modals.mobile")} {t("modals.errors.is-required-h")}

)}
setPassword(e.target.value)} placeholder={t("modals.password")} className="flex-1 font-light text-foreground placeholder:text-[rgba(212,212,212,1)] focus:outline-none" />
{error === 4 && (

{t("modals.password")} {t("modals.errors.is-required-h")}

)}
{t("modals.register.already-have-account")}{" "}
{t("modals.login-with")}
)} {isCountrySelecting && ( <> {t("modals.select-country")} )} {isCountryCodeSelecting && ( )}
); }; // File: src/components/forms/social-login.tsx import { useCallback, useEffect, useState } from "react"; import { IResolveParams, LoginSocialApple, LoginSocialFacebook, LoginSocialGoogle } from "reactjs-social-login"; import useModalsStore from "../../store/modalsStore"; import { SOCIAL_LOGIN_URL } from "../../lib/urls"; import { toast } from "react-toastify"; import useUserStore from "../../store/userStore"; import axios from "axios"; import { useTranslation } from "react-i18next"; import Cookies from "js-cookie"; const REDIRECT_URI = window.location.href; export default function SocialLogin() { const closeModal = useModalsStore((state) => state.closeModal); const [provider, setProvider] = useState(""); const [profile, setProfile] = useState(); const onLogoutSuccess = useCallback(() => { setProfile(null); setProvider(""); alert("logout success"); }, []); const { t } = useTranslation(); const setUser = useUserStore((state) => state.setUser); async function login() { try { const formData = new FormData(); formData.append("social_type", provider); formData.append("device_type", "web"); if (profile.id) formData.append("social_profile_id", profile.id); else formData.append("social_profile_id", "1000"); formData.append("social_token", profile.access_token); const res = await axios.post("/user/login/social", formData); const data = res.data; console.log(data); setUser(data.data); Cookies.set("token", data.data.token); localStorage.setItem("user", JSON.stringify(data.data)); toast.success(t("messages.login-success")); closeModal(); } catch (err) { console.log(err); } } useEffect(() => { if (profile) { console.log(profile); } if (provider) { console.log(provider); } }, [profile, provider]); return (
{ setProvider(provider); setProfile(data); login(); }} onReject={(err) => { console.log(err); }} fieldsProfile="id" > { setProvider(provider); setProfile(data); login(); }} onReject={(err) => { console.log(err); }} > {" "} {/* { setProvider(provider); setProfile(data); login(); }} onReject={(err) => { console.log(err); }} > */} {" "} {/* */}
); } // File: src/components/header/header.tsx import { useEffect, useState } from "react"; import HeaderLogo from "../../assets/header-logo"; import { cn } from "../../lib/utils"; import NavList from "./nav-list"; import SearchBox from "./search-box"; import { TopHeader } from "./top-header"; import { useLocation } from "react-router-dom"; export default function Header() { const [toggle, setToggle] = useState(false); const location = useLocation(); useEffect(() => { setToggle(false); }, [location.pathname]); return ( <>
); } // File: src/components/header/language-switcher.tsx import { useEffect, useRef, useState } from "react"; import { cn } from "../../lib/utils"; import { useTranslation } from "react-i18next"; export default function LanguageSwitcher() { const [toggle, setToggle] = useState(false); const { i18n } = useTranslation(); const ref = useRef(null); useEffect(() => { function handleClickOutside(event: MouseEvent) { if (ref.current && !ref.current.contains(event.target as Node)) { setToggle(false); } } document.addEventListener("mousedown", handleClickOutside); return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, [ref]); return (
); } // File: src/components/header/nav-list.tsx import { useTranslation } from "react-i18next"; import NavMenu from "./nav-menu"; import { Link, useNavigate } from "react-router-dom"; import useModalsStore from "../../store/modalsStore"; import { getToken } from "../../lib/utils"; export default function NavList() { const { t } = useTranslation(); const navigate = useNavigate(); const setLoginModalOpen = useModalsStore( (state) => state.setLoginModalOpen, ); const checkLogin = () => { if (!getToken()) { setLoginModalOpen(true); return false; } return true; }; return (
{t("navlist.home")}
); } // {NAV_LIST.map((item) => ( // // {t("navlist." + item.title)} // {item.icon} // // ))} const NAV_LIST = [ { title: "home", href: "/", icon: ( ), }, { title: "account", href: "/account", icon: ( ), }, { title: "cart", href: "/cart", icon: ( ), }, { title: "notifications", href: "/notifications", icon: ( ), }, ]; // File: src/components/header/nav-menu.tsx import { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { cn } from "../../lib/utils"; export default function NavMenu() { const [toggle, setToggle] = useState(false); const { t } = useTranslation(); const ref = useRef(null); useEffect(() => { function handleClickOutside(event: MouseEvent) { if (ref.current && !ref.current.contains(event.target as Node)) { setToggle(false); } } document.addEventListener("mousedown", handleClickOutside); return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, [ref]); return (
{t("navlist.menu")}
); } const MENU_LIST = [ { href: "/wallet", title: "wallet", icon: ( ), }, { href: "/terms-conditions", title: "terms-and-conditions", icon: ( ), }, { href: "/offers", title: "offers", icon: ( ), }, { href: "/register-store", title: "register-as-a-store", icon: ( ), }, { href: "technical-support", title: "technical-support", icon: ( ), }, ]; // File: src/components/header/search-box.tsx import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; export default function SearchBox() { const { t } = useTranslation(); const navigate = useNavigate(); const handleSearch = (e: React.FormEvent) => { e.preventDefault(); const search = new FormData(e.currentTarget).get("search") as string; if (search) navigate(`/search?q=${search}`); }; return (
); } // File: src/components/header/top-header.tsx import { useTranslation } from "react-i18next"; import useModalsStore from "../../store/modalsStore"; import useUserStore from "../../store/userStore"; import LanguageSwitcher from "./language-switcher"; export const TopHeader = () => { const region = useUserStore((state) => state.region); const emirate = useUserStore((state) => state.emirate); const { t } = useTranslation(); const setMapModalOpen = useModalsStore((state) => state.setMapModalOpen); const mainAddress = useUserStore((state) => state.mainAddress); return (
); }; // File: src/components/home/add-to-favourite-button.tsx import React, { useState } from "react"; import useModalsStore from "../../store/modalsStore"; import Cookies from "js-cookie"; import axios from "axios"; export default function AddToFavouriteButton({ in_favourite, productId, }: { in_favourite: boolean; productId: number; }) { const setLoginModalOpen = useModalsStore( (state) => state.setLoginModalOpen, ); const token = Cookies.get("token"); const [inFavourite, setInFavourite] = useState(in_favourite); const addToFavourite = ( e: React.MouseEvent, ) => { e.stopPropagation(); e.preventDefault(); if (!token) { setLoginModalOpen(true); return; } if (inFavourite) { removeFromFav(); setInFavourite(false); } else { addToFav(); setInFavourite(true); } }; const addToFav = async () => { try { const formData = new FormData(); formData.append("product_id", String(productId)); await axios.post(`/user/favourites/add/product`, formData); } catch (error) { console.log(error); } }; const removeFromFav = async () => { try { const formData = new FormData(); formData.append("product_id", String(productId)); await axios.post(`/user/favourites/delete/product`, formData); } catch (error) { console.log(error); } }; return ( ); } // File: src/components/home/categories-section.tsx import { Swiper, SwiperSlide } from "swiper/react"; import { Navigation, A11y, Autoplay, Scrollbar } from "swiper/modules"; import "swiper/css"; import "swiper/css/scrollbar"; import "swiper/css/a11y"; import "swiper/css/autoplay"; import "swiper/css/navigation"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; import { Category } from "../../types/home-page"; export default function CategoriesSection({ categories }: { categories: Category[] }) { const { i18n, t } = useTranslation(); return (

{t("categories")}

{categories.map((category) => (
{category.name}
{category.name}
))}
); } // File: src/components/home/hero.tsx import { Swiper, SwiperSlide } from "swiper/react"; import { Navigation, Pagination, A11y, Autoplay } from "swiper/modules"; import "swiper/css"; import "swiper/css/navigation"; import "swiper/css/pagination"; import "swiper/css/a11y"; import "swiper/css/autoplay"; import { useTranslation } from "react-i18next"; import { Slider } from "../../types/home-page"; export default function Hero({ sliders }: { sliders: Slider[] }) { const { i18n } = useTranslation(); return (
{sliders.map((slide) => ( {slide.id.toString()} ))}
); } // File: src/components/home/home-products-section.tsx import { Swiper, SwiperSlide } from "swiper/react"; import { Navigation, A11y, Autoplay } from "swiper/modules"; import "swiper/css"; import "swiper/css/pagination"; import "swiper/css/a11y"; import "swiper/css/autoplay"; import { useTranslation } from "react-i18next"; import ProductCard from "./product-card"; import Product from "../../types/product"; import { HomeSectionItem } from "../../types/home-page"; export default function HomeProductsSection({ items, title, }: { items: HomeSectionItem[]; title: string; }) { const { i18n } = useTranslation(); return (

{title}

{items.map((item) => ( ))}
); } // File: src/components/home/home-shops-section.tsx import { Swiper, SwiperSlide } from "swiper/react"; import { Navigation, A11y, Autoplay } from "swiper/modules"; import "swiper/css"; import "swiper/css/pagination"; import "swiper/css/a11y"; import "swiper/css/autoplay"; import { useTranslation } from "react-i18next"; import { HomeSectionItem } from "../../types/home-page"; import ShopCard from "./shop-card"; import { Shop } from "../../types/shop"; export default function HomeShopsSection({ items, title }: { items: HomeSectionItem[]; title: string }) { const { i18n } = useTranslation(); return (

{title}

{items.map((item) => ( ))}
); } // File: src/components/home/product-card.tsx import { useTranslation } from "react-i18next"; import { cn } from "../../lib/utils"; import { Link } from "react-router-dom"; import { useEffect, useState } from "react"; import Product from "../../types/product"; import useModalsStore from "../../store/modalsStore"; import Cookies from "js-cookie"; import axios from "axios"; import AddToFavouriteButton from "./add-to-favourite-button"; export default function ProductCard({ product }: { product: Product }) { let _remainingDays = 0, _remainingHours = 0, _remainginMinutes = 0, _remainginSeconds = 0, isExpired = false; if (product.limited_offer.has_limited_offer && product.limited_offer.limited_offer_expired_at) { const expiredAt = new Date(product.limited_offer.limited_offer_expired_at); const now = new Date(); const diff = expiredAt.getTime() - now.getTime(); isExpired = diff < 0; _remainingDays = Math.floor(diff / (1000 * 60 * 60 * 24)); _remainingHours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); _remainginMinutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); _remainginSeconds = Math.floor((diff % (1000 * 60)) / 1000); } let discountPrecentage = 0; if (product.old_price && product.price) { discountPrecentage = (1 - Number(product.price) / Number(product.old_price)) * 100; } const [remainingDays, setRemainingDays] = useState(_remainingDays); const [remainingHours, setRemainingHours] = useState(_remainingHours); const [remainginMinutes, setRemainginMinutes] = useState(_remainginMinutes); const [remainginSeconds, setRemainginSeconds] = useState(_remainginSeconds); useEffect(() => { if (isExpired || !product.limited_offer.has_limited_offer) return; const interval = setInterval(() => { if (isExpired) return; if (remainginSeconds > 0) { setRemainginSeconds(remainginSeconds - 1); } else { if (remainginMinutes > 0) { setRemainginMinutes(remainginMinutes - 1); setRemainginSeconds(59); } else { if (remainingHours > 0) { setRemainingHours(remainingHours - 1); setRemainginMinutes(59); } else { if (remainingDays > 0) { setRemainingDays(remainingDays - 1); setRemainingHours(23); } } } } }, 1000); return () => clearInterval(interval); }, [remainginSeconds, remainginMinutes, remainingHours, remainingDays, isExpired, product.limited_offer.has_limited_offer]); const { t } = useTranslation(); return (
{product.badge && (
{product.badge.name}
)} {product.badges && product.badges.length > 0 && product.badges.map((badge) => (
{badge.name}
))} {!!product.limited_offer.has_limited_offer && !isExpired && (
{remainingDays < 10 ? `0${remainingDays}` : remainingDays}: {remainingHours < 10 ? `0${remainingHours}` : remainingHours}: {remainginMinutes < 10 ? `0${remainginMinutes}` : remainginMinutes}: {remainginSeconds < 10 ? `0${remainginSeconds}` : remainginSeconds}
)}
{product.name}
{product.old_price && (
-{discountPrecentage.toFixed(0)}%
)}
{product.shop?.name}
{product.name}
{product.category.name}
{product.price} {t("AED")}
{product.old_price && (
{product.old_price} {t("AED")}
)} ); } // File: src/components/home/products-section.tsx import { Swiper, SwiperSlide } from "swiper/react"; import { Navigation, A11y, Autoplay } from "swiper/modules"; import "swiper/css"; import "swiper/css/pagination"; import "swiper/css/a11y"; import "swiper/css/autoplay"; import { useTranslation } from "react-i18next"; import ProductCard from "./product-card"; import Product from "../../types/product"; export default function ProductsSection({ products, title, }: { products: Product[]; title: string; }) { const { i18n } = useTranslation(); return (

{title}

{products.map((product) => ( ))}
); } // File: src/components/home/shop-card.tsx import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; import { Shop } from "../../types/shop"; export default function ShopCard({ shop }: { shop: Shop }) { const { t } = useTranslation(); return (
{shop.name}
{shop.department && (
{shop.department.name}
)}
{t("rate")} {shop.rating}
{shop.name}
); } // File: src/components/home/shops-section.tsx import { Swiper, SwiperSlide } from "swiper/react"; import { Navigation, A11y, Autoplay } from "swiper/modules"; import "swiper/css"; import "swiper/css/pagination"; import "swiper/css/a11y"; import "swiper/css/autoplay"; import { useTranslation } from "react-i18next"; import ShopCard from "./shop-card"; import { Shop } from "../../types/shop"; export default function ShopsSection({ shops, title }: { shops: Shop[]; title: string }) { const { i18n } = useTranslation(); return (

{title}

{shops.map((shop) => ( ))}
); } // File: src/components/loading-screen.tsx import LoadingSpinner from "./loading-spinner"; export default function LoadingScreen() { return (
); } // File: src/components/loading-spinner.tsx import { cn } from "../lib/utils"; export default function LoadingSpinner({ className }: { className?: string }) { return (
Loading...
); } // File: src/components/modals/MapModal.tsx import React, { useState } from "react"; import { GoogleMap, useJsApiLoader, MarkerF, Libraries, } from "@react-google-maps/api"; import LoadingSpinner from "../loading-spinner"; import Logo from "../../assets/logo"; import { useTranslation } from "react-i18next"; import { cn } from "../../lib/utils"; import { Country } from "../../types/user"; import axios from "axios"; import useUserStore, { Region } from "../../store/userStore"; import useModalsStore from "../../store/modalsStore"; import Search from "./search"; import { toast } from "react-toastify"; import { useNavigate } from "react-router-dom"; import Cookies from "js-cookie"; import { CloseIcon } from "../../assets/icons"; import CustomModal from "../custom-modal"; const libraries: Libraries = ["places"]; const mapContainerStyle: React.CSSProperties = { width: "100%", height: "678px", }; interface MapState { map?: google.maps.Map; marker: google.maps.LatLngLiteral; infoWindowOpen: boolean; userLocation?: google.maps.LatLngLiteral; } const uaeCenter: google.maps.LatLngLiteral = { lat: 24.469072681902656, lng: 54.37144234303175, }; const ksaCenter: google.maps.LatLngLiteral = { lat: 24.774265, lng: 46.738586, }; const omCenter: google.maps.LatLngLiteral = { lat: 23.614328, lng: 58.545284, }; const zoom = 14; const MapModal = () => { const isMapModalOpen = useModalsStore((state) => state.isMapModalOpen); const setMapModalOpen = useModalsStore((state) => state.setMapModalOpen); const { isLoaded } = useJsApiLoader({ id: "google-map-script", googleMapsApiKey: "AIzaSyCMR0GifIRTiL2qcJgMhq1l4xp2ydau7FI", libraries: libraries, }); const [map, setMap] = useState(null); const [mapCenter, setMapCenter] = useState(uaeCenter); const [marker, setMarker] = useState(uaeCenter); const [loading, setLoading] = useState(false); const handleMapLoad = (mapInstance: google.maps.Map) => { setMap(mapInstance); }; const handleMapClick = (event: google.maps.MapMouseEvent) => { if (event.latLng) { setMarker({ lat: event.latLng.lat(), lng: event.latLng.lng(), }); localStorage.setItem("latLng", JSON.stringify(marker)); } }; const getUserLocation = () => { if (navigator.geolocation) { navigator.geolocation.getCurrentPosition( (position) => { const userCoords = { lat: position.coords.latitude, lng: position.coords.longitude, }; setMarker(userCoords); if (map) { map.panTo(userCoords); } }, (error) => { console.error("Error getting user location:", error); }, ); } }; const { t } = useTranslation(); const [step, setStep] = useState(1); const [cou, setCou] = useState("uae"); const setRegion = useUserStore((state) => state.setRegion); const setCountry = useUserStore((state) => state.setCountry); const setEmirate = useUserStore((state) => state.setEmirate); const region = useUserStore((state) => state.region); const emirate = useUserStore((state) => state.emirate); const setBranch = useUserStore((state) => state.setBranch); const [regions, setRegions] = useState([]); const user = useUserStore((state) => state.user); const setLoginModalOpen = useModalsStore( (state) => state.setLoginModalOpen, ); async function geolocation() { try { setLoading(true); const formData = new FormData(); formData.append("lat", marker.lat.toString()); formData.append("lng", marker.lng.toString()); const response = await axios.post("/geolocation", formData); if ( response.data.data.available && response.data.data.emirate && response.data.data.region ) { setEmirate(response.data.data.emirate); setRegion(response.data.data.region); setBranch(response.data.data.branch); localStorage.setItem( "emirate", JSON.stringify(response.data.data.emirate), ); localStorage.setItem( "branch", JSON.stringify(response.data.data.branch), ); localStorage.setItem( "region", JSON.stringify(response.data.data.region), ); end(); } else if ( response.data.data.available && response.data.data.emirate && !response.data.data.region ) { setEmirate(response.data.data.emirate); setRegions(response.data.data.regions); setStep(3); } else { toast.error(t("invalid-country")); } setLoading(false); } catch (err) { toast.error(t("invalid-country")); } } async function getBranch(regionId: number) { try { const formData = new FormData(); formData.append("emirate_id", emirate?.id.toString()!); formData.append("region_id", regionId.toString()); const response = await axios.post("/branches", formData); if (response.data.data) { setBranch(response.data.data); end(); } else { toast.error(t("invalid-country")); } } catch (err) { toast.error(t("something-went-wrong")); } } const setLatLng = useUserStore((state) => state.setLatLng); const setMainAddress = useUserStore((state) => state.setMainAddress); const end = () => { setLatLng(marker); if (!user) { setMapModalOpen(false); setLoginModalOpen(true); setStep(1); } else { console.log("asdas"); setStep(4); } }; return (
{step === 1 && (
{isLoaded ? (
) : ( )}
)} {step === 2 && ( <>
{t("modals.select-country")}
)} {step === 3 && ( <>
{t("modals.select-region")}
{regions?.map((_region) => ( ))}
)}
{user?.address .filter((add) => add.emirate?.id === emirate?.id) .map((address) => ( ))}
); }; export default MapModal; // File: src/components/modals/language-country-modal.tsx import { useState } from "react"; import Logo from "../../assets/logo"; import { cn } from "../../lib/utils"; import { Country, Language } from "../../types/user"; import useUserStore from "../../store/userStore"; import { useTranslation } from "react-i18next"; import useModalsStore from "../../store/modalsStore"; import Cookies from "js-cookie"; export default function LanguageCountryModal() { const { isLanguageCountryModalOpen, setLanguageCountryModalOpen, setLoginModalOpen, } = useModalsStore(); const [step, setStep] = useState<1 | 2>(1); const user = useUserStore((state) => state.user); const { i18n, t } = useTranslation(); const [lang, setLang] = useState(i18n.language as Language); const setLanguage = useUserStore((state) => state.setLanguage); const [cou, setCou] = useState("uae"); const setCountry = useUserStore((state) => state.setCountry); const setMapModalOpen = useModalsStore((state) => state.setMapModalOpen); const handleContinue = () => { switch (step) { case 1: setStep(2); break; case 2: setCountry(cou); localStorage.setItem("country", cou); setLanguageCountryModalOpen(false); if (localStorage.getItem("firstVisit")) { setMapModalOpen(true); } setStep(1); break; } }; return (
{step === 1 && ( <> {t("modals.select-language")} )} {step === 2 && ( <> {t("modals.select-country")} )}
); } // File: src/components/modals/modal.tsx import { cn } from "../../lib/utils"; import useModalsStore from "../../store/modalsStore"; import LoadingSpinner from "../loading-spinner"; export default function Modal() { const { title, icon, confirmText, cancelText, onConfirm, onCancel, loading, variant, content, } = useModalsStore((state) => state.modalContent); const isModalOpen = useModalsStore((state) => state.isModalOpen); return (
{icon}
{title} {content &&
{content}
}
); } // File: src/components/modals/search.tsx import React from "react"; import usePlacesAutocomplete, { getGeocode, getLatLng, } from "use-places-autocomplete"; export default function Search({ setMarker, setMapCenter, }: { setMarker: React.Dispatch>; setMapCenter: React.Dispatch< React.SetStateAction >; }) { const { ready, value, suggestions: { status, data }, setValue, clearSuggestions, } = usePlacesAutocomplete({ requestOptions: { /* Define search scope here */ }, debounce: 300, }); const handleInput = (e: { target: { value: string } }) => { // Update the keyword of the input element setValue(e.target.value); }; const handleSelect = ({ description }: { description: string }) => () => { // When user selects a place, we can replace the keyword without request data from API // by setting the second parameter to "false" setValue(description, false); clearSuggestions(); // Get latitude and longitude via utility functions getGeocode({ address: description }) .then((results) => getLatLng(results[0])) .then(({ lat, lng }) => { setMarker({ lat, lng }); setMapCenter({ lat, lng }); }) .catch((error) => { console.log("😱 Error: ", error); }); }; const renderSuggestions = () => data.map((suggestion) => { const { place_id, structured_formatting: { main_text, secondary_text }, } = suggestion; return (
  • {main_text}{" "} {secondary_text}
  • ); }); return (
    {status === "OK" && (
      {renderSuggestions()}
    )}
    ); } // File: src/components/modals/select-country-code.tsx import { useTranslation } from "react-i18next"; import countries from "../../assets/countries.json"; import { cn } from "../../lib/utils"; export default function SelectCountryCode({ countryCode, setCountryCode, setIsCountryCodeSelecting, }: { countryCode: string; setCountryCode: (code: string) => void; setIsCountryCodeSelecting: (isSelecting: boolean) => void; }) { const { t } = useTranslation(); return ( <> {t("modals.select-country-code")}
    {countries.map((country) => ( ))}
    ); } // File: src/components/scroll-to-top.tsx import { useEffect } from "react"; import { useLocation } from "react-router-dom"; export default function ScrollToTop() { const { pathname } = useLocation(); useEffect(() => { window.scrollTo(0, 0); }, [pathname]); return null; } // File: src/components/terms-conditions/terms-accordion.tsx import { useState } from "react"; import { cn } from "../../lib/utils"; export default function TermsAccordion({ title, children }: { title: string; children: React.ReactNode }) { const [isOpen, setIsOpen] = useState(false); return (
    {isOpen &&
    {children}
    }
    ); } // File: src/error-page.tsx import { Link } from "react-router-dom"; export default function ErrorPage() { return ( <>

    404 Error

    Sorry! We could not find you the page you are looking for. Please check URL in address bar and try again.

    Go to Homepage
    ); } const ErrorImage = () => ( ); // File: src/i18n.ts import i18n from "i18next"; import { initReactI18next } from "react-i18next"; import LanguageDetector from "i18next-browser-languagedetector"; import Backend from "i18next-http-backend"; import translationsEN from "./locales/en.json"; import translationsAR from "./locales/ar.json"; const resources = { en: { translation: translationsEN, }, ar: { translation: translationsAR, }, }; i18n.use(Backend) // detect user language // learn more: https://github.com/i18next/i18next-browser-languageDetector .use(LanguageDetector) // pass the i18n instance to react-i18next. .use(initReactI18next) // init i18next // for all options read: https://www.i18next.com/overview/configuration-options .init({ debug: true, fallbackLng: "en", interpolation: { escapeValue: false, // not needed for react as it escapes by default }, resources, }); export default i18n; // File: src/index.tsx import ReactDOM from "react-dom/client"; import { createBrowserRouter, RouterProvider } from "react-router-dom"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { ToastContainer } from "react-toastify"; import "./index.css"; import "./i18n"; import "react-toastify/dist/ReactToastify.css"; import Root from "./routes/root"; import ErrorPage from "./error-page"; import Home from "./routes/home"; import Category from "./routes/category"; import Product from "./routes/product"; import Shop from "./routes/shop"; import Account from "./routes/account"; import EditAccount from "./components/account/edit-account"; import ChangePassword from "./components/account/change-password"; import Favourite from "./components/account/favourite"; import ChangeLanguage from "./components/account/change-language"; import Cart from "./routes/cart"; import RetiredMilitary from "./components/account/retired-military"; import SpecialCustomer from "./components/account/special-customer"; import ShareApp from "./components/account/share-app"; import MyPoints from "./components/account/my-points"; import Orders from "./components/account/orders"; import TermsConditions from "./routes/terms-conditions"; import Offers from "./routes/offers"; import RegisterStore from "./routes/register-store"; import RequestReviewAll from "./routes/request-review-all"; import Addresses from "./components/account/addresses"; import AddNewAddress from "./components/account/add-new-address"; import RequestReviewGifts from "./routes/request-review-gifts"; import Search from "./routes/search"; import RequestReviewSacrifices from "./routes/request-review-sacrifices"; const router = createBrowserRouter([ { path: "/", element: , errorElement: , children: [ { path: "/", element: }, { path: "search", element: }, { path: "category/:categoryId", element: }, { path: "product/:productId", element: }, { path: "shop/:shopId", element: }, { path: "cart", element: }, { path: "request-review/all", element: }, { path: "change-password", element: }, { path: "account", element: , children: [ { path: "edit-profile", element: }, { path: "", element: }, { path: "favourite", element: }, { path: "change-language", element: }, { path: "retired-military", element: }, { path: "share-app", element: }, { path: "orders", element: }, { path: "my-points", element: }, { path: "special-customer", element: }, { path: "addresses", element: }, { path: "add-new-address", element: }, ], }, { path: "offers", element: }, { path: "register-store", element: }, { path: "terms-conditions", element: }, { path: "add-new-address", element: }, { path: "request-review/gifts", element: }, { path: "request-review/sacrifices", element: }, { path: "*", element: }, ], }, ]); export const queryClient = new QueryClient({ defaultOptions: { queries: { refetchOnWindowFocus: false, }, }, }); const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement); root.render( , ); // File: src/lib/urls.ts export const LOGIN_URL = process.env.REACT_APP_BACKEND_URL + "/user/login"; export const USER_URL = process.env.REACT_APP_BACKEND_URL + "/user/login_by_id"; export const REGISTER_URL = process.env.REACT_APP_BACKEND_URL + "/user/register"; export const SEND_OTP_URL = process.env.REACT_APP_BACKEND_URL + "/user/forget_password/send_code"; export const VERIFY_OTP_URL = process.env.REACT_APP_BACKEND_URL + "/user/forget_password/check_code"; export const RESET_PASSWORD_URL = process.env.REACT_APP_BACKEND_URL + "/user/forget_password/reset_password"; export const HOME_URL = process.env.REACT_APP_BACKEND_URL + "/home"; export const CATEGORY_URL = process.env.REACT_APP_BACKEND_URL + "/categories"; export const SOCIAL_LOGIN_URL = process.env.REACT_APP_BACKEND_URL + "/user/login/social"; export const PRODUCT_URL = process.env.REACT_APP_BACKEND_URL + "/product"; // File: src/lib/utils.ts import { type ClassValue, clsx } from "clsx"; import { md5 } from "js-md5"; import { twMerge } from "tailwind-merge"; import i18n from "../i18n"; import Cookies from "js-cookie"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } export function md5Encrypt(text: string) { return md5(text + "Jjwar@W"); } export const toArabic = (numString: string) => { if (i18n.language === "en") return numString; const arabicNumbers = ["Ù ", "Ù¡", "Ù¢", "Ù£", "Ù¤", "Ù¥", "Ù¦", "Ù§", "Ù¨", "Ù©"]; return numString.replace(/\d/g, (digit) => arabicNumbers[Number(digit)]); }; export const getToken = () => { return Cookies.get("token"); }; // File: src/loading-page.tsx import LoadingSpinner from "./components/loading-spinner"; export default function LoadingPage() { return (
    ); } // File: src/routes/account.tsx import { useTranslation } from "react-i18next"; import { Link, Outlet, useNavigate } from "react-router-dom"; import { useState, Fragment } from "react"; import Cookies from "js-cookie"; import axios from "axios"; import { toast } from "react-toastify"; import useModalsStore from "../store/modalsStore"; import useUserStore from "../store/userStore"; import { queryClient } from ".."; import Logo from "../assets/logo"; import { BankCard, Chevron, DiscountCoupon, Earth, Favorite, Logout, Medal, RetiredMilitary, SavedAddresses, Share, Star, Orders, Camera, Edit, } from "../assets/icons"; const linkData = [ { to: "/account/orders", bgColor: "bg-white", Component: Orders, text: "account.orders" }, { to: "/account/my-points", bgColor: "bg-[rgba(255,253,221,1)]", Component: Star, text: "account.my-points" }, { to: "/account/edit-profile", bgColor: "bg-[rgba(241,255,247,1)]", Component: Edit, text: "account.edit" }, ]; const links = [ { to: "/account/favourite", icon: , text: "account.favourite" }, { to: "/account/addresses", icon: , text: "account.saved-addresses" }, { to: "/account", icon: , text: "account.bank-cards" }, { to: "/account", icon: , text: "account.discount-coupons" }, { to: "/account/retired-military", icon: , text: "account.retired-military-association" }, { to: "/account/share-app", icon: , text: "account.share-app" }, { to: "/account/change-language", icon: , text: "account.change-language" }, ]; export default function Account() { const { t } = useTranslation(); const navigate = useNavigate(); const [loading, setLoading] = useState(false); const { user, setUser } = useUserStore(); const { setModalContent, setModalOpen } = useModalsStore(); const handleLogout = async () => { setLoading(true); setUser(null); try { await axios.get("/user/logout"); axios.defaults.headers.common.authorization = ""; queryClient.invalidateQueries({ queryKey: ["global"] }); toast.success(t("account.success-logout")); setModalOpen(false); localStorage.remove("user"); Cookies.remove("token"); navigate("/"); } catch (error) { console.error(error); } finally { setLoading(false); } }; const handleLogoutModal = () => { setModalContent({ title: ( <> {t("do-you-want")} {t("logout")} {t("?")} ), confirmText: t("yes"), cancelText: t("no"), onConfirm: handleLogout, variant: "wide-btns", onCancel: () => setModalOpen(false), loading, icon: , }); setModalOpen(true); }; return (
    {user?.first_name}
    {user?.first_name} {user?.last_name} +{user?.country_code} {user?.mobile}
    {linkData.map(({ to, bgColor, Component, text }, index) => ( {t(text)} ))}
    {t("account.special-customer")}
    {t("account.get-points")} {t("account.get-points-desc")}
    {links.map(({ to, icon, text }, index) => ( {icon} {t(text)}
    ))}
    ); } // File: src/routes/cart.tsx import { useTranslation } from "react-i18next"; import { useEffect, useState } from "react"; import { getCart } from "../actions/getCart"; import axios from "axios"; import { useQuery } from "@tanstack/react-query"; import { cn } from "../lib/utils"; import AddToFavouriteButton from "../components/home/add-to-favourite-button"; import LoadingScreen from "../components/loading-screen"; import { CartItem } from "../types/cart"; import QuantityPrice from "../components/cart/quantity-price"; import { Link } from "react-router-dom"; import useModalsStore from "../store/modalsStore"; import NoDataIcon from "../assets/no-data-icon"; import { Delete, Warning } from "../assets/icons"; const tabButtons = [ { key: "all", filter: (item: CartItem) => item.department.id !== 168 && item.department.id !== 2 && item.department.id !== 3, }, { key: "gifts", filter: (item: CartItem) => item.department.id === 168, }, { key: "sacrifices", filter: (item: CartItem) => item.department.id === 2, }, { key: "donations", filter: (item: CartItem) => item.department.id === 3, }, ]; export default function Cart() { const { t } = useTranslation(); const setModalOpen = useModalsStore((state) => state.setModalOpen); const setModalContent = useModalsStore((state) => state.setModalContent); const modalContent = useModalsStore((state) => state.modalContent); const resetModalContent = useModalsStore( (state) => state.resetModalContent, ); const { data, isLoading, refetch } = useQuery({ queryKey: ["global", "cart"], queryFn: getCart, }); const [tab, setTab] = useState("all"); const [filteredCartItems, setFilteredCartItems] = useState([]); useEffect(() => { if (data) { setFilteredCartItems( tab === "all" ? data.filter( (item) => item.department.id !== 168 && item.department.id !== 2 && item.department.id !== 3, ) : tab === "gifts" ? data.filter((item) => item.department.id === 168) : tab === "sacrifices" ? data.filter((item) => item.department.id === 2) : data.filter((item) => item.department.id === 3), ); } }, [data]); useEffect(() => { setTotalPrice( filteredCartItems.reduce( (acc, item) => acc + item.details.reduce( (acc, detail) => acc + +detail.quantity * +detail.product.price, 0, ), 0, ), ); }, [filteredCartItems]); const [totalPrice, setTotalPrice] = useState(0); if (isLoading) return ; if (!data) return null; const deleteFromCart = async (cart_detail_id: number) => { // setModalContent({ // ...modalContent, // loading: true, // }); try { const formData = new FormData(); formData.append("cart_detail_id", cart_detail_id.toString()); await axios.post("/cart/delete", formData); await refetch(); resetModalContent(); setModalOpen(false); } catch (error) { console.error(error); } }; const handleDeleteFromCart = (cart_detail_id: number) => { setModalContent({ title: t("delete-product"), confirmText: t("yes"), cancelText: t("no"), onConfirm: () => { deleteFromCart(cart_detail_id); }, onCancel: () => { setModalOpen(false); }, loading: false, icon: , }); setModalOpen(true); }; const handleTabClick = ( key: string, filter: (item: CartItem) => boolean, ) => { setTab(key); setFilteredCartItems(data.filter(filter)); }; return (

    {t("cart")}

    {tabButtons.map(({ key, filter }) => ( ))}
    {filteredCartItems.length > 0 ? (
    {filteredCartItems.map((item, idx) => (
    {t("delivery-no")} {idx + 1}
    {item.details.map((detail) => (
    {detail.product.name}
    { detail.product .name } { detail.category .name }
    ))}
    ))}
    {t("price")} {totalPrice} {t("AED")}
    {t("complete-order")}
    ) : ( )}
    ); } // File: src/routes/category.tsx import { Link, useParams } from "react-router-dom"; import { getCategory } from "../actions/getCategory"; import { useTranslation } from "react-i18next"; import { useQuery } from "@tanstack/react-query"; import ShopsSection from "../components/home/shops-section"; import ProductsSection from "../components/home/products-section"; import LoadingScreen from "../components/loading-screen"; import { useState } from "react"; export default function Category() { const { categoryId } = useParams(); const [subCategoryIndex, setSubCategoryIndex] = useState(0); const { t } = useTranslation(); const { data, isLoading } = useQuery({ queryKey: ["global", "category", { categoryId }], queryFn: () => getCategory({ categoryId: categoryId || "", }), }); if (isLoading) return ; if (!data) return null; return (

    {data.name}

    {data.sub_categories.length > 0 && (
    {data.sub_categories?.map((subCategory, idx) => ( ))}
    )} {data.sub_categories.length > 0 && (data.sub_categories[subCategoryIndex]?.sub_categories?.length ?? 0) > 0 && (

    {t("species")} {data.sub_categories[subCategoryIndex].name}{" "} {t("species")}

    {data.sub_categories[subCategoryIndex]?.sub_categories?.map((subCategory) => ( {subCategory.name} {subCategory.name} ))}
    )} {data.products.length > 0 && } {data.shops.length > 0 && }
    ); } // File: src/routes/home.tsx import { useQuery } from "@tanstack/react-query"; import CategoriesSection from "../components/home/categories-section"; import Hero from "../components/home/hero"; import ProductsSection from "../components/home/products-section"; import { getHomePage } from "../actions/getHomePage"; import { useTranslation } from "react-i18next"; import HomeProductsSection from "../components/home/home-products-section"; import HomeShopsSection from "../components/home/home-shops-section"; import LoadingPage from "../loading-page"; export default function Home() { const { t } = useTranslation(); const { data, isFetching } = useQuery({ queryKey: ["global", "home-page"], queryFn: getHomePage, }); if (isFetching) return ; if (!data) return null; return ( <>
    {data.limited_offer && data.limited_offer.length > 0 && ( )} {data.home_sections.map((section) => { if (section.type === "product" && section.items.length > 0) { return ; } if (section.type === "shop" && section.items.length > 0) { return ; } return null; })}
    ); } // File: src/routes/offers.tsx import { useQuery } from "@tanstack/react-query"; import axios from "axios"; import { useTranslation } from "react-i18next"; import Product from "../types/product"; import { Shop } from "../types/shop"; import LoadingScreen from "../components/loading-screen"; import ProductCard from "../components/home/product-card"; import ShopCard from "../components/home/shop-card"; export default function Offers() { const { t } = useTranslation(); const { data, isFetching } = useQuery({ queryKey: ["global", "offers"], queryFn: async () => { const response = await axios.get("/offers"); return response.data.data as { products: Product[]; shops: Shop[]; }; }, }); if (isFetching) return ; if (!data) return null; return (

    {t("offers")}

    {t("all-products")}

    {data.products.map((product) => ( ))}

    {t("all-shops")}

    {data.shops.map((shop) => ( ))}
    ); } // File: src/routes/product.tsx import { useQuery } from "@tanstack/react-query"; import { useTranslation } from "react-i18next"; import { getProduct } from "../actions/getProduct"; import LoadingSpinner from "../components/loading-spinner"; import { useEffect, useState } from "react"; import { useParams } from "react-router"; import Accordion from "../components/accordion"; import AttributeCard from "../components/attribute-card"; import { cn } from "../lib/utils"; import { Swiper, SwiperSlide } from "swiper/react"; import "swiper/css"; import useUserStore from "../store/userStore"; import { toast } from "react-toastify"; import axios from "axios"; import useModalsStore from "../store/modalsStore"; import AddToFavouriteButton from "../components/home/add-to-favourite-button"; import LoadingScreen from "../components/loading-screen"; import { useNavigate } from "react-router-dom"; import Logo from "../assets/logo"; import HeaderLogo from "../assets/header-logo"; export default function Product() { const { productId } = useParams(); const { i18n, t } = useTranslation(); const { data, isLoading } = useQuery({ queryKey: ["global", "product", { productId }], queryFn: () => getProduct({ productId: productId || "", }), }); const [subProductIndex, setSubProductIndex] = useState(0); const [containsIndex, setContainsIndex] = useState(0); const [packagingIndex, setPackagingIndex] = useState(0); const [cuttingIndex, setCuttingIndex] = useState(0); const [wantCook, setWantCook] = useState(false); const [quantity, setQuantity] = useState(1); const [price, setPrice] = useState("0"); const [remainingDays, setRemainingDays] = useState(0); const [remainingHours, setRemainingHours] = useState(0); const [remainginMinutes, setRemainginMinutes] = useState(0); const [remainginSeconds, setRemainginSeconds] = useState(0); const [discountPrecentage, setDiscountPrecentage] = useState(0); const [isExpired, setIsExpired] = useState(false); const setModalOpen = useModalsStore((state) => state.setModalOpen); const setModalContent = useModalsStore((state) => state.setModalContent); useEffect(() => { let _remainingDays = 0, _remainingHours = 0, _remainginMinutes = 0, _remainginSeconds = 0, _isExpired = false; if ( data?.limited_offer.has_limited_offer && data?.limited_offer.limited_offer_expired_at ) { const expiredAt = new Date( data?.limited_offer.limited_offer_expired_at, ); const now = new Date(); const diff = expiredAt.getTime() - now.getTime(); _isExpired = diff < 0; _remainingDays = Math.floor(diff / (1000 * 60 * 60 * 24)); _remainingHours = Math.floor( (diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60), ); _remainginMinutes = Math.floor( (diff % (1000 * 60 * 60)) / (1000 * 60), ); _remainginSeconds = Math.floor((diff % (1000 * 60)) / 1000); } let _discountPrecentage = 0; if (data?.old_price && data?.price) { _discountPrecentage = (1 - Number(data?.price) / Number(data?.old_price)) * 100; } setRemainingDays(_remainingDays); setRemainingHours(_remainingHours); setRemainginMinutes(_remainginMinutes); setRemainginSeconds(_remainginSeconds); setDiscountPrecentage(_discountPrecentage); setIsExpired(_isExpired); setPrice(data?.price || "0"); }, [data]); const navigate = useNavigate(); useEffect(() => { if (!data || isExpired || !data.limited_offer.has_limited_offer) return; const interval = setInterval(() => { if (isExpired) return; if (remainginSeconds > 0) { setRemainginSeconds(remainginSeconds - 1); } else { if (remainginMinutes > 0) { setRemainginMinutes(remainginMinutes - 1); setRemainginSeconds(59); } else { if (remainingHours > 0) { setRemainingHours(remainingHours - 1); setRemainginMinutes(59); } else { if (remainingDays > 0) { setRemainingDays(remainingDays - 1); setRemainingHours(23); } } } } }, 1000); return () => clearInterval(interval); }, [ remainginSeconds, remainginMinutes, remainingHours, remainingDays, data, isExpired, ]); const user = useUserStore((state) => state.user); const setLoginModal = useModalsStore((state) => state.setLoginModalOpen); const [loading, setLoading] = useState(false); if (isLoading) return ; if (!data) return null; const addToCart = async () => { if (!user) { toast.error(t("messages.login-cart")); setLoginModal(true); return; } setLoading(true); const attributes = data.attributes?.map((attribute) => { if (attribute.section_name === "cutting") { return { attribute_id: attribute.id, value_id: attribute.values[cuttingIndex].id, }; } if (attribute.section_name === "without") { return { attribute_id: attribute.id, value_id: attribute.values[containsIndex].id, }; } return { attribute_id: attribute.id, value_id: null, }; }) || []; const cartProduct = { department_id: data.department.id, category_id: data.category.id, product_id: data.id, quantity: quantity, shop_id: data.shop?.id || null, attributes: attributes, }; try { await axios.post("/cart/add", cartProduct); toast.success(t("messages.added-to-cart")); setModalOpen(true); setModalContent({ confirmText: t("go-to-cart"), cancelText: t("continue-shopping"), title: t("new-product-added"), onCancel() { setModalOpen(false); navigate("/"); }, onConfirm() { setModalOpen(false); navigate("/cart"); }, icon: , content: (
    {data.name} {+data.price * quantity} {t("AED")}
    ), }); } catch (error) { toast.error(t("messages.failed-to-add")); } setLoading(false); }; return ( <>

    {data.name}

    {data.badge && (
    {data.badge.name}
    )} {data.badges && data.badges.length > 0 && data.badges.map((badge) => (
    {badge.name}
    ))} {!!data.limited_offer.has_limited_offer && !isExpired && (
    {remainingDays < 10 ? `0${remainingDays}` : remainingDays} : {remainingHours < 10 ? `0${remainingHours}` : remainingHours} : {remainginMinutes < 10 ? `0${remainginMinutes}` : remainginMinutes} : {remainginSeconds < 10 ? `0${remainginSeconds}` : remainginSeconds}
    )}
    {data.old_price && data.old_price !== "0.00" && (
    -{discountPrecentage.toFixed(0)}%
    )} {data.name}
    {data.name} {data.price} {t("AED")}
    {data.shop?.name || data.department.name} {data.category.name}
    {data.description && (

    {data.description}

    )} {data.sub_product && data.sub_product.length > 0 && (
    {data.sub_product.map((subProduct, index) => (
    {data.sub_product && index !== data.sub_product.length - 1 && (
    )}
    ))}
    )} {data.attributes && data.attributes.length > 0 && data.attributes.map((attribute) => { if (attribute.section_name === "textarea") return (