diff --git a/apps/contacts/apps/web/src/lib/components/duplicates/MergeModal.svelte b/apps/contacts/apps/web/src/lib/components/duplicates/MergeModal.svelte index 359602d04..bfe45fccf 100644 --- a/apps/contacts/apps/web/src/lib/components/duplicates/MergeModal.svelte +++ b/apps/contacts/apps/web/src/lib/components/duplicates/MergeModal.svelte @@ -2,6 +2,7 @@ import { _ } from 'svelte-i18n'; import type { Contact } from '$lib/api/contacts'; import { getDisplayName, getInitials } from '$lib/utils/contact-display'; + import { CONTACT_FIELD_LABELS, getMatchTypeLabel } from '$lib/constants/contact-fields'; interface Props { isOpen: boolean; @@ -25,17 +26,6 @@ } }); - function getMatchTypeLabel(type: 'email' | 'phone' | 'name') { - switch (type) { - case 'email': - return 'E-Mail'; - case 'phone': - return 'Telefon'; - case 'name': - return 'Name'; - } - } - function getFieldValue(contact: Contact, field: keyof Contact): string { const value = contact[field]; if (value === null || value === undefined) return '-'; @@ -52,16 +42,20 @@ } // Fields to display in comparison - const comparisonFields: { key: keyof Contact; label: string }[] = [ - { key: 'firstName', label: 'Vorname' }, - { key: 'lastName', label: 'Nachname' }, - { key: 'email', label: 'E-Mail' }, - { key: 'phone', label: 'Telefon' }, - { key: 'mobile', label: 'Mobil' }, - { key: 'company', label: 'Firma' }, - { key: 'jobTitle', label: 'Position' }, - { key: 'city', label: 'Stadt' }, + const comparisonFieldKeys: (keyof Contact)[] = [ + 'firstName', + 'lastName', + 'email', + 'phone', + 'mobile', + 'company', + 'jobTitle', + 'city', ]; + const comparisonFields = comparisonFieldKeys.map((key) => ({ + key, + label: CONTACT_FIELD_LABELS[key] || key, + })); {#if isOpen} diff --git a/apps/contacts/apps/web/src/lib/constants/contact-fields.ts b/apps/contacts/apps/web/src/lib/constants/contact-fields.ts new file mode 100644 index 000000000..8862bd979 --- /dev/null +++ b/apps/contacts/apps/web/src/lib/constants/contact-fields.ts @@ -0,0 +1,27 @@ +export const CONTACT_FIELD_LABELS: Record = { + firstName: 'Vorname', + lastName: 'Nachname', + email: 'E-Mail', + phone: 'Telefon', + mobile: 'Mobil', + company: 'Firma', + jobTitle: 'Position', + street: 'Straße', + city: 'Stadt', + postalCode: 'Postleitzahl', + country: 'Land', + website: 'Webseite', + notes: 'Notizen', + birthday: 'Geburtstag', +}; + +export function getMatchTypeLabel(type: 'email' | 'phone' | 'name'): string { + switch (type) { + case 'email': + return 'E-Mail'; + case 'phone': + return 'Telefon'; + case 'name': + return 'Name'; + } +} diff --git a/apps/contacts/apps/web/src/lib/utils/error-helpers.ts b/apps/contacts/apps/web/src/lib/utils/error-helpers.ts new file mode 100644 index 000000000..fab20b130 --- /dev/null +++ b/apps/contacts/apps/web/src/lib/utils/error-helpers.ts @@ -0,0 +1,6 @@ +export function getErrorMessage( + error: unknown, + fallback: string = 'Ein Fehler ist aufgetreten' +): string { + return error instanceof Error ? error.message : fallback; +} diff --git a/apps/contacts/apps/web/src/routes/(app)/archive/+page.svelte b/apps/contacts/apps/web/src/routes/(app)/archive/+page.svelte index 7d9a7583c..df02c4638 100644 --- a/apps/contacts/apps/web/src/routes/(app)/archive/+page.svelte +++ b/apps/contacts/apps/web/src/routes/(app)/archive/+page.svelte @@ -5,6 +5,7 @@ import type { Contact } from '$lib/api/contacts'; import { getDisplayName, getInitials } from '$lib/utils/contact-display'; import { ContactListSkeleton } from '$lib/components/skeletons'; + import { getErrorMessage } from '$lib/utils/error-helpers'; import '$lib/i18n'; import { CaretLeft, @@ -42,7 +43,7 @@ const result = await contactsApi.list({ isArchived: true }); contacts = result.contacts || result; } catch (e) { - error = e instanceof Error ? e.message : 'Fehler beim Laden des Archivs'; + error = getErrorMessage(e, 'Fehler beim Laden des Archivs'); } finally { loading = false; } @@ -58,7 +59,7 @@ await contactsApi.toggleArchive(contact.id); contacts = contacts.filter((c) => c.id !== contact.id); } catch (e) { - error = e instanceof Error ? e.message : 'Fehler beim Wiederherstellen'; + error = getErrorMessage(e, 'Fehler beim Wiederherstellen'); } } @@ -70,7 +71,7 @@ await contactsApi.delete(contact.id); contacts = contacts.filter((c) => c.id !== contact.id); } catch (e) { - error = e instanceof Error ? e.message : 'Fehler beim Löschen'; + error = getErrorMessage(e, 'Fehler beim Löschen'); } } diff --git a/apps/contacts/apps/web/src/routes/(app)/data/+page.svelte b/apps/contacts/apps/web/src/routes/(app)/data/+page.svelte index ac523236f..3feed3400 100644 --- a/apps/contacts/apps/web/src/routes/(app)/data/+page.svelte +++ b/apps/contacts/apps/web/src/routes/(app)/data/+page.svelte @@ -18,6 +18,7 @@ AddressBook, Check, } from '@manacore/shared-icons'; + import { getErrorMessage } from '$lib/utils/error-helpers'; import '$lib/i18n'; type Tab = 'import' | 'export'; @@ -105,7 +106,7 @@ preview = await importApi.preview(file); importStep = 'preview'; } catch (e) { - importError = e instanceof Error ? e.message : 'Fehler beim Verarbeiten der Datei'; + importError = getErrorMessage(e, 'Fehler beim Verarbeiten der Datei'); } finally { isLoading = false; } @@ -124,7 +125,7 @@ ContactsEvents.contactImported(fileExt as 'csv' | 'vcard', importResult?.imported); // Live query auto-updates — no manual reload needed } catch (e) { - importError = e instanceof Error ? e.message : 'Fehler beim Importieren'; + importError = getErrorMessage(e, 'Fehler beim Importieren'); } finally { isImporting = false; } @@ -146,7 +147,7 @@ try { await importApi.downloadTemplate(); } catch (e) { - importError = e instanceof Error ? e.message : 'Fehler beim Herunterladen'; + importError = getErrorMessage(e, 'Fehler beim Herunterladen'); } } @@ -164,7 +165,7 @@ }); exportSuccess = true; } catch (e) { - exportError = e instanceof Error ? e.message : 'Export fehlgeschlagen'; + exportError = getErrorMessage(e, 'Export fehlgeschlagen'); } finally { isExporting = false; } diff --git a/apps/contacts/apps/web/src/routes/(app)/duplicates/+page.svelte b/apps/contacts/apps/web/src/routes/(app)/duplicates/+page.svelte index 976ef2e7f..1d0a209dc 100644 --- a/apps/contacts/apps/web/src/routes/(app)/duplicates/+page.svelte +++ b/apps/contacts/apps/web/src/routes/(app)/duplicates/+page.svelte @@ -5,8 +5,10 @@ import MergeModal from '$lib/components/duplicates/MergeModal.svelte'; import { DuplicateListSkeleton } from '$lib/components/skeletons'; import { toastStore } from '@manacore/shared-ui'; + import { getErrorMessage } from '$lib/utils/error-helpers'; import { ArrowsClockwise } from '@manacore/shared-icons'; import { getDisplayName, getInitials } from '$lib/utils/contact-display'; + import { getMatchTypeLabel } from '$lib/constants/contact-fields'; let duplicates = $state([]); let loading = $state(true); @@ -21,24 +23,13 @@ const result = await duplicatesApi.findDuplicates(); duplicates = result.duplicates; } catch (e) { - error = e instanceof Error ? e.message : 'Fehler beim Laden der Duplikate'; + error = getErrorMessage(e, 'Fehler beim Laden der Duplikate'); console.error('Failed to load duplicates:', e); } finally { loading = false; } } - function getMatchTypeLabel(type: 'email' | 'phone' | 'name') { - switch (type) { - case 'email': - return 'E-Mail'; - case 'phone': - return 'Telefon'; - case 'name': - return 'Name'; - } - } - function getMatchTypeIcon(type: 'email' | 'phone' | 'name') { switch (type) { case 'email': @@ -70,7 +61,7 @@ } handleCloseMergeModal(); } catch (e) { - toastStore.error(e instanceof Error ? e.message : 'Fehler beim Zusammenführen'); + toastStore.error(getErrorMessage(e, 'Fehler beim Zusammenführen')); } }