first commit
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
'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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user