"use client"; import { useState, useTransition } from "react"; import { useRouter } from "next/navigation"; import { ChevronDown, ChevronUp, Plus, Trash2 } from "lucide-react"; import { GalleryPlacement } from "@/generated/prisma/enums"; import { Input } from "@/components/ui/input"; import { MediaManagerModal } from "@/features/media/components/media-manager-modal"; import { addGalleryImageAction, deleteGalleryImageAction, reorderGalleryImagesAction, updateGalleryImageAction, } from "@/features/gallery/actions/gallery"; import type { GalleryImageSummary } from "@/services/gallery/gallery.service"; export interface GalleryManagerProps { placement: GalleryPlacement; title: string; description: string; /** Portfolio images carry a category tag for the customer-facing filter pills; the hero gallery has no use for one. */ showCategory: boolean; initialImages: GalleryImageSummary[]; } export function GalleryManager({ placement, title, description, showCategory, initialImages }: GalleryManagerProps) { const router = useRouter(); const [images, setImages] = useState(initialImages); const [pickerOpen, setPickerOpen] = useState(false); const [error, setError] = useState(null); const [isPending, startTransition] = useTransition(); const run = (fn: () => Promise<{ success: boolean; error?: string }>) => { setError(null); startTransition(async () => { const result = await fn(); if (!result.success) { setError(result.error ?? "Something went wrong."); return; } router.refresh(); }); }; const handleSelect = (url: string) => { run(async () => { const result = await addGalleryImageAction({ placement, imageUrl: url }); if (result.success) setImages((prev) => [...prev, result.image]); return result; }); }; const move = (index: number, direction: -1 | 1) => { const targetIndex = index + direction; if (targetIndex < 0 || targetIndex >= images.length) return; const reordered = [...images]; const [moved] = reordered.splice(index, 1); if (!moved) return; reordered.splice(targetIndex, 0, moved); setImages(reordered); run(() => reorderGalleryImagesAction(placement, reordered.map((i) => i.id))); }; const remove = (id: string) => { setImages((prev) => prev.filter((i) => i.id !== id)); run(() => deleteGalleryImageAction(id)); }; const setCategory = (id: string, category: string) => { setImages((prev) => prev.map((i) => (i.id === id ? { ...i, category } : i))); run(() => updateGalleryImageAction(id, { category })); }; return (

{title}

{description}

{error && (

{error}

)}
{images.map((image, index) => (
{/* eslint-disable-next-line @next/next/no-img-element -- local/user-supplied URL, same pattern used app-wide */}
{showCategory && ( setCategory(image.id, e.target.value)} placeholder="Category (optional)" className="px-3 py-2 text-xs" /> )}
))}
setPickerOpen(false)} onSelect={handleSelect} category={placement === GalleryPlacement.HERO ? "gallery" : "portfolio"} />
); }