Refactor code structure for improved readability and maintainability /SEO Content (3 languages, 32 tools each):
en.json — Full English SEO content added ar.json — Full Arabic SEO content added fr.json — Full French SEO content added
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useState } from 'react';
|
||||
import { ChevronDown, ChevronUp } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { ToolFAQ } from '@/config/seoData';
|
||||
|
||||
interface FAQSectionProps {
|
||||
@@ -7,6 +8,7 @@ interface FAQSectionProps {
|
||||
}
|
||||
|
||||
export default function FAQSection({ faqs }: FAQSectionProps) {
|
||||
const { t } = useTranslation();
|
||||
const [openIndex, setOpenIndex] = useState<number | null>(0);
|
||||
|
||||
if (!faqs || faqs.length === 0) return null;
|
||||
@@ -14,7 +16,7 @@ export default function FAQSection({ faqs }: FAQSectionProps) {
|
||||
return (
|
||||
<section className="mt-12">
|
||||
<h2 className="mb-6 text-xl font-bold text-slate-900 dark:text-white">
|
||||
Frequently Asked Questions
|
||||
{t('seo.headings.faq')}
|
||||
</h2>
|
||||
<div className="divide-y divide-slate-200 rounded-xl border border-slate-200 bg-white dark:divide-slate-700 dark:border-slate-700 dark:bg-slate-800">
|
||||
{faqs.map((faq, idx) => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { getToolSEO } from '@/config/seoData';
|
||||
|
||||
interface RelatedToolsProps {
|
||||
@@ -14,6 +15,7 @@ const CATEGORY_COLORS: Record<string, string> = {
|
||||
};
|
||||
|
||||
export default function RelatedTools({ currentSlug }: RelatedToolsProps) {
|
||||
const { t } = useTranslation();
|
||||
const currentTool = getToolSEO(currentSlug);
|
||||
if (!currentTool) return null;
|
||||
|
||||
@@ -26,7 +28,7 @@ export default function RelatedTools({ currentSlug }: RelatedToolsProps) {
|
||||
return (
|
||||
<section className="mt-12">
|
||||
<h2 className="mb-6 text-xl font-bold text-slate-900 dark:text-white">
|
||||
Related Tools
|
||||
{t('seo.headings.relatedTools')}
|
||||
</h2>
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
{relatedTools.map((tool) => (
|
||||
|
||||
@@ -6,6 +6,11 @@ import { generateToolSchema, generateBreadcrumbs, generateFAQ } from '@/utils/se
|
||||
import FAQSection from './FAQSection';
|
||||
import RelatedTools from './RelatedTools';
|
||||
|
||||
interface SEOFAQ {
|
||||
q: string;
|
||||
a: string;
|
||||
}
|
||||
|
||||
interface ToolLandingPageProps {
|
||||
/** The tool slug matching seoData.ts entries */
|
||||
slug: string;
|
||||
@@ -76,28 +81,78 @@ export default function ToolLandingPage({ slug, children }: ToolLandingPageProps
|
||||
|
||||
{/* SEO Content Below Tool */}
|
||||
<div className="mx-auto mt-16 max-w-3xl">
|
||||
{/* Feature bullets */}
|
||||
{seo.features.length > 0 && (
|
||||
<section className="mb-12">
|
||||
<h2 className="mb-4 text-xl font-bold text-slate-900 dark:text-white">
|
||||
Why Use Our {toolTitle}?
|
||||
</h2>
|
||||
<p className="mb-6 text-slate-600 dark:text-slate-400">
|
||||
{toolDesc}
|
||||
</p>
|
||||
<ul className="space-y-3">
|
||||
{seo.features.map((feature, idx) => (
|
||||
<li key={idx} className="flex items-start gap-3">
|
||||
<CheckCircle className="mt-0.5 h-5 w-5 shrink-0 text-green-500" />
|
||||
<span className="text-slate-700 dark:text-slate-300">{feature}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
)}
|
||||
{/* What this tool does */}
|
||||
<section className="mb-12">
|
||||
<h2 className="mb-4 text-xl font-bold text-slate-900 dark:text-white">
|
||||
{t('seo.headings.whatItDoes')}
|
||||
</h2>
|
||||
<p className="text-slate-600 dark:text-slate-400">
|
||||
{t(`seo.${seo.i18nKey}.whatItDoes`)}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* How to use */}
|
||||
{(() => {
|
||||
const steps = t(`seo.${seo.i18nKey}.howToUse`, { returnObjects: true }) as string[];
|
||||
return Array.isArray(steps) && steps.length > 0 ? (
|
||||
<section className="mb-12">
|
||||
<h2 className="mb-4 text-xl font-bold text-slate-900 dark:text-white">
|
||||
{t('seo.headings.howToUse')}
|
||||
</h2>
|
||||
<ol className="list-decimal space-y-2 pl-5 text-slate-700 dark:text-slate-300">
|
||||
{steps.map((step, idx) => (
|
||||
<li key={idx}>{step}</li>
|
||||
))}
|
||||
</ol>
|
||||
</section>
|
||||
) : null;
|
||||
})()}
|
||||
|
||||
{/* Benefits */}
|
||||
{(() => {
|
||||
const benefits = t(`seo.${seo.i18nKey}.benefits`, { returnObjects: true }) as string[];
|
||||
return Array.isArray(benefits) && benefits.length > 0 ? (
|
||||
<section className="mb-12">
|
||||
<h2 className="mb-4 text-xl font-bold text-slate-900 dark:text-white">
|
||||
{t('seo.headings.whyUse', { tool: toolTitle })}
|
||||
</h2>
|
||||
<ul className="space-y-3">
|
||||
{benefits.map((benefit, idx) => (
|
||||
<li key={idx} className="flex items-start gap-3">
|
||||
<CheckCircle className="mt-0.5 h-5 w-5 shrink-0 text-green-500" />
|
||||
<span className="text-slate-700 dark:text-slate-300">{benefit}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
) : null;
|
||||
})()}
|
||||
|
||||
{/* Common use cases */}
|
||||
{(() => {
|
||||
const useCases = t(`seo.${seo.i18nKey}.useCases`, { returnObjects: true }) as string[];
|
||||
return Array.isArray(useCases) && useCases.length > 0 ? (
|
||||
<section className="mb-12">
|
||||
<h2 className="mb-4 text-xl font-bold text-slate-900 dark:text-white">
|
||||
{t('seo.headings.useCases')}
|
||||
</h2>
|
||||
<ul className="list-disc space-y-2 pl-5 text-slate-700 dark:text-slate-300">
|
||||
{useCases.map((useCase, idx) => (
|
||||
<li key={idx}>{useCase}</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
) : null;
|
||||
})()}
|
||||
|
||||
{/* FAQ Section */}
|
||||
<FAQSection faqs={seo.faqs} />
|
||||
{(() => {
|
||||
const faqData = t(`seo.${seo.i18nKey}.faq`, { returnObjects: true }) as SEOFAQ[];
|
||||
const faqs = Array.isArray(faqData)
|
||||
? faqData.map((f) => ({ question: f.q, answer: f.a }))
|
||||
: [];
|
||||
return <FAQSection faqs={faqs} />;
|
||||
})()}
|
||||
|
||||
{/* Related Tools */}
|
||||
<RelatedTools currentSlug={slug} />
|
||||
|
||||
Reference in New Issue
Block a user