Skip to content

Commit

Permalink
Merge pull request #274 from ShivanshPlays/wishlist-frontend
Browse files Browse the repository at this point in the history
a global wishlist context, a dedicated wishlist page with styling, responsivenessand functionality, and a functional wishlist button on the dishes page
  • Loading branch information
Vimall03 authored Nov 9, 2024
2 parents 3376bfd + a4ed63a commit 7771d63
Show file tree
Hide file tree
Showing 10 changed files with 304 additions and 9 deletions.
4 changes: 4 additions & 0 deletions alimento-nextjs/actions/customer/wishlist/DELETE_wishlist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 } },
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -27,13 +29,27 @@ const DishCardFE: React.FC<DishCardProps> = ({
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<boolean>(false);
// State to track if the Dish is Wishlisted
const [isAlreadyWishlisted, setIsAlreadyWishlisted] = useState<boolean>(false);


useEffect(() => {
const checkWishlistStatus = async () => {
if (customerId) {
const Wishlisted = await isWishlisted(customerId, id);
setIsAlreadyWishlisted(Wishlisted);
}
};

if (customerId && id) {
checkWishlistStatus();
}
}, []);

return (
<div className="bg-white overflow-hidden shadow-lg rounded-lg">
<Carousel
Expand Down Expand Up @@ -71,17 +87,21 @@ const DishCardFE: React.FC<DishCardProps> = ({
<Button variant="outline" className="text-blue-600">
Add
</Button>
{customerId && session.data?.user.role === 'customer' && !isAlreadyBookmarked && (
{customerId && session.data?.user.role === 'customer' && !isAlreadyWishlisted && (
<Button
onClick={() => {
addToWishlists(customerId, id);
setIsAlreadyWishlisted(true); // Update the state to reflect the Wishlist status
}}
variant="outline"
className="text-red-600"
>
Bookmark
Wishlist
</Button>
)}
{customerId && session.data?.user.role === 'customer' && isAlreadyBookmarked && (
{customerId && session.data?.user.role === 'customer' && isAlreadyWishlisted && (
<Button variant="outline" className="text-green-600">
Bookmarked
Wishlisted
</Button>
)}
</div>
Expand Down
2 changes: 1 addition & 1 deletion alimento-nextjs/app/(PublicRoutes)/dishes/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<div className="bg-gray-50 text-gray-800">
Expand Down
Original file line number Diff line number Diff line change
@@ -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<wishlistItemProps> = ({
dish,
dishId,
customerId,
}) => {
const { removeFromWishlists, loading, error } = useWishlist();


return (

<div className="border flex items-center justify-center flex-col dark:border-gray-200 max-w-full p-4 rounded-lg shadow-md hover:shadow-lg transition">
{error? toast.error(error):null}
<Image
width={1000}
height={1000}
src={dish.images[0].url}
alt={dish.name}
className="w-full h-full object-cover rounded border-b"
/>
<h2 className="mt-2 text-xl dark:text-gray-200 font-semibold">
{dish.name}
</h2>
<p className="text-lg text-gray-600">{dish.price}</p>
<div className="mt-4 flex justify-between gap-2">
<Button
disabled={loading}
onClick={() => {
removeFromWishlists(customerId, dishId);
}}
variant="outline"
className="dark:text-gray-200 dark:hover:bg-[#f8f8f8] dark:hover:text-gray-700"
>
Remove
</Button>
</div>
</div>
);
};

export default WishlistItem;
71 changes: 71 additions & 0 deletions alimento-nextjs/app/(PublicRoutes)/wishlist/page.tsx
Original file line number Diff line number Diff line change
@@ -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 <Spinner />;
}

return (
<>
<Toaster />
<div className="h-full dark:bg-DarkGray pb-10">
<div className="relative flex items-center justify-center h-96 bg-cover bg-center bg-no-repeat mb-20 p-8 bg-[url('/wishlist.jpg')]">
{/* Overlay */}
<div className="absolute inset-0 bg-black opacity-50"></div>

{/* Title Text */}
<div className="relative z-10 text-center">
<h1 className="text-3xl md:text-5xl lg:text-7xl text-white font-extrabold">
Wish List
</h1>
</div>
</div>

<div className="mt-5 flex items-center justify-center">
{Wishlists && Wishlists.length > 0 ? (
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6 px-6 max-w-screen-xl mx-auto">
{Wishlists.map((item) => (
item.dish && item.dish.id && (
<WishlistItem
key={item.id}
dishId={item.dish.id}
customerId={session?.user.id || ""}
dish={item.dish}
/>
)
))}
</div>
) : (
<div className="text-center text-gray-700 dark:text-gray-500">
<p className="text-lg">Your wishlist is empty!</p>
<Link href="/shops">
<Button className="mt-4 px-6 py-3 bg-customTeal text-white dark:bg-Green dark:hover:bg-opacity-80 transition duration-200 transform hover:scale-105">
Continue Shopping
</Button>
</Link>
</div>
)}
</div>
</div>
</>
);
};

export default WishlistPage;
12 changes: 11 additions & 1 deletion alimento-nextjs/components/common/main-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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`),
},
];

Expand Down
134 changes: 134 additions & 0 deletions alimento-nextjs/context/customerWishlistProvider.tsx
Original file line number Diff line number Diff line change
@@ -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<void>;
addToWishlists: (customerId: string, dishId: string) => Promise<void>;
removeFromWishlists: (customerId: string, dishId: string) => Promise<void>;
isWishlisted: (customerId: string, dishId: string) => Promise<boolean>;
};

// Create context with an initial value of null
const WishlistContext = createContext<WishlistContextType | null>(null);

export function WishlistProvider({ children }: { children: React.ReactNode }) {
const [Wishlists, setWishlists] = useState<WishlistWithDish[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<string | null>(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 (
<WishlistContext.Provider
value={{
Wishlists,
loading,
error,
fetchWishlists,
addToWishlists,
removeFromWishlists,
isWishlisted,
}}
>
{children}
</WishlistContext.Provider>
);
}

// 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;
}
3 changes: 2 additions & 1 deletion alimento-nextjs/lib/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 <SessionProvider><GlobalDishProvider>{children}</GlobalDishProvider></SessionProvider>
return <SessionProvider><GlobalDishProvider><WishlistProvider>{children}</WishlistProvider></GlobalDishProvider></SessionProvider>
};
Binary file added alimento-nextjs/public/wishlist.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added alimento-nextjs/public/wishlistpng.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 7771d63

Please sign in to comment.