diff --git a/src/admin/AdminBatchEditPanelClient.tsx b/src/admin/AdminBatchEditPanelClient.tsx index f543e8e9..0df6d5f5 100644 --- a/src/admin/AdminBatchEditPanelClient.tsx +++ b/src/admin/AdminBatchEditPanelClient.tsx @@ -6,7 +6,6 @@ import SiteGrid from '@/components/SiteGrid'; import { useAppState } from '@/state/AppState'; import { clsx } from 'clsx/lite'; import { IoCloseSharp } from 'react-icons/io5'; -import DeleteButton from './DeleteButton'; import { useState } from 'react'; import { Tags } from '@/tag'; import { usePathname } from 'next/navigation'; @@ -14,6 +13,7 @@ import { PATH_GRID_INFERRED } from '@/site/paths'; import PhotoTagFieldset from './PhotoTagFieldset'; import { tagMultiplePhotosAction } from '@/photo/actions'; import { toastSuccess } from '@/toast'; +import DeletePhotosButton from './DeletePhotosButton'; export default function AdminBatchEditPanelClient({ uniqueTags, @@ -96,7 +96,11 @@ export default function AdminBatchEditPanelClient({ > Tag ... - + } } diff --git a/src/admin/DeletePhotosButton.tsx b/src/admin/DeletePhotosButton.tsx new file mode 100644 index 00000000..66133f2f --- /dev/null +++ b/src/admin/DeletePhotosButton.tsx @@ -0,0 +1,53 @@ +'use client'; + +import LoaderButton from '@/components/primitives/LoaderButton'; +import { photoQuantityText } from '@/photo'; +import { deletePhotosAction } from '@/photo/actions'; +import { toastSuccess, toastWarning } from '@/toast'; +import { clsx } from 'clsx/lite'; +import { ComponentProps, useState } from 'react'; +import { BiTrash } from 'react-icons/bi'; + +export default function DeletePhotosButton({ + photoIds = [], + onDelete, + className, + ...rest +}: { + photoIds?: string[] + onDelete?: () => void +} & ComponentProps) { + const [isLoading, setIsLoading] = useState(false); + + const photosText = photoQuantityText(photoIds.length, false); + + 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={`Are you sure you want to delete ${photosText}? This action cannot be undone.`} + onClick={() => { + setIsLoading(true); + deletePhotosAction(photoIds) + .then(() => { + toastSuccess(`${photosText} deleted`); + onDelete?.(); + }) + .catch(() => toastWarning(`Failed to delete ${photosText}`)) + .finally(() => setIsLoading(false)); + }} + /> + ); +} diff --git a/src/components/primitives/LoaderButton.tsx b/src/components/primitives/LoaderButton.tsx index c63683cf..e909e502 100644 --- a/src/components/primitives/LoaderButton.tsx +++ b/src/components/primitives/LoaderButton.tsx @@ -59,7 +59,7 @@ export default function LoaderButton(props: { > {(icon || isLoading) && @@ -67,9 +67,7 @@ export default function LoaderButton(props: { ? : icon} } diff --git a/src/photo/actions.ts b/src/photo/actions.ts index 9240bbf5..35d29a21 100644 --- a/src/photo/actions.ts +++ b/src/photo/actions.ts @@ -236,6 +236,17 @@ export const toggleFavoritePhotoAction = async ( } }); +export const deletePhotosAction = async (photoIds: string[]) => + runAuthenticatedAdminServerAction(async () => { + for (const photoId of photoIds) { + const photo = await getPhoto(photoId); + if (photo) { + await deletePhoto(photoId).then(() => deleteFile(photo.url)); + } + } + revalidateAllKeysAndPaths(); + }); + export const deletePhotoAction = async ( photoId: string, photoUrl: string,