diff --git a/frontend/public/locales/en/translation.json b/frontend/public/locales/en/translation.json index 304e480..e163951 100644 --- a/frontend/public/locales/en/translation.json +++ b/frontend/public/locales/en/translation.json @@ -14,6 +14,10 @@ "collectionDialog": { "title": "Add New Collection" }, + "confirmationDialog": { + "title": "Delete Collection", + "warningMessages": "Are you sure you want to delete the collection {{name}}? The action is irreversible and will also delete the associated cards." + }, "editorToolbar": { "heading1": "Heading 1", "heading2": "Heading 2", @@ -89,6 +93,7 @@ "backToCollection": "Back to Collection", "cancel": "Cancel", "close": "Close", + "collectionDeletedDescription": "Collection deleted successfully", "comingSoon": "Coming soon", "confirmPassword": "Confirm Password", "create": "Create", @@ -119,6 +124,7 @@ "default": "Something went wrong", "emailIsRequired": "Email is required", "errorCreatingAccount": "Error creating account", + "errorDeletingCollection": "Error deleting collection", "errorLoadingCard": "Error loading card", "errorSavingCard": "Error saving card", "invalidCredentials": "Invalid credentials", diff --git a/frontend/public/locales/es/translation.json b/frontend/public/locales/es/translation.json index 62f764e..0e7119f 100644 --- a/frontend/public/locales/es/translation.json +++ b/frontend/public/locales/es/translation.json @@ -14,6 +14,10 @@ "collectionDialog": { "title": "Agregar Nueva Colección" }, + "confirmationDialog": { + "title": "Eliminar colección", + "warningMessages": "¿Estás seguro de que quieres eliminar la colección {{name}}? Esta acción es irreversible y también eliminará las tarjetas asociadas." + }, "editorToolbar": { "heading1": "Encabezado 1", "heading2": "Encabezado 2", @@ -89,6 +93,7 @@ "backToCollection": "Volver a la Colección", "cancel": "Cancelar", "close": "Cerrar", + "collectionDeletedDescription": "Colección eliminada exitosamente", "comingSoon": "Próximamente", "confirmPassword": "Confirmar Contraseña", "create": "Crear", @@ -119,6 +124,7 @@ "default": "Algo salió mal", "emailIsRequired": "Correo electrónico es requerido", "errorCreatingAccount": "Error al crear la cuenta", + "errorDeletingCollection": "Error al eliminar la colección", "errorLoadingCard": "Error al cargar la tarjeta", "errorSavingCard": "Error al guardar la tarjeta", "invalidCredentials": "Credenciales inválidas", diff --git a/frontend/public/locales/nl/translation.json b/frontend/public/locales/nl/translation.json index 39a694c..6e3c3c0 100644 --- a/frontend/public/locales/nl/translation.json +++ b/frontend/public/locales/nl/translation.json @@ -14,6 +14,10 @@ "collectionDialog": { "title": "Nieuwe Collectie Toevoegen" }, + "confirmationDialog": { + "title": "Collectie verwijderen", + "warningMessages": "Weet u zeker dat u de verzameling {{name}} wilt verwijderen? Deze actie is onomkeerbaar en verwijdert ook de bijbehorende kaarten." + }, "editorToolbar": { "heading1": "Kop 1", "heading2": "Kop 2", @@ -89,6 +93,7 @@ "backToCollection": "Terug naar Collectie", "cancel": "Annuleren", "close": "Sluiten", + "collectionDeletedDescription": "Collectie succesvol verwijderd", "comingSoon": "Binnenkort beschikbaar", "confirmPassword": "Bevestig Wachtwoord", "create": "Creëren", @@ -119,6 +124,7 @@ "default": "Er is iets misgegaan", "emailIsRequired": "E-mail is vereist", "errorCreatingAccount": "Fout bij het aanmaken van account", + "errorDeletingCollection": "Fout bij het verwijderen van de collectie", "errorLoadingCard": "Fout bij het laden van kaart", "errorSavingCard": "Fout bij het opslaan van kaart", "invalidCredentials": "Ongeldige inloggegevens", diff --git a/frontend/src/components/commonUI/ConfirmationDialog.tsx b/frontend/src/components/commonUI/ConfirmationDialog.tsx new file mode 100644 index 0000000..dc643bd --- /dev/null +++ b/frontend/src/components/commonUI/ConfirmationDialog.tsx @@ -0,0 +1,78 @@ +import type React from 'react' +import { useEffect, useRef } from 'react' +import { useTranslation } from 'react-i18next' +import { + DialogActionTrigger, + DialogBody, + DialogCloseTrigger, + DialogContent, + DialogFooter, + DialogHeader, + DialogRoot, + DialogTitle, +} from '../ui/dialog' +import { BlueButton, RedButton } from './Button' + +interface ConfirmationProps { + isOpen: boolean + message: string + onClose: () => void + onConfirm: () => void +} + +const ConfirmationDialog: React.FC = ({ + isOpen, + message, + onClose, + onConfirm, +}: ConfirmationProps) => { + const { t } = useTranslation() + const closeButtonRef = useRef(null) + const cancelButtonRef = useRef(null) + + useEffect(() => { + if (isOpen && cancelButtonRef.current) { + setTimeout(() => { + cancelButtonRef.current?.focus() + }, 50) + } + }, [isOpen]) + + if (!isOpen) return null + + return ( + { + if (!detail.open) { + onClose() + } + }} + > + + + {t('components.confirmationDialog.title')} + + +

{message}

+
+ + + + {t('general.actions.cancel')} + + + + {t('general.actions.delete')} + + + +
+
+ ) +} + +export default ConfirmationDialog diff --git a/frontend/src/routes/_layout/collections/index.tsx b/frontend/src/routes/_layout/collections/index.tsx index 1ad7305..e68ed18 100644 --- a/frontend/src/routes/_layout/collections/index.tsx +++ b/frontend/src/routes/_layout/collections/index.tsx @@ -2,11 +2,13 @@ import type { Collection } from '@/client/types.gen' import CollectionDialog from '@/components/collections/CollectionDialog' import CollectionListItem from '@/components/collections/CollectionListItem' import AiPromptDialog from '@/components/commonUI/AiPromptDialog' +import ConfirmationDialog from '@/components/commonUI/ConfirmationDialog' import EmptyState from '@/components/commonUI/EmptyState' import ErrorState from '@/components/commonUI/ErrorState' import ListSkeleton from '@/components/commonUI/ListSkeleton' import ScrollableContainer from '@/components/commonUI/ScrollableContainer' import SpeedDial, { type SpeedDialActionItem } from '@/components/commonUI/SpeedDial' +import { toaster } from '@/components/ui/toaster' import { createCollection, deleteCollection as deleteCollectionApi, @@ -32,6 +34,9 @@ function Collections() { const [isSpeedDialLoading, setIsSpeedDialLoading] = useState(false) const [isAiDialogOpen, setIsAiDialogOpen] = useState(false) const [isAddDialogOpen, setIsAddDialogOpen] = useState(false) + const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false) + const [collectionToDelete, setCollectionToDelete] = useState(null) + const [collectionNameToDelete, setCollectionNameToDelete] = useState('') const { data, error, isLoading } = useQuery({ queryKey: ['collections'], @@ -77,12 +82,39 @@ function Collections() { } } - const handleDeleteCollection = async (collectionId: string) => { + const openDeleteConfirmation = (collectionId: string, collectionName: string) => { + setCollectionToDelete(collectionId) + setCollectionNameToDelete(collectionName) + setIsDeleteDialogOpen(true) + } + + const closeDeleteDialog = () => { + setIsDeleteDialogOpen(false) + setCollectionToDelete(null) + setCollectionNameToDelete('') + } + + const handleDeleteCollection = async () => { + if (!collectionToDelete) return + try { - await deleteCollectionApi(collectionId) + await deleteCollectionApi(collectionToDelete) queryClient.invalidateQueries({ queryKey: ['collections'] }) + + toaster.create({ + title: t('general.actions.collectionDeletedDescription'), + type: 'success', + }) } catch (error) { console.error(error) + toaster.create({ + title: t('general.errors.errorDeletingCollection'), + type: 'error', + }) + } finally { + setIsDeleteDialogOpen(false) + setCollectionToDelete(null) + setCollectionNameToDelete('') } } @@ -124,7 +156,7 @@ function Collections() { openDeleteConfirmation(collection.id, collection.name)} onRename={renameCollection} /> )) @@ -147,6 +179,15 @@ function Collections() { title={t('components.AiCollectionDialog.title')} placeholder={t('components.AiCollectionDialog.placeholder')} /> + + ) }