puffin-app/components/ContactClient.tsx
Matt 6b12e2ae2a
All checks were successful
Build and Push Docker Images / docker (push) Successful in 2m12s
Fix header spacing and homepage centering issues
- Changed layout padding from Tailwind pt-48 to inline style paddingTop: 110px for reliable CSS specificity
- Added negative margin to homepage hero section to maintain vertical centering
- Updated client components (About, Contact, HowItWorks) from py-12 to pb-12 for proper spacing
- All pages now have proper header clearance without content cutoff

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 19:13:15 +01:00

220 lines
7.4 KiB
TypeScript

'use client';
import React, { useState } from 'react';
import { Mail, Phone, Loader2 } from 'lucide-react';
import { validateEmail, sendContactFormEmail } from '../src/utils/email';
import { analytics } from '../src/utils/analytics';
export function ContactClient() {
const [formData, setFormData] = useState({
name: '',
email: '',
phone: '',
company: '',
message: ''
});
const [submitted, setSubmitted] = useState(false);
const [sending, setSending] = useState(false);
const [error, setError] = useState<string | null>(null);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setSending(true);
setError(null);
try {
// Validate email
if (!validateEmail(formData.email)) {
throw new Error('Please enter a valid email address');
}
// Send via SMTP backend
await sendContactFormEmail(formData, 'contact');
setSubmitted(true);
analytics.event('contact', 'form_submitted');
// Reset form after delay
setTimeout(() => {
setFormData({
name: '',
email: '',
phone: '',
company: '',
message: ''
});
setSubmitted(false);
}, 3000);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to send message. Please try again.');
analytics.error(err as Error, 'Contact form submission failed');
} finally {
setSending(false);
}
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
};
return (
<div className="max-w-4xl mx-auto pb-12 px-4 sm:px-6 lg:px-8">
<div className="text-center mb-12">
<h1 className="text-4xl font-bold text-gray-900 mb-4">Contact Us</h1>
<p className="text-xl text-gray-600">
Ready to start your sustainability journey? Get in touch with our team today.
</p>
</div>
<div className="bg-white rounded-xl shadow-lg p-8 sm:p-12">
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 sm:gap-12">
<div className="space-y-8">
<div>
<h2 className="text-2xl font-bold text-gray-900 mb-6">Get in Touch</h2>
<p className="text-gray-600 mb-8 text-justify">
Have questions about our carbon offsetting solutions? Our team is here to help you make a difference in maritime sustainability.
</p>
</div>
<div className="space-y-6">
<div className="flex items-center space-x-4">
<Mail className="text-blue-600" size={24} />
<div>
<h3 className="font-semibold text-gray-900">Email Us</h3>
<a
href="mailto:info@puffinoffset.com"
className="text-blue-600 hover:text-blue-700"
>
info@puffinoffset.com
</a>
</div>
</div>
<div className="flex items-center space-x-4">
<Phone className="text-blue-600" size={24} />
<div>
<h3 className="font-semibold text-gray-900">Call Us</h3>
<a
href="tel:+33671187253"
className="text-blue-600 hover:text-blue-700"
>
+33 6 71 18 72 53
</a>
</div>
</div>
</div>
</div>
<form onSubmit={handleSubmit} className="space-y-6">
{submitted && (
<div className="bg-green-50 border border-green-200 rounded-lg p-4 mb-6">
<p className="text-green-700">
Thank you for your message. Your email client will open shortly.
</p>
</div>
)}
{error && (
<div className="bg-red-50 border border-red-200 rounded-lg p-4 mb-6">
<p className="text-red-700">{error}</p>
</div>
)}
<div>
<label htmlFor="name" className="block text-sm font-medium text-gray-700 mb-1">
Name *
</label>
<input
type="text"
id="name"
name="name"
value={formData.name}
onChange={handleChange}
className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
required
/>
</div>
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-1">
Email *
</label>
<input
type="email"
id="email"
name="email"
value={formData.email}
onChange={handleChange}
className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
required
/>
</div>
<div>
<label htmlFor="phone" className="block text-sm font-medium text-gray-700 mb-1">
Phone
</label>
<input
type="tel"
id="phone"
name="phone"
value={formData.phone}
onChange={handleChange}
className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="+1 234 567 8900"
/>
</div>
<div>
<label htmlFor="company" className="block text-sm font-medium text-gray-700 mb-1">
Company
</label>
<input
type="text"
id="company"
name="company"
value={formData.company}
onChange={handleChange}
className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div>
<label htmlFor="message" className="block text-sm font-medium text-gray-700 mb-1">
Message *
</label>
<textarea
id="message"
name="message"
value={formData.message}
onChange={handleChange}
rows={4}
className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
required
></textarea>
</div>
<button
type="submit"
disabled={sending}
className={`w-full flex items-center justify-center bg-blue-600 text-white py-3 rounded-lg transition-colors ${
sending ? 'opacity-50 cursor-not-allowed' : 'hover:bg-blue-700'
}`}
>
{sending ? (
<>
<Loader2 className="animate-spin mr-2" size={20} />
Preparing Email...
</>
) : (
'Send Message'
)}
</button>
<p className="text-sm text-gray-500 text-center">
* Required fields
</p>
</form>
</div>
</div>
</div>
);
}