import { useEffect, useMemo, useState } from 'react'; import { Box, Button, Stack, Typography, Paper, TextField, MenuItem } from '@mui/material'; type MediaItem = { key: string; size: number; lastModified: string | null; }; export default function MediaLibrary({ onInsert, onSetFeature, showSetFeature }: { onInsert: (url: string) => void; onSetFeature?: (url: string) => void; showSetFeature?: boolean }) { const [items, setItems] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const [query, setQuery] = useState(''); const [sortBy, setSortBy] = useState<'date_desc' | 'date_asc' | 'name_asc' | 'size_desc'>('date_desc'); const load = async () => { try { setLoading(true); setError(''); const res = await fetch('/api/media/list?prefix=images/'); if (!res.ok) throw new Error(await res.text()); const data = await res.json(); setItems(Array.isArray(data.items) ? data.items : []); } catch (e: any) { setError(e?.message || 'Failed to load media'); } finally { setLoading(false); } }; useEffect(() => { load(); }, []); const del = async (key: string) => { try { setError(''); const res = await fetch('/api/media/obj', { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ key }), }); if (!res.ok) throw new Error(await res.text()); await load(); } catch (e: any) { setError(e?.message || 'Delete failed'); } }; const filtered = useMemo(() => { const q = query.trim().toLowerCase(); let arr = items.filter(it => it.key.toLowerCase().includes(q)); arr.sort((a, b) => { if (sortBy === 'date_desc') return (new Date(b.lastModified || 0).getTime()) - (new Date(a.lastModified || 0).getTime()); if (sortBy === 'date_asc') return (new Date(a.lastModified || 0).getTime()) - (new Date(b.lastModified || 0).getTime()); if (sortBy === 'size_desc') return (b.size - a.size); // name_asc const an = a.key.split('/').slice(-1)[0].toLowerCase(); const bn = b.key.split('/').slice(-1)[0].toLowerCase(); return an.localeCompare(bn); }); return arr; }, [items, query, sortBy]); const fmtSize = (n: number) => { if (n >= 1024 * 1024) return (n / (1024 * 1024)).toFixed(1) + ' MB'; if (n >= 1024) return (n / 1024).toFixed(1) + ' KB'; return n + ' B'; }; const fmtDate = (s: string | null) => s ? new Date(s).toLocaleString() : ''; const copyUrl = async (url: string) => { try { await navigator.clipboard.writeText(url); } catch {} }; return ( Media Library setQuery(e.target.value)} /> setSortBy(e.target.value as any)}> Newest Oldest Name Largest {error && {error}} {filtered.map((it) => { const url = `/api/media/obj?key=${encodeURIComponent(it.key)}`; const name = it.key.split('/').slice(-1)[0]; return ( {name} {name} {fmtSize(it.size)} · {fmtDate(it.lastModified)} {showSetFeature && onSetFeature && ( )} ); })} {filtered.length === 0 && !loading && ( No images found. )} ); }