diff --git a/packages/frontend/src/crm/activities/ActivityFormModal.tsx b/packages/frontend/src/crm/activities/ActivityFormModal.tsx new file mode 100644 index 0000000..fd7863a --- /dev/null +++ b/packages/frontend/src/crm/activities/ActivityFormModal.tsx @@ -0,0 +1,291 @@ +import { useState, useEffect } from 'react'; +import { Modal } from '../../components/Modal'; +import { useCreateActivity, useUpdateActivity } from '../hooks'; +import type { Activity, ActivityType } from '../types'; + +interface ActivityFormModalProps { + isOpen: boolean; + onClose: () => void; + contactId: string; + activity?: Activity | null; + onSuccess: () => void; +} + +const ACTIVITY_TYPE_LABELS: Record = { + NOTE: 'Notiz', + CALL: 'Anruf', + EMAIL: 'E-Mail', + MEETING: 'Meeting', + TASK: 'Aufgabe', +}; + +const ACTIVITY_TYPES: ActivityType[] = [ + 'NOTE', + 'CALL', + 'EMAIL', + 'MEETING', + 'TASK', +]; + +const labelStyle: React.CSSProperties = { + fontSize: '0.875rem', + fontWeight: 500, + color: 'var(--color-text)', + marginBottom: '0.25rem', + display: 'block', +}; + +const inputStyle: React.CSSProperties = { + width: '100%', + padding: '0.625rem 0.75rem', + border: '1px solid var(--color-border)', + borderRadius: 'var(--radius-sm)', + fontSize: '0.9375rem', + outline: 'none', + boxSizing: 'border-box', + background: 'var(--color-bg-card)', + color: 'var(--color-text)', +}; + +export function ActivityFormModal({ + isOpen, + onClose, + contactId, + activity, + onSuccess, +}: ActivityFormModalProps) { + const isEditMode = !!activity; + const createMutation = useCreateActivity(); + const updateMutation = useUpdateActivity(); + const mutation = isEditMode ? updateMutation : createMutation; + + const [error, setError] = useState(''); + const [type, setType] = useState('NOTE'); + const [subject, setSubject] = useState(''); + const [description, setDescription] = useState(''); + const [scheduledAt, setScheduledAt] = useState(''); + const [completedAt, setCompletedAt] = useState(''); + + useEffect(() => { + if (isOpen) { + setError(''); + if (activity) { + setType(activity.type); + setSubject(activity.subject); + setDescription(activity.description ?? ''); + setScheduledAt( + activity.scheduledAt + ? activity.scheduledAt.slice(0, 16) + : '', + ); + setCompletedAt( + activity.completedAt + ? activity.completedAt.slice(0, 16) + : '', + ); + } else { + setType('NOTE'); + setSubject(''); + setDescription(''); + setScheduledAt(''); + setCompletedAt(''); + } + } + }, [isOpen, activity]); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + setError(''); + + if (!subject.trim()) { + setError('Betreff ist ein Pflichtfeld'); + return; + } + + if (isEditMode && activity) { + updateMutation.mutate( + { + id: activity.id, + data: { + type, + subject: subject.trim(), + ...(description ? { description } : {}), + ...(scheduledAt ? { scheduledAt: new Date(scheduledAt).toISOString() } : {}), + ...(completedAt ? { completedAt: new Date(completedAt).toISOString() } : {}), + }, + }, + { + onSuccess: () => onSuccess(), + onError: (err: unknown) => { + const msg = + (err as { response?: { data?: { error?: { message?: string } } } }) + ?.response?.data?.error?.message ?? 'Fehler beim Speichern'; + setError(msg); + }, + }, + ); + } else { + createMutation.mutate( + { + contactId, + type, + subject: subject.trim(), + ...(description ? { description } : {}), + ...(scheduledAt ? { scheduledAt: new Date(scheduledAt).toISOString() } : {}), + ...(completedAt ? { completedAt: new Date(completedAt).toISOString() } : {}), + }, + { + onSuccess: () => onSuccess(), + onError: (err: unknown) => { + const msg = + (err as { response?: { data?: { error?: { message?: string } } } }) + ?.response?.data?.error?.message ?? 'Fehler beim Anlegen'; + setError(msg); + }, + }, + ); + } + }; + + return ( + +
+ {error && ( +
+ {error} +
+ )} + + {/* Typ */} +
+ + +
+ + {/* Betreff */} +
+ + setSubject(e.target.value)} + placeholder="Betreff der Aktivität" + required + /> +
+ + {/* Beschreibung */} +
+ +