Files
portfolio/app/components/Contact.tsx
Dennis Konkol ded873e6b4 🎨 Complete Portfolio Redesign: Modern Dark Theme + Admin Dashboard + Enhanced Markdown Editor
 New Features:
- Complete dark theme redesign with glassmorphism effects
- Responsive admin dashboard with collapsible projects list
- Enhanced markdown editor with live preview
- Project image upload functionality
- Improved project management (create, edit, delete, publish/unpublish)
- Slug-based project URLs
- Legal pages (Impressum, Privacy Policy)
- Modern animations with Framer Motion

🔧 Improvements:
- Fixed hydration errors with mounted state
- Enhanced UI/UX with better spacing and proportions
- Improved markdown rendering with custom components
- Better project image placeholders with initials
- Conditional rendering for GitHub/Live Demo links
- Enhanced toolbar with categorized quick actions
- Responsive grid layout for admin dashboard

📱 Technical:
- Next.js 15 + TypeScript + Tailwind CSS
- Local storage for project persistence
- Optimized performance and responsive design
2025-09-01 23:30:10 +00:00

261 lines
9.2 KiB
TypeScript

"use client";
import { useState, useEffect } from 'react';
import { motion } from 'framer-motion';
import { Mail, Phone, MapPin, Send, Github, Linkedin, Twitter } from 'lucide-react';
const Contact = () => {
const [mounted, setMounted] = useState(false);
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);
// Simulate form submission
setTimeout(() => {
setIsSubmitting(false);
alert('Thank you for your message! I will get back to you soon.');
setFormData({ name: '', email: '', subject: '', message: '' });
}, 2000);
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
setFormData({
...formData,
[e.target.name]: e.target.value
});
};
const contactInfo = [
{
icon: Mail,
title: 'Email',
value: 'contact@dki.one',
href: 'mailto:contact@dki.one'
},
{
icon: Phone,
title: 'Phone',
value: '+49 123 456 789',
href: 'tel:+49123456789'
},
{
icon: MapPin,
title: 'Location',
value: 'Osnabrück, Germany',
href: '#'
}
];
const socialLinks = [
{ icon: Github, href: 'https://github.com/Denshooter', label: 'GitHub' },
{ icon: Linkedin, href: 'https://linkedin.com/in/dkonkol', label: 'LinkedIn' },
{ icon: Twitter, href: 'https://twitter.com/dkonkol', label: 'Twitter' }
];
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>
{/* Social Links */}
<div>
<h4 className="text-lg font-semibold text-white mb-4">Follow Me</h4>
<div className="flex space-x-4">
{socialLinks.map((social) => (
<motion.a
key={social.label}
href={social.href}
target="_blank"
rel="noopener noreferrer"
whileHover={{ scale: 1.1, y: -2 }}
whileTap={{ scale: 0.95 }}
className="p-3 bg-gray-800/50 hover:bg-gray-700/50 rounded-lg text-gray-300 hover:text-white transition-colors"
>
<social.icon size={20} />
</motion.a>
))}
</div>
</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;