Untitled
unknown
plain_text
15 days ago
26 kB
2
Indexable
Never
import { atomIsDetectFooterInViewport } from '@src/atoms/routes/buy/single-book/isDetectFooterInViewport'; import IncrementAndDecrementBuyCartItemQuantity from '@src/components/routes/buy/global/incrementAndDecrementBuyCartItemQunatity'; import TOASTMSG from '@src/constants/global/toastMessages'; import convertNumber from '@src/functions/global/convertNumber'; import { generateUniqueId } from '@src/functions/global/generate-unique-id'; import { rialToToman } from '@src/functions/global/rialToToman'; import toast from '@src/functions/global/toast'; import useLocalStorage from '@src/hooks/global/localStorage'; import { APIaddToFavirateOrChangeInformmeStatus } from '@src/services/routes/global/addToFavirateOrChangeInformmeStatus'; import { APIfetchFavirateList } from '@src/services/routes/global/fetchFavirateList'; import { BuyCartItemType } from '@src/types/global/buyCartItem.type'; import { UserAuthType } from '@src/types/global/userAuth.type'; import { BookType } from '@src/types/routes/global/book.type'; import { FavoriteBookType } from '@src/types/routes/global/favirateBook.type'; import IconChevron from '@src/utils/icons/global/chevron'; import IconCircleDollar from '@src/utils/icons/routes/buy/global/circleDollar'; import IconGuardTick from '@src/utils/icons/routes/buy/single-book/guardTick'; import IconShoppingCartWithEye from '@src/utils/icons/routes/buy/single-book/shoppingCartWithEye'; import IconStarSpeed from '@src/utils/icons/routes/buy/single-book/starSpeed'; import IconCheckBoxTick from '@src/utils/icons/routes/global/checkBoxTick'; import { useAtom } from 'jotai'; import Link from 'next/link'; import { useRouter } from 'next/router'; import { FC, useEffect, useState } from 'react'; import { ThreeDots } from 'react-loader-spinner'; import useSWR, { mutate } from 'swr'; interface IProps { singleBookData: BookType; } const BuySingleBook: FC<IProps> = ({ ...props }): JSX.Element => { // local-storage state status : undefined (loading before set local-storage data), [] (initial value and not found key in local storage), buyCartItemsData const [localStorageBuyCartItems, setLocalStorageBuyCartItems] = useLocalStorage<undefined | [] | BuyCartItemType[]>('buyCartItems', []); // get phone number and token from local storage for api const [localStorageUserAuthData] = useLocalStorage< undefined | null | UserAuthType >('userAuth', null); // for fetch favirate books list from api async function fetcherFetchFavirateList() { try { const { data } = await APIfetchFavirateList({ phoneNumber: localStorageUserAuthData!.loginid, }); return data; } catch (error: any) { if (!error.response) { toast(TOASTMSG.routes.global.pleaseCheckNetworkConnection); } else { toast(TOASTMSG.routes.global.sorryUnexpectedError); } } return null; } const { data: favirateBooksData } = useSWR<FavoriteBookType[] | null>( localStorageUserAuthData ? `favirateList` : null, fetcherFetchFavirateList, ); // for hide mobile-bottom-buyBook in /buy/single-book/:slug after detect footer in view-port const [atomStateIsDetectFooterInViewport] = useAtom( atomIsDetectFooterInViewport, ); // for show loader after disabled informme button const [disabledInformmeBtn, setDisabledInformmeBtn] = useState<boolean>(false); // router for detect current route and navigate user to signin page const router = useRouter(); // check is book in favirate list const [findedBookInFavirateList, setFindedBookInFavirateList] = useState<null | FavoriteBookType>(null); useEffect((): void => { if (localStorageUserAuthData && favirateBooksData) { // check is book in favirate list setFindedBookInFavirateList( favirateBooksData.find( (item) => item.isbn === String(props.singleBookData.isbn), ) ?? null, ); } }, [localStorageUserAuthData, favirateBooksData]); // book quality option for select option const bookQualityOption: { bookId: number; label: 'در حد نو' | 'خوب' | 'قابل قبول' | 'نو'; value: 'high' | 'medium' | 'low' | 'super-high'; status: 1 | 2 | 3 | 4; advantages: string[]; quantity: number; discount: number; orginalPrice: number; priceWithoutDiscount: number; priceWithDiscount: number; }[] = [ { bookId: props.singleBookData.inventoryid, label: 'در حد نو', value: 'high', status: 1, advantages: [ 'خوانده نشده و یا با احتیاط خوانده شده', 'عدم / حداقل مصرف شدگی', 'دوستدار محیط زیست', 'بدون خط خوردگی و یا هرگونه یادداشت شدگی', 'مناسب برای هدیه دادن', ], quantity: props.singleBookData.buy_status1 - props.singleBookData.sell_status1, discount: props.singleBookData.current_discount, orginalPrice: rialToToman({ price: props.singleBookData.current_price, }), priceWithoutDiscount: rialToToman({ price: props.singleBookData.sell_price, }), priceWithDiscount: rialToToman({ price: props.singleBookData.sell_price * (1 - props.singleBookData.current_discount / 100), }), }, { bookId: props.singleBookData.inventoryid, label: 'خوب', value: 'medium', status: 2, advantages: [ 'وضعیت کاملا سالم با کمی اثرات سطحی مصرف شدگی', 'مناسب برای محیط زیست', 'خوانا و تمیز', 'نوشته های کم و اثر ورق خوردگی', 'مناسب برای هدیه دادن', ], quantity: props.singleBookData.buy_status2 - props.singleBookData.sell_status2, discount: props.singleBookData.current_discount, orginalPrice: rialToToman({ price: props.singleBookData.current_price, }), priceWithoutDiscount: rialToToman({ price: (props.singleBookData.sell_price * 85) / 100, }), priceWithDiscount: rialToToman({ price: (props.singleBookData.sell_price * (1 - props.singleBookData.current_discount / 100) * 85) / 100, }), }, { bookId: props.singleBookData.inventoryid, label: 'قابل قبول', value: 'low', status: 3, advantages: [ 'دوستدار محیط زیست', 'اثرات مصرف شدگی بر روی بعضی از صفحات قابل مشاهده است.', 'گوشه بعضی از صفحات برای علامتگذاری تا شده است.', 'ارزان', 'مناسب برای استفاده شخصی', ], quantity: props.singleBookData.buy_status3 - props.singleBookData.sell_status3, discount: props.singleBookData.current_discount, orginalPrice: rialToToman({ price: props.singleBookData.current_price, }), priceWithoutDiscount: rialToToman({ price: (props.singleBookData.sell_price * 70) / 100, }), priceWithDiscount: rialToToman({ price: (props.singleBookData.sell_price * (1 - props.singleBookData.current_discount / 100) * 70) / 100, }), }, { bookId: props.singleBookData.inventoryid, label: 'نو', value: 'super-high', status: 4, advantages: [ 'این کتاب به طور مستقیم از ناشر آن تهیه شده و کاملا نو میباشد.', ], quantity: props.singleBookData.buy_status4 - props.singleBookData.sell_status4, discount: Number(process.env['discountForBookWithQualitySuperHigh']), orginalPrice: rialToToman({ price: props.singleBookData.current_price, }), priceWithoutDiscount: rialToToman({ price: props.singleBookData.current_price, }), priceWithDiscount: rialToToman({ price: props.singleBookData.current_price * (1 - Number(process.env['discountForBookWithQualitySuperHigh']) / 100), }), }, ]; // selected quality (default active from high quality to low quality, if not quantity active high quality) // default active 'high quantity' and update state with useEffect dependencies const [selectedQuality, setSelectedQuality] = useState(bookQualityOption[0]); useEffect((): void => { setSelectedQuality( bookQualityOption[0]?.quantity ? bookQualityOption[0] : bookQualityOption[1]?.quantity ? bookQualityOption[1] : bookQualityOption[2]?.quantity ? bookQualityOption[2] : bookQualityOption[3]?.quantity ? bookQualityOption[3] : bookQualityOption[0], ); }, [props.singleBookData]); // add book to buy cart handler const addBookToBuyCartHandler = (): void => { // show toast add book to buy cart successfully toast({ type: 'success', text: `کتاب "${props.singleBookData.title}" با کیفیت "${ selectedQuality!.label }" به سبد خرید اضافه شد.`, }); // add book to buyCartItems (localStorage) localStorageBuyCartItems && setLocalStorageBuyCartItems([ { bookDataForCheckout: { status: selectedQuality!.status, quantity: 1, isbn: props.singleBookData.isbn, sell_price: Math.round(selectedQuality!.priceWithoutDiscount * 10), current_discount: selectedQuality!.discount, weight: props.singleBookData.weight, modifyPrice: Math.round(selectedQuality!.priceWithDiscount * 10), }, id: props.singleBookData.inventoryid, name: props.singleBookData.title, isbn: props.singleBookData.isbn, url: props.singleBookData.bookurl, image: props.singleBookData.image_address, author: props.singleBookData.authors, translator: props.singleBookData.translator, publisher: props.singleBookData.publisher, weight: props.singleBookData.weight, qualityType: selectedQuality!.label, quantity: 1, maxQuantity: selectedQuality!.quantity, discount: selectedQuality!.discount, orginalPrice: selectedQuality!.orginalPrice, priceWithoutDiscount: selectedQuality!.priceWithoutDiscount, priceWithDiscount: selectedQuality!.priceWithDiscount, }, ...localStorageBuyCartItems, ]); }; // find book with current selected qualityType in buyCartItems (localStorage) // finded ? cartItem : null const [ findedBookWithCurrentQualityTypeInBuyCartItems, setFindedBookWithCurrentQualityTypeInBuyCartItems, ] = useState<null | BuyCartItemType>(null); useEffect((): void => { if (localStorageBuyCartItems !== undefined) { const findedBook = localStorageBuyCartItems.find( (item) => item.id === selectedQuality!.bookId && item.qualityType === selectedQuality!.label, ); setFindedBookWithCurrentQualityTypeInBuyCartItems( findedBook ? findedBook : null, ); } }, [localStorageBuyCartItems, selectedQuality]); // for add book to favirate list or change informme status async function fetcherAddToFavirateOrChangeInformmeStatus() { try { const { msg } = await APIaddToFavirateOrChangeInformmeStatus({ phoneNumber: localStorageUserAuthData!.loginid, token: localStorageUserAuthData!.token, isbn: String(props.singleBookData.isbn), informmeStatus: findedBookInFavirateList?.informme === '1' ? false : true, }); toast({ text: msg, type: 'success' }); // if is book in favirate list change informme status and book is not in favirate list add book to favirate list let updatedData = []; findedBookInFavirateList ? (updatedData = favirateBooksData!.map((item) => { if (item.isbn === findedBookInFavirateList.isbn) { return { ...item, informme: findedBookInFavirateList.informme === '1' ? '0' : '1', }; } else { return item; } })) : (updatedData = favirateBooksData!.concat({ isbn: String(props.singleBookData.isbn), informme: '1', title: props.singleBookData.title, authors: props.singleBookData.authors, translator: props.singleBookData.translator, publisher: props.singleBookData.publisher, image_address: props.singleBookData.image_address, bookurl: props.singleBookData.bookurl, })); mutate(`favirateList`, updatedData, false); } catch (error: any) { if (!error.response) { toast(TOASTMSG.routes.global.pleaseCheckNetworkConnection); } else { toast(TOASTMSG.routes.global.sorryUnexpectedError); } } finally { setDisabledInformmeBtn(false); } } const addToFavirateListOrChangeInformmeStatusHandler = (): void => { // if user is login ? add book to favirate list or change informme status : redirect to login page if (localStorageUserAuthData) { setDisabledInformmeBtn(true); fetcherAddToFavirateOrChangeInformmeStatus(); } else { toast(TOASTMSG.routes.global.pleaseLogin); router.push(`/auth/sign-in/?navigate=${router.asPath}`); } }; return ( <section className="h-fit rounded-xl border border-[#e3e3e3] bg-[#fafafa] px-4"> {localStorageBuyCartItems ? ( <div className="pt-3"> {/* only render if book is in stock */} {!!selectedQuality?.quantity && ( <> {/* select quality */} <div className="bo flex w-full flex-col gap-2 pb-2.5 pt-1"> <span className="whitespace-nowrap text-sm font-semibold text-[#5B5B63]"> انتخاب کیفیت کتاب </span> <div className="grid w-full grid-cols-2 gap-1.5"> {bookQualityOption.map((item) => ( <button key={generateUniqueId()} disabled={!item.quantity} className={`t relative rounded-[10px] border pb-0.5 pt-1.5 text-sm text-gray-800 ${ selectedQuality?.label === item.label ? 'border-c-purple bg-c-purple/5' : 'border-[#adadad]' } ${ !item.quantity ? 'pointer-events-none border-opacity-75 opacity-50' : 'pointer-events-auto border-opacity-100 opacity-100' }`} onClick={() => { setSelectedQuality(item); }} > <div className={`absolute right-[5px] top-[5px] flex size-[18px] items-center justify-center rounded-md bg-c-purple ${ selectedQuality?.label === item.label ? 'block' : 'hidden' }`} > <IconCheckBoxTick color="#fff" /> </div> <p className="font-medium">{item.label}</p> { // is quality in stock ? show priceWithDiscount : show 'ناموجود' item.quantity ? ( <span className="text-[13px] font-normal"> {Math.round( Number(item.priceWithDiscount), ).toLocaleString('fa-IR')}{' '} <span className="text-[13px] font-normal"> تومان </span> </span> ) : ( <span className="text-[13px] font-normal"> ناموجود </span> ) } </button> ))} </div> </div> {/* advantages quality */} <div className="space-y-2 border-t border-[#E3E3E3] py-4"> {selectedQuality?.advantages.map((item) => { return ( <div key={item} className="flex items-center gap-1 text-sm text-[#535660]" > <IconChevron size={12} color="#666974" position="left" /> <span>{item}</span> </div> ); })} </div> {/* quantity */} <div className="flex items-center gap-2 border-t border-[#e3e3e3] py-4"> <IconStarSpeed /> <span className="text-sm font-semibold text-c-purple"> تعداد موجود در انبار :{' '} {convertNumber({ number: String(selectedQuality.quantity), type: 'to-persian', })}{' '} عدد </span> </div> </> )} {/* orginal-price */} {!!(selectedQuality?.status !== 4) && ( <div className={`flex items-center gap-2 border-t ${ selectedQuality?.quantity ? 'border-[#e3e3e3] py-4' : 'border-transparent pb-4 pt-2' }`} > <IconCircleDollar /> <div className="flex items-center gap-1 text-sm text-[#535660]"> <span> قیمت نو این کتاب : </span> {selectedQuality!.orginalPrice > selectedQuality!.priceWithDiscount ? ( <p> {Math.round(selectedQuality!.orginalPrice).toLocaleString( 'fa-IR', )}{' '} تومان! </p> ) : ( <hr className="h-[2px] w-[40px] bg-[#535660]" /> )} </div> </div> )} {/* only render if book is in stock */} {!!selectedQuality?.quantity && ( <> {/* quality guarante */} <div className="flex items-center gap-2 border-t py-4"> <IconGuardTick /> <p className="text-sm text-[#535660]"> تایید کیفیت توسط ریباکس! </p> </div> </> )} {/* add to cart or informme */} <div className={`container fixed inset-x-0 z-30 flex w-full justify-between border-t border-[#e3e3e3] bg-white py-3 transition-all duration-500 lg:static lg:mb-3 lg:flex-col lg:space-y-2 lg:bg-transparent lg:pb-0 lg:pt-4 ${ atomStateIsDetectFooterInViewport ? 'bottom-[-300px]' : 'bottom-0' }`} > {/* price-with-discount, price-without-discount, discount */} <div className={`flex-col justify-center lg:items-center lg:justify-start ${ selectedQuality?.quantity ? 'flex' : 'hidden' }`} > {selectedQuality?.discount ? ( <del className="ml-3 text-sm text-[#898D98] lg:text-[14.5px]"> {Math.round( selectedQuality!.priceWithoutDiscount, ).toLocaleString('fa-IR')}{' '} تومان </del> ) : null} <div> <div className="flex justify-center gap-2 pt-1 lg:pt-0"> <div className="flex items-center gap-1.5 text-[#535660]"> <span className="whitespace-nowrap text-[17px] font-semibold"> {Math.round( Number(selectedQuality!.priceWithDiscount), ).toLocaleString('fa-IR')}{' '} <span className="text-sm font-medium">تومان</span> </span> </div> {/* discount [optinal] */} {selectedQuality?.discount ? ( <span className="h-[23px] rounded-[13px] bg-c-red px-3 pt-1 text-[13px] text-white"> {convertNumber({ number: String(selectedQuality.discount), type: 'to-persian', })} % </span> ) : null} </div> <div className="flex items-center gap-1.5 font-medium text-[#535660] lg:hidden"> <span className="text-sm">کیفیت : </span> <span className="pb-0.5 text-[13.5px] font-medium"> {selectedQuality?.label} </span> </div> </div> </div> {/* check bookId+qualityType is in BuyCartItems ? show [increment and decrement quantity] : (is book qunatity ? show [add book to cart] : show informme) */} <div className="flex w-fit items-center justify-end lg:block lg:w-full"> {findedBookWithCurrentQualityTypeInBuyCartItems ? ( <> {/* increment and decrement quantity + go to /buy/cart */} <div className="flex items-center justify-center gap-4"> <div> <IncrementAndDecrementBuyCartItemQuantity buyCartItem={ findedBookWithCurrentQualityTypeInBuyCartItems } /> </div> <div className="hidden justify-center lg:flex"> <Link href="/buy/cart" className="flex items-center gap-1" > <IconShoppingCartWithEye /> <span className="pt-1 text-[12.5px] font-medium text-c-royal-blue"> مشاهده سبد خرید </span> </Link> </div> </div> </> ) : ( <> {selectedQuality?.quantity ? ( <> {/* add to cart btn */} <div className="flex justify-center"> <button onClick={addBookToBuyCartHandler} className="btn-purple w-full rounded-lg px-3 pb-2 pt-3 text-[12.5px] font-medium text-white transition-all duration-300 lg:mb-1.5 lg:max-w-[260px] lg:rounded-[10px] lg:py-[11px] lg:text-sm" > افزودن به سبد خرید </button> </div> </> ) : ( <> {/* add book to imformme or delete book from informe */} <div className="flex w-full justify-center"> <button disabled={ disabledInformmeBtn || (localStorageUserAuthData ? !favirateBooksData : false) } // if user not login or favirateBooksData not fetched or disableld is true ===> disable button onClick={ addToFavirateListOrChangeInformmeStatusHandler } className="btn-orange w-full max-w-xs rounded-[10px] py-3 text-sm font-medium text-white transition-all duration-300 lg:mb-1.5 lg:max-w-[260px] lg:py-[11px]" > {/* if disabledInformmeBtn is true ? show loader : (is book in favirates book and informme === '1' ? موجود شد خبرم کنید' : 'دیگر نیازی نیست خبرم کنید') */} {disabledInformmeBtn ? ( <div className="flex w-full justify-center"> <ThreeDots color="#fff" width={50} height={21} /> </div> ) : Number(findedBookInFavirateList?.informme) ? ( 'دیگر نیازی نیست خبرم کنید' ) : ( 'موجود شد خبرم کنید' )} </button> </div> </> )} </> )} </div> </div> </div> ) : ( <div className="flex justify-center py-4"> <ThreeDots color="#5036BB" width={70} height={50} /> </div> )} </section> ); }; export default BuySingleBook;
Leave a Comment