import { Mail, Github, Linkedin } from 'lucide-react'; import { useState } from 'react'; import type { FormEvent } from 'react'; import { personalConfig, getObfuscatedEmail, createEmailLink } from '../../config/personal'; import { getApiUrl, ENDPOINTS } from '../../config/api'; interface ContactSectionProps { title?: string; subtitle?: string; email?: string; github?: string; linkedin?: string; connectTitle?: string; connectDescription?: string; } // TODO Create Backend API for handling form submissions securely export default function ContactSection({ title = "Get In Touch", subtitle = "Let's discuss your next project or collaboration opportunity", github = personalConfig.social.github.username, connectTitle = "Let's Connect", connectDescription = "I'm always interested in new opportunities and exciting projects. Whether you have a question or just want to say hi, feel free to reach out!" }: ContactSectionProps) { // Anti-spam honeypot state const [honeypot, setHoneypot] = useState(''); const [isSubmitting, setIsSubmitting] = useState(false); const [formStatus, setFormStatus] = useState<{ type: 'success' | 'error' | null; message: string; }>({ type: null, message: '' }); // Email obfuscation function using config const handleEmailClick = (e: React.MouseEvent) => { e.preventDefault(); window.location.href = createEmailLink(); }; const handleSubmit = async (e: FormEvent) => { e.preventDefault(); // Check honeypot - if filled, it's likely spam if (honeypot) { console.warn('Spam detected - honeypot field was filled'); setFormStatus({ type: 'error', message: 'Invalid form submission detected.' }); setIsSubmitting(false); return; } // Prevent rapid submissions if (isSubmitting) { return; } setIsSubmitting(true); setFormStatus({ type: null, message: '' }); // Clear previous status // Get form data const formData = new FormData(e.currentTarget); const data = { firstName: formData.get('firstName') as string, lastName: formData.get('lastName') as string, email: formData.get('email') as string, subject: formData.get('subject') as string, message: formData.get('message') as string, }; // Basic validation if (!data.firstName || !data.lastName || !data.email || !data.message) { setFormStatus({ type: 'error', message: 'Please fill in all required fields.' }); setIsSubmitting(false); return; } // Email validation const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(data.email)) { setFormStatus({ type: 'error', message: 'Please enter a valid email address.' }); setIsSubmitting(false); return; } try { // Submit to your own backend API const apiUrl = `${getApiUrl()}${ENDPOINTS.contact}`; const response = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ firstName: data.firstName, lastName: data.lastName, email: data.email, subject: data.subject || 'Contact from Portfolio', message: data.message, website: honeypot, // Include honeypot for spam detection }), }); const result = await response.json(); if (result.success) { setFormStatus({ type: 'success', message: result.message }); e.currentTarget.reset(); setHoneypot(''); // Reset honeypot // Clear success message after 5 seconds setTimeout(() => { setFormStatus({ type: null, message: '' }); }, 5000); } else { // Handle different types of server errors let errorMessage = 'Form submission failed'; if (response.status === 400) { if (result.errors && Array.isArray(result.errors)) { // Validation errors from server const validationErrors = result.errors.map((err: any) => typeof err === 'object' ? err.message : err ).join(', '); errorMessage = `Validation errors: ${validationErrors}`; } else if (result.message) { errorMessage = result.message; } else { errorMessage = 'Please check your form data and try again.'; } } else if (response.status === 429) { errorMessage = 'Too many requests. Please wait a moment and try again.'; } else if (response.status === 500) { errorMessage = 'Server error. Our team has been notified. Please try again later.'; } else if (result.message) { errorMessage = result.message; } throw new Error(errorMessage); } } catch (error) { console.error('Form submission error:', error); let userMessage = 'Sorry, there was an error sending your message.'; if (error instanceof Error) { // Network errors if (error.message.includes('Failed to fetch') || error.message.includes('NetworkError')) { userMessage = 'Network error: Please check your internet connection and try again.'; } // Timeout errors else if (error.message.includes('timeout')) { userMessage = 'Request timeout: The server is taking too long to respond. Please try again.'; } // CORS errors else if (error.message.includes('CORS')) { userMessage = 'Connection error: Please refresh the page and try again.'; } // Server validation errors or custom error messages else if (error.message.length > 0 && !error.message.includes('Form submission failed')) { userMessage = error.message; } // Generic fallback else { userMessage = 'Unexpected error occurred. Please try again or contact us directly.'; } } setFormStatus({ type: 'error', message: userMessage }); } setIsSubmitting(false); }; return (
{/* Section Header */}

{title}

{subtitle}

{/* Contact Content Grid */}
{/* Contact Info */}

{connectTitle}

{connectDescription}

{getObfuscatedEmail()}
{github}
{personalConfig.social.linkedin.username}
{/* Contact Form */}

Send a Message

I'll get back to you as soon as possible

{/* Status Message Display */} {formStatus.type && (

{formStatus.message}

)}
{/* Honeypot field - hidden from users but visible to bots */}
setHoneypot(e.target.value)} className="contact-section__honeypot-input" tabIndex={-1} autoComplete="off" />
); }