"use client"; import { useState } from "react"; import { useRouter } from "next/navigation"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { createProject } from "@/actions/projects"; import { useToast } from "@/components/ui/use-toast"; const projectSchema = z.object({ name: z.string().min(2, "Name must be at least 2 characters"), code: z.string().min(2, "Project code required").regex(/^[A-Z0-9_\-]+$/i, "Alphanumeric, dash, underscore"), showId: z.string().min(1, "Show ID required").max(10, "Max 10 chars").regex(/^[A-Z0-9_]+$/i, "Letters, numbers, underscore only"), projectType: z.enum(["STANDARD", "EPISODIC"]).default("STANDARD"), description: z.string().optional(), clientId: z.string().optional(), deadline: z.string().optional(), }); type ProjectFormValues = z.infer; interface NewProjectDialogProps { open: boolean; onClose: () => void; clients?: { id: string; company: string }[]; onSuccess?: (projectId: string) => void; } export function NewProjectDialog({ open, onClose, clients = [], onSuccess, }: NewProjectDialogProps) { const [isSubmitting, setIsSubmitting] = useState(false); const { toast } = useToast(); const router = useRouter(); const { register, handleSubmit, setValue, reset, formState: { errors }, } = useForm({ resolver: zodResolver(projectSchema), defaultValues: { projectType: "STANDARD" }, }); const onSubmit = async (data: ProjectFormValues) => { setIsSubmitting(true); try { const result = await createProject({ name: data.name, code: data.code.toUpperCase(), showId: data.showId.toUpperCase(), projectType: data.projectType, description: data.description, clientId: data.clientId || undefined, deadline: data.deadline ? new Date(data.deadline) : undefined, }); toast({ title: "Project created" }); reset(); router.push(`/projects/${result.project.id}`); router.refresh(); onSuccess?.(result.project.id); onClose(); } catch (err) { toast({ title: "Failed to create project", description: (err as Error).message, variant: "destructive", }); } finally { setIsSubmitting(false); } }; return ( !o && onClose()}> New Project
{errors.name && (

{errors.name.message}

)}
{errors.code && (

{errors.code.message}

)}
{errors.showId && (

{errors.showId.message}

)}

Used in shot codes (e.g. PRJX_SC010_0010)