From be6ce75a77a4e707711970aa8822709701375a8a Mon Sep 17 00:00:00 2001 From: Ender Date: Sun, 26 Oct 2025 22:36:11 +0100 Subject: [PATCH] feat: add file upload button to media library - Added file upload button with multi-file support alongside existing paste functionality - Implemented handleFileUpload function to process multiple image files sequentially - Added file input reference and reset logic to allow repeated uploads - Included upload progress indicator showing count of files being processed - Added validation to skip non-image files with error messaging - Enhanced UI with disabled state during upload to prevent concurrent --- apps/admin/src/components/MediaLibrary.tsx | 54 +++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/apps/admin/src/components/MediaLibrary.tsx b/apps/admin/src/components/MediaLibrary.tsx index 11a7b8d..620cc3c 100644 --- a/apps/admin/src/components/MediaLibrary.tsx +++ b/apps/admin/src/components/MediaLibrary.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useState } from 'react'; +import { useEffect, useMemo, useState, useRef } from 'react'; import { Box, Button, Stack, Typography, Paper, TextField, MenuItem } from '@mui/material'; type MediaItem = { @@ -138,6 +138,42 @@ export default function MediaLibrary({ } catch {} }; + const fileInputRef = useRef(null); + + const handleFileUpload = async (files: FileList | null) => { + if (!files || files.length === 0) return; + + setUploading(true); + setUploadingCount(files.length); + setError(''); + + for (let i = 0; i < files.length; i++) { + const file = files[i]; + if (!file.type.startsWith('image/')) { + setError(`Skipped ${file.name}: not an image`); + continue; + } + + try { + const fd = new FormData(); + fd.append('image', file, file.name); + const res = await fetch('/api/media/image', { method: 'POST', body: fd }); + if (!res.ok) throw new Error(await res.text()); + } catch (err: any) { + setError(err?.message || `Failed to upload ${file.name}`); + } + } + + setUploading(false); + setUploadingCount(0); + await load(); + + // Reset file input + if (fileInputRef.current) { + fileInputRef.current.value = ''; + } + }; + return ( @@ -155,6 +191,22 @@ export default function MediaLibrary({ Name Largest + handleFileUpload(e.target.files)} + /> + Tip: paste an image (Cmd/Ctrl+V) to upload{uploading ? ` — uploading ${uploadingCount}…` : ''}