"use client"; import { useEffect, useState, useRef } from "react"; import { api } from "@/lib/api"; import { cn } from "@/lib/utils"; import { Upload, Trash2, Copy, Film, Image as ImageIcon, Check, Pencil, X } from "lucide-react"; interface MediaItem { id: string; slug: string; type: "image" | "video"; mimeType: string; size: number; originalFilename: string; uploadedBy: string; createdAt: string; url: string; title?: string; description?: string; altText?: string; } interface EditForm { title: string; description: string; altText: string; } function formatFileSize(bytes: number): string { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; } export default function GalleryPage() { const [media, setMedia] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); const [uploading, setUploading] = useState(false); const [copiedId, setCopiedId] = useState(null); const [editingItem, setEditingItem] = useState(null); const [editForm, setEditForm] = useState({ title: "", description: "", altText: "" }); const [saving, setSaving] = useState(false); const fileInputRef = useRef(null); const loadMedia = async () => { try { const data = await api.getMediaList(); setMedia(data); } catch (err: any) { setError(err.message); } finally { setLoading(false); } }; useEffect(() => { loadMedia(); }, []); const handleUpload = async (e: React.ChangeEvent) => { const files = e.target.files; if (!files || files.length === 0) return; setUploading(true); setError(""); try { for (const file of Array.from(files)) { await api.uploadMedia(file); } await loadMedia(); } catch (err: any) { setError(err.message); } finally { setUploading(false); if (fileInputRef.current) fileInputRef.current.value = ""; } }; const handleDelete = async (id: string) => { if (!confirm("Delete this media item? This cannot be undone.")) return; try { await api.deleteMedia(id); await loadMedia(); } catch (err: any) { setError(err.message); } }; const handleCopyUrl = async (item: MediaItem) => { const url = `${window.location.origin}/media/${item.id}`; await navigator.clipboard.writeText(url); setCopiedId(item.id); setTimeout(() => setCopiedId(null), 2000); }; const openEdit = (item: MediaItem) => { setEditingItem(item); setEditForm({ title: item.title || "", description: item.description || "", altText: item.altText || "", }); }; const handleSaveEdit = async () => { if (!editingItem) return; setSaving(true); setError(""); try { await api.updateMedia(editingItem.id, { title: editForm.title, description: editForm.description, altText: editForm.altText, }); setEditingItem(null); await loadMedia(); } catch (err: any) { setError(err.message); } finally { setSaving(false); } }; if (loading) { return (
Loading gallery...
); } return (

Media Gallery

{error &&

{error}

} {media.length === 0 ? (

No media uploaded yet.

Upload images or videos to get started.

) : (
{media.map((item) => (
{item.type === "image" ? ( {item.altText ) : (
)} {item.type}

{item.title || item.originalFilename}

{formatFileSize(item.size)}

))}
)} {editingItem && (
setEditingItem(null)} />

Edit Media

{editingItem.type === "image" ? ( ) : (
)}

{editingItem.originalFilename}

{editingItem.type} · {formatFileSize(editingItem.size)} · {editingItem.mimeType}

setEditForm({ ...editForm, title: e.target.value })} placeholder="SEO title for this media" className="bg-surface-container-highest text-on-surface rounded-lg px-4 py-3 w-full focus:outline-none focus:ring-1 focus:ring-primary/40 text-sm" />