diff --git a/packages/frontend/src/lib/translations/bg.json b/packages/frontend/src/lib/translations/bg.json index 9eab6e72..b5703cc5 100644 --- a/packages/frontend/src/lib/translations/bg.json +++ b/packages/frontend/src/lib/translations/bg.json @@ -268,7 +268,14 @@ "view": "Преглед", "no_emails_found": "Няма намерени архивирани съобщения.", "prev": "Предишен", - "next": "Следващ" + "next": "Следващ", + "delete_error_title": "Неуспешно изтриване на имейл", + "delete_selected": "Изтриване на избраните", + "bulk_delete_confirmation_title": "Сигурни ли сте, че искате да изтриете {{count}} избрани имейла?", + "bulk_delete_confirmation_description": "Това действие не може да бъде отменено и ще премахне завинаги избраните имейли и техните прикачени файлове.", + "deleting": "Изтриване", + "confirm": "Потвърждение", + "cancel": "Отказ" }, "dashboard_page": { "title": "Табло за управление", diff --git a/packages/frontend/src/lib/translations/de.json b/packages/frontend/src/lib/translations/de.json index bc21d500..8cb0ccd2 100644 --- a/packages/frontend/src/lib/translations/de.json +++ b/packages/frontend/src/lib/translations/de.json @@ -335,7 +335,14 @@ "view": "Ansehen", "no_emails_found": "Keine archivierten E-Mails gefunden.", "prev": "Zurück", - "next": "Weiter" + "next": "Weiter", + "delete_error_title": "E-Mail konnte nicht gelöscht werden", + "delete_selected": "Ausgewählte löschen", + "bulk_delete_confirmation_title": "Möchten Sie wirklich {{count}} ausgewählte E-Mails löschen?", + "bulk_delete_confirmation_description": "Diese Aktion kann nicht rückgängig gemacht werden und entfernt die ausgewählten E-Mails und ihre Anhänge dauerhaft.", + "deleting": "Wird gelöscht", + "confirm": "Bestätigen", + "cancel": "Abbrechen" }, "dashboard_page": { "title": "Dashboard", @@ -622,7 +629,6 @@ "id": "ID", "name": "Name", "state": "Status", - "created_at": "Erstellt am", "processed_at": "Verarbeitet am", "finished_at": "Beendet am", diff --git a/packages/frontend/src/lib/translations/el.json b/packages/frontend/src/lib/translations/el.json index f7b8c7f6..3383fe56 100644 --- a/packages/frontend/src/lib/translations/el.json +++ b/packages/frontend/src/lib/translations/el.json @@ -236,7 +236,14 @@ "view": "Προβολή", "no_emails_found": "Δεν βρέθηκαν αρχειοθετημένα email.", "prev": "Προηγούμενο", - "next": "Επόμενο" + "next": "Επόμενο", + "delete_error_title": "Αποτυχία διαγραφής email", + "delete_selected": "Διαγραφή επιλεγμένων", + "bulk_delete_confirmation_title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε {{count}} επιλεγμένο(-α) email;", + "bulk_delete_confirmation_description": "Αυτή η ενέργεια δεν μπορεί να αναιρεθεί και θα αφαιρέσει οριστικά τα επιλεγμένα email και τα συνημμένα τους.", + "deleting": "Διαγραφή", + "confirm": "Επιβεβαίωση", + "cancel": "Ακύρωση" }, "dashboard_page": { "title": "Πίνακας ελέγχου", diff --git a/packages/frontend/src/lib/translations/en.json b/packages/frontend/src/lib/translations/en.json index 053f2b29..a9eb6444 100644 --- a/packages/frontend/src/lib/translations/en.json +++ b/packages/frontend/src/lib/translations/en.json @@ -335,7 +335,14 @@ "view": "View", "no_emails_found": "No archived emails found.", "prev": "Prev", - "next": "Next" + "next": "Next", + "delete_error_title": "Failed to delete email", + "delete_selected": "Delete Selected", + "bulk_delete_confirmation_title": "Are you sure you want to delete {{count}} selected emails?", + "bulk_delete_confirmation_description": "This action cannot be undone and will permanently remove the selected emails and their attachments.", + "deleting": "Deleting", + "confirm": "Confirm", + "cancel": "Cancel" }, "dashboard_page": { "title": "Dashboard", diff --git a/packages/frontend/src/lib/translations/es.json b/packages/frontend/src/lib/translations/es.json index e19fa5a5..97f88dc8 100644 --- a/packages/frontend/src/lib/translations/es.json +++ b/packages/frontend/src/lib/translations/es.json @@ -335,7 +335,14 @@ "view": "Ver", "no_emails_found": "No se encontraron correos electrónicos archivados.", "prev": "Anterior", - "next": "Siguiente" + "next": "Siguiente", + "delete_error_title": "Error al eliminar el correo", + "delete_selected": "Eliminar seleccionados", + "bulk_delete_confirmation_title": "¿Está seguro de que desea eliminar {{count}} correos seleccionados?", + "bulk_delete_confirmation_description": "Esta acción no se puede deshacer y eliminará permanentemente los correos seleccionados y sus archivos adjuntos.", + "deleting": "Eliminando", + "confirm": "Confirmar", + "cancel": "Cancelar" }, "dashboard_page": { "title": "Tablero", diff --git a/packages/frontend/src/lib/translations/et.json b/packages/frontend/src/lib/translations/et.json index 642749ef..576f98d8 100644 --- a/packages/frontend/src/lib/translations/et.json +++ b/packages/frontend/src/lib/translations/et.json @@ -254,7 +254,14 @@ "view": "Vaade", "no_emails_found": "Arhiveeritud e-kirju ei leitud.", "prev": "Eelmine", - "next": "Järgmine" + "next": "Järgmine", + "delete_error_title": "Meili kustutamine ebaõnnestus", + "delete_selected": "Kustuta valitud", + "bulk_delete_confirmation_title": "Kas olete kindel, et soovite kustutada {{count}} valitud meili?", + "bulk_delete_confirmation_description": "Seda toimingut ei saa tagasi võtta ja see eemaldab valitud meilid ja nende manused jäädavalt.", + "deleting": "Kustutamine", + "confirm": "Kinnita", + "cancel": "Tühista" } } } diff --git a/packages/frontend/src/lib/translations/fr.json b/packages/frontend/src/lib/translations/fr.json index 9be4b8b3..1ef45a1a 100644 --- a/packages/frontend/src/lib/translations/fr.json +++ b/packages/frontend/src/lib/translations/fr.json @@ -335,7 +335,14 @@ "view": "Voir", "no_emails_found": "Aucun e-mail archivé trouvé.", "prev": "Préc", - "next": "Suiv" + "next": "Suiv", + "delete_error_title": "Impossible de supprimer l'e-mail", + "delete_selected": "Supprimer la sélection", + "bulk_delete_confirmation_title": "Voulez-vous vraiment supprimer {{count}} e-mails sélectionnés ?", + "bulk_delete_confirmation_description": "Cette action est irréversible et supprimera définitivement les e-mails sélectionnés et leurs pièces jointes.", + "deleting": "Suppression en cours", + "confirm": "Confirmer", + "cancel": "Annuler" }, "dashboard_page": { "title": "Tableau de bord", diff --git a/packages/frontend/src/lib/translations/it.json b/packages/frontend/src/lib/translations/it.json index 1ddd4aac..a8078366 100644 --- a/packages/frontend/src/lib/translations/it.json +++ b/packages/frontend/src/lib/translations/it.json @@ -295,7 +295,14 @@ "view": "Visualizza", "no_emails_found": "Nessuna email archiviata trovata.", "prev": "Prec", - "next": "Succ" + "next": "Succ", + "delete_error_title": "Impossibile eliminare l'email", + "delete_selected": "Elimina selezionati", + "bulk_delete_confirmation_title": "Sei sicuro di voler eliminare {{count}} email selezionate?", + "bulk_delete_confirmation_description": "Questa azione non può essere annullata e rimuoverà definitivamente le email selezionate e i loro allegati.", + "deleting": "Eliminazione in corso", + "confirm": "Conferma", + "cancel": "Annulla" }, "dashboard_page": { "title": "Dashboard", @@ -358,7 +365,6 @@ "id": "ID", "name": "Nome", "state": "Stato", - "created_at": "Creato il", "processed_at": "Elaborato il", "finished_at": "Terminato il", diff --git a/packages/frontend/src/lib/translations/ja.json b/packages/frontend/src/lib/translations/ja.json index 3cfb33d3..91d0816b 100644 --- a/packages/frontend/src/lib/translations/ja.json +++ b/packages/frontend/src/lib/translations/ja.json @@ -254,7 +254,14 @@ "view": "表示", "no_emails_found": "アーカイブされたメールは見つかりませんでした。", "prev": "前へ", - "next": "次へ" + "next": "次へ", + "delete_error_title": "メールの削除に失敗しました", + "delete_selected": "選択したものを削除", + "bulk_delete_confirmation_title": "選択した{{count}}件のメールを削除してもよろしいですか?", + "bulk_delete_confirmation_description": "この操作は取り消せません。選択したメールと添付ファイルは完全に削除されます。", + "deleting": "削除中", + "confirm": "確認", + "cancel": "キャンセル" } } } diff --git a/packages/frontend/src/lib/translations/nl.json b/packages/frontend/src/lib/translations/nl.json index e6033a22..9cbb7819 100644 --- a/packages/frontend/src/lib/translations/nl.json +++ b/packages/frontend/src/lib/translations/nl.json @@ -254,7 +254,14 @@ "view": "Bekijken", "no_emails_found": "Geen gearchiveerde e-mails gevonden.", "prev": "Vorige", - "next": "Volgende" + "next": "Volgende", + "delete_error_title": "Verwijderen van e-mail mislukt", + "delete_selected": "Geselecteerde verwijderen", + "bulk_delete_confirmation_title": "Weet u zeker dat u {{count}} geselecteerde e-mails wilt verwijderen?", + "bulk_delete_confirmation_description": "Deze actie kan niet ongedaan worden gemaakt en verwijdert de geselecteerde e-mails en hun bijlagen definitief.", + "deleting": "Verwijderen", + "confirm": "Bevestigen", + "cancel": "Annuleren" } } } diff --git a/packages/frontend/src/lib/translations/pt.json b/packages/frontend/src/lib/translations/pt.json index 11e1b2ef..fceb7eec 100644 --- a/packages/frontend/src/lib/translations/pt.json +++ b/packages/frontend/src/lib/translations/pt.json @@ -254,7 +254,14 @@ "view": "Visualizar", "no_emails_found": "Nenhum e-mail arquivado encontrado.", "prev": "Anterior", - "next": "Próximo" + "next": "Próximo", + "delete_error_title": "Falha ao excluir e-mail", + "delete_selected": "Excluir selecionados", + "bulk_delete_confirmation_title": "Tem certeza que deseja excluir {{count}} e-mails selecionados?", + "bulk_delete_confirmation_description": "Esta ação não pode ser desfeita e removerá permanentemente os e-mails selecionados e seus anexos.", + "deleting": "Excluindo", + "confirm": "Confirmar", + "cancel": "Cancelar" } } } diff --git a/packages/frontend/src/routes/dashboard/archived-emails/+page.svelte b/packages/frontend/src/routes/dashboard/archived-emails/+page.svelte index 7d9e2dfd..675be1bc 100644 --- a/packages/frontend/src/routes/dashboard/archived-emails/+page.svelte +++ b/packages/frontend/src/routes/dashboard/archived-emails/+page.svelte @@ -3,23 +3,81 @@ import * as Table from '$lib/components/ui/table'; import { Button } from '$lib/components/ui/button'; import * as Select from '$lib/components/ui/select'; + import * as Dialog from '$lib/components/ui/dialog'; + import { Checkbox } from '$lib/components/ui/checkbox'; import { goto } from '$app/navigation'; import { t } from '$lib/translations'; import * as Pagination from '$lib/components/ui/pagination/index.js'; import ChevronLeft from 'lucide-svelte/icons/chevron-left'; import ChevronRight from 'lucide-svelte/icons/chevron-right'; + import { api } from '$lib/api.client'; + import { setAlert } from '$lib/components/custom/alert/alert-state.svelte'; + import type { PaginatedArchivedEmails } from '@open-archiver/types'; let { data }: { data: PageData } = $props(); let ingestionSources = $derived(data.ingestionSources); - let archivedEmails = $derived(data.archivedEmails); + let archivedEmails = $state(data.archivedEmails); let selectedIngestionSourceId = $derived(data.selectedIngestionSourceId); + let selectedIds = $state([]); + let isBulkDeleteDialogOpen = $state(false); + let isBulkDeleting = $state(false); + + // Keep archivedEmails in sync with navigation; preserve selectedIds across + // pages but reset when the source changes. + let previousSourceId = $state(data.selectedIngestionSourceId); + $effect(() => { + archivedEmails = data.archivedEmails; + if (data.selectedIngestionSourceId !== previousSourceId) { + selectedIds = []; + previousSourceId = data.selectedIngestionSourceId; + } + }); + + const allEmailIds = $derived(archivedEmails.items.map((e) => e.id)); + const allCurrentPageSelected = $derived( + allEmailIds.length > 0 && allEmailIds.every((id) => selectedIds.includes(id)) + ); + const someCurrentPageSelected = $derived(allEmailIds.some((id) => selectedIds.includes(id))); + const handleSourceChange = (value: string | undefined) => { if (value) { goto(`/dashboard/archived-emails?ingestionSourceId=${value}`); } }; + + const confirmBulkDelete = async () => { + isBulkDeleting = true; + const deletedIds: string[] = []; + try { + for (const id of selectedIds) { + const res = await api(`/archived-emails/${id}`, { method: 'DELETE' }); + if (!res.ok) { + const errorBody = await res.json(); + selectedIds = selectedIds.filter((i) => !deletedIds.includes(i)); + isBulkDeleteDialogOpen = false; + setAlert({ + type: 'error', + title: $t('app.archived_emails_page.delete_error_title'), + message: errorBody.message || JSON.stringify(errorBody), + duration: 5000, + show: true, + }); + return; + } + deletedIds.push(id); + } + selectedIds = []; + isBulkDeleteDialogOpen = false; + await goto( + `/dashboard/archived-emails${selectedIngestionSourceId ? `?ingestionSourceId=${selectedIngestionSourceId}` : ''}`, + { invalidateAll: true } + ); + } finally { + isBulkDeleting = false; + } + }; @@ -27,7 +85,14 @@
-

