feat(02-02): add ContactSection client component and wire into homepage
- Create src/app/_components/ContactSection.tsx: useActionState form with name, email, phone, message fields and gold Send Message button - Honeypot field (name="website") with display:none, tabIndex=-1, autoComplete="off" - Success state: form replaced by thank-you message on successful submission - Error state: inline alert for validation failures - page.tsx: replace <section id="contact" /> stub with <ContactSection /> - useActionState imported from 'react' (not react-dom, React 19 compatible) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,77 @@
|
|||||||
|
'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 (
|
||||||
|
<section id="contact" style={{ backgroundColor: '#FAF9F7', padding: '4rem 2rem' }}>
|
||||||
|
<div style={{ maxWidth: '600px', margin: '0 auto' }}>
|
||||||
|
<h2 style={{ color: '#1B2B4B', marginBottom: '0.5rem' }}>Get in Touch</h2>
|
||||||
|
<p style={{ color: '#555', marginBottom: '2rem' }}>
|
||||||
|
Ready to find your Utah home? Teressa is here to help.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{state.status === 'success' ? (
|
||||||
|
<div
|
||||||
|
role="status"
|
||||||
|
style={{
|
||||||
|
padding: '2rem',
|
||||||
|
backgroundColor: '#e8f5e9',
|
||||||
|
borderRadius: '8px',
|
||||||
|
color: '#2e7d32',
|
||||||
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p style={{ fontSize: '1.1rem', fontWeight: 600 }}>
|
||||||
|
Thanks! Teressa will be in touch soon.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<form action={action} style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
|
||||||
|
{/* Honeypot — hidden from humans, bots fill it in */}
|
||||||
|
<input
|
||||||
|
name="website"
|
||||||
|
type="text"
|
||||||
|
style={{ display: 'none' }}
|
||||||
|
tabIndex={-1}
|
||||||
|
autoComplete="off"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input name="name" type="text" required placeholder="Your full name" />
|
||||||
|
<input name="email" type="email" required placeholder="Email address" />
|
||||||
|
<input name="phone" type="tel" required placeholder="Phone number" />
|
||||||
|
<textarea name="message" required placeholder="How can Teressa help you?" rows={5} />
|
||||||
|
|
||||||
|
{state.status === 'error' && (
|
||||||
|
<p role="alert" style={{ color: '#c62828' }}>
|
||||||
|
{state.message}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={pending}
|
||||||
|
style={{
|
||||||
|
backgroundColor: '#C9A84C',
|
||||||
|
color: '#1B2B4B',
|
||||||
|
padding: '0.75rem 2rem',
|
||||||
|
border: 'none',
|
||||||
|
borderRadius: '4px',
|
||||||
|
fontWeight: 700,
|
||||||
|
cursor: pending ? 'not-allowed' : 'pointer',
|
||||||
|
opacity: pending ? 0.7 : 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{pending ? 'Sending...' : 'Send Message'}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user