diff --git a/alimento-nextjs/actions/customer/wishlist/DELETE_wishlist.ts b/alimento-nextjs/actions/customer/wishlist/DELETE_wishlist.ts index 1b58366..f883f78 100644 --- a/alimento-nextjs/actions/customer/wishlist/DELETE_wishlist.ts +++ b/alimento-nextjs/actions/customer/wishlist/DELETE_wishlist.ts @@ -11,6 +11,10 @@ export async function deleteWishlist({ dishId: string; }): Promise<{ success: boolean; error?: string; data?: Wishlist }> { try { + if (!customerId || !dishId) { + // console.log(customerId,dishId) + return { success: false, error: 'Customer ID or Dish ID is missing.' }; + } const Wishlist = await prismadb.wishlist.delete({ where: { customerId_dishId: { customerId, dishId } }, }); diff --git a/alimento-nextjs/app/(PublicRoutes)/dishes/components/dishCardFE.tsx b/alimento-nextjs/app/(PublicRoutes)/dishes/components/dishCardFE.tsx index 0dd51b9..467b79b 100644 --- a/alimento-nextjs/app/(PublicRoutes)/dishes/components/dishCardFE.tsx +++ b/alimento-nextjs/app/(PublicRoutes)/dishes/components/dishCardFE.tsx @@ -11,6 +11,8 @@ import Image from 'next/image'; import Autoplay from 'embla-carousel-autoplay'; import { Button } from '@/components/ui/button'; import { useSession } from 'next-auth/react'; +import { checkWishlistExists } from '@/actions/customer/wishlist/EXISTS_wishlist'; +import { useWishlist } from '@/context/customerWishlistProvider'; interface DishCardProps { id: string; @@ -27,13 +29,27 @@ const DishCardFE: React.FC = ({ description, images, }) => { + const { addToWishlists, isWishlisted } = useWishlist(); const session = useSession(); const customerId = session.data?.user.id; - // State to track if the Dish is bookmarked - const [isAlreadyBookmarked, setIsAlreadyBookmarked] = useState(false); + // State to track if the Dish is Wishlisted + const [isAlreadyWishlisted, setIsAlreadyWishlisted] = useState(false); + useEffect(() => { + const checkWishlistStatus = async () => { + if (customerId) { + const Wishlisted = await isWishlisted(customerId, id); + setIsAlreadyWishlisted(Wishlisted); + } + }; + + if (customerId && id) { + checkWishlistStatus(); + } + }, []); + return (
= ({ - {customerId && session.data?.user.role === 'customer' && !isAlreadyBookmarked && ( + {customerId && session.data?.user.role === 'customer' && !isAlreadyWishlisted && ( )} - {customerId && session.data?.user.role === 'customer' && isAlreadyBookmarked && ( + {customerId && session.data?.user.role === 'customer' && isAlreadyWishlisted && ( )}
diff --git a/alimento-nextjs/app/(PublicRoutes)/dishes/page.tsx b/alimento-nextjs/app/(PublicRoutes)/dishes/page.tsx index 3c30c2f..e01ae6b 100644 --- a/alimento-nextjs/app/(PublicRoutes)/dishes/page.tsx +++ b/alimento-nextjs/app/(PublicRoutes)/dishes/page.tsx @@ -45,7 +45,7 @@ const FoodPage: React.FC = () => { fetchData(); }, [query, sort, tags, categories]); // Added tags as a dependency to refetch when tags change - console.log(foodItems); + // console.log(foodItems); return (
diff --git a/alimento-nextjs/app/(PublicRoutes)/wishlist/components/wishlistItem.tsx b/alimento-nextjs/app/(PublicRoutes)/wishlist/components/wishlistItem.tsx new file mode 100644 index 0000000..c1644b4 --- /dev/null +++ b/alimento-nextjs/app/(PublicRoutes)/wishlist/components/wishlistItem.tsx @@ -0,0 +1,55 @@ +"use client"; +import { deleteWishlist } from "@/actions/customer/wishlist/DELETE_wishlist"; +import { DishWithImages } from "@/app/vendor/[vendorId]/page"; +import { Button } from "@/components/ui/button"; +import { useWishlist } from "@/context/customerWishlistProvider"; +import Image from "next/image"; +import { useEffect } from "react"; +import toast from "react-hot-toast"; + +interface wishlistItemProps { + dish: DishWithImages; + dishId: string; + customerId: string; +} +// WishlistItem.js +const WishlistItem: React.FC = ({ + dish, + dishId, + customerId, +}) => { + const { removeFromWishlists, loading, error } = useWishlist(); + + + return ( + +
+ {error? toast.error(error):null} + {dish.name} +

+ {dish.name} +

+

₹{dish.price}

+
+ +
+
+ ); +}; + +export default WishlistItem; diff --git a/alimento-nextjs/app/(PublicRoutes)/wishlist/page.tsx b/alimento-nextjs/app/(PublicRoutes)/wishlist/page.tsx new file mode 100644 index 0000000..5e37bbc --- /dev/null +++ b/alimento-nextjs/app/(PublicRoutes)/wishlist/page.tsx @@ -0,0 +1,71 @@ +"use client"; +import { Button } from "@/components/ui/button"; +import { useSession } from "next-auth/react"; +import { useEffect } from "react"; +import { Toaster } from "react-hot-toast"; +import Link from "next/link"; +import { Spinner } from "@/components/ui/spinner"; +import WishlistItem from "./components/wishlistItem"; +import { useWishlist } from "@/context/customerWishlistProvider"; + +const WishlistPage = () => { + const { data: session } = useSession(); + const { Wishlists, fetchWishlists, loading } = useWishlist(); + + useEffect(() => { + if (session?.user?.id) { + fetchWishlists(session.user.id); + } + }, [session]); + + if (loading) { + return ; + } + + return ( + <> + +
+
+ {/* Overlay */} +
+ + {/* Title Text */} +
+

+ Wish List +

+
+
+ +
+ {Wishlists && Wishlists.length > 0 ? ( +
+ {Wishlists.map((item) => ( + item.dish && item.dish.id && ( + + ) + ))} +
+ ) : ( +
+

Your wishlist is empty!

+ + + +
+ )} +
+
+ + ); +}; + +export default WishlistPage; diff --git a/alimento-nextjs/components/common/main-nav.tsx b/alimento-nextjs/components/common/main-nav.tsx index 5d0f13a..7b5bbcb 100644 --- a/alimento-nextjs/components/common/main-nav.tsx +++ b/alimento-nextjs/components/common/main-nav.tsx @@ -18,7 +18,17 @@ const MainNav = () => { { href: `/orders`, label: 'Orders', - active: pathname.startsWith(`/`), + active: pathname.startsWith(`/orders`), + }, + { + href: `/dishes`, + label: 'Dishes', + active: pathname.startsWith(`/dishes`), + }, + { + href: `/wishlist`, + label: 'Wishlist', + active: pathname.startsWith(`/wishlist`), }, ]; diff --git a/alimento-nextjs/components/ui/spinner.tsx b/alimento-nextjs/components/ui/spinner.tsx new file mode 100644 index 0000000..f2b971f --- /dev/null +++ b/alimento-nextjs/components/ui/spinner.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { cn } from '@/lib/utils'; +import { VariantProps, cva } from 'class-variance-authority'; +import { Loader2 } from 'lucide-react'; + +const spinnerVariants = cva('flex-col items-center justify-center', { + variants: { + show: { + true: 'flex', + false: 'hidden', + }, + }, + defaultVariants: { + show: true, + }, +}); + +const loaderVariants = cva('animate-spin text-primary', { + variants: { + size: { + small: 'size-6', + medium: 'size-8', + large: 'size-12', + }, + }, + defaultVariants: { + size: 'medium', + }, +}); + +interface SpinnerContentProps + extends VariantProps, + VariantProps { + className?: string; + children?: React.ReactNode; +} + +export function Spinner({ size, show, children, className }: SpinnerContentProps) { + return ( + + + {children} + + ); +} diff --git a/alimento-nextjs/context/customerWishlistProvider.tsx b/alimento-nextjs/context/customerWishlistProvider.tsx new file mode 100644 index 0000000..1df1e1f --- /dev/null +++ b/alimento-nextjs/context/customerWishlistProvider.tsx @@ -0,0 +1,134 @@ +"use client"; + +import { createWishlist } from "@/actions/customer/wishlist/CREATE_wishlist"; +import { deleteWishlist } from "@/actions/customer/wishlist/DELETE_wishlist"; +import { checkWishlistExists } from "@/actions/customer/wishlist/EXISTS_wishlist"; +import { getWishlistsBycustomer, WishlistWithDish } from "@/actions/customer/wishlist/GETBYCUSTOMER_wishlist"; +import React, { createContext, useContext, useState, useEffect } from "react"; + + + +type WishlistContextType = { + Wishlists: WishlistWithDish[]; + loading: boolean; + error: string | null; + fetchWishlists: (customerId: string) => Promise; + addToWishlists: (customerId: string, dishId: string) => Promise; + removeFromWishlists: (customerId: string, dishId: string) => Promise; + isWishlisted: (customerId: string, dishId: string) => Promise; +}; + +// Create context with an initial value of null +const WishlistContext = createContext(null); + +export function WishlistProvider({ children }: { children: React.ReactNode }) { + const [Wishlists, setWishlists] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + // Fetch all Wishlists for a specific customer + const fetchWishlists = async (customerId: string) => { + setLoading(true); + setError(null); + try { + const response = await getWishlistsBycustomer({ customerId }); + if (response.success) { + console.log(response.data) + + if(!response.data){ + return + } + setWishlists(response.data); + } else { + setError(response.error || "Unknown error"); + } + } catch (err) { + console.error("[Wishlist_FETCH_ERROR]", err); + setError("Error fetching Wishlists"); + } finally { + setLoading(false); + } + }; + + // Add a Wishlist + const addToWishlists = async (customerId: string, dishId: string) => { + setLoading(true); + setError(null); + try { + const response = await createWishlist({ customerId, dishId }); + if (response.success && response.data) { + setWishlists((prev) => [...prev, response.data as WishlistWithDish]); + } else { + setError(response.error || "Unknown error"); + } + } catch (err) { + console.error("[Wishlist_ADD_ERROR]", err); + setError("Error adding Wishlist"); + } finally { + setLoading(false); + } + }; + + // Remove a Wishlist + const removeFromWishlists = async (customerId: string, dishId: string) => { + setLoading(true); + setError(null); + try { + // console.log(customerId,dishId) + const response = await deleteWishlist({ customerId, dishId }); + if (response.success && response.data) { + setWishlists((prev) => + prev.filter((Wishlist) => Wishlist.dishId !== dishId) + ); + } else { + setError(response.error || "Unknown error"); + } + } catch (err) { + console.error("[Wishlist_REMOVE_ERROR]", err); + setError("Error removing Wishlist"); + } finally { + setLoading(false); + } + }; + + // Check if a Wishlist exists + const isWishlisted = async (customerId: string, dishId: string) => { + setLoading(true); + setError(null); + try { + const response = await checkWishlistExists({ customerId, dishId }); + return response.success && response.exists; + } catch (err) { + console.error("[Wishlist_CHECK_ERROR]", err); + setError("Error checking Wishlist"); + return false; + } finally { + setLoading(false); + } + }; + + return ( + + {children} + + ); +} + +// Custom hook for accessing the Wishlist context +export function useWishlist() { + const context = useContext(WishlistContext); + if (!context) { + throw new Error("useWishlist must be used within a WishlistProvider"); + } + return context; +} diff --git a/alimento-nextjs/lib/providers.tsx b/alimento-nextjs/lib/providers.tsx index daa9cd7..e4a7d54 100644 --- a/alimento-nextjs/lib/providers.tsx +++ b/alimento-nextjs/lib/providers.tsx @@ -2,7 +2,8 @@ import React from 'react'; import { SessionProvider } from 'next-auth/react'; import { GlobalDishProvider } from '@/context/dishFormContext'; +import { WishlistProvider } from '@/context/customerWishlistProvider'; export const Providers = ({ children }: { children: React.ReactNode }) => { - return {children} + return {children} }; diff --git a/alimento-nextjs/public/wishlist.jpg b/alimento-nextjs/public/wishlist.jpg new file mode 100644 index 0000000..3de37cb Binary files /dev/null and b/alimento-nextjs/public/wishlist.jpg differ diff --git a/alimento-nextjs/public/wishlistpng.png b/alimento-nextjs/public/wishlistpng.png new file mode 100644 index 0000000..6cf2362 Binary files /dev/null and b/alimento-nextjs/public/wishlistpng.png differ