134 lines
5.0 KiB
TypeScript
134 lines
5.0 KiB
TypeScript
"use client";
|
|
import React, { useEffect, useState } from "react";
|
|
import { HelpCircle, ChevronDown } from "lucide-react";
|
|
import { motion, AnimatePresence } from "motion/react";
|
|
import { useSettingsStore } from "../lib/store/settingsStore";
|
|
import { safeJsonLd } from "../lib/schema";
|
|
import api from "../lib/services/api";
|
|
|
|
export interface FAQItem {
|
|
id: string;
|
|
question: string;
|
|
answer: string;
|
|
order: number;
|
|
}
|
|
|
|
export default function FAQSection() {
|
|
const getText = useSettingsStore((state) => state.getText);
|
|
const [faqs, setFaqs] = useState<FAQItem[]>([]);
|
|
const [openIndex, setOpenIndex] = useState<number | null>(0);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
async function loadFaqs() {
|
|
try {
|
|
setIsLoading(true);
|
|
const res = await api.get("/faq");
|
|
const list = Array.isArray(res.data) ? res.data : [];
|
|
setFaqs(list);
|
|
} catch (err) {
|
|
console.error("Failed to load FAQs:", err);
|
|
setFaqs([]);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
}
|
|
loadFaqs();
|
|
}, []);
|
|
|
|
if (!isLoading && faqs.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
// Schema.org FAQPage JSON-LD structure
|
|
const faqSchema = {
|
|
"@context": "https://schema.org",
|
|
"@type": "FAQPage",
|
|
"mainEntity": faqs.map((faq) => ({
|
|
"@type": "Question",
|
|
"name": faq.question,
|
|
"acceptedAnswer": {
|
|
"@type": "Answer",
|
|
"text": faq.answer,
|
|
},
|
|
})),
|
|
};
|
|
|
|
return (
|
|
<section className="py-8 sm:py-20 bg-medical-gray-50/70 border-t border-medical-gray-100 font-vazir" dir="rtl">
|
|
{/* Schema.org FAQPage Rich Snippet */}
|
|
{faqs.length > 0 && (
|
|
<script
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{ __html: safeJsonLd(faqSchema) }}
|
|
/>
|
|
)}
|
|
|
|
<div className="max-w-4xl mx-auto px-4">
|
|
{/* Header */}
|
|
<div className="text-center mb-6 sm:mb-12">
|
|
<div className="inline-flex items-center gap-2 px-3.5 py-1.5 bg-canina-blue/10 text-canina-blue rounded-full text-[10px] font-black uppercase tracking-widest mb-3 sm:mb-4">
|
|
<HelpCircle className="w-3.5 h-3.5" />
|
|
<span>{getText("faq_badge", "پاسخ به سوالات پرتکرار")}</span>
|
|
</div>
|
|
<h2 className="text-2xl sm:text-4xl md:text-5xl font-black text-medical-gray-900 leading-tight font-lalezar tracking-wide">
|
|
{getText("faq_title_1", "سوالات متداول")} <span className="text-canina-blue">{getText("faq_title_2", "درباره محصولات و سفارش")}</span>
|
|
</h2>
|
|
<p className="text-sm sm:text-base text-medical-gray-500 font-medium mt-2 sm:mt-4">
|
|
{getText("faq_desc", "پاسخ به متداولترین سوالات درباره استانداردها، دوز مصرفی، ارسال و اصالت مکملهای کنینا.")}
|
|
</p>
|
|
</div>
|
|
|
|
{/* FAQ List */}
|
|
{isLoading ? (
|
|
<div className="space-y-4">
|
|
{[1, 2, 3].map((i) => (
|
|
<div key={i} className="h-16 bg-white rounded-2xl border border-medical-gray-100 animate-pulse" />
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div className="space-y-4">
|
|
{faqs.map((faq, idx) => {
|
|
const isOpen = openIndex === idx;
|
|
return (
|
|
<div
|
|
key={faq.id || idx}
|
|
className="bg-white rounded-2xl border border-medical-gray-100 overflow-hidden shadow-xs hover:border-canina-blue/30 transition-all"
|
|
>
|
|
<button
|
|
onClick={() => setOpenIndex(isOpen ? null : idx)}
|
|
className="w-full p-6 text-right flex items-center justify-between gap-4 font-bold text-medical-gray-900 hover:text-canina-blue transition-colors"
|
|
>
|
|
<span className="text-base font-shabnam">{faq.question}</span>
|
|
<ChevronDown
|
|
className={`w-5 h-5 shrink-0 text-medical-gray-400 transition-transform duration-300 ${
|
|
isOpen ? "rotate-180 text-canina-blue" : ""
|
|
}`}
|
|
/>
|
|
</button>
|
|
|
|
<AnimatePresence initial={false}>
|
|
{isOpen && (
|
|
<motion.div
|
|
initial={{ height: 0, opacity: 0 }}
|
|
animate={{ height: "auto", opacity: 1 }}
|
|
exit={{ height: 0, opacity: 0 }}
|
|
transition={{ duration: 0.25 }}
|
|
className="overflow-hidden"
|
|
>
|
|
<div className="px-6 pb-6 pt-0 text-sm text-medical-gray-600 leading-relaxed font-normal border-t border-medical-gray-50">
|
|
{faq.answer}
|
|
</div>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|