Improve upload delete UI feedback

This commit is contained in:
Sam Becker 2024-07-26 14:18:33 -05:00
parent e35489cba2
commit fd628ab409
6 changed files with 82 additions and 44 deletions

View File

@ -36,7 +36,7 @@ export default function AdminUploadsClient({
setIsAdding, setIsAdding,
setUrlAddStatuses, setUrlAddStatuses,
}} />} }} />}
<AdminUploadsTable {...{ isAdding, urlAddStatuses }} /> <AdminUploadsTable {...{ isAdding, urlAddStatuses, setUrlAddStatuses }} />
</div> </div>
); );
} }

View File

@ -7,18 +7,18 @@ import { clsx } from 'clsx/lite';
import { FaRegCircleCheck } from 'react-icons/fa6'; import { FaRegCircleCheck } from 'react-icons/fa6';
import { pathForAdminUploadUrl } from '@/site/paths'; import { pathForAdminUploadUrl } from '@/site/paths';
import AddButton from './AddButton'; import AddButton from './AddButton';
import FormWithConfirm from '@/components/FormWithConfirm';
import { deleteBlobPhotoAction } from '@/photo/actions';
import DeleteFormButton from './DeleteFormButton';
import { UrlAddStatus } from './AdminUploadsClient'; import { UrlAddStatus } from './AdminUploadsClient';
import ResponsiveDate from '@/components/ResponsiveDate'; import ResponsiveDate from '@/components/ResponsiveDate';
import DeleteBlobButton from './DeleteBlobButton';
export default function AdminUploadsTable({ export default function AdminUploadsTable({
isAdding, isAdding,
urlAddStatuses, urlAddStatuses,
setUrlAddStatuses,
}: { }: {
isAdding?: boolean isAdding?: boolean
urlAddStatuses: UrlAddStatus[] urlAddStatuses: UrlAddStatus[]
setUrlAddStatuses?: (urlAddStatuses: UrlAddStatus[]) => void
}) { }) {
const isComplete = urlAddStatuses.every(({ status }) => status === 'added'); const isComplete = urlAddStatuses.every(({ status }) => status === 'added');
@ -86,24 +86,13 @@ export default function AdminUploadsTable({
</> </>
: <> : <>
<AddButton path={pathForAdminUploadUrl(url)} /> <AddButton path={pathForAdminUploadUrl(url)} />
<FormWithConfirm <DeleteBlobButton
action={deleteBlobPhotoAction} url={url}
confirmText="Are you sure you want to delete this upload?" shouldRedirectToAdminPhotos={urlAddStatuses.length <= 1}
> onDelete={() => setUrlAddStatuses?.(urlAddStatuses.filter(
<input ({ url: urlToRemove }) => urlToRemove !== url,
type="hidden" ))}
name="redirectToPhotos" />
value={urlAddStatuses.length < 2 ? 'true' : 'false'}
readOnly
/>
<input
type="hidden"
name="url"
value={url}
readOnly
/>
<DeleteFormButton />
</FormWithConfirm>
</>} </>}
</span> </span>
</div> </div>

View File

@ -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 (
<DeleteButton
confirmText="Are you sure you want to delete this upload?"
onClick={() => {
setIsDeleting(true);
deleteUploadAction(url)
.then(() => {
onDelete?.();
if (shouldRedirectToAdminPhotos) {
router.push(PATH_ADMIN_PHOTOS);
} else {
setIsDeleting(false);
}
})
.catch(() => setIsDeleting(false));
}}
isLoading={isDeleting}
/>
);
}

View File

@ -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 <typeof LoaderButton>) {
return (
<LoaderButton
{...rest}
title="Delete"
icon={<BiTrash size={16} />}
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,
)}
/>
);
}

View File

@ -5,15 +5,13 @@ import { photoQuantityText } from '@/photo';
import { deletePhotosAction } from '@/photo/actions'; import { deletePhotosAction } from '@/photo/actions';
import { useAppState } from '@/state/AppState'; import { useAppState } from '@/state/AppState';
import { toastSuccess, toastWarning } from '@/toast'; import { toastSuccess, toastWarning } from '@/toast';
import { clsx } from 'clsx/lite';
import { ComponentProps, useState } from 'react'; import { ComponentProps, useState } from 'react';
import { BiTrash } from 'react-icons/bi'; import DeleteButton from './DeleteButton';
export default function DeletePhotosButton({ export default function DeletePhotosButton({
photoIds = [], photoIds = [],
onDelete, onDelete,
clearLocalState = true, clearLocalState = true,
className,
onClick, onClick,
onFinish, onFinish,
confirmText, confirmText,
@ -34,19 +32,8 @@ export default function DeletePhotosButton({
const { invalidateSwr, registerAdminUpdate } = useAppState(); const { invalidateSwr, registerAdminUpdate } = useAppState();
return ( return (
<LoaderButton <DeleteButton
{...rest} {...rest}
title="Delete"
icon={<BiTrash size={16} />}
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} isLoading={isLoading}
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
confirmText={confirmText ?? `Are you sure you want to delete ${photosText}? This action cannot be undone.`} confirmText={confirmText ?? `Are you sure you want to delete ${photosText}? This action cannot be undone.`}

View File

@ -283,15 +283,10 @@ export const renamePhotoTagGloballyAction = async (formData: FormData) =>
} }
}); });
export const deleteBlobPhotoAction = async (formData: FormData) => export const deleteUploadAction = async (url: string) =>
runAuthenticatedAdminServerAction(async () => { runAuthenticatedAdminServerAction(async () => {
await deleteFile(formData.get('url') as string); await deleteFile(url);
revalidateAdminPaths(); revalidateAdminPaths();
if (formData.get('redirectToPhotos') === 'true') {
redirect(PATH_ADMIN_PHOTOS);
}
}); });
// Accessed from admin photo edit page // Accessed from admin photo edit page