Add member address, state normalization, account inheritance, fix member form

- Address field on member table (jsonb, same format as account)
- Members inherit email, phone, address from account when not provided
- State normalization: "Texas" → "TX", "california" → "CA" via shared util
- Member form drops zodResolver to fix optional field validation flashing
- Account name auto-format: "First Last - Account"
- US state lookup with full name + code support
This commit is contained in:
Ryan Moon
2026-03-28 12:31:02 -05:00
parent ce2a61ced9
commit b9e984cfa3
10 changed files with 243 additions and 28 deletions

View File

@@ -1,7 +1,4 @@
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { MemberCreateSchema } from '@forte/shared/schemas'
import type { MemberCreateInput } from '@forte/shared/schemas'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
@@ -21,34 +18,40 @@ export function MemberForm({ accountId, defaultValues, onSubmit, loading }: Memb
handleSubmit,
watch,
formState: { errors },
} = useForm<MemberCreateInput>({
resolver: zodResolver(MemberCreateSchema),
} = useForm({
defaultValues: {
accountId,
firstName: defaultValues?.firstName ?? '',
lastName: defaultValues?.lastName ?? '',
dateOfBirth: defaultValues?.dateOfBirth ?? undefined,
isMinor: defaultValues?.isMinor ?? undefined,
email: defaultValues?.email ?? undefined,
phone: defaultValues?.phone ?? undefined,
notes: defaultValues?.notes ?? undefined,
dateOfBirth: defaultValues?.dateOfBirth ?? '',
isMinor: defaultValues?.isMinor ?? false,
email: defaultValues?.email ?? '',
phone: defaultValues?.phone ?? '',
notes: defaultValues?.notes ?? '',
},
})
const dateOfBirth = watch('dateOfBirth')
function handleFormSubmit(data: Record<string, unknown>) {
// Strip empty strings to undefined for the API
const cleaned: Record<string, unknown> = { accountId }
for (const [key, value] of Object.entries(data)) {
cleaned[key] = value === '' ? undefined : value
}
onSubmit(cleaned)
}
return (
<form onSubmit={handleSubmit(onSubmit)} noValidate className="space-y-4">
<input type="hidden" {...register('accountId')} />
<form onSubmit={handleSubmit(handleFormSubmit)} noValidate className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="firstName">First Name *</Label>
<Input id="firstName" {...register('firstName')} />
<Input id="firstName" {...register('firstName', { required: 'Required' })} />
{errors.firstName && <p className="text-sm text-destructive">{errors.firstName.message}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="lastName">Last Name *</Label>
<Input id="lastName" {...register('lastName')} />
<Input id="lastName" {...register('lastName', { required: 'Required' })} />
{errors.lastName && <p className="text-sm text-destructive">{errors.lastName.message}</p>}
</div>
</div>

View File

@@ -28,7 +28,7 @@ function NewAccountPage() {
// Auto-generate account name from member if not provided
if (!accountData.name && memberFirstName && memberLastName) {
accountData.name = `${memberLastName}, ${memberFirstName}`
accountData.name = `${memberFirstName} ${memberLastName} - Account`
}
const account = await accountMutations.create(accountData)