Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

landing page bugfix #45

Merged
merged 1 commit into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 11 additions & 31 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@ import GoogleAuthSuccess from './components/authentication/GoogleAuthSucces';
import { ToastContainer } from 'react-toastify';
import Searchpage from './containers/searchResults/SearchPage';
import { useDispatch } from 'react-redux';
import { ProductResponse, Product } from './types/Types';
import { useEffect, useRef } from 'react';
import { useGetProductsQuery } from './services/productApi';
import { setError, setIsLoading, setProductFetched, setProductsDataList } from './redux/slices/productsSlice';
import { useEffect } from 'react';
import { productsApi } from './services/productApi';
import Checkout from './components/checkout/Checkout';
import RestrictedRoute from './components/dashboard/RestrictedRoute';
import AdminPage from './pages/admin';
Expand All @@ -37,38 +35,20 @@ import { cartApi } from './services/cartApi';
import VerifyOTPPage from './pages/VerifyOTPPage';
import PaymentSuccessCard from './components/checkout/PaymentSuccessCard';
import PaymentPage from './pages/PaymentPage';
import { useSelector } from 'react-redux';
import { RootState } from './redux/store';

import BuyerRestrictedRoutes from './containers/buyer/BuyerRestrictedRoutes';
const App = () => {
const { data, error, isLoading } = useGetProductsQuery();
const isAuthenticated = useSelector((state: RootState) => state.user.token);
const dispatch = useDispatch();
const firstRender = useRef(true);

const productsData: ProductResponse = data as unknown as ProductResponse;

useEffect(() => {
const fetchProducts = async () => {
if (firstRender.current) {
firstRender.current = false;
return;
}
if (error) {
dispatch(setError(error));
dispatch(setIsLoading(false));
dispatch(setProductFetched(false));
return;
}
if (!isLoading && productsData) {
const productsList = productsData.data as unknown as Product[];
dispatch(setProductsDataList([...productsList]));
dispatch(setIsLoading(false));
dispatch(setProductFetched(true));
}
};
fetchProducts();

dispatch<any>(cartApi.endpoints.getCarts.initiate());
}, [productsData, isLoading, dispatch]);
dispatch<any>(productsApi.endpoints.getProducts.initiate())
if (isAuthenticated) {
dispatch<any>(cartApi.endpoints.getCarts.initiate());
console.log("Cart")
}
}, [dispatch]);

