✨ 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
261 lines
9.2 KiB
TypeScript
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's Connect
|
|
</h3>
|
|
<p className="text-gray-400 leading-relaxed">
|
|
I'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;
|