import { useState } from 'react'; import { useParams, Link, useNavigate } from 'react-router-dom'; import { useContact, useDeals, useDeleteContact } from '../hooks'; import { ContactFormModal } from './ContactFormModal'; import { ActivityFormModal } from '../activities/ActivityFormModal'; import { Modal } from '../../components/Modal'; import type { Contact, Activity, ActivityType, ContactType } from '../types'; import styles from './ContactDetailPage.module.css'; const TYPE_COLORS: Record = { PERSON: { bg: '#dbeafe', color: '#1e40af' }, ORGANIZATION: { bg: '#d1fae5', color: '#065f46' }, }; const TYPE_LABELS: Record = { PERSON: 'Person', ORGANIZATION: 'Organisation', }; const ACTIVITY_TYPE_LABELS: Record = { NOTE: 'Notiz', CALL: 'Anruf', EMAIL: 'E-Mail', MEETING: 'Meeting', TASK: 'Aufgabe', }; function activityIcon(type: ActivityType): React.ReactNode { const s = { width: 14, height: 14, stroke: 'currentColor', fill: 'none', strokeWidth: 1.5, strokeLinecap: 'round' as const, strokeLinejoin: 'round' as const, }; switch (type) { case 'NOTE': return ( ); case 'CALL': return ( ); case 'EMAIL': return ( ); case 'MEETING': return ( ); case 'TASK': return ( ); } } function contactDisplayName(c: Contact): string { if (c.type === 'ORGANIZATION') return c.companyName ?? '—'; return [c.firstName, c.lastName].filter(Boolean).join(' ') || '—'; } function formatDate(iso: string): string { return new Date(iso).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', }); } const currencyFormatter = new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR', }); export function ContactDetailPage() { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const { data, isLoading, error } = useContact(id!); const { data: dealsData } = useDeals({ contactId: id, pageSize: 50 }); const deleteMutation = useDeleteContact(); const [isEditOpen, setEditOpen] = useState(false); const [isActivityOpen, setActivityOpen] = useState(false); const [isDeleteOpen, setDeleteOpen] = useState(false); if (isLoading) return

Laden...

; if (error || !data) return (

Kontakt konnte nicht geladen werden

); const contact = data.data; const activities: Activity[] = contact.activities ?? []; const deals = dealsData?.data ?? []; return (
{/* Zurück */} Zurück zu Kontakte {/* Header */}

{contactDisplayName(contact)}

{TYPE_LABELS[contact.type]}
{/* 2-Spalten Layout */}
{/* Links: Info + Deals */}
{/* Info Card */}

Kontaktdaten

{contact.type === 'PERSON' && contact.firstName && ( <> Vorname {contact.firstName} )} {contact.type === 'PERSON' && contact.lastName && ( <> Nachname {contact.lastName} )} {contact.companyName && ( <> Firma {contact.companyName} )} {contact.email && ( <> E-Mail {contact.email} )} {contact.phone && ( <> Telefon {contact.phone} )} {contact.mobile && ( <> Mobil {contact.mobile} )} {contact.website && ( <> Website {contact.website} )} {(contact.street || contact.zip || contact.city) && ( <> Adresse {contact.street && <>{contact.street}
} {contact.zip} {contact.city} {contact.country && contact.country !== 'DE' && ( <>, {contact.country} )}
)}
{/* Tags */} {contact.tags && contact.tags.length > 0 && (
Tags
{contact.tags.map((tag) => ( {tag} ))}
)} {/* Notizen */} {contact.notes && (
Notizen

{contact.notes}

)}
{/* Verknüpfte Vorgänge */} {deals.length > 0 && (

Verknüpfte Vorgänge ({deals.length})

{deals.map((deal) => ( navigate(`/crm/deals/${deal.id}`)} > ))}
Titel Stage Wert
{deal.title} {deal.stage && ( {deal.stage.name} )} {deal.value ? currencyFormatter.format(parseFloat(deal.value)) : '—'}
)}
{/* Rechts: Aktivitäten-Timeline */}

Aktivitäten

{activities.length === 0 ? (

Keine Aktivitäten vorhanden

) : (
{activities.map((act) => (
{activityIcon(act.type)}
{act.subject}
{ACTIVITY_TYPE_LABELS[act.type]} ·{' '} {formatDate(act.createdAt)}
{act.description && (
{act.description}
)}
))}
)}
{/* Modals */} setEditOpen(false)} contact={contact} onSuccess={() => setEditOpen(false)} /> setActivityOpen(false)} contactId={contact.id} onSuccess={() => setActivityOpen(false)} /> {/* Löschen-Modal */} setDeleteOpen(false)} title="Kontakt löschen" maxWidth="420px" >

Soll der Kontakt{' '} {contactDisplayName(contact)} wirklich gelöscht werden?

Alle Aktivitäten werden ebenfalls gelöscht.

); }