Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

a global wishlist context, a dedicated wishlist page with styling, responsivenessand functionality, and a functional wishlist button on the dishes page #274

Merged
merged 3 commits into from
Nov 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
45 changes: 45 additions & 0 deletions alimento-nextjs/components/ui/spinner.tsx
Original file line number Diff line number Diff line change
@@ -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<typeof spinnerVariants>,
VariantProps<typeof loaderVariants> {
className?: string;
children?: React.ReactNode;
}

export function Spinner({ size, show, children, className }: SpinnerContentProps) {
return (
<span className={spinnerVariants({ show })}>
<Loader2 className={cn(loaderVariants({ size }),'text-customTeal dark:text-Green ', className)} />
{children}
</span>
);
}
Loading