// components/ContactPage.jsx /** * Contact Page Component * * Includes the contact form with client-side validation (on blur and submit) * and handles form submission to the backend API. Also includes the * debug viewer for submitted messages. */ // No imports needed as components are loaded via script tags // and React hooks are available globally function ContactPage() { // --- State Management --- const [name, setName] = useState(''); const [email, setEmail] = useState(''); const [message, setMessage] = useState(''); // Stores validation errors for each field const [errors, setErrors] = useState({}); // Tracks submission status: null, 'submitting', 'success', 'error' const [submitStatus, setSubmitStatus] = useState(null); // User feedback message after submission attempt const [feedbackMessage, setFeedbackMessage] = useState(''); // Tracks which fields have been touched (for validation timing) const [touched, setTouched] = useState({ name: false, email: false, message: false }); // --- Validation Logic --- // Validates a single field based on its name and value const validateField = (fieldName, value) => { let error = ''; switch (fieldName) { case 'name': if (!value.trim()) error = 'Name is required.'; else if (value.length > 100) error = 'Name cannot exceed 100 characters.'; break; case 'email': if (!value.trim()) error = 'Email is required.'; else if (!/\S+@\S+\.\S+/.test(value)) error = 'Please enter a valid email address.'; else if (value.length > 100) error = 'Email cannot exceed 100 characters.'; break; case 'message': if (!value.trim()) error = 'Message cannot be empty.'; else if (value.length > 5000) error = 'Message cannot exceed 5000 characters.'; break; default: // Should not happen break; } return error; // Returns empty string if valid }; // Validates the entire form, updates errors state, returns true if valid const validateForm = () => { const nameError = validateField('name', name); const emailError = validateField('email', email); const messageError = validateField('message', message); const newErrors = {}; if (nameError) newErrors.name = nameError; if (emailError) newErrors.email = emailError; if (messageError) newErrors.message = messageError; setErrors(newErrors); // Update errors state return Object.keys(newErrors).length === 0; // Form is valid if no errors }; // --- Event Handlers --- // Handle field losing focus (onBlur) const handleBlur = (event) => { const { name: fieldName, value } = event.target; // Mark the field as touched setTouched(prev => ({ ...prev, [fieldName]: true })); // Validate the field and update its error state const error = validateField(fieldName, value); setErrors(prev => ({ ...prev, [fieldName]: error })); }; // Handle changes in input fields (onChange) const handleChange = (event) => { const { name: fieldName, value } = event.target; // Update the corresponding state variable for the field if (fieldName === 'name') setName(value); else if (fieldName === 'email') setEmail(value); else if (fieldName === 'message') setMessage(value); // Re-validate the field on change *only if* it has already been touched // This provides instant feedback after the initial blur validation if (touched[fieldName]) { const error = validateField(fieldName, value); setErrors(prev => ({ ...prev, [fieldName]: error })); } }; // Handle form submission (onSubmit) const handleSubmit = (event) => { event.preventDefault(); // Prevent standard form submission // Mark all fields as touched to ensure errors are shown on submit attempt setTouched({ name: true, email: true, message: true }); // Validate the entire form if (!validateForm()) { setSubmitStatus(null); // Clear any previous submission status setFeedbackMessage('Please correct the errors in the form.'); // Show general error return; // Stop submission if form is invalid } // --- Form is valid, proceed with submission --- setSubmitStatus('submitting'); // Set status to submitting setFeedbackMessage(''); // Clear previous feedback const formData = { name, email, message }; const apiUrl = '/api/submit_contact.php'; // Send data to the backend API fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData), }) .then(response => response.json()) // Expect JSON response .then(data => { // Check API response success flag if (!data.success) { // Handle backend validation errors or other API errors if (data.details) { // Check if specific field errors were returned setErrors(prev => ({ ...prev, ...data.details })); // Merge backend errors } // Throw error to be caught below throw new Error(data.error || 'Submission failed.'); } // --- Submission Successful --- setSubmitStatus('success'); setFeedbackMessage(data.message || 'Message sent successfully!'); // Clear form fields and reset state setName(''); setEmail(''); setMessage(''); setErrors({}); setTouched({ name: false, email: false, message: false }); }) .catch(error => { // --- Submission Failed --- console.error('Submission error:', error); setSubmitStatus('error'); // Display the error message from the caught error setFeedbackMessage(error.message || 'An unexpected error occurred.'); }); }; // --- Render Component UI --- return (

Contact Us

Have questions or feedback? Send us a message!

{/* Contact Form */}
{/* Name Field */}
{/* Show error message only if field is touched and has an error */} {errors.name && touched.name &&

{errors.name}

}
{/* Email Field (similar structure) */}
{errors.email && touched.email &&

{errors.email}

}
{/* Message Field (similar structure) */}
{errors.message && touched.message &&

{errors.message}

}
{/* Submission Status/Feedback Message */} {feedbackMessage && (
{feedbackMessage}
)} {/* Submit Button */}
{/* Debug Viewer for Contact Messages */} {/* WARNING: Remove or secure this in production */}
); } // No props expected // ContactPage.propTypes = {}; // Make ContactPage available globally window.ContactPage = ContactPage;