"use client"; import { useState, useEffect } from "react"; import { motion } from "framer-motion"; import { Mail, MapPin, Send } from "lucide-react"; import { useToast } from "@/components/Toast"; import { useLocale, useTranslations } from "next-intl"; import type { JSONContent } from "@tiptap/react"; import RichTextClient from "./RichTextClient"; const Contact = () => { const { showEmailSent, showEmailError } = useToast(); const locale = useLocale(); const t = useTranslations("home.contact"); const [cmsDoc, setCmsDoc] = useState(null); useEffect(() => { (async () => { try { const res = await fetch( `/api/content/page?key=${encodeURIComponent("home-contact")}&locale=${encodeURIComponent(locale)}`, ); const data = await res.json(); // Only use CMS content if it exists for the active locale. if (data?.content?.content && data?.content?.locale === locale) { setCmsDoc(data.content.content as JSONContent); } else { setCmsDoc(null); } } catch { // ignore; fallback to static setCmsDoc(null); } })(); }, [locale]); const [formData, setFormData] = useState({ name: "", email: "", subject: "", message: "", }); const [errors, setErrors] = useState>({}); const [isSubmitting, setIsSubmitting] = useState(false); const [touched, setTouched] = useState>({}); const validateForm = () => { const newErrors: Record = {}; if (!formData.name.trim()) { newErrors.name = "Name is required"; } else if (formData.name.trim().length < 2) { newErrors.name = "Name must be at least 2 characters"; } if (!formData.email.trim()) { newErrors.email = "Email is required"; } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) { newErrors.email = "Please enter a valid email address"; } if (!formData.subject.trim()) { newErrors.subject = "Subject is required"; } else if (formData.subject.trim().length < 3) { newErrors.subject = "Subject must be at least 3 characters"; } if (!formData.message.trim()) { newErrors.message = "Message is required"; } else if (formData.message.trim().length < 10) { newErrors.message = "Message must be at least 10 characters"; } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!validateForm()) { return; } 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: "" }); setTouched({}); setErrors({}); } else { const errorData = await response.json(); showEmailError( errorData.error || "Failed to send message. Please try again.", ); } } catch (error) { if (process.env.NODE_ENV === 'development') { console.error("Error sending email:", error); } showEmailError( "Network error. Please check your connection and try again.", ); } finally { setIsSubmitting(false); } }; const handleChange = ( e: React.ChangeEvent, ) => { const { name, value } = e.target; setFormData({ ...formData, [name]: value, }); // Clear error when user starts typing if (errors[name]) { setErrors({ ...errors, [name]: "", }); } }; const handleBlur = ( e: React.FocusEvent, ) => { setTouched({ ...touched, [e.target.name]: true, }); validateForm(); }; const contactInfo = [ { icon: Mail, title: "Email", value: "contact@dk0.dev", href: "mailto:contact@dk0.dev", }, { icon: MapPin, title: "Location", value: "Osnabrück, Germany", }, ]; return (
{/* Section Header */}

{t("title")}

{cmsDoc ? ( ) : (

{t("subtitle")}

)}
{/* Contact Information */}

{t("getInTouch")}

{t("getInTouchBody")}

{/* Contact Details */}
{contactInfo.map((info, index) => (

{info.title}

{info.value}

))}
{/* Contact Form */}

Send Message

{errors.name && touched.name && (

{errors.name}

)}
{errors.email && touched.email && (

{errors.email}

)}
{errors.subject && touched.subject && (

{errors.subject}

)}