Files
portfolio/app/components/Contact.tsx
2025-09-10 10:59:14 +02:00

253 lines
8.7 KiB
TypeScript

"use client";
import { useState, useEffect } from 'react';
import { motion } from 'framer-motion';
import { Mail, MapPin, Send } from 'lucide-react';
import { useToast } from '@/components/Toast';
const Contact = () => {
const [mounted, setMounted] = useState(false);
const { showEmailSent, showEmailError } = useToast();
useEffect(() => {
setMounted(true);
}, []);
const [formData, setFormData] = useState({
name: '',
email: '',
subject: '',
message: ''
});
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setIsSubmitting(true);
try {
const response = await fetch('/api/email', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: formData.name,
email: formData.email,
subject: formData.subject,
message: formData.message,
}),
});
if (response.ok) {
showEmailSent(formData.email);
setFormData({ name: '', email: '', subject: '', message: '' });
} else {
const errorData = await response.json();
showEmailError(errorData.error || 'Unbekannter Fehler');
}
} catch (error) {
console.error('Error sending email:', error);
showEmailError('Netzwerkfehler beim Senden der E-Mail');
} finally {
setIsSubmitting(false);
}
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
setFormData({
...formData,
[e.target.name]: e.target.value
});
};
const contactInfo = [
{
icon: Mail,
title: 'Email',
value: 'contact@dk0.dev',
href: 'mailto:contact@dk0.dev'
},
{
icon: MapPin,
title: 'Location',
value: 'Osnabrück, Germany',
}
];
if (!mounted) {
return null;
}
return (
<section id="contact" className="py-20 px-4 relative">
<div className="max-w-7xl mx-auto">
{/* Section Header */}
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8 }}
className="text-center mb-16"
>
<h2 className="text-4xl md:text-5xl font-bold mb-6 gradient-text">
Get In Touch
</h2>
<p className="text-xl text-gray-400 max-w-2xl mx-auto">
Have a project in mind or want to collaborate? I would love to hear from you!
</p>
</motion.div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
{/* Contact Information */}
<motion.div
initial={{ opacity: 0, x: -30 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8 }}
className="space-y-8"
>
<div>
<h3 className="text-2xl font-bold text-white mb-6">
Let&apos;s Connect
</h3>
<p className="text-gray-400 leading-relaxed">
I&apos;m always open to discussing new opportunities, interesting projects,
or just having a chat about technology and innovation.
</p>
</div>
{/* Contact Details */}
<div className="space-y-4">
{contactInfo.map((info, index) => (
<motion.a
key={info.title}
href={info.href}
initial={{ opacity: 0, x: -20 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6, delay: index * 0.1 }}
whileHover={{ x: 5 }}
className="flex items-center space-x-4 p-4 rounded-lg glass-card hover:bg-gray-800/30 transition-colors group"
>
<div className="p-3 bg-blue-500/20 rounded-lg group-hover:bg-blue-500/30 transition-colors">
<info.icon className="w-6 h-6 text-blue-400" />
</div>
<div>
<h4 className="font-semibold text-white">{info.title}</h4>
<p className="text-gray-400">{info.value}</p>
</div>
</motion.a>
))}
</div>
</motion.div>
{/* Contact Form */}
<motion.div
initial={{ opacity: 0, x: 30 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8 }}
className="glass-card p-8 rounded-2xl"
>
<h3 className="text-2xl font-bold text-white mb-6">Send Message</h3>
<form onSubmit={handleSubmit} className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label htmlFor="name" className="block text-sm font-medium text-gray-300 mb-2">
Name
</label>
<input
type="text"
id="name"
name="name"
value={formData.name}
onChange={handleChange}
required
className="w-full px-4 py-3 bg-gray-800/50 border border-gray-700 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all"
placeholder="Your name"
/>
</div>
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-300 mb-2">
Email
</label>
<input
type="email"
id="email"
name="email"
value={formData.email}
onChange={handleChange}
required
className="w-full px-4 py-3 bg-gray-800/50 border border-gray-700 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all"
placeholder="your@email.com"
/>
</div>
</div>
<div>
<label htmlFor="subject" className="block text-sm font-medium text-gray-300 mb-2">
Subject
</label>
<input
type="text"
id="subject"
name="subject"
value={formData.subject}
onChange={handleChange}
required
className="w-full px-4 py-3 bg-gray-800/50 border border-gray-700 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all"
placeholder="What's this about?"
/>
</div>
<div>
<label htmlFor="message" className="block text-sm font-medium text-gray-300 mb-2">
Message
</label>
<textarea
id="message"
name="message"
value={formData.message}
onChange={handleChange}
required
rows={5}
className="w-full px-4 py-3 bg-gray-800/50 border border-gray-700 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all resize-none"
placeholder="Tell me more about your project..."
/>
</div>
<motion.button
type="submit"
disabled={isSubmitting}
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
className="w-full btn-primary py-4 text-lg font-semibold disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center space-x-2"
>
{isSubmitting ? (
<>
<div className="w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin"></div>
<span>Sending...</span>
</>
) : (
<>
<Send size={20} />
<span>Send Message</span>
</>
)}
</motion.button>
</form>
</motion.div>
</div>
</div>
</section>
);
};
export default Contact;