first commit
This commit is contained in:
265
src/components/recentlyViewed/RecentlyViewed.jsx
Normal file
265
src/components/recentlyViewed/RecentlyViewed.jsx
Normal file
@@ -0,0 +1,265 @@
|
||||
// import React from "react";
|
||||
// import { useSelector } from "react-redux";
|
||||
// import { Link } from "react-router-dom";
|
||||
// import { selectRecentProducts } from "../../features/recentlyViewed/recentlyViewedApi";
|
||||
|
||||
// const RecentlyViewedCard = ({ product }) => {
|
||||
// const image = product.image || "/placeholder-product.png";
|
||||
// const price = product.price || 0;
|
||||
|
||||
// return (
|
||||
// <Link
|
||||
// to={product.url || "#"}
|
||||
// className="group block rounded-2xl overflow-hidden bg-white
|
||||
// border border-gray-100
|
||||
// transition-all duration-500
|
||||
// hover:-translate-y-1 hover:shadow-xl"
|
||||
// >
|
||||
// {/* Image */}
|
||||
// <div className="relative aspect-[3/4] overflow-hidden">
|
||||
// <img
|
||||
// src={image}
|
||||
// alt={product.name}
|
||||
// className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105"
|
||||
// />
|
||||
|
||||
// {/* Soft gradient */}
|
||||
// <div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent" />
|
||||
|
||||
// {/* "Viewed" badge */}
|
||||
// <div className="absolute top-3 left-3 bg-primary-dark backdrop-blur
|
||||
// text-white text-xs font-semibold
|
||||
// px-3 py-1 rounded-full shadow-sm">
|
||||
// Viewed
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// {/* Content */}
|
||||
// <div className="p-4">
|
||||
// <h3 className="text-gray-900 text-base font-medium leading-snug line-clamp-2">
|
||||
// {product.name || "Product Name"}
|
||||
// </h3>
|
||||
// <div className="mt-2 flex items-center gap-2">
|
||||
// <span className="text-lg font-semibold text-gray-900">₹{price}</span>
|
||||
// </div>
|
||||
// <div className="mt-3 h-[2px] w-0 bg-primary-dark transition-all duration-500 group-hover:w-10" />
|
||||
// </div>
|
||||
// </Link>
|
||||
// );
|
||||
// };
|
||||
|
||||
// const RecentlyViewed = ({ limit = 6 }) => {
|
||||
// const allProducts = useSelector(selectRecentProducts);
|
||||
// const recentProducts = allProducts.slice(0, limit);
|
||||
|
||||
// if (recentProducts.length === 0) return null;
|
||||
|
||||
// return (
|
||||
// <section className="bg-[#A95F5F] mt-12 px-5 md:px-20 p-24">
|
||||
// <div className="max-w-7xl mx-auto px-6">
|
||||
// {/* Heading */}
|
||||
// <div className="text-center mb-14">
|
||||
// <h2 className="text-3xl sm:text-4xl font-bold text-gray-900">
|
||||
// Recently Viewed Products
|
||||
// </h2>
|
||||
// <p className="text-gray-100 mt-2 text-lg">
|
||||
// Continue shopping from where you left off
|
||||
// </p>
|
||||
// </div>
|
||||
|
||||
// {/* Product Grid */}
|
||||
// <div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-8">
|
||||
// {recentProducts.map((product) => (
|
||||
// <RecentlyViewedCard key={product.id} product={product} />
|
||||
// ))}
|
||||
// </div>
|
||||
// </div>
|
||||
// </section>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default RecentlyViewed;
|
||||
|
||||
import React from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Heart } from "lucide-react";
|
||||
import { selectRecentProducts } from "../../features/recentlyViewed/recentlyViewedApi";
|
||||
import {
|
||||
useAddToWishlistMutation,
|
||||
useRemoveFromWishlistMutation,
|
||||
useGetWishlistQuery,
|
||||
} from "../../features/wishlist/wishlistAPI";
|
||||
|
||||
const RecentlyViewedCard = ({ product, wishlistIds, handleWishlist }) => {
|
||||
const discount =
|
||||
product.comparePrice && product.comparePrice > product.price
|
||||
? Math.round(
|
||||
((product.comparePrice - product.price) / product.comparePrice) * 100,
|
||||
)
|
||||
: 0;
|
||||
|
||||
const offersCount = Math.floor(Math.random() * 3) + 1; // mock offers
|
||||
|
||||
return (
|
||||
<Link
|
||||
to={product.url || "#"}
|
||||
className="group bg-white rounded-2xl overflow-hidden hover:shadow-xl transition-shadow duration-300 border border-gray-200"
|
||||
>
|
||||
{/* Image */}
|
||||
<div className="relative bg-gray-50 overflow-hidden aspect-[3/4]">
|
||||
<img
|
||||
src={product.image || "/placeholder-product.png"}
|
||||
alt={product.name}
|
||||
className="w-full h-full object-cover object-top group-hover:scale-105 transition-transform duration-500"
|
||||
loading="lazy"
|
||||
/>
|
||||
|
||||
{/* Viewed Badge */}
|
||||
<div className="absolute top-3 left-3 bg-primary-dark text-white text-xs font-semibold px-3 py-1.5 rounded shadow-sm">
|
||||
Viewed
|
||||
</div>
|
||||
|
||||
{/* Offers Badge */}
|
||||
{offersCount > 0 && (
|
||||
<div className="absolute bottom-3 left-3 bg-gray-800/90 backdrop-blur-sm text-white text-xs font-semibold px-3 py-1.5 rounded">
|
||||
{offersCount} OFFER{offersCount > 1 ? "S" : ""} FOR YOU
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Wishlist */}
|
||||
<div className="absolute top-3 right-3 flex flex-col gap-2">
|
||||
<button
|
||||
onClick={(e) => handleWishlist(e, product.id)}
|
||||
className="w-9 h-9 bg-white rounded-full flex items-center justify-center shadow-md hover:shadow-lg transition-shadow"
|
||||
title="Add to Wishlist"
|
||||
>
|
||||
<Heart
|
||||
size={16}
|
||||
className={
|
||||
wishlistIds.includes(product.id)
|
||||
? "text-red-500 fill-red-500"
|
||||
: "text-gray-700"
|
||||
}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Product Info */}
|
||||
<div className="p-4">
|
||||
{/* Brand/Category */}
|
||||
<p className="text-xs text-gray-500 uppercase tracking-wide mb-1">
|
||||
{product.brand || "Brand"}
|
||||
</p>
|
||||
|
||||
{/* Product Name */}
|
||||
<h3 className="text-lg font-medium text-primary-dark line-clamp-2 min-h-[2.5rem]">
|
||||
{product.name || "Product Name"}
|
||||
</h3>
|
||||
|
||||
{/* Price */}
|
||||
<div className="flex items-center gap-2 mb-1 mt-1">
|
||||
<span className="text-lg font-bold text-gray-900">
|
||||
₹{product.price?.toLocaleString() || "0"}
|
||||
</span>
|
||||
{product.comparePrice && (
|
||||
<span className="text-sm text-gray-400 line-through">
|
||||
₹{product.comparePrice.toLocaleString()}
|
||||
</span>
|
||||
)}
|
||||
{discount > 0 && (
|
||||
<p className="text-xs font-semibold text-orange-600">
|
||||
{discount}% OFF
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Color Variants */}
|
||||
{product.variants?.length > 0 && (
|
||||
<div className="flex items-center gap-1.5 mt-2">
|
||||
{product.variants.slice(0, 5).map((variant, idx) => {
|
||||
const colorValue = variant.color?.toLowerCase() || "#e5e7eb";
|
||||
return (
|
||||
<div
|
||||
key={variant._id || idx}
|
||||
className="w-5 h-5 rounded-full border-2 border-gray-700 hover:border-gray-500 transition-colors cursor-pointer"
|
||||
style={{ backgroundColor: colorValue }}
|
||||
title={variant.color}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{product.variants.length > 5 && (
|
||||
<span className="text-xs text-gray-500 ml-1">
|
||||
+{product.variants.length - 5}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
const RecentlyViewed = ({ limit = 6 }) => {
|
||||
const allProducts = useSelector(selectRecentProducts);
|
||||
const recentProducts = allProducts.slice(0, limit);
|
||||
|
||||
const { data: wishlistData } = useGetWishlistQuery();
|
||||
const [addToWishlist] = useAddToWishlistMutation();
|
||||
const [removeFromWishlist] = useRemoveFromWishlistMutation();
|
||||
|
||||
const wishlistIds =
|
||||
wishlistData?.data?.wishlist?.map(
|
||||
(item) => item.product?._id || item.productId,
|
||||
) ||
|
||||
wishlistData?.data?.products?.map((item) => item._id) ||
|
||||
[];
|
||||
|
||||
const handleWishlist = async (e, productId) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
try {
|
||||
if (wishlistIds.includes(productId)) {
|
||||
await removeFromWishlist(productId).unwrap();
|
||||
} else {
|
||||
await addToWishlist(productId).unwrap();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Wishlist error:", error);
|
||||
}
|
||||
};
|
||||
|
||||
if (recentProducts.length === 0) return null;
|
||||
|
||||
return (
|
||||
<section className="bg-[#A95F5F] mt-12 px-5 md:px-20 py-12">
|
||||
<div className="max-w-7xl mx-auto px-6">
|
||||
{/* Heading */}
|
||||
<div className="text-center mb-14">
|
||||
<h2 className="text-3xl sm:text-4xl font-bold text-gray-900">
|
||||
Recently Viewed Products
|
||||
</h2>
|
||||
<p className="text-gray-100 mt-2 text-lg">
|
||||
Continue shopping from where you left off
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Product Grid */}
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-6 lg:gap-8">
|
||||
{recentProducts.map((product) => (
|
||||
<RecentlyViewedCard
|
||||
key={product.id}
|
||||
product={product}
|
||||
wishlistIds={wishlistIds}
|
||||
handleWishlist={handleWishlist}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default RecentlyViewed;
|
||||
Reference in New Issue
Block a user