482 lines
26 KiB
TypeScript
482 lines
26 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import { Mail, Phone, MapPin, Send, User, MessageSquare } from 'lucide-react';
|
|
import { submitContactForm } from '../services/api/contactService';
|
|
import { pageContentService } from '../services/api';
|
|
import type { PageContent } from '../services/api/pageContentService';
|
|
import { useCompanySettings } from '../contexts/CompanySettingsContext';
|
|
import { toast } from 'react-toastify';
|
|
import Recaptcha from '../components/common/Recaptcha';
|
|
import { recaptchaService } from '../services/api/systemSettingsService';
|
|
|
|
const ContactPage: React.FC = () => {
|
|
const { settings } = useCompanySettings();
|
|
const [pageContent, setPageContent] = useState<PageContent | null>(null);
|
|
const [formData, setFormData] = useState({
|
|
name: '',
|
|
email: '',
|
|
phone: '',
|
|
subject: '',
|
|
message: '',
|
|
});
|
|
const [loading, setLoading] = useState(false);
|
|
const [errors, setErrors] = useState<Record<string, string>>({});
|
|
const [recaptchaToken, setRecaptchaToken] = useState<string | null>(null);
|
|
|
|
const validateForm = (): boolean => {
|
|
const newErrors: Record<string, string> = {};
|
|
|
|
if (!formData.name.trim()) {
|
|
newErrors.name = 'Name is required';
|
|
}
|
|
|
|
if (!formData.email.trim()) {
|
|
newErrors.email = 'Email is required';
|
|
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
|
|
newErrors.email = 'Please enter a valid email address';
|
|
}
|
|
|
|
if (!formData.subject.trim()) {
|
|
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 long';
|
|
}
|
|
|
|
setErrors(newErrors);
|
|
return Object.keys(newErrors).length === 0;
|
|
};
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
|
|
if (!validateForm()) {
|
|
return;
|
|
}
|
|
|
|
// Verify reCAPTCHA if enabled
|
|
if (recaptchaToken) {
|
|
try {
|
|
const verifyResponse = await recaptchaService.verifyRecaptcha(recaptchaToken);
|
|
if (verifyResponse.status === 'error' || !verifyResponse.data.verified) {
|
|
toast.error('reCAPTCHA verification failed. Please try again.');
|
|
setRecaptchaToken(null);
|
|
return;
|
|
}
|
|
} catch (error) {
|
|
toast.error('reCAPTCHA verification failed. Please try again.');
|
|
setRecaptchaToken(null);
|
|
return;
|
|
}
|
|
}
|
|
|
|
setLoading(true);
|
|
try {
|
|
await submitContactForm(formData);
|
|
toast.success('Thank you for contacting us! We will get back to you soon.');
|
|
|
|
// Reset form
|
|
setFormData({
|
|
name: '',
|
|
email: '',
|
|
phone: '',
|
|
subject: '',
|
|
message: '',
|
|
});
|
|
setErrors({});
|
|
setRecaptchaToken(null);
|
|
} catch (error: any) {
|
|
const errorMessage = error?.response?.data?.detail || error?.message || 'Failed to send message. Please try again.';
|
|
toast.error(errorMessage);
|
|
setRecaptchaToken(null);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
const fetchPageContent = async () => {
|
|
try {
|
|
const response = await pageContentService.getPageContent('contact');
|
|
if (response.status === 'success' && response.data?.page_content) {
|
|
setPageContent(response.data.page_content);
|
|
|
|
// Update document title and meta tags
|
|
if (response.data.page_content.meta_title) {
|
|
document.title = response.data.page_content.meta_title;
|
|
}
|
|
if (response.data.page_content.meta_description) {
|
|
let metaDescription = document.querySelector('meta[name="description"]');
|
|
if (!metaDescription) {
|
|
metaDescription = document.createElement('meta');
|
|
metaDescription.setAttribute('name', 'description');
|
|
document.head.appendChild(metaDescription);
|
|
}
|
|
metaDescription.setAttribute('content', response.data.page_content.meta_description);
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
console.error('Error fetching page content:', err);
|
|
// Silently fail - use default content
|
|
}
|
|
};
|
|
|
|
fetchPageContent();
|
|
}, []);
|
|
|
|
// Get phone, email, and address from centralized company settings
|
|
const displayPhone = settings.company_phone || 'Available 24/7 for your convenience';
|
|
const displayEmail = settings.company_email || "We'll respond within 24 hours";
|
|
const displayAddress = settings.company_address || 'Visit us at our hotel reception';
|
|
|
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
|
const { name, value } = e.target;
|
|
setFormData((prev) => ({ ...prev, [name]: value }));
|
|
|
|
// Clear error when user starts typing
|
|
if (errors[name]) {
|
|
setErrors((prev) => {
|
|
const newErrors = { ...prev };
|
|
delete newErrors[name];
|
|
return newErrors;
|
|
});
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gradient-to-b from-[#0f0f0f] via-[#1a1a1a] to-[#0f0f0f] w-full" style={{ width: '100vw', position: 'relative', left: '50%', right: '50%', marginLeft: '-50vw', marginRight: '-50vw', marginTop: '-1.5rem', marginBottom: '-1.5rem' }}>
|
|
{/* Full-width hero section */}
|
|
<div className="w-full bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#1a1a1a] border-b border-[#d4af37]/10 pt-6 sm:pt-7 md:pt-8 overflow-hidden relative">
|
|
{/* Decorative Elements */}
|
|
<div className="absolute inset-0 opacity-10">
|
|
<div className="absolute top-10 left-10 w-32 sm:w-48 h-32 sm:h-48 bg-[#d4af37] rounded-full blur-3xl"></div>
|
|
<div className="absolute bottom-10 right-10 w-40 sm:w-64 h-40 sm:h-64 bg-[#c9a227] rounded-full blur-3xl"></div>
|
|
</div>
|
|
<div className="absolute top-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-[#d4af37]/50 to-transparent"></div>
|
|
|
|
<div className="w-full max-w-[1920px] mx-auto px-3 sm:px-4 md:px-6 lg:px-8 xl:px-12 2xl:px-16 3xl:px-20 py-4 sm:py-5 md:py-6 relative z-10">
|
|
<div className="max-w-2xl mx-auto text-center px-2">
|
|
<div className="flex justify-center mb-2 sm:mb-3 md:mb-4">
|
|
<div className="relative group">
|
|
<div className="absolute inset-0 bg-gradient-to-br from-[#d4af37] via-[#f5d76e] to-[#c9a227] rounded-xl blur-lg opacity-40 group-hover:opacity-60 transition-opacity duration-500"></div>
|
|
<div className="relative p-2 sm:p-2.5 md:p-3 bg-gradient-to-br from-[#1a1a1a] to-[#0a0a0a] rounded-lg border-2 border-[#d4af37]/40 backdrop-blur-sm shadow-xl shadow-[#d4af37]/20 group-hover:border-[#d4af37]/60 transition-all duration-300">
|
|
<Mail className="w-5 h-5 sm:w-6 sm:h-6 md:w-7 md:h-7 text-[#d4af37] drop-shadow-lg" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<h1 className="text-2xl xs:text-3xl sm:text-4xl md:text-5xl font-serif font-semibold mb-2 sm:mb-3 tracking-tight leading-tight px-2">
|
|
<span className="bg-gradient-to-r from-white via-[#d4af37] to-white bg-clip-text text-transparent">
|
|
{pageContent?.title || 'Contact Us'}
|
|
</span>
|
|
</h1>
|
|
<div className="w-12 sm:w-16 md:w-20 h-0.5 bg-gradient-to-r from-transparent via-[#d4af37] to-transparent mx-auto mb-2 sm:mb-3"></div>
|
|
<p className="text-sm sm:text-base md:text-lg text-gray-300 font-light leading-relaxed max-w-xl mx-auto tracking-wide px-2 sm:px-4">
|
|
{pageContent?.subtitle || pageContent?.description || "Experience the pinnacle of hospitality. We're here to make your stay extraordinary."}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div className="absolute bottom-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-[#d4af37]/30 to-transparent"></div>
|
|
</div>
|
|
|
|
{/* Full-width content area */}
|
|
<div className="w-full py-4 sm:py-6 md:py-8 lg:py-10 xl:py-12">
|
|
<div className="w-full max-w-[1920px] mx-auto px-3 sm:px-4 md:px-6 lg:px-8 xl:px-12 2xl:px-16 3xl:px-20">
|
|
<div className="grid grid-cols-1 lg:grid-cols-12 gap-4 sm:gap-5 md:gap-6 lg:gap-7 xl:gap-8 2xl:gap-10 max-w-7xl mx-auto">
|
|
{/* Contact Info Section */}
|
|
<div className="lg:col-span-4">
|
|
<div className="bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#0a0a0a]
|
|
rounded-xl sm:rounded-2xl border-2 border-[#d4af37]/30 p-5 sm:p-6 md:p-8 lg:p-10
|
|
shadow-2xl shadow-[#d4af37]/10 backdrop-blur-xl
|
|
relative overflow-hidden h-full group hover:border-[#d4af37]/50 transition-all duration-500">
|
|
{/* Subtle background gradient */}
|
|
<div className="absolute inset-0 bg-gradient-to-br from-[#d4af37]/5 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
|
|
|
<div className="relative z-10">
|
|
<div className="flex items-center gap-2 sm:gap-3 mb-6 sm:mb-7 md:mb-8">
|
|
<div className="w-0.5 sm:w-1 h-6 sm:h-8 bg-gradient-to-b from-[#d4af37] to-[#c9a227] rounded-full"></div>
|
|
<h2 className="text-xl sm:text-2xl md:text-3xl font-serif font-semibold
|
|
text-white tracking-tight">
|
|
Get in Touch
|
|
</h2>
|
|
</div>
|
|
|
|
<div className="space-y-5 sm:space-y-6 md:space-y-7">
|
|
<div className="flex items-start gap-3 sm:gap-4 md:gap-5 group/item hover:translate-x-1 transition-transform duration-300">
|
|
<div className="p-2.5 sm:p-3 md:p-4 bg-gradient-to-br from-[#d4af37]/20 to-[#d4af37]/10 rounded-lg sm:rounded-xl border border-[#d4af37]/40 flex-shrink-0 group-hover/item:bg-gradient-to-br group-hover/item:from-[#d4af37]/30 group-hover/item:to-[#d4af37]/20 group-hover/item:border-[#d4af37]/60 transition-all duration-300 shadow-lg shadow-[#d4af37]/10">
|
|
<Mail className="w-5 h-5 sm:w-6 sm:h-6 text-[#d4af37] drop-shadow-lg" />
|
|
</div>
|
|
<div>
|
|
<h3 className="text-sm sm:text-base font-medium text-[#d4af37] mb-1 sm:mb-2 tracking-wide">Email</h3>
|
|
<a href={`mailto:${displayEmail}`} className="text-gray-300 font-light text-xs sm:text-sm leading-relaxed hover:text-[#d4af37] transition-colors">
|
|
{displayEmail}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex items-start gap-3 sm:gap-4 md:gap-5 group/item hover:translate-x-1 transition-transform duration-300">
|
|
<div className="p-2.5 sm:p-3 md:p-4 bg-gradient-to-br from-[#d4af37]/20 to-[#d4af37]/10 rounded-lg sm:rounded-xl border border-[#d4af37]/40 flex-shrink-0 group-hover/item:bg-gradient-to-br group-hover/item:from-[#d4af37]/30 group-hover/item:to-[#d4af37]/20 group-hover/item:border-[#d4af37]/60 transition-all duration-300 shadow-lg shadow-[#d4af37]/10">
|
|
<Phone className="w-5 h-5 sm:w-6 sm:h-6 text-[#d4af37] drop-shadow-lg" />
|
|
</div>
|
|
<div>
|
|
<h3 className="text-sm sm:text-base font-medium text-[#d4af37] mb-1 sm:mb-2 tracking-wide">Phone</h3>
|
|
<a href={`tel:${displayPhone.replace(/\s+/g, '').replace(/[()]/g, '')}`} className="text-gray-300 font-light text-xs sm:text-sm leading-relaxed hover:text-[#d4af37] transition-colors">
|
|
{displayPhone}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex items-start gap-3 sm:gap-4 md:gap-5 group/item hover:translate-x-1 transition-transform duration-300">
|
|
<div className="p-2.5 sm:p-3 md:p-4 bg-gradient-to-br from-[#d4af37]/20 to-[#d4af37]/10 rounded-lg sm:rounded-xl border border-[#d4af37]/40 flex-shrink-0 group-hover/item:bg-gradient-to-br group-hover/item:from-[#d4af37]/30 group-hover/item:to-[#d4af37]/20 group-hover/item:border-[#d4af37]/60 transition-all duration-300 shadow-lg shadow-[#d4af37]/10">
|
|
<MapPin className="w-5 h-5 sm:w-6 sm:h-6 text-[#d4af37] drop-shadow-lg" />
|
|
</div>
|
|
<div>
|
|
<h3 className="text-sm sm:text-base font-medium text-[#d4af37] mb-1 sm:mb-2 tracking-wide">Location</h3>
|
|
<p className="text-gray-300 font-light text-xs sm:text-sm leading-relaxed whitespace-pre-line">
|
|
{displayAddress}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Google Maps */}
|
|
{pageContent?.map_url && (
|
|
<div className="mt-6 sm:mt-7 md:mt-8 pt-6 sm:pt-7 md:pt-8 border-t border-[#d4af37]/30">
|
|
<h3 className="text-sm sm:text-base font-medium text-[#d4af37] mb-3 sm:mb-4 tracking-wide">
|
|
Find Us
|
|
</h3>
|
|
<div className="relative rounded-lg overflow-hidden border-2 border-[#d4af37]/30 shadow-lg shadow-[#d4af37]/10 group hover:border-[#d4af37]/50 transition-all duration-300">
|
|
<iframe
|
|
src={pageContent.map_url}
|
|
width="100%"
|
|
height="200"
|
|
style={{ border: 0 }}
|
|
allowFullScreen
|
|
loading="lazy"
|
|
referrerPolicy="no-referrer-when-downgrade"
|
|
className="w-full h-40 sm:h-44 md:h-48 rounded-lg"
|
|
title="Hotel Location"
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<div className="mt-5 sm:mt-6 md:mt-7 pt-5 sm:pt-6 md:pt-7 border-t border-[#d4af37]/30">
|
|
<p className="text-gray-400 font-light text-xs sm:text-sm leading-relaxed tracking-wide">
|
|
{pageContent?.content || "Our team is here to help you with any questions about your stay, bookings, or special requests. We're committed to exceeding your expectations."}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Contact Form Section */}
|
|
<div className="lg:col-span-8">
|
|
<div className="bg-gradient-to-br from-[#1a1a1a] via-[#0f0f0f] to-[#0a0a0a]
|
|
rounded-xl sm:rounded-2xl border-2 border-[#d4af37]/30 p-5 sm:p-6 md:p-8 lg:p-10
|
|
shadow-2xl shadow-[#d4af37]/10 backdrop-blur-xl
|
|
relative overflow-hidden">
|
|
{/* Subtle background pattern */}
|
|
<div className="absolute inset-0 opacity-5">
|
|
<div className="absolute top-0 right-0 w-48 sm:w-64 md:w-96 h-48 sm:h-64 md:h-96 bg-[#d4af37] rounded-full blur-3xl"></div>
|
|
</div>
|
|
|
|
<div className="relative z-10">
|
|
<div className="flex items-center gap-2 sm:gap-3 mb-6 sm:mb-7 md:mb-8">
|
|
<div className="w-0.5 sm:w-1 h-6 sm:h-8 bg-gradient-to-b from-[#d4af37] to-[#c9a227] rounded-full"></div>
|
|
<h2 className="text-xl sm:text-2xl md:text-3xl font-serif font-semibold
|
|
text-white tracking-tight">
|
|
Send Us a Message
|
|
</h2>
|
|
</div>
|
|
|
|
<form onSubmit={handleSubmit} className="space-y-5 sm:space-y-6 md:space-y-7">
|
|
{/* Name Field */}
|
|
<div>
|
|
<label htmlFor="name" className="block text-xs sm:text-sm font-medium text-gray-300 mb-2 sm:mb-3 tracking-wide">
|
|
<span className="flex items-center gap-1.5 sm:gap-2">
|
|
<User className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-[#d4af37] drop-shadow-lg" />
|
|
Full Name <span className="text-[#d4af37] font-semibold">*</span>
|
|
</span>
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="name"
|
|
name="name"
|
|
value={formData.name}
|
|
onChange={handleChange}
|
|
className={`w-full px-4 sm:px-5 py-3 sm:py-4 bg-gradient-to-br from-[#0f0f0f] to-[#0a0a0a] border-2 rounded-lg
|
|
text-white text-sm sm:text-base placeholder-gray-500/60
|
|
focus:outline-none focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] focus:shadow-lg focus:shadow-[#d4af37]/20
|
|
transition-all duration-300 hover:border-[#d4af37]/40
|
|
${errors.name ? 'border-red-500/50 focus:border-red-500 focus:ring-red-500/50' : 'border-[#d4af37]/30'}`}
|
|
placeholder="Enter your full name"
|
|
/>
|
|
{errors.name && (
|
|
<p className="mt-1.5 sm:mt-2 text-xs text-red-400 font-light">{errors.name}</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Email and Phone Row */}
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 sm:gap-5 md:gap-6 lg:gap-7">
|
|
{/* Email Field */}
|
|
<div>
|
|
<label htmlFor="email" className="block text-xs sm:text-sm font-medium text-gray-300 mb-2 sm:mb-3 tracking-wide">
|
|
<span className="flex items-center gap-1.5 sm:gap-2">
|
|
<Mail className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-[#d4af37] drop-shadow-lg" />
|
|
Email <span className="text-[#d4af37] font-semibold">*</span>
|
|
</span>
|
|
</label>
|
|
<input
|
|
type="email"
|
|
id="email"
|
|
name="email"
|
|
value={formData.email}
|
|
onChange={handleChange}
|
|
className={`w-full px-4 sm:px-5 py-3 sm:py-4 bg-gradient-to-br from-[#0f0f0f] to-[#0a0a0a] border-2 rounded-lg
|
|
text-white text-sm sm:text-base placeholder-gray-500/60
|
|
focus:outline-none focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] focus:shadow-lg focus:shadow-[#d4af37]/20
|
|
transition-all duration-300 hover:border-[#d4af37]/40
|
|
${errors.email ? 'border-red-500/50 focus:border-red-500 focus:ring-red-500/50' : 'border-[#d4af37]/30'}`}
|
|
placeholder="your.email@example.com"
|
|
/>
|
|
{errors.email && (
|
|
<p className="mt-1.5 sm:mt-2 text-xs text-red-400 font-light">{errors.email}</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Phone Field */}
|
|
<div>
|
|
<label htmlFor="phone" className="block text-xs sm:text-sm font-medium text-gray-300 mb-2 sm:mb-3 tracking-wide">
|
|
<span className="flex items-center gap-1.5 sm:gap-2">
|
|
<Phone className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-[#d4af37] drop-shadow-lg" />
|
|
Phone <span className="text-gray-500 text-xs">(Optional)</span>
|
|
</span>
|
|
</label>
|
|
<input
|
|
type="tel"
|
|
id="phone"
|
|
name="phone"
|
|
value={formData.phone}
|
|
onChange={handleChange}
|
|
className="w-full px-4 sm:px-5 py-3 sm:py-4 bg-gradient-to-br from-[#0f0f0f] to-[#0a0a0a] border-2 border-[#d4af37]/30 rounded-lg
|
|
text-white text-sm sm:text-base placeholder-gray-500/60
|
|
focus:outline-none focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] focus:shadow-lg focus:shadow-[#d4af37]/20
|
|
transition-all duration-300 hover:border-[#d4af37]/40"
|
|
placeholder="+1 (555) 123-4567"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Subject Field */}
|
|
<div>
|
|
<label htmlFor="subject" className="block text-xs sm:text-sm font-medium text-gray-300 mb-2 sm:mb-3 tracking-wide">
|
|
<span className="flex items-center gap-1.5 sm:gap-2">
|
|
<MessageSquare className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-[#d4af37] drop-shadow-lg" />
|
|
Subject <span className="text-[#d4af37] font-semibold">*</span>
|
|
</span>
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="subject"
|
|
name="subject"
|
|
value={formData.subject}
|
|
onChange={handleChange}
|
|
className={`w-full px-4 sm:px-5 py-3 sm:py-4 bg-gradient-to-br from-[#0f0f0f] to-[#0a0a0a] border-2 rounded-lg
|
|
text-white text-sm sm:text-base placeholder-gray-500/60
|
|
focus:outline-none focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] focus:shadow-lg focus:shadow-[#d4af37]/20
|
|
transition-all duration-300 hover:border-[#d4af37]/40
|
|
${errors.subject ? 'border-red-500/50 focus:border-red-500 focus:ring-red-500/50' : 'border-[#d4af37]/30'}`}
|
|
placeholder="What is this regarding?"
|
|
/>
|
|
{errors.subject && (
|
|
<p className="mt-1.5 sm:mt-2 text-xs text-red-400 font-light">{errors.subject}</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Message Field */}
|
|
<div>
|
|
<label htmlFor="message" className="block text-xs sm:text-sm font-medium text-gray-300 mb-2 sm:mb-3 tracking-wide">
|
|
<span className="flex items-center gap-1.5 sm:gap-2">
|
|
<MessageSquare className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-[#d4af37] drop-shadow-lg" />
|
|
Message <span className="text-[#d4af37] font-semibold">*</span>
|
|
</span>
|
|
</label>
|
|
<textarea
|
|
id="message"
|
|
name="message"
|
|
value={formData.message}
|
|
onChange={handleChange}
|
|
rows={6}
|
|
className={`w-full px-4 sm:px-5 py-3 sm:py-4 bg-gradient-to-br from-[#0f0f0f] to-[#0a0a0a] border-2 rounded-lg
|
|
text-white text-sm sm:text-base placeholder-gray-500/60 resize-none
|
|
focus:outline-none focus:ring-2 focus:ring-[#d4af37]/50 focus:border-[#d4af37] focus:shadow-lg focus:shadow-[#d4af37]/20
|
|
transition-all duration-300 hover:border-[#d4af37]/40
|
|
${errors.message ? 'border-red-500/50 focus:border-red-500 focus:ring-red-500/50' : 'border-[#d4af37]/30'}`}
|
|
placeholder="Tell us more about your inquiry..."
|
|
/>
|
|
{errors.message && (
|
|
<p className="mt-1.5 sm:mt-2 text-xs text-red-400 font-light">{errors.message}</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* reCAPTCHA */}
|
|
<div className="pt-2 sm:pt-3">
|
|
<Recaptcha
|
|
onChange={(token) => setRecaptchaToken(token)}
|
|
onError={(error) => {
|
|
console.error('reCAPTCHA error:', error);
|
|
setRecaptchaToken(null);
|
|
}}
|
|
theme="dark"
|
|
size="normal"
|
|
className="flex justify-center"
|
|
/>
|
|
</div>
|
|
|
|
{/* Submit Button */}
|
|
<div className="pt-2 sm:pt-3 md:pt-4">
|
|
<button
|
|
type="submit"
|
|
disabled={loading}
|
|
className="group w-full sm:w-auto inline-flex items-center justify-center gap-2 sm:gap-3
|
|
bg-gradient-to-r from-[#d4af37] via-[#f5d76e] to-[#d4af37]
|
|
text-[#0f0f0f] font-semibold
|
|
active:scale-[0.98]
|
|
disabled:opacity-50 disabled:cursor-not-allowed
|
|
transition-all duration-500
|
|
tracking-wide text-sm sm:text-base
|
|
px-6 sm:px-8 md:px-10 py-3 sm:py-3.5 md:py-4 rounded-lg
|
|
shadow-2xl shadow-[#d4af37]/40 hover:shadow-[#d4af37]/60 hover:scale-[1.02]
|
|
relative overflow-hidden
|
|
touch-manipulation min-h-[44px] sm:min-h-[48px] md:min-h-[52px]"
|
|
>
|
|
<div className="absolute inset-0 bg-gradient-to-r from-white/0 via-white/30 to-white/0 translate-x-[-100%] group-hover:translate-x-[100%] transition-transform duration-1000"></div>
|
|
{loading ? (
|
|
<>
|
|
<div className="w-4 h-4 sm:w-5 sm:h-5 border-2 border-[#0f0f0f] border-t-transparent rounded-full animate-spin relative z-10" />
|
|
<span className="relative z-10">Sending...</span>
|
|
</>
|
|
) : (
|
|
<>
|
|
<Send className="w-4 h-4 sm:w-5 sm:h-5 relative z-10 group-hover:translate-x-1 transition-transform duration-300" />
|
|
<span className="relative z-10">Send Message</span>
|
|
</>
|
|
)}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ContactPage;
|
|
|