Untitled

mail@pastecode.io avatar
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