Skip to content

Commit

Permalink
Merge pull request #245 from ShivanshPlays/AddNewDish
Browse files Browse the repository at this point in the history
  • Loading branch information
Vimall03 authored Nov 8, 2024
2 parents f8d57a7 + 4247d92 commit 175f9af
Show file tree
Hide file tree
Showing 13 changed files with 426 additions and 12 deletions.
2 changes: 1 addition & 1 deletion alimento-nextjs/actions/dish/dishCREATE.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export async function createDish({
images,
}: {
name: string;
description?: string;
description: string;
price: number;
category: Category;
tags: Tag[];
Expand Down
8 changes: 3 additions & 5 deletions alimento-nextjs/app/api/testing/dish/route.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@

import { createDish } from "@/actions/dish/dishCREATE";
import { getAllDishes } from "@/actions/dish/dishGETALL";
import { Category, Tag } from "@prisma/client";
import { NextResponse } from "next/server";

// POST route: Creates a new dish
export async function POST(req: Request) {
try {
const body = await req.json();
const { name, description, price, category, tags, vendorId } = body;
const { name, description, price, category, tags, vendorId ,images} = body;

if (!name || !price || !category || !vendorId) {
if (!name || !price || !category || !vendorId || !images) {
return new NextResponse("Name, price, category, and vendor ID are required!", { status: 400 });
}

const resp = await createDish({ name, description, price, category, tags, vendorId });
const resp = await createDish({ name, description, price, category, tags, vendorId, images });

if (!resp.success) {
return new NextResponse(resp.error || "Failed to create dish", { status: 500 });
Expand All @@ -30,7 +29,6 @@ export async function POST(req: Request) {
// GET route: Retrieves all dishes with optional filtering
export async function GET(req: Request) {
try {
const url = new URL(req.url);

const resp = await getAllDishes({});

Expand Down
2 changes: 0 additions & 2 deletions alimento-nextjs/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import type { Metadata } from "next";
import "./globals.css";
import { Providers } from "@/lib/providers";
import Navbar from "@/components/common/navbar";
import Footer from "@/components/common/footer";


export const metadata: Metadata = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React from 'react';
import { Button } from '@/components/ui/button'; // Import the Shadcn Button component
import Link from 'next/link';
import { DishWithImages } from '../page';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
"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 { createDish } from "@/actions/dish/dishCREATE";
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 CreateDishFormValues = z.infer<typeof formSchema>;

export const CreateDishForm: React.FC = () => {
const params = useParams();
const router = useRouter();
const [loading, setLoading] = useState(false);

const title = "Create Dish";
const description = "Fill in the details to create a new Dish.";
const toastMessage = "Dish created.";
const action = "Create";

const form = useForm<CreateDishFormValues>({
resolver: zodResolver(formSchema),
defaultValues: {
name: "",
price: 0,
description: "",
category: "APPETIZER",
tags: [],
images: [],
},
});

const onSubmit = async (data: CreateDishFormValues) => {
try {
if (!params.vendorId) {
toast.error("vendorId is required!");
return;
}

const vendorId = Array.isArray(params.vendorId)
? params.vendorId[0]
: params.vendorId;

if (!vendorId) {
toast.error("Valid vendorId is required!");
return;
}

setLoading(true);

console.log(data,vendorId)
await createDish({
vendorId: vendorId,
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 (
<>
<div className="flex items-center justify-between">
<Heading title={title} description={description} />
</div>
<Separator />

<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-8 w-full"
>
<FormField
control={form.control}
name="images"
render={({ field }) => (
<FormItem>
<FormLabel>Images</FormLabel>
<FormControl>
<ImageUpload
value={field.value}
disabled={loading}
onChange={handleImageChange}
onRemove={handleImageRemove}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>

<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input
disabled={loading}
placeholder="Dish Name"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="price"
render={({ field }) => (
<FormItem>
<FormLabel>Price</FormLabel>
<FormControl>
<Input
type="number"
disabled={loading}
placeholder="Price"
{...field}
// Ensure value is a number and valid for form submission
onChange={(e) => {
const value = parseFloat(e.target.value);
field.onChange(isNaN(value) ? 0 : value);
}}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
render={({ field }) => (
<FormItem>
<FormLabel>Description</FormLabel>
<FormControl>
<Input
disabled={loading}
placeholder="Description"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="category"
render={({ field }) => (
<FormItem>
<FormLabel>Category</FormLabel>
<FormControl>
<select
disabled={loading}
{...field}
className="border rounded-md p-2"
>
<option value="" disabled>
Select a category
</option>
<option value="APPETIZER">Appetizer</option>
<option value="MAIN_COURSE">Main Course</option>
<option value="DESSERT">Dessert</option>
<option value="BEVERAGE">Beverage</option>
<option value="SNACK">Snack</option>
</select>
</FormControl>
<FormMessage />
</FormItem>
)}
/>

<FormField
control={form.control}
name="tags"
render={({ field }) => (
<FormItem>
<FormLabel>Tags</FormLabel>
<FormControl>
<TagSelectorCreateDish
dishTags={field.value}
setDishTags={(tags) => field.onChange(tags)}
disabled={loading}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>

<Button disabled={loading} className="ml-auto" type="submit">
{action}
</Button>
</form>
</Form>
<Separator />
</>
);
};
15 changes: 15 additions & 0 deletions alimento-nextjs/app/vendor/[vendorId]/createDish/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

import { CreateDishForm } from "./components/postDishForm";

const DishPage = () => {

return (
<div className="flex-col">
<div className="flex-1 space-y-4 p-8 pt-6">
<CreateDishForm/>
</div>
</div>
);
};

export default DishPage;
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
} from "@/components/ui/select";
import { useGlobalDish } from "@/context/dishFormContext";
import { Tag } from "@prisma/client";
import { useState } from "react";

const TagSelector = () => {
const tags = Object.values(Tag)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { useGlobalDish } from '@/context/dishFormContext';
import { Category, Prisma } from '@prisma/client';
import { Category } from '@prisma/client';
import { ChevronDown } from 'lucide-react';

const categories =Object.values(Category)
Expand Down
1 change: 0 additions & 1 deletion alimento-nextjs/components/multistepForm/pages/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { MouseEventHandler, useEffect } from 'react';
import { Toaster } from 'react-hot-toast';
import FormPage1 from './formPage1';
import FormPage2 from './formPage2';
import TagSelector from './formPage3';
import FormPage3 from './formPage3';
import FormPage4 from './formPage4';

Expand Down
13 changes: 13 additions & 0 deletions alimento-nextjs/components/ui/heading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
interface HeadingProps {
title: string;
description: string;
}

export const Heading: React.FC<HeadingProps> = ({ title, description }) => {
return (
<div className="dark:text-gray-200 text-center ">
<h2 className="text-4xl font-bold tracking-tight font-handlee text-customTeal dark:text-Green">{title}</h2>
<p className="text-lg text-muted-foreground"> {description}</p>
</div>
);
};
Loading

0 comments on commit 175f9af

Please sign in to comment.