`,
})
}
```
3. Create `lib/contact-action.ts`:
```typescript
'use server'
import { z } from 'zod'
import { sendContactEmail } from './contact-mailer'
const ContactSchema = z.object({
name: z.string().min(1, 'Name is required'),
email: z.string().email('Valid email address required'),
phone: z.string().min(7, 'Phone number is required'),
message: z.string().min(10, 'Message must be at least 10 characters'),
website: z.string(), // honeypot — must remain empty
})
export type ContactState = {
status: 'idle' | 'success' | 'error'
message?: string
}
export async function submitContact(
_prev: ContactState,
formData: FormData,
): Promise {
const raw = {
name: formData.get('name'),
email: formData.get('email'),
phone: formData.get('phone'),
message: formData.get('message'),
website: formData.get('website') ?? '',
}
const parsed = ContactSchema.safeParse(raw)
if (!parsed.success) {
return { status: 'error', message: 'Please fill in all required fields correctly.' }
}
// Honeypot check — if filled, silently succeed (don't reveal rejection to bots)
if (parsed.data.website !== '') {
return { status: 'success' }
}
try {
await sendContactEmail(parsed.data)
return { status: 'success' }
} catch (err) {
console.error('Contact form email error:', err)
return { status: 'error', message: 'Something went wrong. Please try again or call directly.' }
}
}
```
4. Add SMTP placeholder vars to `.env.local` (append — do not overwrite existing vars):
```
# Contact form SMTP — fill in before testing email delivery
CONTACT_EMAIL_USER=your_email@example.com
CONTACT_EMAIL_PASS=your_app_password
CONTACT_SMTP_HOST=smtp.gmail.com
CONTACT_SMTP_PORT=587
```
cd /Users/ccopeland/temp/red/teressa-copeland-homes && npm run build 2>&1 | tail -20Build succeeds. lib/contact-mailer.ts and lib/contact-action.ts exist with no TypeScript errors. nodemailer appears in package.json dependencies.Task 2: Build ContactSection client component and wire into page
teressa-copeland-homes/app/_components/ContactSection.tsx
teressa-copeland-homes/app/page.tsx
Create `app/_components/ContactSection.tsx` — 'use client':
```typescript
'use client'
import { useActionState } from 'react'
import { submitContact, type ContactState } from '@/lib/contact-action'
const initialState: ContactState = { status: 'idle' }
export function ContactSection() {
const [state, action, pending] = useActionState(submitContact, initialState)
return (
Get in Touch
Ready to find your Utah home? Teressa is here to help.
{state.status === 'success' ? (
Thanks! Teressa will be in touch soon.
) : (
)}
)
}
```
Update `app/page.tsx`: replace the `` stub with ``. Import ContactSection at the top. Ensure all section imports are present and in correct order: SiteNav, HeroSection, TestimonialsSection (from plan 01), ListingsPlaceholder, ContactSection, SiteFooter.
cd /Users/ccopeland/temp/red/teressa-copeland-homes && npm run build 2>&1 | tail -20Build succeeds. ContactSection renders a form with name, email, phone, message fields and a gold "Send Message" button. Form wires to submitContact server action via useActionState.
- `npm run build` exits 0
- `lib/contact-action.ts` has 'use server' at top
- `useActionState` imported from 'react' (not react-dom)
- Honeypot field has display:none, tabIndex={-1}, autoComplete="off"
- No SMTP credentials hardcoded — all from process.env.*
- `.env.local` has CONTACT_* placeholder vars with instructions
- Contact form renders with all four required fields
- Submitting with the honeypot filled returns success without sending email
- Missing required fields return an error message
- On real success, form is replaced by thank-you message
- Build passes with zero TypeScript errors