atlp-rwanda/hackers-ec-Fe

View on GitHub
src/pages/SingleProduct.tsx

Summary

Maintainability
F
4 days
Test Coverage
C
76%
//import { FaCartPlus, FaPlus, FaMinus } from 'react-icons/fa';
import { useAppDispatch, useAppSelector } from '../redux/hooks/hooks';
import { useEffect, useState } from 'react';
import { IoMdArrowDropdown } from 'react-icons/io';
import { IoChevronBackSharp } from 'react-icons/io5';
import { Link, useNavigate, useParams } from 'react-router-dom';
import Slider from 'react-slick';
import { ScaleLoader } from 'react-spinners';
import { DynamicData } from '../@types/DynamicData';
import { ButtonIcon } from '../components/buttons/ButtonIcon';
import ProductImageCard from '../components/cards/ProductImageCard';
import SinglePageAddToCart from '../components/carts/SingleProductAddToCart';
import ReviewForm from '../components/Forms/ReviewForm';
import RatingModel from '../components/product/review/ratingModel';
import StarsRatings from '../components/product/review/Stars';
import UseScrollBarRemove from '../hooks/UseScrollBarRemove';
import useToast from '../hooks/useToast';
import useToken from '../hooks/useToken';
import { fetchReview } from '../redux/features/getReviewSice';
import { getSinleProducts } from '../redux/features/productSlice';
import fetchInfo from '../utils/userDetails';
import RecommendedProduct from './RecommendedProduct';
import AddToWish from '../components/wishes/AddToWish';
import { ThemeContext } from '../hooks/useWishcontext';
import useWish from '../hooks/useWishlist';

