566 lines
22 KiB
TypeScript
566 lines
22 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useRef } from 'react';
|
|
import { motion, useInView } from 'framer-motion';
|
|
import {
|
|
Mail,
|
|
Phone,
|
|
MapPin,
|
|
Clock,
|
|
Send,
|
|
MessageSquare,
|
|
Headphones,
|
|
Building2,
|
|
CheckCircle2,
|
|
Loader2,
|
|
} from 'lucide-react';
|
|
import { LandingHeader, LandingFooter } from '@/components/layout';
|
|
|
|
export default function ContactPage() {
|
|
const [formData, setFormData] = useState({
|
|
firstName: '',
|
|
lastName: '',
|
|
email: '',
|
|
company: '',
|
|
phone: '',
|
|
subject: '',
|
|
message: '',
|
|
});
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
const [isSubmitted, setIsSubmitted] = useState(false);
|
|
const [error, setError] = useState('');
|
|
|
|
const heroRef = useRef(null);
|
|
const formRef = useRef(null);
|
|
const contactRef = useRef(null);
|
|
|
|
const isHeroInView = useInView(heroRef, { once: true });
|
|
const isFormInView = useInView(formRef, { once: true });
|
|
const isContactInView = useInView(contactRef, { once: true });
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setError('');
|
|
setIsSubmitting(true);
|
|
|
|
// Simulate form submission
|
|
await new Promise((resolve) => setTimeout(resolve, 1500));
|
|
|
|
setIsSubmitting(false);
|
|
setIsSubmitted(true);
|
|
};
|
|
|
|
const handleChange = (
|
|
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>
|
|
) => {
|
|
setFormData((prev) => ({
|
|
...prev,
|
|
[e.target.name]: e.target.value,
|
|
}));
|
|
};
|
|
|
|
const contactMethods = [
|
|
{
|
|
icon: Mail,
|
|
title: 'Email',
|
|
description: 'Envoyez-nous un email',
|
|
value: 'contact@xpeditis.com',
|
|
link: 'mailto:contact@xpeditis.com',
|
|
color: 'from-blue-500 to-cyan-500',
|
|
},
|
|
{
|
|
icon: Phone,
|
|
title: 'Téléphone',
|
|
description: 'Appelez-nous',
|
|
value: '+33 1 23 45 67 89',
|
|
link: 'tel:+33123456789',
|
|
color: 'from-green-500 to-emerald-500',
|
|
},
|
|
{
|
|
icon: MessageSquare,
|
|
title: 'Chat en direct',
|
|
description: 'Discutez avec notre équipe',
|
|
value: 'Disponible 24/7',
|
|
link: '#chat',
|
|
color: 'from-purple-500 to-pink-500',
|
|
},
|
|
{
|
|
icon: Headphones,
|
|
title: 'Support',
|
|
description: 'Centre d\'aide',
|
|
value: 'support.xpeditis.com',
|
|
link: 'https://support.xpeditis.com',
|
|
color: 'from-orange-500 to-red-500',
|
|
},
|
|
];
|
|
|
|
const offices = [
|
|
{
|
|
city: 'Paris',
|
|
address: '123 Avenue des Champs-Élysées',
|
|
postalCode: '75008 Paris, France',
|
|
phone: '+33 1 23 45 67 89',
|
|
email: 'paris@xpeditis.com',
|
|
isHQ: true,
|
|
},
|
|
{
|
|
city: 'Rotterdam',
|
|
address: 'Wilhelminakade 123',
|
|
postalCode: '3072 AP Rotterdam, Netherlands',
|
|
phone: '+31 10 123 4567',
|
|
email: 'rotterdam@xpeditis.com',
|
|
isHQ: false,
|
|
},
|
|
{
|
|
city: 'Hambourg',
|
|
address: 'Am Sandtorkai 50',
|
|
postalCode: '20457 Hamburg, Germany',
|
|
phone: '+49 40 123 4567',
|
|
email: 'hamburg@xpeditis.com',
|
|
isHQ: false,
|
|
},
|
|
];
|
|
|
|
const subjects = [
|
|
{ value: '', label: 'Sélectionnez un sujet' },
|
|
{ value: 'demo', label: 'Demande de démonstration' },
|
|
{ value: 'pricing', label: 'Questions sur les tarifs' },
|
|
{ value: 'partnership', label: 'Partenariat' },
|
|
{ value: 'support', label: 'Support technique' },
|
|
{ value: 'press', label: 'Relations presse' },
|
|
{ value: 'careers', label: 'Recrutement' },
|
|
{ value: 'other', label: 'Autre' },
|
|
];
|
|
|
|
const containerVariants = {
|
|
hidden: { opacity: 0, y: 50 },
|
|
visible: {
|
|
opacity: 1,
|
|
y: 0,
|
|
transition: {
|
|
duration: 0.6,
|
|
staggerChildren: 0.1,
|
|
},
|
|
},
|
|
};
|
|
|
|
const itemVariants = {
|
|
hidden: { opacity: 0, y: 20 },
|
|
visible: {
|
|
opacity: 1,
|
|
y: 0,
|
|
transition: { duration: 0.5 },
|
|
},
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen bg-white">
|
|
<LandingHeader activePage="contact" />
|
|
|
|
{/* Hero Section */}
|
|
<section ref={heroRef} className="relative pt-32 pb-20 bg-gradient-to-br from-brand-navy to-brand-navy/95 overflow-hidden">
|
|
<div className="absolute inset-0 opacity-10">
|
|
<div className="absolute top-20 left-20 w-96 h-96 bg-brand-turquoise rounded-full blur-3xl" />
|
|
<div className="absolute bottom-20 right-20 w-96 h-96 bg-brand-green rounded-full blur-3xl" />
|
|
</div>
|
|
|
|
<div className="relative z-10 max-w-7xl mx-auto px-6 lg:px-8">
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 30 }}
|
|
animate={isHeroInView ? { opacity: 1, y: 0 } : {}}
|
|
transition={{ duration: 0.8 }}
|
|
className="text-center"
|
|
>
|
|
<motion.div
|
|
initial={{ scale: 0.8, opacity: 0 }}
|
|
animate={isHeroInView ? { scale: 1, opacity: 1 } : {}}
|
|
transition={{ duration: 0.6, delay: 0.2 }}
|
|
className="inline-flex items-center space-x-2 bg-white/10 backdrop-blur-sm px-4 py-2 rounded-full mb-8 border border-white/20"
|
|
>
|
|
<Mail className="w-5 h-5 text-brand-turquoise" />
|
|
<span className="text-white/90 text-sm font-medium">Nous contacter</span>
|
|
</motion.div>
|
|
|
|
<h1 className="text-4xl lg:text-6xl font-bold text-white mb-6 leading-tight">
|
|
Une question ?
|
|
<br />
|
|
<span className="text-transparent bg-clip-text bg-gradient-to-r from-brand-turquoise to-brand-green">
|
|
Nous sommes là pour vous
|
|
</span>
|
|
</h1>
|
|
|
|
<p className="text-xl text-white/80 mb-10 max-w-3xl mx-auto leading-relaxed">
|
|
Notre équipe est disponible pour répondre à toutes vos questions sur notre plateforme,
|
|
nos services et nos tarifs. N'hésitez pas à nous contacter !
|
|
</p>
|
|
</motion.div>
|
|
</div>
|
|
|
|
{/* Wave */}
|
|
<div className="absolute bottom-0 left-0 right-0">
|
|
<svg className="w-full h-16" viewBox="0 0 1440 60" preserveAspectRatio="none">
|
|
<path
|
|
d="M0,30 C240,50 480,10 720,30 C960,50 1200,10 1440,30 L1440,60 L0,60 Z"
|
|
fill="white"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Contact Methods */}
|
|
<section ref={contactRef} className="py-16 bg-gray-50">
|
|
<motion.div
|
|
variants={containerVariants}
|
|
initial="hidden"
|
|
animate={isContactInView ? 'visible' : 'hidden'}
|
|
className="max-w-7xl mx-auto px-6 lg:px-8"
|
|
>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
{contactMethods.map((method, index) => {
|
|
const IconComponent = method.icon;
|
|
return (
|
|
<motion.a
|
|
key={index}
|
|
href={method.link}
|
|
variants={itemVariants}
|
|
whileHover={{ y: -5 }}
|
|
className="bg-white p-6 rounded-2xl shadow-lg border border-gray-100 hover:shadow-xl transition-all group"
|
|
>
|
|
<div
|
|
className={`w-12 h-12 rounded-xl bg-gradient-to-br ${method.color} flex items-center justify-center mb-4 group-hover:scale-110 transition-transform`}
|
|
>
|
|
<IconComponent className="w-6 h-6 text-white" />
|
|
</div>
|
|
<h3 className="text-lg font-bold text-brand-navy mb-1">{method.title}</h3>
|
|
<p className="text-gray-500 text-sm mb-2">{method.description}</p>
|
|
<p className="text-brand-turquoise font-medium">{method.value}</p>
|
|
</motion.a>
|
|
);
|
|
})}
|
|
</div>
|
|
</motion.div>
|
|
</section>
|
|
|
|
{/* Contact Form & Info */}
|
|
<section ref={formRef} className="py-20">
|
|
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
|
|
{/* Form */}
|
|
<motion.div
|
|
initial={{ opacity: 0, x: -50 }}
|
|
animate={isFormInView ? { opacity: 1, x: 0 } : {}}
|
|
transition={{ duration: 0.8 }}
|
|
>
|
|
<h2 className="text-3xl font-bold text-brand-navy mb-6">Envoyez-nous un message</h2>
|
|
<p className="text-gray-600 mb-8">
|
|
Remplissez le formulaire ci-dessous et nous vous répondrons dans les plus brefs délais.
|
|
</p>
|
|
|
|
{isSubmitted ? (
|
|
<motion.div
|
|
initial={{ opacity: 0, scale: 0.9 }}
|
|
animate={{ opacity: 1, scale: 1 }}
|
|
className="bg-green-50 border border-green-200 rounded-2xl p-8 text-center"
|
|
>
|
|
<div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
|
<CheckCircle2 className="w-8 h-8 text-green-600" />
|
|
</div>
|
|
<h3 className="text-2xl font-bold text-green-800 mb-2">Message envoyé !</h3>
|
|
<p className="text-green-700 mb-6">
|
|
Merci pour votre message. Notre équipe vous répondra dans les 24 heures.
|
|
</p>
|
|
<button
|
|
onClick={() => {
|
|
setIsSubmitted(false);
|
|
setFormData({
|
|
firstName: '',
|
|
lastName: '',
|
|
email: '',
|
|
company: '',
|
|
phone: '',
|
|
subject: '',
|
|
message: '',
|
|
});
|
|
}}
|
|
className="text-brand-turquoise font-medium hover:underline"
|
|
>
|
|
Envoyer un autre message
|
|
</button>
|
|
</motion.div>
|
|
) : (
|
|
<form onSubmit={handleSubmit} className="space-y-6">
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div>
|
|
<label htmlFor="firstName" className="block text-sm font-medium text-gray-700 mb-2">
|
|
Prénom *
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="firstName"
|
|
name="firstName"
|
|
required
|
|
value={formData.firstName}
|
|
onChange={handleChange}
|
|
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-brand-turquoise focus:border-transparent transition-all"
|
|
placeholder="Jean"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label htmlFor="lastName" className="block text-sm font-medium text-gray-700 mb-2">
|
|
Nom *
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="lastName"
|
|
name="lastName"
|
|
required
|
|
value={formData.lastName}
|
|
onChange={handleChange}
|
|
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-brand-turquoise focus:border-transparent transition-all"
|
|
placeholder="Dupont"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div>
|
|
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-2">
|
|
Email *
|
|
</label>
|
|
<input
|
|
type="email"
|
|
id="email"
|
|
name="email"
|
|
required
|
|
value={formData.email}
|
|
onChange={handleChange}
|
|
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-brand-turquoise focus:border-transparent transition-all"
|
|
placeholder="jean.dupont@exemple.com"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label htmlFor="phone" className="block text-sm font-medium text-gray-700 mb-2">
|
|
Téléphone
|
|
</label>
|
|
<input
|
|
type="tel"
|
|
id="phone"
|
|
name="phone"
|
|
value={formData.phone}
|
|
onChange={handleChange}
|
|
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-brand-turquoise focus:border-transparent transition-all"
|
|
placeholder="+33 6 12 34 56 78"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="company" className="block text-sm font-medium text-gray-700 mb-2">
|
|
Entreprise
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="company"
|
|
name="company"
|
|
value={formData.company}
|
|
onChange={handleChange}
|
|
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-brand-turquoise focus:border-transparent transition-all"
|
|
placeholder="Votre entreprise"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="subject" className="block text-sm font-medium text-gray-700 mb-2">
|
|
Sujet *
|
|
</label>
|
|
<select
|
|
id="subject"
|
|
name="subject"
|
|
required
|
|
value={formData.subject}
|
|
onChange={handleChange}
|
|
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-brand-turquoise focus:border-transparent transition-all"
|
|
>
|
|
{subjects.map((subject) => (
|
|
<option key={subject.value} value={subject.value}>
|
|
{subject.label}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="message" className="block text-sm font-medium text-gray-700 mb-2">
|
|
Message *
|
|
</label>
|
|
<textarea
|
|
id="message"
|
|
name="message"
|
|
required
|
|
rows={5}
|
|
value={formData.message}
|
|
onChange={handleChange}
|
|
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-brand-turquoise focus:border-transparent transition-all resize-none"
|
|
placeholder="Comment pouvons-nous vous aider ?"
|
|
/>
|
|
</div>
|
|
|
|
{error && (
|
|
<div className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-lg">
|
|
{error}
|
|
</div>
|
|
)}
|
|
|
|
<button
|
|
type="submit"
|
|
disabled={isSubmitting}
|
|
className="w-full px-8 py-4 bg-brand-turquoise text-white rounded-lg hover:bg-brand-turquoise/90 transition-all font-semibold text-lg flex items-center justify-center space-x-2 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
{isSubmitting ? (
|
|
<>
|
|
<Loader2 className="w-5 h-5 animate-spin" />
|
|
<span>Envoi en cours...</span>
|
|
</>
|
|
) : (
|
|
<>
|
|
<Send className="w-5 h-5" />
|
|
<span>Envoyer le message</span>
|
|
</>
|
|
)}
|
|
</button>
|
|
</form>
|
|
)}
|
|
</motion.div>
|
|
|
|
{/* Offices */}
|
|
<motion.div
|
|
initial={{ opacity: 0, x: 50 }}
|
|
animate={isFormInView ? { opacity: 1, x: 0 } : {}}
|
|
transition={{ duration: 0.8, delay: 0.2 }}
|
|
>
|
|
<h2 className="text-3xl font-bold text-brand-navy mb-6">Nos bureaux</h2>
|
|
<p className="text-gray-600 mb-8">
|
|
Retrouvez-nous dans nos bureaux à travers l'Europe ou contactez-nous par email.
|
|
</p>
|
|
|
|
<div className="space-y-6">
|
|
{offices.map((office, index) => (
|
|
<div
|
|
key={index}
|
|
className={`bg-white p-6 rounded-2xl border-2 transition-all ${
|
|
office.isHQ
|
|
? 'border-brand-turquoise shadow-lg'
|
|
: 'border-gray-200 hover:border-brand-turquoise/50'
|
|
}`}
|
|
>
|
|
<div className="flex items-start space-x-4">
|
|
<div
|
|
className={`w-12 h-12 rounded-xl flex items-center justify-center flex-shrink-0 ${
|
|
office.isHQ ? 'bg-brand-turquoise' : 'bg-gray-100'
|
|
}`}
|
|
>
|
|
<Building2 className={`w-6 h-6 ${office.isHQ ? 'text-white' : 'text-gray-600'}`} />
|
|
</div>
|
|
<div className="flex-1">
|
|
<div className="flex items-center space-x-2 mb-2">
|
|
<h3 className="text-xl font-bold text-brand-navy">{office.city}</h3>
|
|
{office.isHQ && (
|
|
<span className="px-2 py-1 bg-brand-turquoise/10 text-brand-turquoise text-xs font-medium rounded-full">
|
|
Siège social
|
|
</span>
|
|
)}
|
|
</div>
|
|
<div className="space-y-2 text-gray-600">
|
|
<div className="flex items-center space-x-2">
|
|
<MapPin className="w-4 h-4 text-gray-400" />
|
|
<span>{office.address}</span>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<span className="text-gray-400 ml-6">{office.postalCode}</span>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<Phone className="w-4 h-4 text-gray-400" />
|
|
<a href={`tel:${office.phone.replace(/\s/g, '')}`} className="hover:text-brand-turquoise">
|
|
{office.phone}
|
|
</a>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<Mail className="w-4 h-4 text-gray-400" />
|
|
<a href={`mailto:${office.email}`} className="hover:text-brand-turquoise">
|
|
{office.email}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Hours */}
|
|
<div className="mt-8 bg-gray-50 p-6 rounded-2xl">
|
|
<div className="flex items-center space-x-3 mb-4">
|
|
<Clock className="w-6 h-6 text-brand-turquoise" />
|
|
<h3 className="text-lg font-bold text-brand-navy">Horaires d'ouverture</h3>
|
|
</div>
|
|
<div className="space-y-2 text-gray-600">
|
|
<div className="flex justify-between">
|
|
<span>Lundi - Vendredi</span>
|
|
<span className="font-medium">9h00 - 18h00</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span>Samedi</span>
|
|
<span className="font-medium">10h00 - 14h00</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span>Dimanche</span>
|
|
<span className="font-medium text-gray-400">Fermé</span>
|
|
</div>
|
|
</div>
|
|
<p className="mt-4 text-sm text-gray-500">
|
|
* Support technique disponible 24/7 pour les clients Enterprise
|
|
</p>
|
|
</div>
|
|
</motion.div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Map Section */}
|
|
<section className="py-20 bg-gray-50">
|
|
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 30 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true }}
|
|
transition={{ duration: 0.8 }}
|
|
className="text-center mb-12"
|
|
>
|
|
<h2 className="text-3xl font-bold text-brand-navy mb-4">Notre présence en Europe</h2>
|
|
<p className="text-gray-600">
|
|
Des bureaux stratégiquement situés pour mieux vous servir
|
|
</p>
|
|
</motion.div>
|
|
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 30 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true }}
|
|
transition={{ duration: 0.8, delay: 0.2 }}
|
|
className="bg-white rounded-2xl shadow-lg overflow-hidden"
|
|
>
|
|
<div className="aspect-[21/9] bg-gradient-to-br from-brand-navy/5 to-brand-turquoise/5 flex items-center justify-center">
|
|
<div className="text-center">
|
|
<MapPin className="w-16 h-16 text-brand-turquoise mx-auto mb-4" />
|
|
<p className="text-gray-500">Carte interactive bientôt disponible</p>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
</div>
|
|
</section>
|
|
|
|
<LandingFooter />
|
|
</div>
|
|
);
|
|
}
|