deafmissoula-website/client/src/components/Footer.tsx
2024-10-19 13:16:45 -06:00

212 lines
No EOL
8.1 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
const Footer: React.FC = () => {
const [formData, setFormData] = useState({
name: '',
email: '',
subject: '',
message: '',
honeypot: '',
});
const [status, setStatus] = useState('');
const [errors, setErrors] = useState<Partial<typeof formData>>({});
const [lastSubmissionTime, setLastSubmissionTime] = useState(0);
useEffect(() => {
const storedTime = localStorage.getItem('lastSubmissionTime');
if (storedTime) {
setLastSubmissionTime(parseInt(storedTime, 10));
}
}, []);
const validateForm = (data: typeof formData) => {
const errors: Partial<typeof formData> = {};
if (!data.name.trim()) errors.name = "Name is required";
if (!data.email.trim()) errors.email = "Email is required";
else if (!/\S+@\S+\.\S+/.test(data.email)) errors.email = "Email is invalid";
if (!data.subject.trim()) errors.subject = "Subject is required";
if (!data.message.trim()) errors.message = "Message is required";
return errors;
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
setFormData({ ...formData, [name]: value });
setErrors({ ...errors, [name]: '' });
};
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const validationErrors = validateForm(formData);
if (Object.keys(validationErrors).length > 0) {
setErrors(validationErrors);
setStatus('Please correct the errors in the form.');
return;
}
if (formData.honeypot) {
setStatus('Form submission rejected.');
return;
}
const currentTime = Date.now();
if (currentTime - lastSubmissionTime < 60000) {
setStatus('Please wait a moment before submitting again.');
return;
}
setStatus('Sending...');
try {
const response = await fetch('/api/sendEmail', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
});
const contentType = response.headers.get("content-type");
if (contentType && contentType.indexOf("application/json") !== -1) {
const data = await response.json();
if (response.ok) {
setStatus('Message sent successfully!');
setFormData({ name: '', email: '', subject: '', message: '', honeypot: '' });
setLastSubmissionTime(currentTime);
localStorage.setItem('lastSubmissionTime', currentTime.toString());
} else {
setStatus(`Failed to send message: ${data.error || 'Unknown error'}`);
}
} else {
setStatus('Received non-JSON response from server');
}
} catch (error: unknown) {
console.error('Error sending message:', error);
setStatus(`An error occurred: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
};
return (
<footer className="bg-[#8C1D40] text-white py-8">
<div className="container mx-auto px-4">
<div className="flex flex-wrap justify-between">
<div className="w-full md:w-1/3 mb-6 md:mb-0">
<h3 className="text-xl font-bold mb-4">MCDi</h3>
<p className="text-sm">Missoula Council of the Deaf, Inc.</p>
<p className="text-sm mt-2">© MCDi {new Date().getFullYear()}</p>
<a
href="https://www.facebook.com/profile.php?id=61564416121195"
target="_blank"
rel="noopener noreferrer"
className="inline-block mt-4 transition-transform duration-300 ease-in-out transform hover:scale-110"
>
<img
src="logos/facebook-logo.png"
alt="Facebook"
className="w-10 h-10 rounded-full shadow-lg hover:shadow-xl"
style={{
filter: 'drop-shadow(0px 4px 6px rgba(0, 0, 0, 0.1))',
}}
/>
</a>
</div>
<div className="w-full md:w-1/3 mb-6 md:mb-0">
<h3 className="text-xl font-bold mb-4">Quick Links</h3>
<ul className="text-sm grid grid-cols-2 md:grid-cols-1 gap-2">
<li><Link to="/" className="hover:text-yellow-200 transition-colors duration-200">Home</Link></li>
<li><Link to="/calendar" className="hover:text-yellow-200 transition-colors duration-200">Calendar</Link></li>
<li><Link to="/bylaws" className="hover:text-yellow-200 transition-colors duration-200">Bylaws</Link></li>
<li><Link to="/sponsors" className="hover:text-yellow-200 transition-colors duration-200">Sponsors</Link></li>
<li><Link to="/meet-board" className="hover:text-yellow-200 transition-colors duration-200">Meet MCDi Board</Link></li>
</ul>
</div>
<div className="w-full md:w-1/3">
<h3 className="text-xl font-bold mb-4">Contact Us</h3>
<form className="text-sm" onSubmit={handleSubmit}>
<div className="mb-2">
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
placeholder="Name"
className={`w-full p-2 text-black rounded ${errors.name ? 'border-red-500' : ''}`}
required
/>
{errors.name && <p className="text-red-500 text-xs mt-1">{errors.name}</p>}
</div>
<div className="mb-2">
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
placeholder="Your Email Address"
className={`w-full p-2 text-black rounded ${errors.email ? 'border-red-500' : ''}`}
required
/>
{errors.email && <p className="text-red-500 text-xs mt-1">{errors.email}</p>}
</div>
<div className="mb-2">
<input
type="text"
name="subject"
value={formData.subject}
onChange={handleChange}
placeholder="Subject"
className={`w-full p-2 text-black rounded ${errors.subject ? 'border-red-500' : ''}`}
required
/>
{errors.subject && <p className="text-red-500 text-xs mt-1">{errors.subject}</p>}
</div>
<div className="mb-2">
<textarea
name="message"
value={formData.message}
onChange={handleChange}
placeholder="Message"
rows={3}
className={`w-full p-2 text-black rounded ${errors.message ? 'border-red-500' : ''}`}
required
></textarea>
{errors.message && <p className="text-red-500 text-xs mt-1">{errors.message}</p>}
</div>
<input
type="text"
name="honeypot"
value={formData.honeypot}
onChange={handleChange}
style={{display: 'none'}}
tabIndex={-1}
autoComplete="off"
/>
<button
type="submit"
className="bg-yellow-400 text-[#8C1D40] font-bold py-2 px-4 rounded hover:bg-yellow-300 transition-colors duration-200"
>
Send
</button>
{status && <p className="mt-2 text-sm">{status}</p>}
</form>
</div>
</div>
<div className="w-full text-center mt-8 text-sm">
<p>
Designed by{' '}
<a
href="https://www.deafgain.org"
target="_blank"
rel="noopener noreferrer"
className="text-yellow-200 hover:text-yellow-400 transition-colors duration-200"
>
DeafGain, LLC
</a>
</p>
</div>
</div>
</footer>
);
};
export default Footer;