diff --git a/alimento-nextjs/actions/dish/dishEDIT.ts b/alimento-nextjs/actions/dish/dishEDIT.ts index f6a12bc..047a24c 100644 --- a/alimento-nextjs/actions/dish/dishEDIT.ts +++ b/alimento-nextjs/actions/dish/dishEDIT.ts @@ -3,26 +3,44 @@ import prismadb from '@/lib/prismadb'; import { Dish, Category, Tag } from '@prisma/client'; -export async function editDish({ - id, +export async function UpdateDish({ name, description, price, category, tags, vendorId, + images, + dishId }: { - id: string; - name?: string; - description?: string; - price?: number; - category?: Category; - tags?: Tag[]; vendorId: string; + dishId: string; + name: string; + description: string; + price: number; + category: Category; + tags: Tag[]; + images: string[] }): Promise<{ success: boolean; error?: string; data?: Dish }> { + // Validate input data + if ( + !vendorId || + !dishId || + !name || + !category || + !description || + !price || + !images || + images.length === 0 + ) { + return { success: false, error: 'All entries are required!' }; + } + try { + // Check if Dish belongs to vendor to prevent unauthorized updates const existingDish = await prismadb.dish.findUnique({ - where: { id }, + where: { id: dishId }, + select: { vendorId: true }, }); if (!existingDish) { @@ -30,23 +48,35 @@ export async function editDish({ } if (existingDish.vendorId !== vendorId) { - return { success: false, error: 'Unauthorized: You do not own this dish' }; + return { success: false, error: 'Unauthorized: You do not own this Dish' }; } + // Delete existing images and then add new images to the Dish + await prismadb.image.deleteMany({ + where: { dishId }, + }); + const updatedDish = await prismadb.dish.update({ - where: { id }, + where: { id: dishId }, data: { name, description, price, category, tags, + vendorId, + images: { + create: images.map(url => ({ url })), // Assuming you're passing URLs + }, }, }); return { success: true, data: updatedDish }; - } catch (error) { - console.error('[EDIT_DISH_ERROR]', error); - return { success: false, error: 'Error updating dish' }; + } catch (err) { + console.error('[UPDATE_Dish_ERROR]', err); + if (err instanceof Error) { + return { success: false, error: err.message }; + } + return { success: false, error: 'An unknown error occurred during Dish update' }; } } diff --git a/alimento-nextjs/actions/dish/dishGET.ts b/alimento-nextjs/actions/dish/dishGET.ts index 20f71f5..ece5783 100644 --- a/alimento-nextjs/actions/dish/dishGET.ts +++ b/alimento-nextjs/actions/dish/dishGET.ts @@ -1,16 +1,23 @@ 'use server'; import prismadb from '@/lib/prismadb'; -import { Dish } from '@prisma/client'; +import { Dish, Image } from '@prisma/client'; + +interface DishWithImage extends Dish{ + images:Image[] +} export async function getDishById({ id, }: { id: string; -}): Promise<{ success: boolean; error?: string; data?: Dish }> { +}): Promise<{ success: boolean; error?: string; data?: DishWithImage }> { try { const dish = await prismadb.dish.findUnique({ where: { id }, + include:{ + images:true + } }); if (!dish) { diff --git a/alimento-nextjs/app/vendor/[vendorId]/[dishId]/components/editDishForm.tsx b/alimento-nextjs/app/vendor/[vendorId]/[dishId]/components/editDishForm.tsx new file mode 100644 index 0000000..dc222b7 --- /dev/null +++ b/alimento-nextjs/app/vendor/[vendorId]/[dishId]/components/editDishForm.tsx @@ -0,0 +1,286 @@ +"use client"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useParams, useRouter } from "next/navigation"; +import { useCallback, useState } from "react"; +import { useForm } from "react-hook-form"; +import toast from "react-hot-toast"; +import * as z from "zod"; + +import { Button } from "@/components/ui/button"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Heading } from "@/components/ui/heading"; +import ImageUpload from "@/components/ui/imageUpload"; +import { Input } from "@/components/ui/input"; +import { Separator } from "@/components/ui/separator"; +import { UpdateDish } from "@/actions/dish/dishEDIT"; +import TagSelectorCreateDish from "@/components/vendor/tagSelectorCreateDishForm"; + +const formSchema = z.object({ + name: z.string().min(1, "Name is required"), + price: z.number().min(0, "Price must be a positive number"), + description: z.string().min(1, "Description is required"), + category: z.enum( + ["APPETIZER", "MAIN_COURSE", "DESSERT", "BEVERAGE", "SNACK"], + { + errorMap: () => ({ message: "Category is required" }), + } + ), + tags: z + .array( + z.enum([ + "SPICY", + "VEGETARIAN", + "VEGAN", + "GLUTEN_FREE", + "DAIRY_FREE", + "NUT_FREE", + "INDIAN", + "CHINESE", + "ITALIAN", + "ARABIC", + ]) + ) + .min(1, "Atleast 1 tag is required"), + images: z.array(z.string()).min(1, "At least one image is required"), +}); + +type EditDishFormValues = z.infer; + +interface EditDishFormProps { + initialData?: EditDishFormValues; +} + +export const EditDishForm: React.FC = ({ initialData }) => { + const params = useParams(); + const router = useRouter(); + const [loading, setLoading] = useState(false); + + const title = "Edit Dish"; + const description = "Update the details to edit your Dish."; + const toastMessage = "Dish updated."; + const action = "Update"; + + const form = useForm({ + resolver: zodResolver(formSchema), + defaultValues: initialData || { + // Use initialData or empty object + name: "", + price: 0, + description: "", + category: "APPETIZER", + tags: [], + images: [], + }, + }); + + const onSubmit = async (data: EditDishFormValues) => { + try { + const vendorId = Array.isArray(params.vendorId) + ? params.vendorId[0] + : params.vendorId; + + if (!vendorId) { + toast.error("Valid sellerId is required!"); + return; + } + + const dishId = Array.isArray(params.dishId) + ? params.dishId[0] + : params.dishId; + + if (!dishId) { + toast.error("dish ID is required!"); + return; + } + + setLoading(true); + + console.log(data, vendorId); + await UpdateDish({ + vendorId: vendorId, + dishId: dishId, + name: data.name, + description: data.description, + price: data.price, + category: data.category, + tags: data.tags, + images: data.images, + }); + + router.push(`/vendor/${vendorId}/`); + toast.success(toastMessage); + } catch (err) { + toast.error("Something went wrong"); + console.error(err); + } finally { + setLoading(false); + } + }; + + // Handle image change + const handleImageChange = useCallback( + (url: string) => { + form.setValue("images", [...form.getValues("images"), url]); + }, + [form] + ); + + // Handle image removal + const handleImageRemove = useCallback( + (url: string) => { + const updatedImages = form + .getValues("images") + .filter((image) => image !== url); + form.setValue("images", updatedImages); + }, + [form] + ); + + return ( + <> +
+ +
+ + +
+ + ( + + Images + + + + + + )} + /> + +
+ ( + + Name + + + + + + )} + /> + ( + + Price + + { + const value = parseFloat(e.target.value); + field.onChange(isNaN(value) ? 0 : value); + }} + /> + + + + )} + /> + ( + + Description + + + + + + )} + /> + ( + + Category + + + + + + )} + /> + + ( + + Tags + + field.onChange(tags)} + disabled={loading} + /> + + + + )} + /> +
+ + + + + + + ); +}; diff --git a/alimento-nextjs/app/vendor/[vendorId]/[dishId]/page.tsx b/alimento-nextjs/app/vendor/[vendorId]/[dishId]/page.tsx new file mode 100644 index 0000000..fd6deb4 --- /dev/null +++ b/alimento-nextjs/app/vendor/[vendorId]/[dishId]/page.tsx @@ -0,0 +1,41 @@ + +import { getDishById } from "@/actions/dish/dishGET"; +import { EditDishForm } from "./components/editDishForm"; +import { Image } from "@prisma/client"; + +type Params = Promise<{ + dishId:string, +}> +const DishPage = async (props:{ + params:Params +}) => { + + const params = await props.params + const { dishId } = params; + + console.log(dishId) + const DishData = await getDishById ({ id:dishId }); + + if (!DishData.success || !DishData.data) { + // Handle error case (e.g., show an error message or redirect) + return
Error fetching Dish: {DishData.error}
; + } + + // Assuming DishData.data has a property 'images' that is an array of objects + const filteredData = { + ...DishData.data, + images: DishData.data.images + .filter((image: Image) => typeof image.url === 'string' && image.url.startsWith('http')) // Ensure images are URLs + .map((image: { url: string }) => image.url), // Extract URLs from the image objects + }; + + return ( +
+
+ {/* Pass filtered data to the form */} +
+
+ ); +}; + +export default DishPage;