diff --git a/src/pages/Contact/Contact.tsx b/src/pages/Contact/Contact.tsx index 666adea..02724d8 100644 --- a/src/pages/Contact/Contact.tsx +++ b/src/pages/Contact/Contact.tsx @@ -6,21 +6,86 @@ import { Send, X, CheckCircle, + AlertCircle, } from "lucide-react"; import { ThemeContext } from "../../context/ThemeContext"; import type { ThemeContextType } from "../../context/ThemeContext"; +// Email validation regex - moved to module scope to avoid recreating on every render +const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + function Contact() { const [showPopup, setShowPopup] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); + const [formData, setFormData] = useState({ + fullName: "", + email: "", + subject: "", + message: "", + }); + const [errors, setErrors] = useState<{ + fullName?: string; + email?: string; + subject?: string; + message?: string; + }>({}); + const themeContext = useContext(ThemeContext) as ThemeContextType; const { mode } = themeContext; - useEffect(() => { - window.scrollTo(0, 0); - }, []); + const validateForm = () => { + const newErrors: typeof errors = {}; + + if (!formData.fullName.trim()) { + newErrors.fullName = "Full name is required"; + } + + if (!formData.email.trim()) { + newErrors.email = "Email address is required"; + } else if (!emailRegex.test(formData.email)) { + newErrors.email = "Please enter a valid email address"; + } + + if (!formData.subject) { + newErrors.subject = "Subject is required"; + } + + 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 handleInputChange = ( + e: React.ChangeEvent< + HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement + > + ) => { + const { name, value } = e.target; + setFormData((prev) => ({ + ...prev, + [name]: value, + })); + // Clear error for this field when user starts typing + if (errors[name as keyof typeof errors]) { + setErrors((prev) => ({ + ...prev, + [name]: undefined, + })); + } + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!validateForm()) { + return; + } - const handleSubmit = async () => { setIsSubmitting(true); // Simulate API call @@ -29,6 +94,15 @@ function Contact() { setIsSubmitting(false); setShowPopup(true); + // Reset form and errors on successful submission + setFormData({ + fullName: "", + email: "", + subject: "", + message: "", + }); + setErrors({}); + // Auto-close popup after 5 seconds setTimeout(() => { setShowPopup(false); @@ -182,8 +256,11 @@ function Contact() { {/* Contact Form */} -
+ {errors.fullName && ( +
+ + {errors.fullName} +
+ )}
{/* Email */} @@ -234,13 +325,27 @@ function Contact() { id="email" name="email" type="email" + name="email" + value={formData.email} + onChange={handleInputChange} placeholder="your.email@example.com" required - className={`w-full p-2 sm:p-3 rounded-lg sm:rounded-xl text-sm sm:text-base transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-purple-500 ${mode === "dark" + className={`w-full p-2 sm:p-3 rounded-lg sm:rounded-xl text-sm sm:text-base transition-all duration-300 focus:outline-none focus:ring-2 ${ + errors.email + ? "focus:ring-red-500 border-red-500" + : "focus:ring-purple-500" + } ${ + mode === "dark" ? "bg-white/5 border border-white/20 text-white placeholder-gray-400" : "bg-gray-50 border border-gray-300 text-gray-800 placeholder-gray-500" }`} /> + {errors.email && ( +
+ + {errors.email} +
+ )} {/* Subject */} @@ -254,12 +359,19 @@ function Contact() { Subject + {errors.subject && ( +
+ + {errors.subject} +
+ )} {/* Message */} @@ -282,19 +400,31 @@ function Contact() { Message + {errors.message && ( +
+ + {errors.message} +
+ )}