refactor(contacts): extract error helper, field labels, match type labels

- Create lib/utils/error-helpers.ts with getErrorMessage() utility
  (replaces inline `e instanceof Error` pattern in archive + data pages)
- Create lib/constants/contact-fields.ts with CONTACT_FIELD_LABELS,
  COMPARISON_FIELDS, and getMatchTypeLabel()
  (deduplicated from MergeModal + duplicates page)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-03-31 13:14:18 +02:00
parent 3211878ae0
commit 4872bc0007
6 changed files with 60 additions and 40 deletions

View file

@ -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,
}));
</script>
{#if isOpen}

View file

@ -0,0 +1,27 @@
export const CONTACT_FIELD_LABELS: Record<string, string> = {
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';
}
}

View file

@ -0,0 +1,6 @@
export function getErrorMessage(
error: unknown,
fallback: string = 'Ein Fehler ist aufgetreten'
): string {
return error instanceof Error ? error.message : fallback;
}

View file

@ -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');
}
}

View file

@ -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;
}

View file

@ -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<DuplicateGroup[]>([]);
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'));
}
}