From 74a7192a7f61aba5cf9c5f7dd196011407903e9e Mon Sep 17 00:00:00 2001 From: Shane Israel Date: Mon, 14 Sep 2026 10:01:53 -0600 Subject: [PATCH 1/5] fix: add the missing Uploader action to the images file manager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Videos tab has had a bulk Uploader action since ownership landed, but the Images tab never grew one, so images indexed from disk before ownership existed were stuck unattributed with no way to adopt them. The server side was already in place — bulk-set-uploader has always taken image_ids — so this is only the missing control: an Uploader button in the organize group, labelled on desktop and icon-only on mobile, opening the same account picker as videos, including the option to clear attribution. The success toast reported "(0)" on both tabs, because runBulkAction counts the updated/moved/deleted array while bulk-set-uploader answers with updated_images / updated_videos counts instead. Both now prefer that count when the response carries one. Fixes #726 --- .../src/components/admin/ImageFileManager.js | 114 +++++++++++++++++- .../src/components/admin/VideoFileManager.js | 2 +- 2 files changed, 114 insertions(+), 2 deletions(-) diff --git a/app/client/src/components/admin/ImageFileManager.js b/app/client/src/components/admin/ImageFileManager.js index 64acbcf8..d91a77f2 100644 --- a/app/client/src/components/admin/ImageFileManager.js +++ b/app/client/src/components/admin/ImageFileManager.js @@ -34,6 +34,7 @@ import SearchIcon from '@mui/icons-material/Search' import FolderIcon from '@mui/icons-material/Folder' import OpenInNewIcon from '@mui/icons-material/OpenInNew' import LockIcon from '@mui/icons-material/Lock' +import PersonIcon from '@mui/icons-material/Person' import LockOpenIcon from '@mui/icons-material/LockOpen' import RefreshIcon from '@mui/icons-material/Refresh' import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown' @@ -305,6 +306,9 @@ export default function ImageFileManager({ setAlert }) { const [moveModalOpen, setMoveModalOpen] = useState(false) const [createFolderDialogOpen, setCreateFolderDialogOpen] = useState(false) const [renameDialogOpen, setRenameDialogOpen] = useState(false) + const [uploaderDialogOpen, setUploaderDialogOpen] = useState(false) + const [uploaderChoice, setUploaderChoice] = useState(null) + const [uploaderOptions, setUploaderOptions] = useState([]) const [colVisAnchor, setColVisAnchor] = useState(null) // Rename form state @@ -493,7 +497,7 @@ export default function ImageFileManager({ setAlert }) { setActionLoading(true) try { const { data } = await Api().post(endpoint, body) - const updatedCount = (data.updated ?? data.moved ?? data.deleted ?? []).length + const updatedCount = data.updated_images ?? (data.updated ?? data.moved ?? data.deleted ?? []).length const errorCount = (data.errors ?? []).length if (errorCount > 0) { setAlert({ @@ -597,6 +601,23 @@ export default function ImageFileManager({ setAlert }) { } } + useEffect(() => { + if (!uploaderDialogOpen) return + Api() + .get('/api/admin/uploaders') + .then((res) => setUploaderOptions(res.data.users || [])) + .catch(() => setUploaderOptions([])) + }, [uploaderDialogOpen]) + + const handleSetUploader = async () => { + const ok = await runBulkAction( + '/api/admin/image-files/bulk-set-uploader', + { image_ids: [...selected], username: uploaderChoice }, + uploaderChoice ? `Attributed to ${uploaderChoice}` : 'Uploader cleared', + ) + if (ok) setUploaderDialogOpen(false) + } + const handleSetPrivacy = async (isPrivate) => { await runBulkAction( '/api/admin/image-files/bulk-set-privacy', @@ -709,6 +730,26 @@ export default function ImageFileManager({ setAlert }) { + + + { + setUploaderChoice(null) + setUploaderDialogOpen(true) + }} + sx={{ + border: '1px solid #3399FF44', + borderRadius: 1, + color: '#7FBFFF', + '&:hover': { bgcolor: '#3399FF12' }, + }} + > + + + + + + + + +