Refactor project structure: update Astro configuration, integrate Tailwind CSS, enhance ContactForm with validation, and improve layout for various pages. Add new Donate and Orphanage pages, and implement responsive design adjustments across components.
This commit is contained in:
@@ -1,46 +1,140 @@
|
||||
import { useState } from "react";
|
||||
import React, { useState } from 'react';
|
||||
|
||||
export default function ContactForm() {
|
||||
const [status, setStatus] = useState("");
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
email: '',
|
||||
subject: '',
|
||||
message: '',
|
||||
});
|
||||
|
||||
const [errors, setErrors] = useState({});
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [submitMessage, setSubmitMessage] = useState(null);
|
||||
|
||||
const validate = () => {
|
||||
let newErrors = {};
|
||||
if (!formData.name) newErrors.name = 'Name is required';
|
||||
if (!formData.email) {
|
||||
newErrors.email = 'Email is required';
|
||||
} else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(formData.email)) {
|
||||
newErrors.email = 'Invalid email address';
|
||||
}
|
||||
if (!formData.subject) newErrors.subject = 'Subject is required';
|
||||
if (!formData.message) newErrors.message = 'Message is required';
|
||||
return newErrors;
|
||||
};
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData({ ...formData, [name]: value });
|
||||
// Clear error for the field as user types
|
||||
if (errors[name]) {
|
||||
setErrors({ ...errors, [name]: null });
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
const form = e.target;
|
||||
// Honeypot check
|
||||
if (form.honey.value !== "") {
|
||||
setStatus("Spam detected.");
|
||||
return;
|
||||
setIsSubmitting(true);
|
||||
setSubmitMessage(null);
|
||||
|
||||
const validationErrors = validate();
|
||||
setErrors(validationErrors);
|
||||
|
||||
if (Object.keys(validationErrors).length === 0) {
|
||||
try {
|
||||
// Simulate API call
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
console.log('Form data submitted:', formData);
|
||||
setSubmitMessage({ type: 'success', text: 'Your message has been sent successfully!' });
|
||||
setFormData({ name: '', email: '', subject: '', message: '' }); // Clear form
|
||||
} catch (error) {
|
||||
setSubmitMessage({ type: 'error', text: 'There was an error sending your message. Please try again later.' });
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
} else {
|
||||
setIsSubmitting(false);
|
||||
setSubmitMessage({ type: 'error', text: 'Please correct the errors in the form.' });
|
||||
}
|
||||
setStatus("Sending...");
|
||||
// Placeholder: would POST to /api/contact
|
||||
setTimeout(() => setStatus("Message sent (placeholder, not yet implemented)."), 1000);
|
||||
};
|
||||
|
||||
return (
|
||||
<form className="max-w-xl mx-auto bg-base-100 p-8 rounded-xl shadow-lg" onSubmit={handleSubmit} autoComplete="off">
|
||||
<div className="mb-4">
|
||||
<label htmlFor="name" className="block font-bold mb-1">Name</label>
|
||||
<input type="text" id="name" name="name" className="input input-bordered w-full" required autoComplete="off" />
|
||||
<form onSubmit={handleSubmit} className="bg-white p-8 rounded-xl shadow-lg border border-gray-200 max-w-2xl mx-auto">
|
||||
<h2 className="text-3xl font-bold text-center text-nigerian-green-700 mb-8">Send Us a Message</h2>
|
||||
|
||||
{submitMessage && (
|
||||
<div className={`p-4 mb-4 rounded-lg text-center ${submitMessage.type === 'success' ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'}`}>
|
||||
{submitMessage.text}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mb-5">
|
||||
<label htmlFor="name" className="block text-gray-700 text-sm font-bold mb-2">Name</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
name="name"
|
||||
value={formData.name}
|
||||
onChange={handleChange}
|
||||
className={`shadow appearance-none border rounded-lg w-full py-3 px-4 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-nigerian-green-500 ${errors.name ? 'border-red-500' : 'border-gray-300'}`}
|
||||
placeholder="Your Name"
|
||||
/>
|
||||
{errors.name && <p className="text-red-500 text-xs italic mt-2">{errors.name}</p>}
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<label htmlFor="email" className="block font-bold mb-1">Email</label>
|
||||
<input type="email" id="email" name="email" className="input input-bordered w-full" required autoComplete="off" />
|
||||
|
||||
<div className="mb-5">
|
||||
<label htmlFor="email" className="block text-gray-700 text-sm font-bold mb-2">Email</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
value={formData.email}
|
||||
onChange={handleChange}
|
||||
className={`shadow appearance-none border rounded-lg w-full py-3 px-4 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-nigerian-green-500 ${errors.email ? 'border-red-500' : 'border-gray-300'}`}
|
||||
placeholder="your.email@example.com"
|
||||
/>
|
||||
{errors.email && <p className="text-red-500 text-xs italic mt-2">{errors.email}</p>}
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<label htmlFor="subject" className="block font-bold mb-1">Subject</label>
|
||||
<input type="text" id="subject" name="subject" className="input input-bordered w-full" required autoComplete="off" />
|
||||
|
||||
<div className="mb-5">
|
||||
<label htmlFor="subject" className="block text-gray-700 text-sm font-bold mb-2">Subject</label>
|
||||
<input
|
||||
type="text"
|
||||
id="subject"
|
||||
name="subject"
|
||||
value={formData.subject}
|
||||
onChange={handleChange}
|
||||
className={`shadow appearance-none border rounded-lg w-full py-3 px-4 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-nigerian-green-500 ${errors.subject ? 'border-red-500' : 'border-gray-300'}`}
|
||||
placeholder="Subject of your message"
|
||||
/>
|
||||
{errors.subject && <p className="text-red-500 text-xs italic mt-2">{errors.subject}</p>}
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<label htmlFor="message" className="block font-bold mb-1">Message</label>
|
||||
<textarea id="message" name="message" className="textarea textarea-bordered w-full" rows={5} required></textarea>
|
||||
|
||||
<div className="mb-6">
|
||||
<label htmlFor="message" className="block text-gray-700 text-sm font-bold mb-2">Message</label>
|
||||
<textarea
|
||||
id="message"
|
||||
name="message"
|
||||
rows="6"
|
||||
value={formData.message}
|
||||
onChange={handleChange}
|
||||
className={`shadow appearance-none border rounded-lg w-full py-3 px-4 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-nigerian-green-500 ${errors.message ? 'border-red-500' : 'border-gray-300'}`}
|
||||
placeholder="Your message..."
|
||||
></textarea>
|
||||
{errors.message && <p className="text-red-500 text-xs italic mt-2">{errors.message}</p>}
|
||||
</div>
|
||||
{/* Honeypot field for spam protection */}
|
||||
<div style={{ display: "none" }}>
|
||||
<label htmlFor="honey">Do not fill this out</label>
|
||||
<input type="text" id="honey" name="honey" tabIndex="-1" autoComplete="off" />
|
||||
|
||||
<div className="flex items-center justify-center">
|
||||
<button
|
||||
type="submit"
|
||||
className="bg-gradient-to-r from-nigerian-green-500 to-kente-gold-500 hover:from-nigerian-green-600 hover:to-kente-gold-600 text-white font-bold py-3 px-8 rounded-lg focus:outline-none focus:shadow-outline transition-all duration-300 transform hover:scale-105"
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{isSubmitting ? 'Sending...' : 'Send Message'}
|
||||
</button>
|
||||
</div>
|
||||
<button type="submit" className="btn btn-primary w-full">Send Message</button>
|
||||
{status && <div className="mt-4 text-center text-sm text-success">{status}</div>}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user