import { useEffect, useState } from "react"; import { Alert, KeyboardAvoidingView, Platform, ScrollView, StyleSheet, Text, View, } from "react-native"; import { Button } from "@/components/ui/Button"; import { Card } from "@/components/ui/Card"; import { Input } from "@/components/ui/Input"; import { fonts, spacing } from "@/constants/theme"; import type { ThemeColors } from "@/lib/theme-palette"; import { useThemedStyles } from "@/lib/use-themed-styles"; import { api } from "@/lib/trpc"; export type ClientFormValues = { name: string; email: string; phone: string; addressLine1: string; addressLine2: string; city: string; state: string; postalCode: string; country: string; defaultHourlyRate: string; currency: string; }; const emptyValues: ClientFormValues = { name: "", email: "", phone: "", addressLine1: "", addressLine2: "", city: "", state: "", postalCode: "", country: "United States", defaultHourlyRate: "", currency: "USD", }; type ClientFormProps = { mode: "create" | "edit"; clientId?: string; scrollPadding: number; onSaved: () => void; onDeleted?: () => void; }; export function ClientForm({ mode, clientId, scrollPadding, onSaved, onDeleted, }: ClientFormProps) { const styles = useThemedStyles(createClientFormStyles); const utils = api.useUtils(); const clientQuery = api.clients.getById.useQuery( { id: clientId ?? "" }, { enabled: mode === "edit" && Boolean(clientId) }, ); const [values, setValues] = useState(emptyValues); const [fieldError, setFieldError] = useState(null); useEffect(() => { const client = clientQuery.data; if (!client) return; setValues({ name: client.name, email: client.email ?? "", phone: client.phone ?? "", addressLine1: client.addressLine1 ?? "", addressLine2: client.addressLine2 ?? "", city: client.city ?? "", state: client.state ?? "", postalCode: client.postalCode ?? "", country: client.country ?? "United States", defaultHourlyRate: client.defaultHourlyRate != null ? String(client.defaultHourlyRate) : "", currency: client.currency ?? "USD", }); }, [clientQuery.data]); const createClient = api.clients.create.useMutation({ onSuccess: () => { void utils.clients.getAll.invalidate(); void utils.dashboard.getStats.invalidate(); onSaved(); }, onError: (err) => setFieldError(err.message), }); const updateClient = api.clients.update.useMutation({ onSuccess: () => { void utils.clients.getAll.invalidate(); if (clientId) void utils.clients.getById.invalidate({ id: clientId }); void utils.dashboard.getStats.invalidate(); onSaved(); }, onError: (err) => setFieldError(err.message), }); const deleteClient = api.clients.delete.useMutation({ onSuccess: () => { void utils.clients.getAll.invalidate(); void utils.dashboard.getStats.invalidate(); onDeleted?.(); }, onError: (err) => Alert.alert("Could not delete client", err.message), }); function patch(field: keyof ClientFormValues, value: string) { setValues((prev) => ({ ...prev, [field]: value })); setFieldError(null); } function handleSave() { if (!values.name.trim()) { setFieldError("Name is required"); return; } const rate = values.defaultHourlyRate.trim() ? Number(values.defaultHourlyRate) : undefined; if (rate !== undefined && (Number.isNaN(rate) || rate < 0)) { setFieldError("Hourly rate must be a valid number"); return; } const payload = { name: values.name.trim(), email: values.email.trim(), phone: values.phone.trim(), addressLine1: values.addressLine1.trim(), addressLine2: values.addressLine2.trim(), city: values.city.trim(), state: values.state.trim(), postalCode: values.postalCode.trim(), country: values.country.trim() || "United States", defaultHourlyRate: rate, currency: values.currency.trim() || "USD", }; if (mode === "create") { createClient.mutate(payload); return; } if (!clientId) return; updateClient.mutate({ id: clientId, ...payload }); } function confirmDelete() { if (!clientId) return; Alert.alert( "Delete client", "This cannot be undone. Clients with invoices cannot be deleted.", [ { text: "Cancel", style: "cancel" }, { text: "Delete", style: "destructive", onPress: () => deleteClient.mutate({ id: clientId }), }, ], ); } const saving = createClient.isPending || updateClient.isPending; return ( patch("name", v)} /> patch("email", v)} keyboardType="email-address" autoCapitalize="none" /> patch("phone", v)} keyboardType="phone-pad" /> patch("addressLine1", v)} /> patch("addressLine2", v)} /> patch("city", v)} /> patch("state", v)} /> patch("postalCode", v)} /> patch("country", v)} /> patch("defaultHourlyRate", v)} keyboardType="decimal-pad" placeholder="Optional" /> patch("currency", v.toUpperCase())} autoCapitalize="characters" maxLength={3} /> {fieldError ? {fieldError} : null}