"use client"; import { useToast } from "@/components/ui/use-toast"; import { useRouter, useParams } from "next/navigation"; import { useState } from "react"; import * as z from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { Dialog, DialogClose, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Cross2Icon, PlusIcon } from "@radix-ui/react-icons"; import { cn } from "@/lib/utils"; const createProjectSchema = z.object({ name: z.string().min(2, { message: "Name must be at least 2 characters.", }), description: z.string().min(2, { message: "Description must be at least 2 characters.", }), stack: z.array(z.string()).min(1, { message: "Project tech stack must have at least one item.", }), }); export function CreateProject({ className }: { className?: string }) { const { toast } = useToast(); const router = useRouter(); const params = useParams(); const [stackInput, setStackInput] = useState(""); const [isDialogOpen, setIsDialogOpen] = useState(false); const [formStep, setFormStep] = useState(0); const form = useForm<z.infer<typeof createProjectSchema>>({ resolver: zodResolver(createProjectSchema), defaultValues: { name: "", description: "", stack: [], }, }); const { setValue } = form; async function createProject(values: z.infer<typeof createProjectSchema>) { try { const res = await fetch(`/w/${params.workspaceID}/p`, { method: "POST", body: JSON.stringify(values), }); if (!res.ok) { const error = await res.json(); throw new Error(error.message); } const { project } = await res.json(); toast({ variant: "default", title: "Project created successfully!", description: `Project ${project.name} was created successfully.`, }); router.push(`/w/${params.workspaceID}/p/${project.id}`); router.refresh(); } catch (err: any) { console.error(err); toast({ variant: "destructive", title: "Uh oh! There was an error creating your project.", description: err.error, }); } } const keyHandler = (e: React.KeyboardEvent<HTMLInputElement>) => { if ((e.key === "Enter" || e.key === "Tab") && stackInput !== "") { e.preventDefault(); setValue("stack", [...form.getValues("stack"), stackInput]); setStackInput(""); } }; return ( <Dialog> <DialogTrigger asChild> <Button className={cn(className)}> <PlusIcon className="mr-2 h-4 w-4" /> Create Project </Button> </DialogTrigger> <DialogContent className="sm:max-w-[425px]"> <DialogHeader> <DialogTitle>Create Project</DialogTitle> </DialogHeader> <Form {...form}> <form onSubmit={form.handleSubmit(createProject)} className="space-y-4" > <FormField control={form.control} name="name" render={({ field }) => ( <FormItem> <FormLabel>Name</FormLabel> <FormControl> <Input {...field} /> </FormControl> <FormDescription>This is your project name.</FormDescription> <FormMessage /> </FormItem> )} /> <FormField control={form.control} name="description" render={({ field }) => ( <FormItem> <FormLabel>Description</FormLabel> <FormControl> <Input placeholder="" {...field} /> </FormControl> <FormDescription> This is your project description. </FormDescription> <FormMessage /> </FormItem> )} /> <FormField control={form.control} name="stack" render={() => ( <FormItem> <FormLabel>Tech Stack</FormLabel> <FormControl> <Input value={stackInput} onChange={(e) => setStackInput(e.target.value)} onKeyDown={keyHandler} /> </FormControl> <FormDescription> This is your project tech stack. </FormDescription> <div> {form.getValues("stack").map((technology) => ( <Badge key={technology} className="mr-2 font-normal rounded-md" variant="outline" > <span className="mr-1">{technology}</span> <Cross2Icon className="inline font-light text-red-500" onClick={() => setValue( "stack", form .getValues("stack") .filter((s) => s !== technology) ) } /> </Badge> ))} </div> <FormMessage /> </FormItem> )} /> <DialogClose asChild> <Button type="submit">Submit</Button> </DialogClose> </form> </Form> </DialogContent> </Dialog> ); }