const SingleProduct = () => {
    const { isLoading, singleProduct } = useAppSelector((state) => state.product);

    const dispatch = useAppDispatch();
    const review = useAppSelector(
        (state) => state.fetchReview.data[state.fetchReview.data.length - 1]?.data,
    );

    const [reviewForm, setReviewForm] = useState(false);
    const [ratingTab, setRatingTab] = useState(false);
    const { accessToken } = useToken();
    const navigate = useNavigate();
    const { id } = useParams<{ id: string }>();
    const { showErrorMessage, showSuccessMessage } = useToast();
    useEffect(() => {
        if (id) {
            dispatch(getSinleProducts(id)).unwrap();
        }
    }, [dispatch, id]);
    useEffect(() => {
        dispatch(fetchReview(id || '')).unwrap();
    }, [dispatch, id]);

    const settings = {
        dots: true,
        infinite: true,
        speed: 500,
        slidesToShow: 3,
        slidesToScroll: 1,
    };
    const handleTotalrating = () => {
        const obj = review;
        if (obj) {
            const total = obj.reduce(
                (acc: number, item: DynamicData) => acc + item.ratings,
                0,
            );
            const per = total / obj.length;
            return Math.ceil(per) || 0;
        }
    };
    const handleRating = () => {
        ratingTab ? setRatingTab(false) : setRatingTab(true);
    };
    const handlePropagation = (event: DynamicData) => {
        event?.stopPropagation();
    };

    const handleReviewForm = () => {
        if (accessToken) {
            reviewForm ? setReviewForm(false) : setReviewForm(true);
        } else {
            navigate('/login');
        }
    };
    const user: DynamicData = new Array(fetchInfo());
    let userRev: DynamicData[] | undefined;
    if (review) {
        userRev = review?.filter((item: DynamicData) => item.userId === user[0].id);
    }
    const { data: wishes } = useWish();

    return (
        <>
            <div
                className="perent_single_product_container min-h-screen relative pb-10 laptop:pb-0 mt-28 mobile:mt-32 ipad:mt-32"
                onClick={() => {
                    if (ratingTab) setRatingTab(false);
                    if (reviewForm) setReviewForm(false);
                }}
            >
                {isLoading ? (
                    <div
                        className="loader_icon w-full h-full flex items-center justify-center absolute"
                        role="progressbar"
                        aria-label="menu"
                    >
                        <ScaleLoader color="#256490" />
                    </div>
                ) : (
                    <>
                        <div className="single_product_wrapper relative bg-neutral-white product_card w-[98%] mobile:max-w-[80%] m-auto h-auto rounded-2xl laptop:mt-44 laptop:mb-[6%]">
                            <div className="top_single_product flex flex-col laptop:flex-row gap-10 p-[5%] ipad:p-[6%] laptop:p-[4%] ">
                                <div className="images flex flex-col w-full laptop:w-[50%]">
                                    <div className="image flex w-full h-[40vh] mobile:h-[40vh] laptop:h-[50vh] rounded-2xl overflow-hidden relative">
                                        <img
                                            src={
                                                singleProduct[0] &&
                                                singleProduct[0]?.images &&
                                                singleProduct[0].images[0]
                                            }
                                            alt="single_product_image"
                                            className="w-full object-cover h-full"
                                        />

                                        <ThemeContext.Provider value={wishes}>
                                            <AddToWish
                                                productId={id || ''}
                                                btnCss="absolute right-4 top-4"
                                            />
                                        </ThemeContext.Provider>
                                    </div>
                                    <Slider {...settings}>
                                        {singleProduct[0]?.images?.map(
                                            (item: string, index: number) => (
                                                <ProductImageCard key={index} image={item} idx={0} />
                                            ),
                                        )}
                                    </Slider>
                                </div>
                                <div className="description flex flex-col flex-1 gap-10 pt-6 px-5">
                                    <h1 className="text-2xl font-semibold">
                                        {singleProduct[0]?.name}
                                    </h1>
                                    <h1 className="text-2xl font-semibold">
                                        {singleProduct[0]?.price}{' '}
                                        <small className="text-base font-normal">RWF</small>
                                    </h1>
                                    {id && <SinglePageAddToCart productId={id} />}
                                </div>
                            </div>
                            <Link to={'/products'}>
                                <ButtonIcon className="absolute py-1 mobile:py-2 -top-8 mobile:top-0 right-0 ipad:top-4 mobile:-right-20 mobile:px-6 ipad:px-8 ipad:right-4 bg-transparent bg-primary-lightblue text-neutral-white border-primary-lightblue border-2 ">
                                    <IoChevronBackSharp />
                                </ButtonIcon>
                            </Link>

                            <div className="review-on-single product relative flex flex-col h-full w-full  p-[5%] ipad:w-full  tablet:py-[2%] tablet:px-[5%] ipad:m-auto   ">
                                <div className="flex flex-col gap-4 mobile:flex-row mobile:justify-between ipad:justify-normal ipad:gap-[20%]">
                                    <div className="flex flex-col gap-5">
                                        <h2 className="font-[500] text-[18px] leading-none">
                                            Customer reviews
                                        </h2>
                                        <div className="flex items-center gap-4">
                                            <h2 className="text-inputCaption">
                                                <p className="bg-blue-100 text-black text-sm font-semibold inline-flex items-center p-1.5 rounded">
                                                    {handleTotalrating()}
                                                </p>
                                            </h2>
                                            <StarsRatings
                                                rate={handleTotalrating()}
                                                filledStar="text-[17px] mobile:text-[1.1rem] bg-opacity-15 ipad:text-[1.1rem]"
                                                emptyStar="text-[17px] mobile:text-[1.1rem] ipad:text-[1.1rem]"
                                            />
                                            <div
                                                className="relative hover:cursor-pointer"
                                                onMouseEnter={() => setRatingTab(true)}
                                                onMouseLeave={() => setRatingTab(false)}
                                            >
                                                <span className="hover:scale-110">
                                                    <IoMdArrowDropdown
                                                        data-testid="drop-down-tab-rate"
                                                        className="dropDown text-[18px] mobile:text-[2rem]"
                                                        onClick={() => handleRating()}
                                                    />
                                                </span>
                                                {ratingTab && <RatingModel />}
                                            </div>
                                        </div>
                                    </div>

                                    <div className="flex flex-col gap-5">
                                        <span className="flex flex-col gap-1">
                                            <h1 className="font-[500] text-[18px] leading-none">
                                                Review this product
                                            </h1>
                                            <p className="text-inputCaption text-[13px]"></p>
                                        </span>

                                        <button
                                            className="bg-custom-gradient w-[11rem]  hover:scale-105  text-neutral-white text-center py-[2%]  rounded  text-[12px] mobile:w-[90%] ipad:max-w-[11rem] mobile:px-2"
                                            onClick={() => handleReviewForm()}
                                            data-testid="first-button-submit-review"
                                        >
                                            {userRev && userRev.length > 0
                                                ? 'Edit your review'
                                                : 'Submit your review'}
                                        </button>
                                    </div>
                                </div>

                                <div className="reviewss border--2 h-full w-full pt-3 flex flex-col">
                                    <h1 className="font-[500] text-[18px]">Top reviews</h1>
                                    {review &&
                                        review.length > 0 &&
                                        review.slice(0, 2).map((item: DynamicData) => (
                                            <div
                                                className="single-review mt-4 border-t   pt-4  border-inputCaption flex  flex-col gap-3 ipad:flex-ow ipad:gap11 ipad:justify-between "
                                                key={item.id}
                                            >
                                                <div className="profile-ra flex gap-2 ipad:gap-3 ">
                                                    <div className="image ">
                                                        <img
                                                            src={item.user?.profileImage}
                                                            alt="website logo"
                                                            className={`w-10 h-10 rounded-full rounded-m ipad:w-[2.5rem] ipad:h-[2.5rem]`}
                                                        />
                                                    </div>
                                                    <div className="rating flex flex-col gap-1 ipad:gap-2">
                                                        <p className=" username text-[16px] leading-none font-[700]">
                                                            {item.user?.firstName} {item.user?.lastName}
                                                        </p>
                                                        <span className="ratings">
                                                            <StarsRatings
                                                                rate={item.ratings}
                                                                filledStar="text-[12px]  bg-opacity-15"
                                                                emptyStar="text-[12px]"
                                                            />
                                                        </span>
                                                    </div>
                                                </div>
                                                <div className="feedback text-inputCaption text-[14px] ipad:w-[70%]">
                                                    {item.feedBack}
                                                </div>
                                            </div>
                                        ))}
                                </div>

                                <div className="flex flex-col gap-4 py-2.5 mobile:flex-row mobile:justify-between ipad:justify-normal ipad:gap-[20%]">
                                    <Link
                                        to={`/reviews/${id}`}
                                        className="font-medium text-blue-600 dark:text-blue-500 hover:underline"
                                    >
                                        view more reviews
                                    </Link>
                                </div>
                            </div>
                        </div>
                        <div
                            className={`${reviewForm ? 'fixed top-0 left-0  h-full w-full z-[200] b-neutral-white flex items-center justify-center' : 'hidden'}`}
                        >
                            <div className="fixed w-full h-full z-[50] bg-neutral-black/50 left-0 top-0"></div>
                            <div
                                className="formWrapper z-[400] w-[90%] h-auto max-h-[90vh] bg-neutral-white rounded-xl mobile:w-[90%] overflow-y-scroll ipad:max-w-[50.5rem] "
                                onClick={handlePropagation}
                            >
                                <UseScrollBarRemove
                                    children={
                                        <ReviewForm
                                            id={id || ''}
                                            handleReviewform={handleReviewForm}
                                            successMessage={showSuccessMessage}
                                            Erromesage={showErrorMessage}
                                        />
                                    }
                                    isVisible={reviewForm}
                                />
                            </div>
                        </div>
                        <div className="relative w-[98%] mobile:max-w-[80%] m-auto h-auto mobile:mt-5 laptop:mt-14 laptop:mb-[3%]">
                            <RecommendedProduct prodId={id as string} />
                        </div>
                    </>
                )}
            </div>
        </>
    );
};

export default SingleProduct;