"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 { createTask } from "@/actions/tasks"; import { useToast } from "@/components/ui/use-toast"; import { TaskType, ShotPriority } from "@prisma/client"; const TASK_TYPE_LABELS: Record = { TRACK: "Track", ROTO: "Roto", KEY: "Key", COMP: "Comp", FX: "FX", LIGHTING: "Lighting", RENDER: "Render", ANIMATION: "Animation", MODEL: "Model", TEXTURE: "Texture", RIG: "Rig", LOOKDEV: "Lookdev", GENERAL: "General", }; // Common shot task templates const SHOT_TEMPLATES: TaskType[] = ["TRACK", "ROTO", "KEY", "COMP", "FX", "LIGHTING"]; // Common asset task templates const ASSET_TEMPLATES: TaskType[] = ["MODEL", "TEXTURE", "RIG", "LOOKDEV"]; const taskSchema = z.object({ title: z.string().min(1, "Title is required"), description: z.string().optional(), type: z.nativeEnum(TaskType), priority: z.nativeEnum(ShotPriority), dueDate: z.string().optional(), estimatedHours: z.string().optional(), assignedArtistId: z.string().optional(), }); type TaskFormValues = z.infer; interface Artist { id: string; name: string | null; email: string; } interface NewTaskDialogProps { projectId: string; shotId?: string; assetId?: string; artists: Artist[]; open: boolean; onClose: () => void; onSuccess?: () => void; /** If provided, pre-fills the task type and title */ prefillType?: TaskType; } export function NewTaskDialog({ projectId, shotId, assetId, artists, open, onClose, onSuccess, prefillType, }: NewTaskDialogProps) { const [isSubmitting, setIsSubmitting] = useState(false); const { toast } = useToast(); const router = useRouter(); const templates = shotId ? SHOT_TEMPLATES : assetId ? ASSET_TEMPLATES : []; const { register, handleSubmit, setValue, watch, reset, formState: { errors }, } = useForm({ resolver: zodResolver(taskSchema), defaultValues: { type: prefillType ?? "GENERAL", priority: "NORMAL", title: prefillType ? TASK_TYPE_LABELS[prefillType] : "", }, }); const selectedType = watch("type"); const applyTemplate = (type: TaskType) => { setValue("type", type); setValue("title", TASK_TYPE_LABELS[type]); }; const onSubmit = async (data: TaskFormValues) => { setIsSubmitting(true); try { await createTask({ ...data, projectId, shotId: shotId ?? "", assetId: assetId ?? "", assignedArtistId: (data.assignedArtistId === "__none__" ? undefined : data.assignedArtistId) ?? undefined, estimatedHours: data.estimatedHours ? Number(data.estimatedHours) : undefined, }); toast({ title: "Task created" }); reset(); router.refresh(); onSuccess?.(); onClose(); } catch (err) { toast({ title: "Failed to create task", description: (err as Error).message, variant: "destructive", }); } finally { setIsSubmitting(false); } }; return ( !o && onClose()}> New Task {templates.length > 0 && (
{templates.map((t) => ( ))}
)}
{errors.title && (

{errors.title.message}

)}
{artists.length > 0 && (
)}