From dd50bf47d8596350793e835c5066c61618ca4a12 Mon Sep 17 00:00:00 2001 From: Apar Agarwal Date: Wed, 8 Oct 2025 03:45:02 +0530 Subject: [PATCH 1/3] feat: Add Recently Viewed section and utility functions for managing recently viewed products --- src/components/Products/ProductCard.jsx | 7 ++ src/components/RecentlyViewed.jsx | 88 +++++++++++++++++++++++++ src/index.css | 29 ++++++++ src/pages/Products/Products.jsx | 4 ++ src/utils/recentlyViewed.js | 32 +++++++++ 5 files changed, 160 insertions(+) create mode 100644 src/components/RecentlyViewed.jsx create mode 100644 src/utils/recentlyViewed.js diff --git a/src/components/Products/ProductCard.jsx b/src/components/Products/ProductCard.jsx index 4df65c1f..bd7fba9e 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 cartTimeoutRef = 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..a331e968 --- /dev/null +++ b/src/components/RecentlyViewed.jsx @@ -0,0 +1,88 @@ +import { useEffect, 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, setItems] = useState([]); + const [currentIndex, setCurrentIndex] = useState(0); + const itemsPerPage = 3; + + useEffect(() => { + // Fetch recently viewed items from localStorage + setItems(getRecentlyViewed()); + }, []); + + 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) => ( +
+ +
+ ))} +
+ + ); +} \ No newline at end of file diff --git a/src/index.css b/src/index.css index eb523a7b..f819c6c6 100644 --- a/src/index.css +++ b/src/index.css @@ -17,6 +17,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); } @@ -42,3 +62,12 @@ transform-origin: center; animation: cart-pop 520ms cubic-bezier(.22,1,.36,1) both; } + +/* Utility to hide scrollbars but preserve scrolling*/ +.scrollbar-hide { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +} +.scrollbar-hide::-webkit-scrollbar { + display: none; /* Safari and Chrome */ +} diff --git a/src/pages/Products/Products.jsx b/src/pages/Products/Products.jsx index 63e2924b..0a95e22e 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(); @@ -182,6 +183,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 }; From 48d915dcdfa47eb04d4a67d4fd57dc108474f96e Mon Sep 17 00:00:00 2001 From: Apar Agarwal Date: Wed, 8 Oct 2025 04:15:21 +0530 Subject: [PATCH 2/3] refactor: Simplify Recently Viewed component state initialization and update CSS theme declaration --- src/components/RecentlyViewed.jsx | 9 ++------- src/index.css | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/components/RecentlyViewed.jsx b/src/components/RecentlyViewed.jsx index a331e968..af870d12 100644 --- a/src/components/RecentlyViewed.jsx +++ b/src/components/RecentlyViewed.jsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useState } from 'react'; import ProductCard from './Products/ProductCard'; import { getRecentlyViewed } from '../utils/recentlyViewed'; @@ -17,15 +17,10 @@ const ChevronRightIcon = (props) => ( export default function RecentlyViewed() { - const [items, setItems] = useState([]); + const [items, setItems] = useState(() => getRecentlyViewed()); const [currentIndex, setCurrentIndex] = useState(0); const itemsPerPage = 3; - useEffect(() => { - // Fetch recently viewed items from localStorage - setItems(getRecentlyViewed()); - }, []); - const nextSlide = () => { const maxStartIndex = Math.max(0, items.length - itemsPerPage); const newIndex = Math.min(currentIndex + itemsPerPage, maxStartIndex); diff --git a/src/index.css b/src/index.css index d3b5dc2f..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; From 16af5f2d3f09b682f1eca47f2c148cc88fe5e8ea Mon Sep 17 00:00:00 2001 From: Apar Agarwal Date: Wed, 8 Oct 2025 04:26:33 +0530 Subject: [PATCH 3/3] Refactor RecentlyViewed component structure --- src/components/RecentlyViewed.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/RecentlyViewed.jsx b/src/components/RecentlyViewed.jsx index af870d12..eb7447eb 100644 --- a/src/components/RecentlyViewed.jsx +++ b/src/components/RecentlyViewed.jsx @@ -17,7 +17,7 @@ const ChevronRightIcon = (props) => ( export default function RecentlyViewed() { - const [items, setItems] = useState(() => getRecentlyViewed()); + const [items] = useState(() => getRecentlyViewed()); const [currentIndex, setCurrentIndex] = useState(0); const itemsPerPage = 3; @@ -80,4 +80,5 @@ export default function RecentlyViewed() { ); -} \ No newline at end of file + +}