-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Product detail wishlist functionality * update product detail wishlist logic * wishlist on product list page functionality * Update wishlist logic to global store * Check if user is authenticated * Tooltip please log in to use this functionality * Wishlist functionality * Style enhancements * Change metadata title * wishlist enhancements * wishlist page enhancements * remove unused component * refactor wishlist on products listing * remove unused actions * remove unused store * refactor wishlist check for product * fix for non logged users * possible cache fix * wishlist enhancements * switch wishlist to client side rendering * remove unused code * switch wishlist to client side rendering * fixed loading wishlist bug * fixed loading wishlist bug * fixed wishlist page * Add isAuthenticated prop to ProductHighlight --------- Co-authored-by: Felipe Cabrera <felipemartincabrera@gmail.com> Co-authored-by: JoaquinEtchegaray <etchegarayjoaco@gmail.com>
- Loading branch information
1 parent
769f527
commit 79c2cd6
Showing
24 changed files
with
405 additions
and
80 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,50 @@ | ||
'use client'; | ||
|
||
import { useEffect } from 'react'; | ||
|
||
import ProductCard from './ProductCard'; | ||
|
||
import useFetch from '~/_hooks/useFetch'; | ||
import { useWishlistState } from '~/_hooks/useStore'; | ||
|
||
import Container from '~/_layouts/Container'; | ||
|
||
interface Props { | ||
relatedProducts?: boolean; | ||
threeColumns?: boolean; | ||
products?: Product[]; | ||
isAuthenticated?: boolean; | ||
} | ||
|
||
const ProductList = ({ relatedProducts, threeColumns, products }: Props) => ( | ||
<Container className="mb-10"> | ||
<div | ||
className={`grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 ${ | ||
threeColumns | ||
? 'lg:grid-cols-3' | ||
: relatedProducts | ||
? 'lg:flex justify-center' | ||
: 'lg:grid-cols-4' | ||
} gap-y-4 justify-items-center`} | ||
> | ||
{products?.map((product, i) => ( | ||
<ProductCard product={product} key={`card-${i}`} /> | ||
))} | ||
</div> | ||
</Container> | ||
); | ||
const ProductList = ({ relatedProducts, threeColumns, products, isAuthenticated }: Props) => { | ||
const { setWishlist } = useWishlistState(); | ||
|
||
/** Get user wishlist */ | ||
const wishlistUrl = isAuthenticated ? '/api/wishlist/' : null; | ||
const { data } = useFetch<{ wishlist: string[] }>(wishlistUrl); | ||
|
||
/** Once wishlist is retrieved, set it to the store */ | ||
useEffect(() => { | ||
if (data) setWishlist(data.wishlist); | ||
}, [data, setWishlist]); | ||
|
||
return ( | ||
<Container className="mb-10"> | ||
<div | ||
className={`grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 ${ | ||
threeColumns | ||
? 'lg:grid-cols-3' | ||
: relatedProducts | ||
? 'lg:flex justify-center' | ||
: 'lg:grid-cols-4' | ||
} gap-y-4 justify-items-center`} | ||
> | ||
{products?.map((product, i) => ( | ||
<ProductCard key={`card-${i}`} product={product} isAuthenticated={isAuthenticated} /> | ||
))} | ||
</div> | ||
</Container> | ||
); | ||
}; | ||
|
||
export default ProductList; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { useState } from 'react'; | ||
import { FaRegHeart } from 'react-icons/fa'; | ||
|
||
import { Spinner } from '~/_components/Globals/Spinner'; | ||
import Tooltip from '~/_components/Globals/Tooltip'; | ||
import { useWishlistState } from '~/_hooks/useStore'; | ||
|
||
type Props = { | ||
product: Product; | ||
isAuthenticated?: boolean; | ||
isHovered?: boolean; | ||
}; | ||
|
||
export default function ProductWishlist({ product, isAuthenticated, isHovered }: Props) { | ||
const { wishlist, setWishlist } = useWishlistState(); | ||
|
||
const [isWishlistLoading, setIsWishlistLoading] = useState(false); | ||
|
||
/***************************************************************************** | ||
* Toggle product from wishlist | ||
****************************************************************************/ | ||
const handleToggleWishlist = async () => { | ||
setIsWishlistLoading(true); | ||
const wishlistReq = await fetch(`/api/wishlist/${product.id}`, { method: 'PUT' }); | ||
const { wishlist } = (await wishlistReq.json()) as { wishlist: string[] }; | ||
setWishlist(wishlist); | ||
setIsWishlistLoading(false); | ||
}; | ||
|
||
if (isWishlistLoading) { | ||
return ( | ||
<span className="mb-3"> | ||
<Spinner size={4} /> | ||
</span> | ||
); | ||
} | ||
|
||
return ( | ||
<Tooltip | ||
content="Please log in to use this functionality" | ||
className={`${isAuthenticated ? 'hidden' : ''}`} | ||
> | ||
<button | ||
onClick={() => { | ||
isAuthenticated && handleToggleWishlist(); | ||
}} | ||
> | ||
<FaRegHeart | ||
className={`cursor-pointer mb-3 transition-all duration-300 hover:text-red-500 | ||
${wishlist?.includes(product.id) ? 'text-red-500' : ''} | ||
${isHovered ? 'md:-translate-x-0' : 'md:opacity-0 md:translate-x-3'} | ||
`} | ||
/> | ||
</button> | ||
</Tooltip> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { useState, useEffect } from 'react'; | ||
|
||
type State<T> = { | ||
data: T | null; | ||
loading: boolean; | ||
error: Error | null; | ||
}; | ||
|
||
const useFetch = <T>(url: string | null): State<T> => { | ||
const [data, setData] = useState<T | null>(null); | ||
const [loading, setLoading] = useState<boolean>(false); | ||
const [error, setError] = useState<Error | null>(null); | ||
|
||
useEffect(() => { | ||
const fetchResource = async () => { | ||
setLoading(true); | ||
setError(null); | ||
|
||
try { | ||
if (!url) return; | ||
|
||
const response = await fetch(url); | ||
if (!response.ok) throw new Error('An error occurred while fetching data.'); | ||
|
||
const result = (await response.json()) as T; | ||
|
||
setData(result); | ||
} catch (e) { | ||
setError(e instanceof Error ? e : new Error('An unexpected error occurred.')); | ||
} finally { | ||
setLoading(false); | ||
} | ||
}; | ||
|
||
fetchResource().catch((e) => | ||
setError(e instanceof Error ? e : new Error('An unexpected error occurred.')) | ||
); | ||
}, [url]); | ||
|
||
return { data, loading, error }; | ||
}; | ||
|
||
export default useFetch; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.