'use client' import { useEffect, useState } from 'react' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { toast } from 'sonner' import { Loader2 } from 'lucide-react' export interface ProjectFormValue { id?: string companyId: string name: string type: string budgetType: string budgetHours: string fixedPriceEuro: string startDate: string dueDate: string } interface Props { open: boolean onOpenChange: (o: boolean) => void companies: { id: string; name: string }[] // When `project` is null the dialog is in "create" mode. project?: ProjectFormValue | null onSaved: () => void } const TYPES = [ { value: 'website', label: 'Website' }, { value: 'shop', label: 'Shop' }, { value: 'app', label: 'App' }, { value: 'retainer', label: 'Retainer' }, ] const BUDGET_TYPES = [ { value: 'fixed', label: 'Fixpreis' }, { value: 'time-materials', label: 'Time & Materials' }, { value: 'retainer', label: 'Retainer' }, ] const EMPTY: ProjectFormValue = { companyId: '', name: '', type: 'website', budgetType: 'fixed', budgetHours: '', fixedPriceEuro: '', startDate: '', dueDate: '', } // Single dialog for create + edit of a Project. // - create mode → POST /api/aos/projects // - edit mode → PATCH /api/aos/projects/[id] export function ProjectFormDialog({ open, onOpenChange, companies, project, onSaved }: Props) { const [form, setForm] = useState(EMPTY) const [saving, setSaving] = useState(false) const isEdit = !!project?.id useEffect(() => { if (open) { setForm(project ?? EMPTY) } }, [open, project]) useEffect(() => { if (open && !isEdit && !form.companyId && companies[0]) { setForm((f) => ({ ...f, companyId: companies[0].id })) } }, [open, isEdit, form.companyId, companies]) const submit = async () => { if (!form.companyId) { toast.error('Firma auswählen'); return } if (!form.name.trim()) { toast.error('Projektname eingeben'); return } const fixedPriceCents = form.fixedPriceEuro ? Math.round((parseFloat(form.fixedPriceEuro.replace(',', '.')) || 0) * 100) : null const budgetHours = form.budgetHours ? parseInt(form.budgetHours, 10) || null : null const payload: Record = { companyId: form.companyId, name: form.name.trim(), type: form.type, budgetType: form.budgetType, budgetHours, fixedPriceCents, startDate: form.startDate || null, dueDate: form.dueDate || null, } setSaving(true) const t = toast.loading(isEdit ? 'Projekt wird gespeichert…' : 'Projekt wird angelegt…') try { const url = isEdit ? `/api/aos/projects/${project!.id}` : '/api/aos/projects' const method = isEdit ? 'PATCH' : 'POST' const r = await fetch(url, { method, headers: { 'content-type': 'application/json' }, body: JSON.stringify(payload), }) if (!r.ok) { const err = await r.json().catch(() => ({})) throw new Error(err.error || `${method} failed`) } toast.success(isEdit ? 'Projekt aktualisiert' : 'Projekt angelegt', { id: t }) onOpenChange(false) onSaved() } catch (err) { toast.error(err instanceof Error ? err.message : 'Speichern fehlgeschlagen', { id: t }) } finally { setSaving(false) } } return ( {isEdit ? 'Projekt bearbeiten' : 'Neues Projekt'} {isEdit ? 'Projekt-Stammdaten aktualisieren. Audit-Log wird automatisch geschrieben (GoBD).' : 'Legt ein neues Projekt an. Audit-Log wird automatisch geschrieben.'}
setForm((f) => ({ ...f, name: e.target.value }))} placeholder="z.B. Website-Relaunch" />
setForm((f) => ({ ...f, fixedPriceEuro: e.target.value }))} placeholder="9.800" inputMode="decimal" />
setForm((f) => ({ ...f, budgetHours: e.target.value }))} placeholder="120" inputMode="numeric" />
setForm((f) => ({ ...f, startDate: e.target.value }))} />
setForm((f) => ({ ...f, dueDate: e.target.value }))} />
) }