190 lines
8.1 KiB
TypeScript
190 lines
8.1 KiB
TypeScript
'use client'
|
|
|
|
import { useRef, useState } from 'react'
|
|
import { useTranslations } from 'next-intl'
|
|
import { motion, useInView } from 'framer-motion'
|
|
import { useForm } from 'react-hook-form'
|
|
import { zodResolver } from '@hookform/resolvers/zod'
|
|
import { Phone, Mail, Instagram, Send } from 'lucide-react'
|
|
import { ContactSchema, type ContactInput } from '@/lib/validations'
|
|
|
|
export default function ContactSection() {
|
|
const t = useTranslations('contact')
|
|
const ref = useRef<HTMLDivElement>(null)
|
|
const isInView = useInView(ref, { once: true, margin: '-80px' })
|
|
const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle')
|
|
|
|
const { register, handleSubmit, reset, formState: { errors } } = useForm<ContactInput>({
|
|
resolver: zodResolver(ContactSchema),
|
|
})
|
|
|
|
const onSubmit = async (data: ContactInput) => {
|
|
setStatus('loading')
|
|
try {
|
|
const res = await fetch('/api/contact', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(data),
|
|
})
|
|
if (!res.ok) throw new Error()
|
|
setStatus('success')
|
|
reset()
|
|
} catch {
|
|
setStatus('error')
|
|
}
|
|
}
|
|
|
|
return (
|
|
<section id="iletisim" className="bg-vesta-cream py-24 md:py-32">
|
|
<div ref={ref} className="container mx-auto px-6">
|
|
<div className="grid md:grid-cols-2 gap-16 items-start">
|
|
{/* Sol: Bilgi */}
|
|
<div>
|
|
<motion.p
|
|
initial={{ opacity: 0, y: 15 }}
|
|
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
|
transition={{ duration: 0.5 }}
|
|
className="text-vesta-earth text-xs tracking-[0.3em] uppercase mb-4"
|
|
>
|
|
{t('label')}
|
|
</motion.p>
|
|
<motion.h2
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
|
transition={{ duration: 0.6, delay: 0.1 }}
|
|
className="text-vesta-dark text-3xl md:text-4xl font-serif font-light leading-snug mb-4"
|
|
>
|
|
{t('title')}
|
|
</motion.h2>
|
|
<motion.p
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
|
transition={{ duration: 0.6, delay: 0.2 }}
|
|
className="text-vesta-dark/50 text-base mb-10"
|
|
>
|
|
{t('subtitle')}
|
|
</motion.p>
|
|
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
|
transition={{ duration: 0.6, delay: 0.3 }}
|
|
className="space-y-4"
|
|
>
|
|
<a
|
|
href={`tel:+904443145`}
|
|
className="flex items-center gap-4 text-vesta-dark hover:text-vesta-earth transition-colors group"
|
|
>
|
|
<div className="w-10 h-10 rounded-full bg-vesta-dark/10 flex items-center justify-center group-hover:bg-vesta-earth/20 transition-colors">
|
|
<Phone className="w-4 h-4" />
|
|
</div>
|
|
<span className="text-lg">{t('phone')}</span>
|
|
</a>
|
|
<a
|
|
href={`mailto:${t('email')}`}
|
|
className="flex items-center gap-4 text-vesta-dark hover:text-vesta-earth transition-colors group"
|
|
>
|
|
<div className="w-10 h-10 rounded-full bg-vesta-dark/10 flex items-center justify-center group-hover:bg-vesta-earth/20 transition-colors">
|
|
<Mail className="w-4 h-4" />
|
|
</div>
|
|
<span className="text-lg">{t('email')}</span>
|
|
</a>
|
|
<a
|
|
href="https://www.instagram.com/vestamugla/"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="flex items-center gap-4 text-vesta-dark hover:text-vesta-earth transition-colors group"
|
|
>
|
|
<div className="w-10 h-10 rounded-full bg-vesta-dark/10 flex items-center justify-center group-hover:bg-vesta-earth/20 transition-colors">
|
|
<Instagram className="w-4 h-4" />
|
|
</div>
|
|
<span className="text-lg">{t('instagram')}</span>
|
|
</a>
|
|
|
|
{/* Sanal Tur */}
|
|
<a
|
|
href="https://emtisiinsaat.com/vr.html"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="inline-flex items-center gap-2 mt-4 border border-vesta-dark/20 text-vesta-dark px-5 py-2.5 rounded-full text-sm hover:bg-vesta-dark hover:text-white transition-colors"
|
|
>
|
|
360° Sanal Tur
|
|
</a>
|
|
</motion.div>
|
|
</div>
|
|
|
|
{/* Sağ: Form */}
|
|
<motion.div
|
|
initial={{ opacity: 0, x: 30 }}
|
|
animate={isInView ? { opacity: 1, x: 0 } : {}}
|
|
transition={{ duration: 0.7, delay: 0.2 }}
|
|
className="bg-white rounded-2xl p-8 shadow-sm"
|
|
>
|
|
{status === 'success' ? (
|
|
<div className="text-center py-8">
|
|
<div className="w-12 h-12 rounded-full bg-vesta-forest/20 flex items-center justify-center mx-auto mb-4">
|
|
<Send className="w-5 h-5 text-vesta-forest" />
|
|
</div>
|
|
<p className="text-vesta-dark font-medium">{t('form.success')}</p>
|
|
</div>
|
|
) : (
|
|
<form onSubmit={handleSubmit(onSubmit)} className="space-y-5">
|
|
<div>
|
|
<input
|
|
{...register('fullName')}
|
|
placeholder={t('form.name')}
|
|
className="w-full border border-gray-200 rounded-xl px-4 py-3 text-sm text-vesta-dark placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-vesta-earth/30 focus:border-vesta-earth"
|
|
/>
|
|
{errors.fullName && (
|
|
<p className="text-red-500 text-xs mt-1">{errors.fullName.message}</p>
|
|
)}
|
|
</div>
|
|
<div>
|
|
<input
|
|
{...register('email')}
|
|
type="email"
|
|
placeholder={t('form.email')}
|
|
className="w-full border border-gray-200 rounded-xl px-4 py-3 text-sm text-vesta-dark placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-vesta-earth/30 focus:border-vesta-earth"
|
|
/>
|
|
{errors.email && (
|
|
<p className="text-red-500 text-xs mt-1">{errors.email.message}</p>
|
|
)}
|
|
</div>
|
|
<div>
|
|
<input
|
|
{...register('phone')}
|
|
type="tel"
|
|
placeholder={t('form.phone')}
|
|
className="w-full border border-gray-200 rounded-xl px-4 py-3 text-sm text-vesta-dark placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-vesta-earth/30 focus:border-vesta-earth"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<textarea
|
|
{...register('message')}
|
|
placeholder={t('form.message')}
|
|
rows={4}
|
|
className="w-full border border-gray-200 rounded-xl px-4 py-3 text-sm text-vesta-dark placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-vesta-earth/30 focus:border-vesta-earth resize-none"
|
|
/>
|
|
{errors.message && (
|
|
<p className="text-red-500 text-xs mt-1">{errors.message.message}</p>
|
|
)}
|
|
</div>
|
|
{status === 'error' && (
|
|
<p className="text-red-500 text-xs">{t('form.error')}</p>
|
|
)}
|
|
<button
|
|
type="submit"
|
|
disabled={status === 'loading'}
|
|
className="w-full bg-vesta-dark text-white py-3.5 rounded-xl text-sm tracking-wide hover:bg-vesta-forest transition-colors disabled:opacity-50"
|
|
>
|
|
{status === 'loading' ? t('form.sending') : t('form.send')}
|
|
</button>
|
|
</form>
|
|
)}
|
|
</motion.div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|