{$t('app.archived_emails_page.header')}

+
+

{$t('app.archived_emails_page.header')}

+ {#if selectedIds.length > 0} + + {/if} +
{#if ingestionSources.length > 0}
+ + + { + if (checked) { + selectedIds = [ + ...selectedIds, + ...allEmailIds.filter((id) => !selectedIds.includes(id)), + ]; + } else { + selectedIds = selectedIds.filter((id) => !allEmailIds.includes(id)); + } + }} + checked={allCurrentPageSelected + ? true + : ((someCurrentPageSelected ? 'indeterminate' : false) as any)} + /> + {$t('app.archived_emails_page.date')} {$t('app.archived_emails_page.subject')} {$t('app.archived_emails_page.sender')} @@ -68,6 +151,18 @@ {#if archivedEmails.items.length > 0} {#each archivedEmails.items as email (email.id)} + + { + if (selectedIds.includes(email.id)) { + selectedIds = selectedIds.filter((id) => id !== email.id); + } else { + selectedIds = [...selectedIds, email.id]; + } + }} + /> + {new Date(email.sentAt).toLocaleString()} @@ -90,16 +185,16 @@ - + {/each} {:else} - {$t('app.archived_emails_page.no_emails_found')} @@ -167,3 +262,38 @@
{/if} + + + + + + + {$t('app.archived_emails_page.bulk_delete_confirmation_title', { + count: selectedIds.length, + } as any)} + + + {$t('app.archived_emails_page.bulk_delete_confirmation_description')} + + + + + + + + + + diff --git a/packages/frontend/src/routes/dashboard/archived-emails/[id]/+page.svelte b/packages/frontend/src/routes/dashboard/archived-emails/[id]/+page.svelte index 3231df24..aa839537 100644 --- a/packages/frontend/src/routes/dashboard/archived-emails/[id]/+page.svelte +++ b/packages/frontend/src/routes/dashboard/archived-emails/[id]/+page.svelte @@ -259,12 +259,12 @@ }); return; } + isDeleteDialogOpen = false; await goto('/dashboard/archived-emails', { invalidateAll: true }); } catch (error) { console.error('Delete failed:', error); } finally { isDeleting = false; - isDeleteDialogOpen = false; } }