const router = createBrowserRouter([
{
Expand Down
124 changes: 100 additions & 24 deletions src/components/Products/ProductCard.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,122 @@
import { FaHeart } from 'react-icons/fa6';
import { useSelector } from 'react-redux';
import { RootState } from '../../redux/store';
import { useAddProductToCartMutation, useDeleteCartMutation } from '../../services/cartApi';
import { useAddProductToWishlistMutation, useRemoveWishListMutation } from '../../services/wishlistApi';
import { Product } from '../../types/Types';
import { TiShoppingCart } from 'react-icons/ti';
import StarRating from '../common/Ratings';
import { IoIosHeart, IoIosHeartEmpty } from 'react-icons/io';
import { MdOutlineAddShoppingCart } from 'react-icons/md';
import { useNavigate } from 'react-router-dom';
interface ProductCardProps {
product: Product;
cartAdded?: boolean,
wishList?: boolean,
wishListId?: string | string
}

const ProductCard: React.FC<ProductCardProps> = ({ product }) => {
const ProductCard: React.FC<ProductCardProps> = ({ product, cartAdded, wishList, wishListId }) => {
const imageUrl = product?.images?.[0] ?? '';
const price = product?.sizes?.[0]?.price ?? '';
const navigate = useNavigate();
const productId = product?.id
const sizeId = product?.sizes[0].id
const [addProductToCart] = useAddProductToCartMutation()
const [deleteCart] = useDeleteCartMutation()
const [addProductToWishlist] = useAddProductToWishlistMutation()
const [removeWishList] = useRemoveWishListMutation()
const isAuthenticated = useSelector((state: RootState) => state.user.token) ? true : false;
const navigate = useNavigate()

// AddCart
const onAddProductToCart = async (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation()
if (isAuthenticated) {
await addProductToCart({ productId, sizeId })
} else {
navigate("/login")
}
}
// RemoveCart
const onRemoveProductToCart = async (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation()
if (isAuthenticated && sizeId) {
await deleteCart({ productId, sizeId })
} else {
navigate("/login")
}
}
// AddWishList
const onAddWishList = async (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation()
if (isAuthenticated && sizeId) {
await addProductToWishlist(sizeId)
} else {
navigate("/login")
}
}
// onRemoveWishList
const onRemoveWishList = async (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation()
if (isAuthenticated && wishListId) {
await removeWishList(wishListId)
} else {
navigate("/login")
}
}
return (
<div
onClick={() => {
navigate(`/products/${product.id}`);
window.scrollTo({ top: 0, behavior: 'smooth' });
window.open(`/products/${product.id}`, '_blank');
// window.scrollTo({ top: 0, behavior: 'smooth' });
}}
className='product-card bg-whiteColor min-w-44 md:min-w-56 lg:min-w-56
shadow-lg rounded-lg p-2 m-4 md:p-4 md:m-4 transition-transform hover:scale-105 cursor-pointer'
className='bg-whiteColor flex-col relative w-[49%] sm:w-52 md:w-[32.5%] lg:w-[19%] 2xl:w-[253px] mb-2 drop-shadow-md rounded-lg cursor-pointer'
>
<div className='product-image flex justify-center'>
<img src={imageUrl} alt={product.name} className='w-32 h-32 rounded-sm object-cover md:h-48 md:w-48' />
<div className='product-image flex justify-center w-full rounded-tl-lg rounded-tr-lg relative'>
<img src={imageUrl} alt={product.name} className='w-full h-52 md:h-60 rounded-tl-lg rounded-tr-lg object-cover object-top' />
<div className='absolute bottom-2 right-2 flex flex-col gap-1'>
{cartAdded
? (
<button
onClick={onRemoveProductToCart}
className={`cursor-pointer p-1 w-9 h-9 flex justify-center items-center rounded-full bg-[#fafafa] drop-shadow-lg ${cartAdded ? 'bg-blackColor' : ''}`}>
<MdOutlineAddShoppingCart size={20} color='white' />
</button>
)
: (
<button
onClick={onAddProductToCart}
className='cursor-pointer p-1 w-9 h-9 flex justify-center items-center rounded-full bg-[#fafafa] drop-shadow-lg'>
<MdOutlineAddShoppingCart size={20} />
</button>
)}
{wishList
? (
<button
onClick={onRemoveWishList}
className={`p-1 rounded-full w-9 h-9 flex justify-center items-center bg-[#fafafa] drop-shadow-md`}>
<IoIosHeart size={30} />
</button>
)
: (
<button
onClick={onAddWishList}
className='p-1 rounded-full w-9 h-9 flex justify-center items-center bg-[#fafafa] drop-shadow-md'>
<IoIosHeartEmpty size={30} />
</button>
)}

</div>
</div>
<div className='product-name-cart-button pt-2'>
<div className='product-name-cart-button p-1 '>
<div className='product-manufacturer'>
<div className='name-price flex justify-between'>
<h3 className='text-md font-bold'>{product.name}</h3>
<p className='text-md font-bold'>${price}</p>
</div>
<p className='text-sm p-2 text-[#949191]'>{product.manufacturer}</p>
</div>
<div className='cart-button mt-4'>
<div className='cart-wish-icons-ratings flex justify-between'>
<div className='cart-wish-icons text-2xl flex gap-2'>
<TiShoppingCart className='cursor-pointer' />
<FaHeart className='cursor-pointer' />
</div>
<div className='ratings flex '>
<div className='flex flex-col justify-between'>
<h3 className='text-xs font-normal tracking-tighter truncate capitalize'>{product.name.toLowerCase()}</h3>
<div>
<StarRating reviews={product.reviews} />
</div>
<p className='flex items-center py-2'>
<span className='self-start'>$</span>
<span className='text-2xl font-bold self-end'>{price.toFixed(2)}</span>
</p>
</div>
</div>
</div>
Expand Down
8 changes: 6 additions & 2 deletions src/components/authentication/GoogleAuthSucces.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { useEffect } from 'react';
import { useNavigate, useLocation, useParams } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import { setToken } from '../../redux/slices/userSlice';
import { setToken, setUser } from '../../redux/slices/userSlice';
import { CustomJwtPayload } from '../../types/Types';
import { jwtDecode } from 'jwt-decode';

const GoogleAuthSuccess = () => {
const navigate = useNavigate();
Expand All @@ -12,11 +14,13 @@ const GoogleAuthSuccess = () => {
useEffect(() => {
if (token) {
dispatch(setToken(token));
const decodedToken = jwtDecode<CustomJwtPayload>(token);
dispatch(setUser(decodedToken.id));
navigate('/');
}
}, [location, dispatch, navigate]);

return <div>Loading...</div>;
};

export default GoogleAuthSuccess;
export default GoogleAuthSuccess;
4 changes: 3 additions & 1 deletion src/components/cart/Cart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ICartProduct, ICartsHookResponse, ICartsResponse } from '../../utils/sc
import { useGetCartsQuery } from '../../services/cartApi';

const Cart: React.FC = () => {
// const isAuthenticated = useSelector((state: RootState) => state.user.token ? true : false);
const {
data: carts,
isLoading,
Expand Down Expand Up @@ -51,7 +52,7 @@ const Cart: React.FC = () => {
} else if (isError) {
content = <div>{error?.toString()}</div>;
}

return (
<div className='w-full min-h-screen overflow-x-hidden font-roboto flex flex-col 2xl:items-center'>
<Navbar />
Expand Down Expand Up @@ -81,4 +82,5 @@ const Cart: React.FC = () => {
</div>
);
};

export default Cart;
6 changes: 4 additions & 2 deletions src/components/common/Ratings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ const StarRating: React.FC<RatingsProps> = ({ reviews }) => {
}
return stars;
};

return <div className="ratings flex">{renderStars()}</div>;
return <div className="ratings flex items-center gap-1 w-12">
<span className='flex'>{renderStars()}</span>
<span className='text-sm'>({reviews.length > 0 ? averageRating : '0'})</span>
</div>;
};

export default StarRating;
2 changes: 1 addition & 1 deletion src/components/navbar/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const Navbar: React.FC = () => {
isLoading: isFetchingUser,
isSuccess: isUserFetched,
data: userData,
} = useGetUserByIdQuery(userId);
} = useGetUserByIdQuery(userId, { skip: !userId });
const handleNavigate = () => {
const token = localStorage.getItem('token') || null;
if (!token) {
Expand Down
2 changes: 1 addition & 1 deletion src/components/navbar/notifications/Notifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const Notifications = () => {
const [notifications, setNotifications] = useState<NotificationProps[]>([]);
const [unRead, setUnRead] = useState<number>(0);

const { data: fetchedNotifications, isLoading, isError } = useGetNotificationsQuery(userId);
const { data: fetchedNotifications, isLoading, isError } = useGetNotificationsQuery(userId, { skip: !userId });

const [deleteAll, { isLoading: areAllDeleting, isSuccess: areDeleted }] = useDeleteAllNotificationsMutation();
const [markAll, { isLoading: areAllUpdating, isSuccess: areAllUpdated }] = useMarkAllNotificationsAsReadMutation();
Expand Down
89 changes: 44 additions & 45 deletions src/containers/Arrivals/NewArrivals.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,49 @@
import ProductCard from '../../components/Products/ProductCard';
import ProductCardSkeleton from '../../components/Products/ProductCardSkeleton';
import { useState } from 'react';
import { Product } from '../../types/Types';
import { BiSolidCircle } from 'react-icons/bi';
import { useSelector } from 'react-redux';
// import ProductCard from '../../components/Products/ProductCard';
// import ProductCardSkeleton from '../../components/Products/ProductCardSkeleton';
// import { Product } from '../../types/Types';
// import { BiSolidCircle } from 'react-icons/bi';
// import { useGetProductsQuery } from '../../services/productApi';

const perPage = 8;
// const perPage = 10;

export default function NewArrivals() {
const [currentPage, setCurrentPage] = useState(0);
const { isLoading, productsDataList: productsList } = useSelector((state: any) => state.products);
const next = () => {
setCurrentPage(prevPage => Math.min(prevPage + 1, Math.floor(productsList.length / perPage)));
};
// export default function NewArrivals() {
// const { data: productsList, isLoading, isSuccess } = useGetProductsQuery()
// // const [currentPage, setCurrentPage] = useState(0);
// // const { isLoading, productsDataList: productsList } = useSelector((state: any) => state.products);
// const next = () => {
// setCurrentPage(prevPage => Math.min(prevPage + 1, Math.floor(productsList.data.length / perPage)));
// };

const prev = () => {
setCurrentPage(prevPage => Math.max(prevPage - 1, 0));
};
// const prev = () => {
// setCurrentPage(prevPage => Math.max(prevPage - 1, 0));
// };

const startIndex = currentPage * perPage;
const endIndex = startIndex + perPage;
const sortedProducts = [...productsList].sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()).slice(0, 13);
const allProductsOnPage = sortedProducts.slice(startIndex, endIndex);
// const startIndex = currentPage * perPage;
// const endIndex = startIndex + perPage;
// const sortedProducts = [...productsList.data].sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()).slice(0, 13);
// const allProductsOnPage = sortedProducts.slice(startIndex, endIndex);

return (
<div className='new-arrivals'>
<div className='arrivals-header py-5'>
<h1 className='text-3xl font-bold'>New Arrivals</h1>
</div>
<div className='grid grid-cols-2 gap-0 sm:grid-cols-0 sm:gap-2 md:grid-cols-3 md:gap-3 lg:grid-cols-4 lg:gap-4'>
{isLoading
? Array.from({ length: perPage }).map((_, index) => <ProductCardSkeleton key={index} />)
: allProductsOnPage.map((product: Product) => <ProductCard key={product.id} product={product} />)}
</div>
<div className='next-previous flex justify-center items-center gap-2 p-7'>
<BiSolidCircle
onClick={prev}
className={`cursor-pointer text-2xl ${currentPage === 0 ? 'text-grayColor' : ''}`}
/>
<BiSolidCircle
onClick={next}
className={`cursor-pointer text-2xl ${
currentPage >= Math.floor(productsList.length / perPage) ? 'text-grayColor' : ''
}`}
/>
</div>
</div>
);
}
// return (
// <div className='new-arrivals'>
// <div className='arrivals-header py-5'>
// <h1 className='text-3xl font-bold'>New Arrivals</h1>
// </div>
// <div className='flex justify-between items-start flex-wrap gap-1 sm:justify-between md:justify-start md:gap-2'>
// {isLoading
// ? Array.from({ length: perPage }).map((_, index) => <ProductCardSkeleton key={index} />)
// : allProductsOnPage.map((product: Product) => <ProductCard key={product.id} product={product} />)}
// </div>
// <div className='next-previous flex justify-center items-center gap-2 p-7'>
// <BiSolidCircle
// onClick={prev}
// className={`cursor-pointer text-2xl ${currentPage === 0 ? 'text-grayColor' : ''}`}
// />
// <BiSolidCircle
// onClick={next}
// className={`cursor-pointer text-2xl ${currentPage >= Math.floor(productsList.data.length / perPage) ? 'text-grayColor' : ''
// }`}
// />
// </div>
// </div>
// );
// }
Loading
Loading