[go: up one dir, main page]

0% found this document useful (0 votes)
122 views17 pages

Type Error in React Contact Page

The document describes a TypeScript error encountered during deployment, specifically related to properties 'center' and 'attribution' not existing on certain types in a React component. It includes a detailed code snippet that outlines the structure of a ContactPage component, which features form handling, chat functionality, and service listings. The document also highlights the use of various libraries such as React, axios, and react-leaflet for UI and API interactions.

Uploaded by

abelbeingar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
122 views17 pages

Type Error in React Contact Page

The document describes a TypeScript error encountered during deployment, specifically related to properties 'center' and 'attribution' not existing on certain types in a React component. It includes a detailed code snippet that outlines the structure of a ContactPage component, which features form handling, chat functionality, and service listings. The document also highlights the use of various libraries such as React, axios, and react-leaflet for UI and API interactions.

Uploaded by

abelbeingar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Voici l'erreur que j'ai lors du deploiement : Property 'center' does not exist on type

'IntrinsicAttributes & MapContainerProps & RefAttributes'.


src/components/pages/[Link](770,19): error TS2322: Type '{ url: string; attribution:
string; }' is not assignable to type 'IntrinsicAttributes & TileLayerProps & RefAttributes'.
Property 'attribution' does not exist on type 'IntrinsicAttributes & TileLayerProps &
RefAttributes'.

Voici le code : import React, { useState, useEffect, useCallback, FormEvent, ChangeEvent,


memo } from "react"; import { motion, AnimatePresence } from "framer-motion"; import
{ MapPin, Phone, Mail, Send, Facebook, Instagram, Linkedin, HelpCircle, Users, Star, Clock,
Package, Truck, Shield, MessageCircle, ChevronDown, Globe, Award, Link, } from "lucide-
react"; import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet"; import
ThemeToggle from './ThemeToggle'; import Slider from "react-slick"; import
"leaflet/dist/[Link]"; import "slick-carousel/slick/[Link]"; import "slick-
carousel/slick/[Link]"; import axios from "axios"; import { toast } from "react-
toastify"; import Navbar from "./Navbar"; import Footer from "./Footer"; import
CookieConsentBanner from "../cookies/CookieConsentBanner"; import { collection, query,
orderBy, onSnapshot, addDoc, Timestamp } from "firebase/firestore"; import { db } from
"../firebase/firebaseConfig";

// Type definitions interface ContactFormData { name: string; email: string; subject: string;
message: string; }

interface FormErrors { name?: string; email?: string; subject?: string; message?: string; }

interface SocialLink { Icon: [Link]<{ size?: number; className?: string }>;


link: string; label: string; }

interface Testimonial { quote: string; author: string; role: string; rating: number; }

interface FAQ { question: string; answer: string; }

interface Service { title: string; description: string; Icon:


[Link]<{ className?: string }>; }

interface ChatMessage { id: string; userId: string; message: string; timestamp: any; }

interface ErrorBoundaryProps { children: [Link]; }

interface ErrorBoundaryState { hasError: boolean; }

// Error Boundary Component class ErrorBoundary extends


[Link]<ErrorBoundaryProps, ErrorBoundaryState> { state = { hasError: false };

static getDerivedStateFromError() { return { hasError: true }; }


render() { if ([Link]) { return (

Une erreur s'est produite.


Veuillez recharger la page ou contacter le support.

); } return [Link]; } }

const ContactPage: [Link] = memo(() => { const [formData, setFormData] =


useState({ name: "", email: "", subject: "", message: "", }); const [formErrors, setFormErrors]
= useState({}); const [isSubmitting, setIsSubmitting] = useState(false); const [activeFaq,
setActiveFaq] = useState<number | null>(null); const [showChat, setShowChat] =
useState(false); const [chatMessages, setChatMessages] = useState<ChatMessage[]>([]);
const [chatInput, setChatInput] = useState(""); const [newsletterEmail, setNewsletterEmail] =
useState(""); const [language, setLanguage] = useState("fr"); const [darkMode,
setDarkMode] = useState(false);

useEffect(() => { [Link]('dark', darkMode); },


[darkMode]);

const toggleDarkMode = () => { setDarkMode((prev) => !prev); };

// Form Validation const validateForm = useCallback(() => { const errors: FormErrors = {}; if
(![Link]()) [Link] = "Le nom est requis"; if (![Link]())
{ [Link] = "L'email est requis"; } else if (!/\S+@\S+.\S+/.test([Link]))
{ [Link] = "L'email est invalide"; } if (![Link]) [Link] = "Le sujet est
requis"; if (![Link]()) [Link] = "Le message est requis";
setFormErrors(errors); return [Link](errors).length === 0; }, [formData]);

