import { useState, useMemo, FormEvent } from 'react'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { toast } from 'sonner'; import { Star, StarOff, X } from 'lucide-react'; import { parseISO, differenceInYears } from 'date-fns'; import api, { getErrorMessage } from '@/lib/api'; import type { Person } from '@/types'; import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetFooter, } from '@/components/ui/sheet'; import { Input } from '@/components/ui/input'; import { DatePicker } from '@/components/ui/date-picker'; import { Textarea } from '@/components/ui/textarea'; import { Label } from '@/components/ui/label'; import { Button } from '@/components/ui/button'; import LocationPicker from '@/components/ui/location-picker'; import CategoryAutocomplete from '@/components/shared/CategoryAutocomplete'; import { splitName } from '@/components/shared/utils'; interface PersonFormProps { person: Person | null; categories: string[]; onClose: () => void; } export default function PersonForm({ person, categories, onClose }: PersonFormProps) { const queryClient = useQueryClient(); const [formData, setFormData] = useState({ first_name: person?.first_name || (person?.name ? splitName(person.name).firstName : ''), last_name: person?.last_name || (person?.name ? splitName(person.name).lastName : ''), nickname: person?.nickname || '', email: person?.email || '', phone: person?.phone || '', mobile: person?.mobile || '', address: person?.address || '', birthday: person?.birthday ? person.birthday.slice(0, 10) : '', category: person?.category || '', is_favourite: person?.is_favourite ?? false, company: person?.company || '', job_title: person?.job_title || '', notes: person?.notes || '', }); const age = useMemo(() => { if (!formData.birthday) return null; try { return differenceInYears(new Date(), parseISO(formData.birthday)); } catch { return null; } }, [formData.birthday]); const set = (key: K, value: (typeof formData)[K]) => { setFormData((prev) => ({ ...prev, [key]: value })); }; const mutation = useMutation({ mutationFn: async (data: typeof formData) => { if (person) { const { data: res } = await api.put(`/people/${person.id}`, data); return res; } const { data: res } = await api.post('/people', data); return res; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['people'] }); queryClient.invalidateQueries({ queryKey: ['dashboard'] }); queryClient.invalidateQueries({ queryKey: ['upcoming'] }); toast.success(person ? 'Person updated' : 'Person created'); onClose(); }, onError: (error) => { toast.error( getErrorMessage(error, person ? 'Failed to update person' : 'Failed to create person') ); }, }); const handleSubmit = (e: FormEvent) => { e.preventDefault(); mutation.mutate({ ...formData, birthday: formData.birthday || null } as typeof formData); }; return (
{person ? 'Edit Person' : 'New Person'}
{/* Row 2: First + Last name */}
set('first_name', e.target.value)} required />
set('last_name', e.target.value)} />
{/* Row 3: Nickname */}
set('nickname', e.target.value)} placeholder="Optional display name" />
{/* Row 4: Birthday + Age */}
set('birthday', v)} />
{/* Row 5: Category */}
set('category', val)} categories={categories} placeholder="e.g. Friend, Family, Colleague" />
{/* Row 6: Mobile + Email */}
set('mobile', e.target.value)} />
set('email', e.target.value)} />
{/* Row 7: Phone */}
set('phone', e.target.value)} placeholder="Landline / work number" />
{/* Row 8: Address */}
set('address', val)} onSelect={(result) => set('address', result.address || result.name)} placeholder="Search or enter address..." />
{/* Row 9: Company + Job Title */}
set('company', e.target.value)} />
set('job_title', e.target.value)} />
{/* Row 10: Notes */}