diff --git a/alimento-nextjs/actions/dish/dishGETALL.ts b/alimento-nextjs/actions/dish/dishGETALL.ts index fcf0cd2..44a4c2a 100644 --- a/alimento-nextjs/actions/dish/dishGETALL.ts +++ b/alimento-nextjs/actions/dish/dishGETALL.ts @@ -1,23 +1,44 @@ 'use server'; +import { DishWithImages } from '@/app/vendor/[vendorId]/page'; import prismadb from '@/lib/prismadb'; import { Dish, Category, Tag } from '@prisma/client'; export async function getAllDishes({ tags, categories, + sort, // Accept the sort parameter (which can be "", "asc", or "desc") + query, // New parameter to handle search query }: { tags?: Tag[]; categories?: Category[]; -}): Promise<{ success: boolean; error?: string; data?: Dish[] }> { + sort?: "" | "asc" | "desc"; // Updated to include empty string ("") + query?: string; // Added query parameter +}): Promise<{ success: boolean; error?: string; data?: DishWithImages[] }> { try { + // Build the `where` clause conditionally based on the presence of tags and categories + const whereConditions: any = { + AND: [ + // Only include the tags filter if the tags array is non-empty + ...(tags && tags.length > 0 ? [{ tags: { hasSome: tags } }] : []), + + // Only include the categories filter if the categories array is non-empty + ...(categories && categories.length > 0 ? [{ category: { in: categories } }] : []), + + // Apply the query filter if it's provided + ...(query ? [{ OR: [ + { name: { contains: query, mode: 'insensitive' } }, + { description: { contains: query, mode: 'insensitive' } } + ] }] : []), + ], + }; + const dishes = await prismadb.dish.findMany({ - where: { - AND: [ - tags ? { tags: { hasSome: tags } } : {}, - categories ? { category: { in: categories } } : {}, - ], + where: whereConditions, + include: { + images: true, // Including images for the dishes }, + orderBy: sort === "" ? undefined : { price: sort }, // If sort is "", don't apply sorting }); return { success: true, data: dishes }; diff --git a/alimento-nextjs/app/(PublicRoutes)/dishes/components/dishCardFE.tsx b/alimento-nextjs/app/(PublicRoutes)/dishes/components/dishCardFE.tsx new file mode 100644 index 0000000..0dd51b9 --- /dev/null +++ b/alimento-nextjs/app/(PublicRoutes)/dishes/components/dishCardFE.tsx @@ -0,0 +1,93 @@ +'use client'; + +import React, { useEffect, useState } from 'react'; +import { Image as ImageInterface } from '@prisma/client'; +import { + Carousel, + CarouselContent, + CarouselItem, +} from '@/components/ui/carousel'; +import Image from 'next/image'; +import Autoplay from 'embla-carousel-autoplay'; +import { Button } from '@/components/ui/button'; +import { useSession } from 'next-auth/react'; + +interface DishCardProps { + id: string; + name: string; + price: number; + description: string; + images: ImageInterface[]; +} + +const DishCardFE: React.FC = ({ + id, + name, + price, + description, + images, +}) => { + const session = useSession(); + const customerId = session.data?.user.id; + + // State to track if the Dish is bookmarked + const [isAlreadyBookmarked, setIsAlreadyBookmarked] = useState(false); + + + return ( +
+ + + {images.map((image, index) => ( + + {name} + + ))} + + +
+

{name}

+

{description}

+

Price: ₹{price}

+
+ + {customerId && session.data?.user.role === 'customer' && !isAlreadyBookmarked && ( + + )} + {customerId && session.data?.user.role === 'customer' && isAlreadyBookmarked && ( + + )} +
+
+
+ ); +}; + +export default DishCardFE; diff --git a/alimento-nextjs/app/(PublicRoutes)/dishes/components/selectMultipleCategories.tsx b/alimento-nextjs/app/(PublicRoutes)/dishes/components/selectMultipleCategories.tsx new file mode 100644 index 0000000..0ab52fc --- /dev/null +++ b/alimento-nextjs/app/(PublicRoutes)/dishes/components/selectMultipleCategories.tsx @@ -0,0 +1,86 @@ +import { + Select, + SelectTrigger, + SelectValue, + SelectContent, + SelectItem, + } from "@/components/ui/select"; + import { Category } from "@prisma/client"; + import React from "react"; + + interface CategorySelectorDishPageProps { + dishCategories: Category[]; + setDishCategories: (Categorys: Category[]) => void; + disabled: boolean; + } + + const CategorySelectorDishPage: React.FC = ({ + dishCategories, + setDishCategories, + disabled, + }) => { + const Categories = Object.values(Category); // Get Categorys from Prisma enum + + // Handle selecting or deselecting a Category + const handleCategorySelect = (Category: Category) => { + if (disabled) return; // Prevent action if disabled + if (dishCategories.includes(Category)) { + // Remove Category if already selected + setDishCategories(dishCategories.filter((t) => t !== Category)); + } else { + // Add Category if not already selected + setDishCategories([...dishCategories, Category]); + } + }; + + return ( +
+
+ handleCategorySelect(Category)} + disabled={disabled} + className="mr-2" + /> + {Category} +
+ + ))} + + +
+ + {/* Display Selected Categorys */} +
+ {dishCategories.map((Category) => ( + + {Category} + + + ))} +
+ + ); + }; + + export default CategorySelectorDishPage; + \ No newline at end of file diff --git a/alimento-nextjs/app/(PublicRoutes)/dishes/page.tsx b/alimento-nextjs/app/(PublicRoutes)/dishes/page.tsx new file mode 100644 index 0000000..3c30c2f --- /dev/null +++ b/alimento-nextjs/app/(PublicRoutes)/dishes/page.tsx @@ -0,0 +1,150 @@ +"use client"; + +import React, { useEffect, useState } from "react"; +import toast, { Toaster } from "react-hot-toast"; +import DishCardFE from "./components/dishCardFE"; +import { DishWithImages } from "@/app/vendor/[vendorId]/page"; +import { getAllDishes } from "@/actions/dish/dishGETALL"; +import { Category, Tag } from "@prisma/client"; +import { Search } from "lucide-react"; +import TagSelectorCreateDish from "@/components/vendor/tagSelectorCreateDishForm"; +import CategorySelectorDishPage from "./components/selectMultipleCategories"; + +const FoodPage: React.FC = () => { + const [foodItems, setFoodItems] = useState([]); + const [loading, setLoading] = useState(true); + const [tags, setTags] = useState([]); // State to manage selected tags + const [categories, setCategories] = useState([]); + const [sort, setSort] = useState<"" | "asc" | "desc">(""); + const [query, setQuery] = useState(""); + + useEffect(() => { + const fetchData = async () => { + setLoading(true); + try { + // Example API call to fetch food data + const foodResponse = await getAllDishes({ + query, + categories, + tags, + sort, + }); + console.log(foodResponse.data, foodResponse.success); + if (!foodResponse || !foodResponse.data) { + toast.error("No data fetched from BE"); + return; + } + setFoodItems(foodResponse.data); + } catch (error) { + console.error("Error fetching food data:", error); + } finally { + setLoading(false); + } + }; + + fetchData(); + }, [query, sort, tags, categories]); // Added tags as a dependency to refetch when tags change + + console.log(foodItems); + + return ( +
+ + {/* Hero Section with Banner Image */} +
+
+
+

Find Your Favorite Food

+

+ Discover delicious dishes and explore a variety of food options. +

+
+
+ + {/* Custom Search Bar */} +
+
+ {/* Search Input with Font Awesome Search Icon */} +
+ setQuery(e.target.value)} + /> + +
+ + {/* Sort by Price Dropdown */} +
+ +
+ +
+ +
+
+ +
+
+
+ + {/* Food Items Section */} +
+

Available Food

+ +
+ {loading ? ( +
+
+
+ ) : ( + foodItems.map((food) => ( + + )) + )} +
+
+
+ ); +}; + +export default FoodPage;