first commit
This commit is contained in:
96
components/infos/ContactSection.tsx
Normal file
96
components/infos/ContactSection.tsx
Normal file
@ -0,0 +1,96 @@
|
||||
"use client";
|
||||
|
||||
import { Mail, Phone, MapPin, Clock } from "lucide-react";
|
||||
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
||||
import { config } from "@/lib/config";
|
||||
|
||||
export default function ContactSection() {
|
||||
const contact = config.contact;
|
||||
|
||||
return (
|
||||
<Card className="bg-secondary">
|
||||
<CardHeader>
|
||||
<CardTitle>Nous contacter</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{contact.phone && (
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="bg-primary/10 p-2 rounded-xl">
|
||||
<Phone className="h-5 w-5 text-primary" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="text-sm text-gray-600 mb-1">Téléphone</p>
|
||||
<a
|
||||
href={`tel:${contact.phone.replace(/\s/g, "")}`}
|
||||
className="text-primary font-semibold hover:underline"
|
||||
>
|
||||
{contact.phone}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{contact.whatsapp && (
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="bg-green-100 p-2 rounded-xl">
|
||||
<Phone className="h-5 w-5 text-green-600" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="text-sm text-gray-600 mb-1">WhatsApp</p>
|
||||
<a
|
||||
href={`https://wa.me/${contact.whatsapp.replace(/[^\d]/g, "")}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-green-600 font-semibold hover:underline"
|
||||
>
|
||||
{contact.whatsapp}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{contact.email && (
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="bg-primary/10 p-2 rounded-xl">
|
||||
<Mail className="h-5 w-5 text-primary" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="text-sm text-gray-600 mb-1">Email</p>
|
||||
<a
|
||||
href={`mailto:${contact.email}`}
|
||||
className="text-primary font-semibold hover:underline break-all"
|
||||
>
|
||||
{contact.email}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{contact.address && (
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="bg-primary/10 p-2 rounded-xl">
|
||||
<MapPin className="h-5 w-5 text-primary" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="text-sm text-gray-600 mb-1">Adresse</p>
|
||||
<p className="text-gray-700 font-medium">{contact.address}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{contact.hours && (
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="bg-primary/10 p-2 rounded-xl">
|
||||
<Clock className="h-5 w-5 text-primary" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="text-sm text-gray-600 mb-1">Horaires</p>
|
||||
<p className="text-gray-700 font-medium">{contact.hours}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
80
components/infos/FAQAccordion.tsx
Normal file
80
components/infos/FAQAccordion.tsx
Normal file
@ -0,0 +1,80 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState, useMemo } from "react";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
AccordionContent,
|
||||
} from "@/components/ui/accordion";
|
||||
import { FAQItem } from "@/app/api/infos/route";
|
||||
|
||||
export default function FAQAccordion() {
|
||||
const [faqItems, setFaqItems] = useState<FAQItem[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchFAQ = async () => {
|
||||
try {
|
||||
const response = await fetch("/api/infos?type=faq");
|
||||
const data = await response.json();
|
||||
setFaqItems(data);
|
||||
} catch (error) {
|
||||
console.error("Erreur lors du chargement de la FAQ:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchFAQ();
|
||||
}, []);
|
||||
|
||||
// Grouper les FAQ par catégorie
|
||||
const faqByCategory = useMemo(() => {
|
||||
const grouped: Record<string, FAQItem[]> = {};
|
||||
faqItems.forEach((item) => {
|
||||
const category = item.category || "Autres";
|
||||
if (!grouped[category]) {
|
||||
grouped[category] = [];
|
||||
}
|
||||
grouped[category].push(item);
|
||||
});
|
||||
return grouped;
|
||||
}, [faqItems]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center py-8">
|
||||
<p className="text-gray-600">Chargement...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{Object.entries(faqByCategory).map(([category, items]) => (
|
||||
<div key={category} className="space-y-3">
|
||||
<h3 className="text-lg font-semibold text-primary flex items-center gap-2">
|
||||
{items[0]?.icon && <span>{items[0].icon}</span>}
|
||||
<span>{category}</span>
|
||||
</h3>
|
||||
<Accordion type="single" collapsible className="w-full">
|
||||
{items.map((item) => (
|
||||
<AccordionItem key={item.id} value={item.id}>
|
||||
<AccordionTrigger className="text-left">
|
||||
{item.question}
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<p className="text-gray-700 leading-relaxed whitespace-pre-line">
|
||||
{item.answer}
|
||||
</p>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
))}
|
||||
</Accordion>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
63
components/infos/LexiqueSection.tsx
Normal file
63
components/infos/LexiqueSection.tsx
Normal file
@ -0,0 +1,63 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
||||
import { LexiqueItem } from "@/app/api/infos/route";
|
||||
|
||||
export default function LexiqueSection() {
|
||||
const [lexiqueItems, setLexiqueItems] = useState<LexiqueItem[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchLexique = async () => {
|
||||
try {
|
||||
const response = await fetch("/api/infos?type=lexique");
|
||||
const data = await response.json();
|
||||
setLexiqueItems(data);
|
||||
} catch (error) {
|
||||
console.error("Erreur lors du chargement du lexique:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchLexique();
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center py-8">
|
||||
<p className="text-gray-600">Chargement...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className="bg-secondary">
|
||||
<CardHeader>
|
||||
<CardTitle>Lexique Tahitien</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-4">
|
||||
{lexiqueItems.map((item) => (
|
||||
<div
|
||||
key={item.id}
|
||||
className="border-b border-primary/20 pb-4 last:border-0 last:pb-0"
|
||||
>
|
||||
<div className="flex items-start justify-between mb-2">
|
||||
<h3 className="text-lg font-semibold text-primary">
|
||||
{item.mot}
|
||||
</h3>
|
||||
<span className="text-sm font-medium text-gray-600">
|
||||
{item.traduction}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-700">{item.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user