const handleInputChange = ( e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement


| HTMLSelectElement> ) => { const { name, value } = [Link]; setFormData((prev) =>
({ ...prev, [name]: value })); setFormErrors((prev) => ({ ...prev, [name]: undefined })); };

const handleSubmit = async (e: FormEvent) => { [Link](); if (!validateForm())


return; setIsSubmitting(true);

try {
const userId = [Link]('userId') || '';
const response = await
[Link]("[Link] {
name: [Link],
email: [Link],
message: [Link],
userId,
});
[Link]([Link], {
position: "top-right",
autoClose: 3000,
});
setFormData({ name: "", email: "", subject: "", message: "" });
} catch (error: any) {
[Link]("Erreur lors de l'envoi du message:", error);
const errorMessage = [Link]?.data?.error || "Erreur lors de
l'envoi du message.";
[Link](errorMessage, {
position: "top-right",
autoClose: 3000,
});
} finally {
setIsSubmitting(false);
}

};

// Newsletter Signup const handleNewsletterSubmit = async (e: FormEvent) =>


{ [Link](); if (!/\S+@\S+.\S+/.test(newsletterEmail)) { [Link]("Veuillez entrer un
email valide.", { position: "top-right", autoClose: 3000, }); return; } try { const response =
await [Link]("[Link] { email: newsletterEmail, });
[Link]([Link], { position: "top-right", autoClose: 3000, });
setNewsletterEmail(""); } catch (error: any) { [Link]("Erreur lors de l'inscription :",
error); const errorMessage = [Link]?.data?.error || "Erreur lors de l'inscription à la
newsletter."; [Link](errorMessage, { position: "top-right", autoClose: 3000, }); } };

// Chat Functionality useEffect(() => { if (!showChat) return; const q = query(collection(db,


"chat_messages"), orderBy("timestamp", "asc")); const unsubscribe = onSnapshot(q,
(snapshot) => { const messages = [Link]((doc) => ({ id: [Link], ...[Link](), }))
as ChatMessage[]; setChatMessages(messages); }); return () => unsubscribe(); },
[showChat]);

const sendChatMessage = async () => { if (![Link]()) return; try { await


addDoc(collection(db, "chat_messages"), { userId: [Link]('userId') ||
'anonymous', message: chatInput, timestamp: [Link](), }); setChatInput(""); } catch
(error) { [Link]("Erreur lors de l'envoi du message :", error); [Link]("Erreur lors de
l'envoi du message de chat.", { position: "top-right", autoClose: 3000, }); } };

// FAQ Toggle const toggleFaq = (index: number) => { setActiveFaq(activeFaq === index ?
null : index); };

// Language Switcher (Placeholder) const switchLanguage = (lang: string) =>


{ setLanguage(lang); };

const renderSection = ( title: string, content: [Link], Icon:


[Link]<{ className?: string }>, index: number ) => ( <[Link]
initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} transition={{ duration: 0.5, delay:
index * 0.1 }} className="bg-white rounded-2xl p-6 border border-gray-200 dark:bg-gray-
800/70 dark:border-gray-700 hover:border-blue-500 dark:hover:border-blue-400 transition-all
shadow-md hover:shadow-lg w-full sm:w-auto" >
{title}
{content} </[Link]> );

