From fd628ab4091e15ae7ea162871d9364c7d523f616 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Fri, 26 Jul 2024 14:18:33 -0500 Subject: [PATCH] Improve upload delete UI feedback --- src/admin/AdminUploadsClient.tsx | 2 +- src/admin/AdminUploadsTable.tsx | 31 ++++++++---------------- src/admin/DeleteBlobButton.tsx | 41 ++++++++++++++++++++++++++++++++ src/admin/DeleteButton.tsx | 26 ++++++++++++++++++++ src/admin/DeletePhotosButton.tsx | 17 ++----------- src/photo/actions.ts | 9 ++----- 6 files changed, 82 insertions(+), 44 deletions(-) create mode 100644 src/admin/DeleteBlobButton.tsx create mode 100644 src/admin/DeleteButton.tsx diff --git a/src/admin/AdminUploadsClient.tsx b/src/admin/AdminUploadsClient.tsx index 9b48061e..924c3b94 100644 --- a/src/admin/AdminUploadsClient.tsx +++ b/src/admin/AdminUploadsClient.tsx @@ -36,7 +36,7 @@ export default function AdminUploadsClient({ setIsAdding, setUrlAddStatuses, }} />} - + ); } diff --git a/src/admin/AdminUploadsTable.tsx b/src/admin/AdminUploadsTable.tsx index f33104f9..da7ea555 100644 --- a/src/admin/AdminUploadsTable.tsx +++ b/src/admin/AdminUploadsTable.tsx @@ -7,18 +7,18 @@ import { clsx } from 'clsx/lite'; import { FaRegCircleCheck } from 'react-icons/fa6'; import { pathForAdminUploadUrl } from '@/site/paths'; import AddButton from './AddButton'; -import FormWithConfirm from '@/components/FormWithConfirm'; -import { deleteBlobPhotoAction } from '@/photo/actions'; -import DeleteFormButton from './DeleteFormButton'; import { UrlAddStatus } from './AdminUploadsClient'; import ResponsiveDate from '@/components/ResponsiveDate'; +import DeleteBlobButton from './DeleteBlobButton'; export default function AdminUploadsTable({ isAdding, urlAddStatuses, + setUrlAddStatuses, }: { isAdding?: boolean urlAddStatuses: UrlAddStatus[] + setUrlAddStatuses?: (urlAddStatuses: UrlAddStatus[]) => void }) { const isComplete = urlAddStatuses.every(({ status }) => status === 'added'); @@ -86,24 +86,13 @@ export default function AdminUploadsTable({ : <> - - - - - + setUrlAddStatuses?.(urlAddStatuses.filter( + ({ url: urlToRemove }) => urlToRemove !== url, + ))} + /> } diff --git a/src/admin/DeleteBlobButton.tsx b/src/admin/DeleteBlobButton.tsx new file mode 100644 index 00000000..8e30e030 --- /dev/null +++ b/src/admin/DeleteBlobButton.tsx @@ -0,0 +1,41 @@ +'use client'; + +import { deleteUploadAction } from '@/photo/actions'; +import DeleteButton from './DeleteButton'; +import { useRouter } from 'next/navigation'; +import { PATH_ADMIN_PHOTOS } from '@/site/paths'; +import { useState } from 'react'; + +export default function DeleteUploadButton({ + url, + shouldRedirectToAdminPhotos, + onDelete, +}: { + url: string + shouldRedirectToAdminPhotos?: boolean + onDelete?: () => void +}) { + const router = useRouter(); + + const [isDeleting, setIsDeleting] = useState(false); + + return ( + { + setIsDeleting(true); + deleteUploadAction(url) + .then(() => { + onDelete?.(); + if (shouldRedirectToAdminPhotos) { + router.push(PATH_ADMIN_PHOTOS); + } else { + setIsDeleting(false); + } + }) + .catch(() => setIsDeleting(false)); + }} + isLoading={isDeleting} + /> + ); +} diff --git a/src/admin/DeleteButton.tsx b/src/admin/DeleteButton.tsx new file mode 100644 index 00000000..e700ec2a --- /dev/null +++ b/src/admin/DeleteButton.tsx @@ -0,0 +1,26 @@ +import LoaderButton from '@/components/primitives/LoaderButton'; +import { clsx } from 'clsx/lite'; +import { ComponentProps } from 'react'; +import { BiTrash } from 'react-icons/bi'; + +export default function DeleteButton({ + className, + ...rest +}: ComponentProps ) { + return ( + } + spinnerColor="text" + className={clsx( + '!text-red-500 dark:!text-red-600', + 'active:!bg-red-100/50 active:dark:!bg-red-950/50', + 'disabled:!bg-red-100/50 disabled:dark:!bg-red-950/50', + '!border-red-200 hover:!border-red-300', + 'dark:!border-red-900/75 dark:hover:!border-red-900', + className, + )} + /> + ); +} diff --git a/src/admin/DeletePhotosButton.tsx b/src/admin/DeletePhotosButton.tsx index b71f89e7..e13681c6 100644 --- a/src/admin/DeletePhotosButton.tsx +++ b/src/admin/DeletePhotosButton.tsx @@ -5,15 +5,13 @@ import { photoQuantityText } from '@/photo'; import { deletePhotosAction } from '@/photo/actions'; import { useAppState } from '@/state/AppState'; import { toastSuccess, toastWarning } from '@/toast'; -import { clsx } from 'clsx/lite'; import { ComponentProps, useState } from 'react'; -import { BiTrash } from 'react-icons/bi'; +import DeleteButton from './DeleteButton'; export default function DeletePhotosButton({ photoIds = [], onDelete, clearLocalState = true, - className, onClick, onFinish, confirmText, @@ -34,19 +32,8 @@ export default function DeletePhotosButton({ const { invalidateSwr, registerAdminUpdate } = useAppState(); return ( - } - spinnerColor="text" - className={clsx( - '!text-red-500 dark:!text-red-600', - 'active:!bg-red-100/50 active:dark:!bg-red-950/50', - 'disabled:!bg-red-100/50 disabled:dark:!bg-red-950/50', - '!border-red-200 hover:!border-red-300', - 'dark:!border-red-900/75 dark:hover:!border-red-900', - className, - )} isLoading={isLoading} // eslint-disable-next-line max-len confirmText={confirmText ?? `Are you sure you want to delete ${photosText}? This action cannot be undone.`} diff --git a/src/photo/actions.ts b/src/photo/actions.ts index 1292e697..734208c0 100644 --- a/src/photo/actions.ts +++ b/src/photo/actions.ts @@ -283,15 +283,10 @@ export const renamePhotoTagGloballyAction = async (formData: FormData) => } }); -export const deleteBlobPhotoAction = async (formData: FormData) => +export const deleteUploadAction = async (url: string) => runAuthenticatedAdminServerAction(async () => { - await deleteFile(formData.get('url') as string); - + await deleteFile(url); revalidateAdminPaths(); - - if (formData.get('redirectToPhotos') === 'true') { - redirect(PATH_ADMIN_PHOTOS); - } }); // Accessed from admin photo edit page