Skip to content

Commit

Permalink
ft(wishlist):add-products-to-wishlist
Browse files Browse the repository at this point in the history
  • Loading branch information
kanu-cast committed Jul 9, 2024
1 parent 7d8cb7c commit f33b815
Show file tree
Hide file tree
Showing 22 changed files with 722 additions and 29,227 deletions.
29,004 changes: 0 additions & 29,004 deletions package-lock.json

This file was deleted.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@
"@storybook/nextjs": "^8.1.3",
"@storybook/react": "^8.1.3",
"@storybook/test": "^8.1.3",
"@tanstack/react-query": "^5.40.1",
"@tanstack/react-table": "^8.17.3",
"@tanstack/react-query-devtools": "^5.44.0",
"@testing-library/dom": "^10.1.0",
"@testing-library/jest-dom": "^6.4.6",
Expand Down
6 changes: 3 additions & 3 deletions src/__tests__/ZcartManagement.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ const mockedRequest = request as jest.Mocked<typeof request>;
const localStorageMock = (function () {
let store:any = {};
return {
getItem(key) {
getItem(key: string | number) {
return store[key] || null;
},
setItem(key, value) {
setItem(key: string | number, value: { toString: () => any; }) {
store[key] = value.toString();
},
clear() {
store = {};
},
removeItem(key) {
removeItem(key: string | number) {
delete store[key];
},
};
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/productList.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-quer
import SideBar from "@/components/Side"
import Provider from '@/app/providers';
import Page from '@/app/products/page';

jest.setTimeout(15000)
const queryClient = new QueryClient();
const ProductListTest = () => {
const { data } = useQuery<any>({
Expand Down
30 changes: 30 additions & 0 deletions src/__tests__/reviewPopupHook.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// WishlistOverlay.test.js

import { renderHook, act } from '@testing-library/react';
import ReviewPopup from '@/hooks/reviewPopup';

describe('ReviewPopup', () => {
it('should initialize with isOpen as false', () => {
const { result } = renderHook(() => ReviewPopup());

expect(result.current.isReviewPopupOpen).toBe(false);
});

it('should toggle isOpen when toggleReviewPopup is called', () => {
const { result } = renderHook(() => ReviewPopup());

expect(result.current.isReviewPopupOpen).toBe(false);

act(() => {
result.current.toggleReviewPopup();
});

expect(result.current.isReviewPopupOpen).toBe(true);

act(() => {
result.current.toggleReviewPopup();
});

expect(result.current.isReviewPopupOpen).toBe(false);
});
});
30 changes: 30 additions & 0 deletions src/__tests__/wiishlistOverlayHook.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// WishlistOverlay.test.js

import { renderHook, act } from '@testing-library/react';
import useWishlistOverlay from '@/hooks/wishlistOverlay';

describe('useWishlistOverlay', () => {
it('should initialize with isOpen as false', () => {
const { result } = renderHook(() => useWishlistOverlay());

expect(result.current.isWishlistOverlayOpen).toBe(false);
});

it('should toggle isOpen when toggleWishlistSlider is called', () => {
const { result } = renderHook(() => useWishlistOverlay());

expect(result.current.isWishlistOverlayOpen).toBe(false);

act(() => {
result.current.toggleWishlistSlider();
});

expect(result.current.isWishlistOverlayOpen).toBe(true);

act(() => {
result.current.toggleWishlistSlider();
});

expect(result.current.isWishlistOverlayOpen).toBe(false);
});
});
28 changes: 28 additions & 0 deletions src/__tests__/wishCounter.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// WishlistCounter.test.tsx

import { renderHook, act } from '@testing-library/react';
import useWishlistCounter from '@/hooks/wishlist';

describe('useWishlistCounter', () => {
it('should initialize wishlistCount as 0', () => {
const { result } = renderHook(() => useWishlistCounter());

expect(result.current.wishlistCount).toBe(0);
});

it('should update wishlistCount correctly', () => {
const { result } = renderHook(() => useWishlistCounter());

act(() => {
result.current.updateWishlistCount(5);
});

expect(result.current.wishlistCount).toBe(5);

act(() => {
result.current.updateWishlistCount(10);
});

expect(result.current.wishlistCount).toBe(10);
});
});
207 changes: 5 additions & 202 deletions src/app/products/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,216 +1,19 @@
// BuyerProductView
'use client';
import React, { useState } from 'react';
import { Swiper, SwiperSlide } from 'swiper/react';
import 'swiper/css';
import 'swiper/css/free-mode';
import 'swiper/css/navigation';
import 'swiper/css/thumbs';
import { FreeMode, Navigation, Thumbs } from 'swiper/modules';
import Image from 'next/image'; //@ts-ignore
import ReactStars from 'react-rating-stars-component';
import { MdOutlineShoppingCart } from 'react-icons/md';
import { FaRegHeart } from 'react-icons/fa6';
import { useParams } from 'next/navigation';
import { Product } from '@/utils/requests';
import {
ProductObj,
ProductType,
ReviewType,
imageType,
} from '@/types/Product';
import Card from '@/components/Card';

import Header from '@/components/Header';
import Footer from '@/components/Footer';
import image from '../../../../public/product.png';
import { useQuery } from '@tanstack/react-query';
import ReviewCard from '@/components/ReviewCard';
import Button from '@/components/Button';
import { averageReviews } from '@/utils/averageReviews';
import { useAppDispatch } from '@/redux/store';
import { handleUserAddCart } from '@/redux/slices/userCartSlice';
//import StripeProvider from '@/components/StripeProvider';

function Page() {
const [thumbsSwiper, setThumbsSwiper] = useState(null);
const { id } :any= useParams();
const handleSwiper = (swiper: any) => {
setThumbsSwiper(swiper);
};
const _id: string = id.toLocaleString();
const { data, isLoading, error } = useQuery<any>({
queryKey: ['product', id],
queryFn: async () => {
try {
const response: ProductType = (await Product.single(
_id,
)) as ProductType;
import ProductWrapper from '@/components/productWrapper';

return response;
} catch (error) {
throw new Error('Error fetching product data');
}
},
});
if (isLoading) return <span>Loading...</span>;

if (error) return <span>Error: {error.message}</span>;

const {
productPictures,
productName,
productPrice,
productDescription,
reviews,
} = data.product;
console.log('this is reviews >>>>>>>>>', reviews);
console.log(' this is average reviews', productPictures);
const { relatedProducts } = data;
const dispatch = useAppDispatch();
const handleNewItem = () => {
const productId = data.product.id;
dispatch(handleUserAddCart({ productPrice, productId }));
};
function Page() {


return (
<div>
{/* // <StripeProvider> */}
<Header />
<div className="w-full mb-5 mt-5 flex flex-col justify-center items-center">
<div className="w-2/3 flex flex-col justify-center items-center gap-5">
<div className="w-full flex">
<div className="w-1/2">
<div className="flex justify-center py-2">
{productPictures && productPictures.length > 0 ? (
<Swiper
spaceBetween={10}
navigation={true}
thumbs={{ swiper: thumbsSwiper }}
modules={[FreeMode, Navigation, Thumbs]}
className="mySwiper2"
>
{productPictures.map((image: imageType) => {
return (
<SwiperSlide key={image.imgId}>
<img src={image.url} alt="image" />
</SwiperSlide>
);
})}
</Swiper>
) : (
<Image src={image} alt={'no image found'} />
)}
</div>
<div className="w-full flex space-x-4 justify-start mt-3">
{productPictures && productPictures.length > 0 ? (
<div className="w-[500px] h-[15em] overflow-hidden">
<Swiper
onSwiper={handleSwiper}
spaceBetween={10}
slidesPerView={4}
freeMode={true}
watchSlidesProgress={true}
modules={[FreeMode, Navigation, Thumbs]}
className="mySwiper mycss"
>
{productPictures.map((image: imageType) => {
return (
<SwiperSlide key={image.imgId}>
<img src={image.url} alt="image" />
</SwiperSlide>
);
})}
</Swiper>
</div>
) : (
<p className="text-red-500">no image found!</p>
)}
</div>
</div>
<div className="w-1/2 flex flex-col ml-10">
<div>
<h1 className="font-medium text-2xl">{productName}</h1>
</div>
<div className="flex flex-col gap-2">
<div className="flex gap-2">
<div className="p-3 rounded-full bg-gray-200 hover:bg-green-500 hover:text-white cursor-pointer">
<FaRegHeart />
</div>
<div className="p-3 rounded-full bg-gray-200 hover:bg-green-500 hover:text-white cursor-pointer">
<MdOutlineShoppingCart
onClick={() => {
handleNewItem();
}}
/>
</div>
</div>
<span className="font-medium text-2xl text-blue-300">
RWF {productPrice}
</span>
</div>
<div className="block">
<ReactStars
count={5}
value={averageReviews(reviews)}
isHalf={true}
size={30}
activeColor="#ffd700"
edit={false}
/>
</div>
<div className="flex flex-col gap-2">
<h2 className="font-medium text-2xl">Description:</h2>
<p className="w-full text-1xl">{productDescription}</p>
</div>
</div>
</div>
<div className="w-full flex flex-col ">
<h2 className="font-medium text-2xl">Related products:</h2>
<div className="w-full">
<div className="product-grid flex justify-left gap-5 mt-5 mx-0">
{relatedProducts && relatedProducts.length > 0 ? (
relatedProducts.map((product: ProductType) => (
<Card
key={product.id}
id={product.id}
productPrice={product.productPrice}
productThumbnail={product.productThumbnail}
productDescription={product.productDescription}
reviews={product.reviews}
productName={product.productName}
/>
))
) : (
<p className="text-red-500 w-full flex self-center">
No related products available.
</p>
)}
</div>
</div>
</div>
<div className="w-full flex flex-col mt-10">
<div className="flex">
<h2 className="font-medium text-2xl mr-5">Reviews:</h2>
<Button name="Add Review" background="blue"></Button>
</div>
<div className="my-10">
{reviews && reviews.length > 0 ? (
reviews.map((review: ReviewType) => (
<ReviewCard
rating={review.rating}
feedback={review.feedback}
image={review.userProfile.profileImage}
firstName={review.userProfile.firstName}
lastName={review.userProfile.lastName}
/>
))
) : (
<p className="text-red-500">No ratings yet.</p>
)}
</div>
</div>
</div>
</div>
<ProductWrapper/>
<Footer />
{/* </StripeProvider> */}
</div>
Expand Down
7 changes: 4 additions & 3 deletions src/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
'use client';
import React from 'react';
import { IoChevronBack } from 'react-icons/io5';
import { IoMdClose } from "react-icons/io";
import { MdClose } from "react-icons/md";

interface Properties {
name?: string;
handle?: () => void;
Expand Down Expand Up @@ -44,8 +45,8 @@ export const BackButton: React.FC<Properties> = ({ handle, isDisabled, rotate })
export const CloseButton: React.FC<Properties> = ({ handle, isDisabled, rotate }) => {
return (
<>
<button onClick={handle} disabled={isDisabled} className='rounded-[40px] w-[30px] h-[30px] flex justify-center items-center text-[30px] float-right'>
<IoMdClose />
<button onClick={handle} disabled={isDisabled} className='rounded-[40px] w-[30px] h-[30px] bg-white flex justify-center items-center text-[30px] float-right'>
<MdClose />
</button>
</>
)
Expand Down
Loading

0 comments on commit f33b815

Please sign in to comment.