diff --git a/src/components/Products/ProductCard.jsx b/src/components/Products/ProductCard.jsx index 8158de6b..086bb89d 100644 --- a/src/components/Products/ProductCard.jsx +++ b/src/components/Products/ProductCard.jsx @@ -1,5 +1,6 @@ import { useState, forwardRef, useRef, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; +import { addRecentlyViewed } from '../../utils/recentlyViewed'; const HeartIcon = ({ isWishlisted = false, animate = false, className = 'h-6 w-6' }) => ( { const cartAddedTimeoutRef = useRef(null); const handleProductClick = () => { + // add to recently viewed list (stored in localStorage) before navigating + try { + addRecentlyViewed(product); + } catch { + // ignore errors (localStorage not available) + } navigate(`/product/${product.id}`); }; diff --git a/src/components/RecentlyViewed.jsx b/src/components/RecentlyViewed.jsx new file mode 100644 index 00000000..eb7447eb --- /dev/null +++ b/src/components/RecentlyViewed.jsx @@ -0,0 +1,84 @@ +import { useState } from 'react'; +import ProductCard from './Products/ProductCard'; +import { getRecentlyViewed } from '../utils/recentlyViewed'; + +// SVG component for the navigation arrows +const ChevronLeftIcon = (props) => ( + + + +); + +const ChevronRightIcon = (props) => ( + + + +); + + +export default function RecentlyViewed() { + const [items] = useState(() => getRecentlyViewed()); + const [currentIndex, setCurrentIndex] = useState(0); + const itemsPerPage = 3; + + const nextSlide = () => { + const maxStartIndex = Math.max(0, items.length - itemsPerPage); + const newIndex = Math.min(currentIndex + itemsPerPage, maxStartIndex); + setCurrentIndex(newIndex); + }; + + const prevSlide = () => { + const newIndex = Math.max(currentIndex - itemsPerPage, 0); + setCurrentIndex(newIndex); + }; + + if (!items || items.length === 0) { + return null; + } + + const canGoNext = currentIndex < items.length - itemsPerPage; + const canGoPrev = currentIndex > 0; + + const visibleItems = items.slice(currentIndex, currentIndex + itemsPerPage); + + return ( +
+ {/* Header section with title and navigation arrows */} +
+

+ RECENTLY + VIEWED +

+ + {/* Navigation Buttons */} +
+ + +
+
+ {/* Recently Viewed Products Grid */} +
+ {visibleItems.map((product) => ( +
+ +
+ ))} +
+ + ); + +} diff --git a/src/index.css b/src/index.css index 1ad7927c..fcb2fcbb 100644 --- a/src/index.css +++ b/src/index.css @@ -1,7 +1,7 @@ @import url('https://fonts.googleapis.com/css2?family=Inter&family=Montserrat:ital,wght@0,700;1,700&family=Poppins&display=swap'); @import 'tailwindcss'; -:root { +@theme { /* Accent Fonts */ --font-montserrat: 'Montserrat', sans-serif; --font-poppins: 'Poppins', sans-serif; @@ -18,6 +18,26 @@ -webkit-text-stroke: 2px black; } +/* Black text stroke utility (explicit name used in RecentlyViewed)Use multiple text-shadow layers to create an outer-only outline so the stroke doesn't bleed into inner counters of glyphs.*/ +.text-stroke-black { + /* multiple offsets around the glyph to simulate an even outline */ + text-shadow: + -1px -1px 0 #000, + 1px -1px 0 #000, + -1px 1px 0 #000, + 1px 1px 0 #000, + 0 -2px 0 #000, + 2px 0 0 #000, + 0 2px 0 #000, + -2px 0 0 #000, + -2px -2px 0 #000, + 2px -2px 0 #000, + 2px 2px 0 #000, + -2px 2px 0 #000; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + /* Product Card Action Button Animations */ @keyframes double-pop { 0% { transform: scale(1); } @@ -30,4 +50,4 @@ .animate-double-pop { transform-origin: center; animation: double-pop 520ms cubic-bezier(.22,1,.36,1) both; -} \ No newline at end of file +} diff --git a/src/pages/Products/Products.jsx b/src/pages/Products/Products.jsx index 3f73cc3d..fc82fc69 100644 --- a/src/pages/Products/Products.jsx +++ b/src/pages/Products/Products.jsx @@ -5,6 +5,7 @@ import SEO from '../../components/SEO'; import ProductGrid from '../../components/Products/ProductGrid'; import SortDropdown from '../../components/Products/SortDropdown'; import ProductSkeleton from '../../components/Products/ProductSkeleton'; +import RecentlyViewed from '../../components/RecentlyViewed'; export default function Products() { const dispatch = useDispatch(); @@ -173,6 +174,9 @@ export default function Products() { + + {/* Recently Viewed Section (bottom of page) */} + {!loading && !error && } ); diff --git a/src/utils/recentlyViewed.js b/src/utils/recentlyViewed.js new file mode 100644 index 00000000..a6e3aae3 --- /dev/null +++ b/src/utils/recentlyViewed.js @@ -0,0 +1,32 @@ +// Simple utility to manage recently viewed products. +// Stores/retrieves an array of product objects in localStorage under the key `recently_viewed`. + +export function getRecentlyViewed() { + try { + const raw = localStorage.getItem('recently_viewed'); + if (!raw) return []; + const parsed = JSON.parse(raw); + if (!Array.isArray(parsed)) return []; + return parsed; + } catch { + // If localStorage access fails (SSR or permission), return empty array + return []; + } +} + +export function addRecentlyViewed(product) { + if (!product) return; + try { + const current = getRecentlyViewed(); + // Remove any existing occurrence + const deduped = current.filter((p) => p.id !== product.id); + // Prepend the new product and cap at 10 + deduped.unshift(product); + const trimmed = deduped.slice(0, 10); + localStorage.setItem('recently_viewed', JSON.stringify(trimmed)); + } catch { + // ignore + } +} + +export default { getRecentlyViewed, addRecentlyViewed };