"use client"; import { useRef, useState, useCallback } from "react"; import { Upload, X, Loader2, ImageIcon, ZoomIn, Pencil } from "lucide-react"; import { cn } from "@/lib/utils"; import type { TakeAttachmentData } from "./TakeEditorPanel"; import { SketchPad } from "./SketchPad"; // ─── Lightbox ───────────────────────────────────────────────────────────────── function Lightbox({ attachments, index, onClose, }: { attachments: TakeAttachmentData[]; index: number; onClose: () => void; }) { const [current, setCurrent] = useState(index); const att = attachments[current]; return (
{/* Image */}
e.stopPropagation()} > {/* eslint-disable-next-line @next/next/no-img-element */} {att.fileName}
{/* Caption */} {att.caption && (

{att.caption}

)}

{att.fileName}

{/* Prev / Next */} {attachments.length > 1 && (
e.stopPropagation()}> {current + 1} / {attachments.length}
)}
); } // ─── Main component ─────────────────────────────────────────────────────────── interface Props { takeId: string; attachments: TakeAttachmentData[]; onChange: (updated: TakeAttachmentData[]) => void; } export function TakeImageGallery({ takeId, attachments, onChange }: Props) { const fileInputRef = useRef(null); const [uploading, setUploading] = useState(false); const [lightboxIndex, setLightboxIndex] = useState(null); const [dragOver, setDragOver] = useState(false); const [showSketch, setShowSketch] = useState(false); const imageAttachments = attachments.filter((a) => a.fileType === "IMAGE"); const uploadFile = useCallback( async (file: File) => { const formData = new FormData(); formData.append("file", file); formData.append("type", "image"); const res = await fetch("/api/upload", { method: "POST", body: formData }); if (!res.ok) throw new Error("Upload failed"); const { url, key } = await res.json(); // Register attachment in DB const attachRes = await fetch(`/api/shoot-log/takes/${takeId}/attachments`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ fileUrl: url, fileKey: key ?? "", fileName: file.name, fileSize: file.size, fileType: "IMAGE", category: "MISCELLANEOUS", }), }); if (!attachRes.ok) throw new Error("Failed to save attachment"); const { attachment } = await attachRes.json(); return attachment as TakeAttachmentData; }, [takeId] ); const handleFiles = useCallback( async (files: FileList | File[]) => { const imageFiles = Array.from(files).filter((f) => f.type.startsWith("image/")); if (!imageFiles.length) return; setUploading(true); try { const results = await Promise.all(imageFiles.map(uploadFile)); onChange([...attachments, ...results]); } finally { setUploading(false); } }, [attachments, onChange, uploadFile] ); const handleDelete = useCallback( async (id: string) => { const res = await fetch( `/api/shoot-log/takes/${takeId}/attachments/${id}`, { method: "DELETE" } ); if (res.ok) { onChange(attachments.filter((a) => a.id !== id)); } }, [attachments, onChange, takeId] ); const onDrop = useCallback( (e: React.DragEvent) => { e.preventDefault(); setDragOver(false); handleFiles(e.dataTransfer.files); }, [handleFiles] ); return (
{/* Upload zone */}
{ e.preventDefault(); setDragOver(true); }} onDragLeave={() => setDragOver(false)} onDrop={onDrop} className={cn( "border-2 border-dashed rounded-lg p-4 text-center transition-colors cursor-pointer", dragOver ? "border-amber-500 bg-amber-500/5" : "border-zinc-700 hover:border-zinc-600" )} onClick={() => fileInputRef.current?.click()} > e.target.files && handleFiles(e.target.files)} /> {uploading ? (
Uploading...
) : (
Drop images here, tap to upload, or use camera
)}
{/* Thumbnail grid */} {imageAttachments.length > 0 && (
{imageAttachments.map((att, i) => (
{/* eslint-disable-next-line @next/next/no-img-element */} {att.caption setLightboxIndex(i)} /> {/* Overlay */}
{/* Caption badge */} {att.caption && (
{att.caption}
)}
))}
)} {/* Sketch button */} {imageAttachments.length === 0 && !uploading && (
No images yet
)} {/* Sketch pad */} {showSketch && ( onChange([...attachments, attachment])} onClose={() => setShowSketch(false)} /> )} {/* Lightbox */} {lightboxIndex !== null && ( setLightboxIndex(null)} /> )}
); }