217 lines
8.4 KiB
TypeScript
217 lines
8.4 KiB
TypeScript
'use client'
|
|
|
|
import React, {useRef, useState} from 'react'
|
|
import {motion} from 'framer-motion'
|
|
import {Button} from '@/components/ui/button'
|
|
import HCaptcha from '@hcaptcha/react-hcaptcha'
|
|
|
|
const ContactFormSection = () => {
|
|
const captchaRef = useRef<HCaptcha | null>(null)
|
|
|
|
const [form, setForm] = useState({
|
|
name: '',
|
|
email: '',
|
|
company: '',
|
|
phone: '',
|
|
website: '',
|
|
message: '',
|
|
})
|
|
|
|
const [captchaToken, setCaptchaToken] = useState('')
|
|
const [submitted, setSubmitted] = useState(false)
|
|
const [loading, setLoading] = useState(false)
|
|
const [error, setError] = useState('')
|
|
|
|
const handleChange = (
|
|
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
|
|
) => {
|
|
setForm({...form, [e.target.name]: e.target.value})
|
|
}
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault()
|
|
setLoading(true)
|
|
setError('')
|
|
|
|
if (!captchaToken) {
|
|
setError('Bitte bestätige, dass du kein Roboter bist.')
|
|
setLoading(false)
|
|
return
|
|
}
|
|
|
|
const res = await fetch('/api/contact', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({...form, captcha: captchaToken}),
|
|
})
|
|
|
|
if (res.ok) {
|
|
setSubmitted(true)
|
|
setForm({
|
|
name: '',
|
|
email: '',
|
|
company: '',
|
|
phone: '',
|
|
website: '',
|
|
message: '',
|
|
})
|
|
setCaptchaToken('')
|
|
captchaRef.current?.resetCaptcha()
|
|
} else {
|
|
const resJson = await res.json()
|
|
setError(
|
|
resJson?.error ||
|
|
'Ein Fehler ist aufgetreten. Bitte versuche es später erneut.'
|
|
)
|
|
}
|
|
|
|
setLoading(false)
|
|
}
|
|
|
|
return (
|
|
<div className="w-full px-6 sm:px-12 py-20 text-left">
|
|
<motion.h2
|
|
className="text-2xl sm:text-3xl font-bold mb-2 text-foreground"
|
|
initial={{opacity: 0, y: 20}}
|
|
whileInView={{opacity: 1, y: 0}}
|
|
viewport={{once: true}}
|
|
transition={{duration: 0.5}}
|
|
>
|
|
Schreib uns eine Nachricht
|
|
</motion.h2>
|
|
|
|
<motion.p
|
|
className="text-sm mb-8 max-w-xl text-muted-foreground"
|
|
initial={{opacity: 0, y: 10}}
|
|
whileInView={{opacity: 1, y: 0}}
|
|
viewport={{once: true}}
|
|
transition={{duration: 0.5, delay: 0.2}}
|
|
>
|
|
Wir freuen uns über dein Interesse und melden uns schnellstmöglich bei
|
|
dir zurück.
|
|
</motion.p>
|
|
|
|
{submitted ? (
|
|
<div className="text-green-600 font-semibold text-lg">
|
|
✅ Deine Nachricht wurde erfolgreich gesendet!
|
|
</div>
|
|
) : (
|
|
<form className="space-y-6" onSubmit={handleSubmit}>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
{[
|
|
{
|
|
label: 'Dein Name *',
|
|
name: 'name',
|
|
type: 'text',
|
|
required: true,
|
|
placeholder: 'Max Mustermann',
|
|
},
|
|
{
|
|
label: 'Deine E-Mail *',
|
|
name: 'email',
|
|
type: 'email',
|
|
required: true,
|
|
placeholder: 'max@example.com',
|
|
},
|
|
{
|
|
label: 'Firmenname (optional)',
|
|
name: 'company',
|
|
type: 'text',
|
|
required: false,
|
|
placeholder: 'Mustermann GmbH',
|
|
},
|
|
{
|
|
label: 'Telefonnummer (optional)',
|
|
name: 'phone',
|
|
type: 'tel',
|
|
required: false,
|
|
placeholder: '+49 123 456789',
|
|
},
|
|
{
|
|
label: 'Webseite (optional)',
|
|
name: 'website',
|
|
type: 'url',
|
|
required: false,
|
|
placeholder: 'https://...',
|
|
},
|
|
].map((field, index) => (
|
|
<motion.div
|
|
key={field.name}
|
|
initial={{opacity: 0, y: 10}}
|
|
whileInView={{opacity: 1, y: 0}}
|
|
viewport={{once: true}}
|
|
transition={{duration: 0.5, delay: index * 0.1}}
|
|
>
|
|
<label className="block font-semibold mb-1 text-foreground">
|
|
{field.label}
|
|
</label>
|
|
<input
|
|
type={field.type}
|
|
name={field.name}
|
|
value={form[field.name as keyof typeof form]}
|
|
onChange={handleChange}
|
|
required={field.required}
|
|
placeholder={field.placeholder}
|
|
className="w-full p-3 rounded-md border bg-background text-foreground border-muted focus:outline-none focus:ring-2 focus:ring-primary transition"
|
|
/>
|
|
</motion.div>
|
|
))}
|
|
</div>
|
|
|
|
<motion.div
|
|
initial={{opacity: 0, y: 10}}
|
|
whileInView={{opacity: 1, y: 0}}
|
|
viewport={{once: true}}
|
|
transition={{duration: 0.5, delay: 0.6}}
|
|
>
|
|
<label className="block font-semibold mb-1 text-foreground">
|
|
Deine Nachricht *
|
|
</label>
|
|
<textarea
|
|
name="message"
|
|
rows={4}
|
|
required
|
|
value={form.message}
|
|
onChange={handleChange}
|
|
placeholder="Worum geht es?"
|
|
className="w-full p-3 rounded-md border bg-background text-foreground border-muted focus:outline-none focus:ring-2 focus:ring-primary transition"
|
|
/>
|
|
</motion.div>
|
|
|
|
<motion.div
|
|
className="pt-2"
|
|
initial={{opacity: 0, y: 10}}
|
|
whileInView={{opacity: 1, y: 0}}
|
|
viewport={{once: true}}
|
|
transition={{duration: 0.5, delay: 0.7}}
|
|
>
|
|
<HCaptcha
|
|
sitekey={process.env.NEXT_PUBLIC_HCAPTCHA_SITE_KEY!}
|
|
onVerify={(token) => setCaptchaToken(token)}
|
|
ref={captchaRef}
|
|
/>
|
|
</motion.div>
|
|
|
|
{error && (
|
|
<div className="text-red-600 font-medium pt-2">❌ {error}</div>
|
|
)}
|
|
|
|
<motion.div
|
|
className="pt-4 flex justify-end"
|
|
initial={{opacity: 0, y: 10}}
|
|
whileInView={{opacity: 1, y: 0}}
|
|
viewport={{once: true}}
|
|
transition={{duration: 0.5, delay: 0.8}}
|
|
>
|
|
<Button type="submit" disabled={loading} size="lg">
|
|
{loading ? 'Sende...' : '📩 Nachricht senden'}
|
|
</Button>
|
|
</motion.div>
|
|
</form>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default ContactFormSection
|