const socialLinks: SocialLink[] = [ { Icon: Facebook, link:


"[Link] label: "Facebook" }, { Icon:
Instagram, link: "[Link] label: "Instagram" }, { Icon:
Linkedin, link: "[Link] label: "LinkedIn" }, ];

const testimonials: Testimonial[] = [ { quote: "Service rapide et fiable ! Ma commande est


arrivée en parfait état.", author: "Jean D.", role: "Client", rating: 5 }, { quote: "Devenir coursier
était simple grâce à leur équipe supportive.", author: "Marie L.", role: "Coursier", rating: 4 },
{ quote: "Support client exceptionnel, ils ont répondu en quelques minutes !", author: "Paul
K.", role: "Client", rating: 5 }, ];

const faqs: FAQ[] = [ { question: "Comment suivre ma commande ?", answer: "Connectez-
vous à votre compte sur notre site et accédez à l'onglet 'Suivi' pour voir l'état de votre
commande en temps réel." }, { question: "Comment devenir coursier ?", answer:
"Remplissez le formulaire d'inscription dans la section 'Devenir Coursier' et notre équipe
vous contactera pour les prochaines étapes." }, { question: "Que faire en cas de problème de
livraison ?", answer: "Contactez notre support client via le formulaire ou par téléphone au
+229 0159334483. Nous résoudrons votre problème rapidement." }, { question: "Quels sont
les délais de livraison ?", answer: "Les délais varient selon la destination, mais notre service
express garantit une livraison en 2 à 4 heures dans Cotonou." }, ];

const services: Service[] = [ { title: "Livraison Rapide", description: "Recevez vos colis en
quelques heures avec notre service express.", Icon: Truck }, { title: "Sécurité Garantie",
description: "Assurance incluse pour protéger vos colis précieux.", Icon: Shield }, { title:
"Suivi en Temps Réel", description: "Suivez votre commande à chaque étape de la
livraison.", Icon: MapPin }, { title: "Support 24/7", description: "Notre équipe est disponible à
tout moment pour vous aider.", Icon: MessageCircle }, ];

const sliderSettings = { dots: true, infinite: true, speed: 500, slidesToShow: 2, slidesToScroll:
1, autoplay: true, autoplaySpeed: 4000, centerMode: true, centerPadding: "20px",
responsive: [ { breakpoint: 1024, settings: { slidesToShow: 2, centerPadding: "20px" } },
{ breakpoint: 768, settings: { slidesToShow: 1, centerMode: true, centerPadding: "10px" } },
{ breakpoint: 480, settings: { slidesToShow: 1, centerMode: true, centerPadding: "5px" } }, ],
lazyLoad: "ondemand" as const, };

return (

{/* Header */} <[Link] initial={{ opacity: 0, y: -50 }} animate={{ opacity: 1, y: 0 }}


transition={{ duration: 0.6 }} className="text-center mb-12 sm:mb-16 flex flex-col items-
center" >

Contactez Dynamism Express


Besoin d'aide ou envie de collaborer ? Notre équipe est là pour vous, 24/7.
Suivre une Commande Devenir Coursier
</[Link]>
{/* Language Switcher */}
<[Link]
initial={{ opacity: 0, x: 20 }}
whileInView={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5 }}
className="flex justify-center sm:justify-end mb-8"
>
<div className="flex items-center space-x-2 bg-gray-100 dark:bg-
gray-800 rounded-full p-2">
<Globe className="w-5 h-5 text-blue-600 dark:text-blue-400" />
<select
onChange={(e) => switchLanguage([Link])}
value={language}
className="bg-transparent text-gray-900 dark:text-gray-100
focus:outline-none"
aria-label="Sélectionner la langue"
>
<option value="fr">Français</option>
<option value="en">English</option>
</select>
</div>
</[Link]>

{/* Services Section */}


<[Link]
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
transition={{ duration: 0.5 }}
className="mb-12 sm:mb-16 flex flex-col items-center"
aria-labelledby="services-title"
>
<h2 id="services-title" className="text-2xl sm:text-3xl font-bold
text-gray-900 dark:text-gray-100 mb-6 sm:mb-8 text-center">
Pourquoi Choisir Dynamism Express ?
</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4
gap-4 sm:gap-6 w-full justify-items-center">
{[Link]((service, index) => (
<[Link]
key={index}
initial={{ opacity: 0, scale: 0.95 }}
whileInView={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.5, delay: index * 0.1 }}
whileHover={{ y: -5 }}
className="bg-white dark:bg-gray-800/70 rounded-lg p-6
border border-gray-200 dark:border-gray-600 hover:border-blue-500
dark:hover:border-blue-400 transition-all shadow-md hover:shadow-lg w-full
max-w-sm"
>
<[Link] className="w-10 h-10 sm:w-12 sm:h-12 text-
blue-600 dark:text-blue-400 mb-4 mx-auto" />
<h3 className="text-base sm:text-lg font-semibold text-
center text-gray-800 dark:text-gray-100 mb-2">
{[Link]}
</h3>
<p className="text-sm text-gray-600 dark:text-gray-300 text-
center">
{[Link]}
</p>
</[Link]>
))}
</div>
</[Link]>

<div className="grid grid-cols-1 md:grid-cols-2 gap-6 sm:gap-8 mb-12


sm:mb-16 w-full">
{/* Contact Form */}
<[Link]
initial={{ opacity: 0, x: -20 }}
whileInView={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5 }}
className="bg-white dark:bg-gray-800/20 rounded-lg p-6 sm:p-8
border border-gray-200 dark:border-gray-700 shadow-lg w-full mx-auto"
aria-labelledby="contact-form-title"
>
<h2 id="contact-form-title" className="text-xl sm:text-2xl font-
semibold text-gray-900 dark:text-white mb-4 sm:mb-6 flex items-center
justify-center">
<Send className="mr-2 sm:mr-3 text-blue-600 dark:text-blue-400
w-5 h-5 sm:w-6 sm:h-6" />
Envoyez-nous un Message
</h2>
<form onSubmit={handleSubmit} className="space-y-4" aria-
label="Formulaire de contact">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label htmlFor="name" className="block text-sm font-medium
text-gray-700 dark:text-gray-200">
Nom Complet
</label>
<input
type="text"
id="name"
name="name"
placeholder="Votre nom"
value={[Link]}
onChange={handleInputChange}
required
aria-label="Nom complet"
className="mt-1 w-full px-3 sm:px-4 py-2 rounded-lg
border border-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:ring-2
focus:ring-blue-500 dark:focus:ring-blue-400 transition-all text-gray-900
dark:text-gray-100 placeholder-gray-500"
/>
{[Link] && (
<p className="text-red-500 dark:text-red-400 text-sm mt-
1">{[Link]}</p>
)}
</div>
<div>
<label htmlFor="email" className="block text-sm font-
medium text-gray-700 dark:text-gray-200">
Adresse Email
</label>
<input
type="email"
id="email"
name="email"
placeholder="votre@[Link]"
value={[Link]}
onChange={handleInputChange}
required
aria-label="Adresse e-mail"
className="mt-1 w-full px-3 sm:px-4 py-2 rounded-lg
border border-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:ring-2
focus:ring-blue-500 dark:focus:ring-blue-400 transition-all text-gray-900
dark:text-gray-100 placeholder-gray-500"
/>
{[Link] && (
<p className="text-red-500 dark:text-red-400 text-sm mt-
1">{[Link]}</p>
)}
</div>
</div>
<div>
<label htmlFor="subject" className="block text-sm font-
medium text-gray-700 dark:text-gray-200">
Sujet
</label>
<select
id="subject"
name="subject"
value={[Link]}
onChange={handleInputChange}
required
aria-label="Sélectionner un sujet"
className="mt-1 w-full px-3 sm:px-4 py-2 rounded-lg border
border-gray-300 dark:bg-gray-700 dark:border-gray-600 focus:ring-2
focus:ring-blue-500 dark:focus:ring-blue-400 transition-all text-gray-900
dark:text-gray-100"
>
<option value="" className="bg-white dark:bg-gray-900">
Sélectionner un sujet
</option>
<option value="support" className="bg-white dark:bg-gray-
900">
Support Client
</option>
<option value="livraison" className="bg-white dark:bg-
gray-900">
Problème de Livraison
</option>
<option value="devenir-coursier" className="bg-white
dark:bg-gray-900">
Devenir Coursier
</option>
<option value="autre" className="bg-white dark:bg-gray-
900">
Autre
</option>
</select>
{[Link] && (
<p className="text-red-500 dark:text-red-400 text-sm mt-
1">{[Link]}</p>
)}
</div>
<div>
<label htmlFor="message" className="block text-sm font-
medium text-gray-700 dark:text-gray-200">
Message
</label>
<textarea
id="message"
name="message"
placeholder="Votre message ici..."
value={[Link]}
onChange={handleInputChange}
required
rows={5}
aria-label="Message"
className="mt-1 w-full px-3 sm:px-4 py-2 rounded-lg border
border-gray-300 dark:bg-gray-700 dark:border-gray-600 focus:ring-2
focus:ring-blue-500 dark:focus:ring-blue-400 transition-all text-gray-900
dark:text-gray-100 placeholder-gray-500"
/>
{[Link] && (
<p className="text-red-500 dark:text-red-400 text-sm mt-
1">{[Link]}</p>
)}
</div>
<button
type="submit"
disabled={isSubmitting}
className="w-full bg-blue-600 dark:bg-blue-500 hover:bg-
blue-700 dark:hover:bg-blue-600 text-white py-2 sm:py-3 rounded-lg
transition-all disabled:opacity-50 disabled:cursor-not-allowed focus:ring-
2 focus:ring-blue-500 focus:ring-offset-2"
aria-label="Envoyer le message"
>
{isSubmitting ? "Envoi en cours..." : "Envoyer le Message"}
</button>
</form>
</[Link]>

{/* Contact Information */}


<div className="space-y-6 flex flex-col items-center md:items-
start w-full">
{renderSection(
"Nos Coordonnées",
<div className="space-y-4 text-gray-700 dark:text-gray-200
text-center md:text-left">
<div className="flex items-center space-x-3 justify-center
md:justify-start">
<MapPin className="w-5 h-5 sm:w-6 sm:h-6 text-blue-600
dark:text-blue-400" />
<span>Quartier Cadjehoun, Cotonou, Bénin</span>
</div>
<div className="flex items-center space-x-3 justify-center
md:justify-start">
<Phone className="w-5 h-5 sm:w-6 sm:h-6 text-blue-600
dark:text-blue-400" />
<a
href="[Link]
className="hover:text-blue-500 dark:hover:text-blue-300
transition"
aria-label="Appeler le support"
>
+229 0159334483
</a>
</div>
<div className="flex items-center space-x-3 justify-center
md:justify-start">
<Mail className="w-5 h-5 sm:w-6 sm:h-6 text-blue-600
dark:text-blue-400" />
<a
href="[Link]
className="hover:text-blue-500 dark:hover:text-blue-300
transition"
aria-label="Envoyer un email"
>
abelbeingar@[Link]
</a>
</div>
<div className="flex items-center space-x-3 justify-center
md:justify-start">
<Clock className="w-5 h-5 sm:w-6 sm:h-6 text-blue-600
dark:text-blue-400" />
<span>Lun-Sam, 8h-20h</span>
</div>
</div>,
Phone,
0
)}

{renderSection(
"Questions Fréquentes",
<div className="space-y-2 w-full">
{[Link]((faq, index) => (
<[Link]
key={index}
initial={{ height: 0, opacity: 0 }}
animate={{ height: activeFaq === index ? "auto" :
"auto", opacity: 1 }}
className="border-b border-gray-200 dark:border-gray-
700"
>
<button
onClick={() => toggleFaq(index)}
className="flex items-center justify-between w-full
py-3 text-left text-gray-700 dark:text-gray-200 hover:text-blue-600
dark:hover:text-blue-400 transition text-sm sm:text-base"
aria-expanded={activeFaq === index}
aria-controls={`faq-${index}`}
>
<div className="flex items-center space-x-2">
<HelpCircle className="w-4 h-4 sm:w-5 sm:h-5 text-
blue-600 dark:text-blue-400" />
<span>{[Link]}</span>
</div>
<ChevronDown
className={`w-4 h-4 sm:w-5 sm:h-5 transition-
transform ${activeFaq === index ? "rotate-180" : ""}`}
/>
</button>
<AnimatePresence>
{activeFaq === index && (
<[Link]
id={`faq-${index}`}
initial={{ height: 0, opacity: 0 }}
animate={{ height: "auto", opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ duration: 0.3 }}
className="pb-3 text-gray-600 dark:text-gray-300
text-sm"
>
{[Link]}
</[Link]>
)}
</AnimatePresence>
</[Link]>
))}
</div>,
HelpCircle,
1
)}

{renderSection(
"Suivez-nous",
<div className="flex space-x-6 justify-center">
{[Link](({ Icon, link, label }, index) => (
<a
key={index}
href={link}
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 dark:text-blue-400 hover:text-
blue-500 dark:hover:text-blue-300 transition"
aria-label={`Suivez-nous sur ${label}`}
>
<Icon size={24} className="transition-transform
hover:scale-110" />
</a>
))}
</div>,
Users,
2
)}
</div>
</div>

{/* Trust Signals */}


<[Link]
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
transition={{ duration: 0.5 }}
className="my-12 sm:my-16 text-center flex flex-col items-center"
aria-labelledby="trust-signals-title"
>
<h2 id="trust-signals-title" className="text-2xl sm:text-3xl font-
bold text-gray-900 dark:text-gray-100 mb-6 sm:mb-8">
La Confiance de Nos Clients
</h2>
<div className="flex flex-col sm:flex-row flex-wrap justify-center
gap-6 sm:gap-8 w-full max-w-3xl">
<div className="flex items-center space-x-3">
<Award className="w-8 h-8 sm:w-10 sm:h-10 text-blue-600
dark:text-blue-400" />
<div>
<p className="text-base sm:text-lg font-semibold text-gray-
900 dark:text-gray-100">Certifié ISO 9001</p>
<p className="text-sm text-gray-600 dark:text-gray-
300">Qualité garantie</p>
</div>
</div>
<div className="flex items-center space-x-3">
<Users className="w-8 h-8 sm:w-10 sm:h-10 text-blue-600
dark:text-blue-400" />
<div>
<p className="text-base sm:text-lg font-semibold text-gray-
900 dark:text-gray-100">+10,000 Clients</p>
<p className="text-sm text-gray-600 dark:text-gray-
300">Satisfaits</p>
</div>
</div>
<div className="flex items-center space-x-3">
<Star className="w-8 h-8 sm:w-10 sm:h-10 text-blue-600
dark:text-blue-400" />
<div>
<p className="text-base sm:text-lg font-semibold text-gray-
900 dark:text-gray-100">4.8/5 Étoiles</p>
<p className="text-sm text-gray-600 dark:text-gray-300">Sur
Trustpilot</p>
</div>
</div>
</div>
</[Link]>

{/* Testimonials Carousel */}


<[Link]
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
transition={{ duration: 0.5 }}
className="my-12 sm:my-16 flex flex-col items-center"
aria-labelledby="testimonials-title"
>
<h2
id="testimonials-title"
className="text-2xl sm:text-3xl font-bold text-gray-900
dark:text-gray-100 mb-6 sm:mb-8 text-center flex items-center justify-
center"
>
<Star className="mr-2 sm:mr-3 text-blue-600 dark:text-blue-400
w-5 h-5 sm:w-6 sm:h-6" />
Ce Que Disent Nos Clients
</h2>
<div className="w-full max-w-4xl">
<Slider {...sliderSettings}>
{[Link](({ quote, author, role, rating }, index) =>
(
<div key={index} className="px-2 sm:px-4">
<div className="bg-white dark:bg-gray-800/70 p-4 sm:p-6
rounded-lg border border-gray-200 dark:border-gray-600 shadow-md">
<div className="flex items-center mb-2">
{[...Array(5)].map((_, i) => (
<Star
key={i}
className={`w-3 h-3 sm:w-4 sm:h-4 ${i < rating ?
"text-yellow-400" : "text-gray-300"}`}
fill={i < rating ? "currentColor" : "none"}
/>
))}
</div>
<p className="italic text-gray-700 dark:text-gray-200
mb-4 text-sm sm:text-base">"{quote}"</p>
<p className="text-right text-xs sm:text-sm text-gray-
600 dark:text-gray-300">
- {author}, <span className="font-
semibold">{role}</span>
</p>
</div>
</div>
))}
</Slider>
</div>
</[Link]>

{/* Newsletter Signup */}


<[Link]
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
className="my-12 sm:my-16 bg-blue-600 dark:bg-blue-500 text-white
rounded-lg p-6 sm:p-8 text-center flex flex-col items-center"
aria-labelledby="newsletter-title"
>
<h2 id="newsletter-title" className="text-xl sm:text-2xl font-bold
mb-4">
Restez Informé !
</h2>
<p className="text-blue-100 mb-6 text-sm sm:text-base max-w-md">
Inscrivez-vous à notre newsletter pour recevoir des mises à jour
et des offres exclusives.
</p>
<form onSubmit={handleNewsletterSubmit} className="flex flex-col
sm:flex-row w-full max-w-md mx-auto gap-2 sm:gap-0">
<input
type="email"
value={newsletterEmail}
onChange={(e) => setNewsletterEmail([Link])}
placeholder="Votre email"
className="flex-1 px-3 sm:px-4 py-2 rounded-t-lg sm:rounded-l-
lg sm:rounded-t-none bg-blue-100 dark:bg-blue-200 text-gray-900
focus:outline-none"
aria-label="Adresse email pour la newsletter"
/>
<button
type="submit"
className="px-4 sm:px-6 py-2 bg-blue-800 hover:bg-blue-900
rounded-b-lg sm:rounded-r-lg sm:rounded-b-none transition-all"
aria-label="S'inscrire à la newsletter"
>
S'inscrire
</button>
</form>
</[Link]>

{/* Leaflet Map */}


<[Link]
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
className="my-12 sm:my-16 bg-white dark:bg-gray-800/70 rounded-lg
border border-gray-200 dark:border-gray-600 shadow-md overflow-hidden w-
full"
aria-labelledby="map-title"
>
<h2 id="map-title" className="text-xl sm:text-2xl font-semibold
text-gray-900 dark:text-gray-100 p-4 sm:p-6 text-center">
Où Nous Trouver
</h2>
<div className="h-80 sm:h-96 w-full">
<MapContainer
center={[6.36536, 2.41808]}
zoom={15}
style={{ height: "100%", width: "100%" }}
scrollWheelZoom={false}
>
<TileLayer
url="[Link]
attribution='© <a
href="[Link]
contributors'
/>
<Marker position={[6.36536, 2.41808]}>
<Popup>
<div className="text-center">
<h3 className="font-bold text-gray-900 dark:text-gray-
100">Dynamism Express</h3>
<p className="text-gray-700 dark:text-gray-200">Quartier
Cadjehoun, Cotonou, Bénin</p>
</div>
</Popup>
</Marker>
</MapContainer>
</div>
</[Link]>
</div>

{/* Chat Widget */}


<AnimatePresence>
{showChat && (
<[Link]
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 20 }}
className="fixed bottom-20 right-4 sm:right-6 bg-white dark:bg-
gray-800 rounded-lg p-4 sm:p-6 w-[90%] sm:w-80 max-h-[400px] shadow-lg
border border-gray-200 dark:border-gray-600"
role="dialog"
aria-labelledby="chat-title"
>
<h3 id="chat-title" className="text-base sm:text-lg font-
semibold text-gray-900 dark:text-gray-100 mb-4">
Chat en Direct
</h3>
<div className="h-40 sm:h-48 overflow-y-auto mb-4 space-y-2">
{[Link]((msg) => (
<div
key={[Link]}
className="bg-gray-100 dark:bg-gray-700 rounded-lg p-2
text-xs sm:text-sm text-gray-900 dark:text-gray-100"
>
<p>{[Link]}</p>
<p className="text-xs text-gray-500 dark:text-gray-400">
{new Date([Link]()).toLocaleTimeString()}
</p>
</div>
))}
</div>
<div className="flex gap-2">
<input
type="text"
value={chatInput}
onChange={(e) => setChatInput([Link])}
placeholder="Tapez votre message..."
className="flex-1 px-2 sm:px-3 py-1 sm:py-2 rounded-lg
border border-gray-300 dark:border-gray-600 dark:bg-gray-700 text-gray-900
dark:text-gray-100 focus:ring-2 focus:ring-blue-500 text-sm"
aria-label="Message de chat"
/>
<button
onClick={sendChatMessage}
className="px-3 sm:px-4 py-1 sm:py-2 bg-blue-600 dark:bg-
blue-500 hover:bg-blue-700 dark:hover:bg-blue-600 rounded-lg text-white"
aria-label="Envoyer le message de chat"
>
<Send className="w-3 h-3 sm:w-4 sm:h-4" />
</button>
</div>
<button
onClick={() => setShowChat(false)}
className="mt-4 text-blue-600 dark:text-blue-400
hover:underline text-xs sm:text-sm"
aria-label="Fermer le chat"
>
Fermer
</button>
</[Link]>
)}
</AnimatePresence>

{/* Chat Button */}


<[Link]
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
onClick={() => setShowChat(!showChat)}
className="fixed bottom-4 sm:bottom-6 right-4 sm:right-6 bg-blue-600
dark:bg-blue-500 text-white p-3 sm:p-4 rounded-full shadow-lg hover:bg-
blue-700 dark:hover:bg-blue-600 transition-all focus:ring-2 focus:ring-
blue-500 focus:ring-offset-2"
aria-label={showChat ? "Fermer le chat en direct" : "Ouvrir le chat
en direct"}
>
<MessageCircle className="w-5 h-5 sm:w-6 sm:h-6" />
</[Link]>

<Footer />
<CookieConsentBanner />
</div>
</ErrorBoundary>

); });

export default ContactPage;

You might also like