"use client"; import { useState, useRef } from "react"; import Image from "next/image"; 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 { Separator } from "@/components/ui/separator"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { updateShot } from "@/actions/shots"; import { useToast } from "@/components/ui/use-toast"; import { Upload, X, Film, ImageIcon } from "lucide-react"; const settingsSchema = z.object({ description: z.string().optional(), status: z.enum(["WAITING", "IN_PROGRESS", "IN_REVIEW", "REVISIONS", "COMPLETE"]), priority: z.enum(["LOW", "NORMAL", "HIGH", "URGENT"]), fps: z.coerce.number().min(1).max(240), frameStart: z.coerce.number().int().optional().or(z.literal("")), frameEnd: z.coerce.number().int().optional().or(z.literal("")), dueDate: z.string().optional(), artistId: z.string().optional(), }); type SettingsFormValues = z.infer; interface Artist { id: string; name: string | null; email: string; } interface ShotSettingsTabProps { shot: { id: string; shotCode: string; description: string | null; status: string; priority: string; fps: number; frameStart?: number | null; frameEnd?: number | null; dueDate: Date | string | null; artistId: string | null; thumbnailUrl: string | null; }; artists: Artist[]; onSaved?: () => void; } export function ShotSettingsTab({ shot, artists, onSaved }: ShotSettingsTabProps) { const { toast } = useToast(); const [isSaving, setIsSaving] = useState(false); const [thumbnailFile, setThumbnailFile] = useState(null); const [thumbnailPreview, setThumbnailPreview] = useState(shot.thumbnailUrl ?? null); const [clearThumbnail, setClearThumbnail] = useState(false); const fileInputRef = useRef(null); const formatDate = (d: Date | string | null) => { if (!d) return ""; return new Date(d).toISOString().split("T")[0]; }; const { register, handleSubmit, setValue, formState: { errors }, } = useForm({ resolver: zodResolver(settingsSchema), defaultValues: { description: shot.description ?? "", status: shot.status as SettingsFormValues["status"], priority: shot.priority as SettingsFormValues["priority"], fps: shot.fps, frameStart: shot.frameStart ?? "", frameEnd: shot.frameEnd ?? "", dueDate: formatDate(shot.dueDate), artistId: shot.artistId ?? "__none__", }, }); const handleThumbnailChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; setThumbnailFile(file); setClearThumbnail(false); const reader = new FileReader(); reader.onload = (ev) => setThumbnailPreview(ev.target?.result as string); reader.readAsDataURL(file); }; const onSubmit = async (values: SettingsFormValues) => { setIsSaving(true); try { let thumbnailUrl: string | null | undefined = undefined; if (clearThumbnail) { thumbnailUrl = null; } else if (thumbnailFile) { const fd = new FormData(); fd.append("file", thumbnailFile); fd.append("type", "image"); const res = await fetch("/api/upload", { method: "POST", body: fd }); if (!res.ok) throw new Error("Thumbnail upload failed"); const data = await res.json(); thumbnailUrl = data.url; } await updateShot({ shotId: shot.id, description: values.description || undefined, status: values.status, priority: values.priority, fps: values.fps, frameStart: values.frameStart !== "" && values.frameStart != null ? Number(values.frameStart) : null, frameEnd: values.frameEnd !== "" && values.frameEnd != null ? Number(values.frameEnd) : null, dueDate: values.dueDate || null, artistId: values.artistId === "__none__" ? null : values.artistId, thumbnailUrl, }); toast({ title: "Shot updated" }); onSaved?.(); } catch (e) { toast({ title: "Failed to save", description: e instanceof Error ? e.message : undefined, variant: "destructive" }); } finally { setIsSaving(false); } }; return (
{/* Details */}
Details