feat: enhance FAQPage with collapsible items and JSON-LD schema for improved user experience and SEO
This commit is contained in:
125
app/faq/page.tsx
125
app/faq/page.tsx
@@ -1,4 +1,9 @@
|
|||||||
export default function FAQPage() {
|
'use client'
|
||||||
|
|
||||||
|
import { motion } from 'framer-motion'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { ChevronDownIcon } from '@ciphera-net/ui'
|
||||||
|
|
||||||
const faqs = [
|
const faqs = [
|
||||||
{
|
{
|
||||||
question: "Is Pulse GDPR compliant?",
|
question: "Is Pulse GDPR compliant?",
|
||||||
@@ -26,24 +31,112 @@ export default function FAQPage() {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
return (
|
// * JSON-LD FAQ Schema for rich snippets
|
||||||
<div className="container mx-auto px-4 py-8 max-w-4xl">
|
const faqSchema = {
|
||||||
<h1 className="text-3xl font-bold mb-8 text-neutral-900 dark:text-white">
|
'@context': 'https://schema.org',
|
||||||
Frequently Asked Questions
|
'@type': 'FAQPage',
|
||||||
</h1>
|
mainEntity: faqs.map((faq) => ({
|
||||||
|
'@type': 'Question',
|
||||||
|
name: faq.question,
|
||||||
|
acceptedAnswer: {
|
||||||
|
'@type': 'Answer',
|
||||||
|
text: faq.answer,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
|
||||||
<div className="space-y-6">
|
function FAQItem({ faq, index }: { faq: typeof faqs[0]; index: number }) {
|
||||||
{faqs.map((faq, index) => (
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
<div key={index} className="bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 rounded-xl p-6">
|
|
||||||
<h2 className="text-xl font-semibold mb-3 text-neutral-900 dark:text-white">
|
return (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
|
viewport={{ once: true }}
|
||||||
|
transition={{ duration: 0.5, delay: index * 0.05 }}
|
||||||
|
className="border-b border-neutral-200 dark:border-neutral-800"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
onClick={() => setIsOpen(!isOpen)}
|
||||||
|
className="w-full py-6 flex items-center justify-between text-left hover:text-brand-orange transition-colors"
|
||||||
|
>
|
||||||
|
<h3 className="text-lg font-semibold text-neutral-900 dark:text-white pr-4">
|
||||||
{faq.question}
|
{faq.question}
|
||||||
</h2>
|
</h3>
|
||||||
<p className="text-neutral-600 dark:text-neutral-400">
|
<ChevronDownIcon
|
||||||
|
className={`w-5 h-5 text-neutral-500 shrink-0 transition-transform duration-300 ${
|
||||||
|
isOpen ? 'rotate-180' : ''
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
{isOpen && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, height: 0 }}
|
||||||
|
animate={{ opacity: 1, height: 'auto' }}
|
||||||
|
exit={{ opacity: 0, height: 0 }}
|
||||||
|
transition={{ duration: 0.3 }}
|
||||||
|
className="pb-6"
|
||||||
|
>
|
||||||
|
<p className="text-neutral-600 dark:text-neutral-400 leading-relaxed">
|
||||||
{faq.answer}
|
{faq.answer}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</motion.div>
|
||||||
))}
|
)}
|
||||||
</div>
|
</motion.div>
|
||||||
</div>
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function FAQPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* * JSON-LD FAQ Schema */}
|
||||||
|
<script
|
||||||
|
type="application/ld+json"
|
||||||
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(faqSchema) }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="container mx-auto px-4 py-16 max-w-4xl">
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
|
viewport={{ once: true }}
|
||||||
|
transition={{ duration: 0.5 }}
|
||||||
|
className="text-center mb-16"
|
||||||
|
>
|
||||||
|
<span className="badge-primary mb-4 inline-flex">FAQ</span>
|
||||||
|
<h1 className="text-3xl sm:text-4xl md:text-5xl font-bold text-neutral-900 dark:text-white mb-4">
|
||||||
|
Frequently asked questions
|
||||||
|
</h1>
|
||||||
|
<p className="text-lg text-neutral-600 dark:text-neutral-400 max-w-2xl mx-auto">
|
||||||
|
Learn more about how Pulse respects your privacy and handles your data.
|
||||||
|
</p>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<div className="max-w-3xl mx-auto">
|
||||||
|
{faqs.map((faq, index) => (
|
||||||
|
<FAQItem key={index} faq={faq} index={index} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* * CTA */}
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
|
viewport={{ once: true }}
|
||||||
|
transition={{ duration: 0.5, delay: 0.3 }}
|
||||||
|
className="text-center mt-12"
|
||||||
|
>
|
||||||
|
<p className="text-neutral-600 dark:text-neutral-400 mb-4">
|
||||||
|
Still have questions?
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href="mailto:support@ciphera.net"
|
||||||
|
className="btn-secondary inline-flex"
|
||||||
|
>
|
||||||
|
Contact us
|
||||||
|
</a>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user