الميزة: إضافة مكونات تحسين محركات البحث لصفحات هبوط الأدوات، بما في ذلك قسم الأسئلة الشائعة وقسم الأدوات ذات الصلة.

- تم تنفيذ مكون قسم الأسئلة الشائعة لعرض الأسئلة المتكررة مع إجابات قابلة للتوسيع.

- تم إنشاء مكون الأدوات ذات الصلة لعرض الأدوات المرتبطة بالأداة الحالية بناءً على بيانات تحسين محركات البحث.

- تم تطوير مكون صفحة هبوط الأدوات لتغليف مكونات الأدوات ببيانات تعريف تحسين محركات البحث، والبيانات المنظمة، والمحتوى الإضافي.

- تم إنشاء إعدادات مركزية لتحسين محركات البحث في ملف seoData.ts لإدارة بيانات تعريف الأدوات، والأسئلة الشائعة، والأدوات ذات الصلة. ----   feat: add SEO components for tool landing pages including FAQ and related tools sections

- Implemented FAQSection component to display frequently asked questions with expandable answers.
- Created RelatedTools component to show tools related to the current tool based on SEO data.
- Developed ToolLandingPage component to wrap tool components with SEO metadata, structured data, and additional content.
- Established central SEO configuration in seoData.ts for managing tool metadata, FAQs, and related tools.
This commit is contained in:
Your Name
2026-03-08 23:19:41 +02:00
parent bf17351f12
commit b5606c2d2d
10 changed files with 1132 additions and 61 deletions

View File

@@ -0,0 +1,107 @@
import { Helmet } from 'react-helmet-async';
import { useTranslation } from 'react-i18next';
import { CheckCircle } from 'lucide-react';
import { getToolSEO } from '@/config/seoData';
import { generateToolSchema, generateBreadcrumbs, generateFAQ } from '@/utils/seo';
import FAQSection from './FAQSection';
import RelatedTools from './RelatedTools';
interface ToolLandingPageProps {
/** The tool slug matching seoData.ts entries */
slug: string;
/** The actual tool component rendered inside the landing page */
children: React.ReactNode;
}
/**
* SEO wrapper that adds structured data, FAQ section, related tools,
* feature bullets, and proper meta tags around any tool component.
*/
export default function ToolLandingPage({ slug, children }: ToolLandingPageProps) {
const { t } = useTranslation();
const seo = getToolSEO(slug);
// Fallback: just render tool without SEO wrapper
if (!seo) return <>{children}</>;
const toolTitle = t(`tools.${seo.i18nKey}.title`);
const toolDesc = t(`tools.${seo.i18nKey}.description`);
const origin = typeof window !== 'undefined' ? window.location.origin : '';
const canonicalUrl = `${origin}/tools/${slug}`;
const toolSchema = generateToolSchema({
name: toolTitle,
description: seo.metaDescription,
url: canonicalUrl,
category: seo.category === 'PDF' ? 'UtilitiesApplication' : 'WebApplication',
});
const breadcrumbSchema = generateBreadcrumbs([
{ name: t('common.appName'), url: origin },
{ name: seo.category, url: `${origin}/#${seo.category.toLowerCase()}` },
{ name: toolTitle, url: canonicalUrl },
]);
const faqSchema = seo.faqs.length > 0 ? generateFAQ(seo.faqs) : null;
return (
<>
<Helmet>
<title>{toolTitle} {seo.titleSuffix} | {t('common.appName')}</title>
<meta name="description" content={seo.metaDescription} />
<meta name="keywords" content={seo.keywords} />
<link rel="canonical" href={canonicalUrl} />
{/* Open Graph */}
<meta property="og:title" content={`${toolTitle}${seo.titleSuffix}`} />
<meta property="og:description" content={seo.metaDescription} />
<meta property="og:url" content={canonicalUrl} />
<meta property="og:type" content="website" />
{/* Twitter */}
<meta name="twitter:card" content="summary" />
<meta name="twitter:title" content={`${toolTitle}${seo.titleSuffix}`} />
<meta name="twitter:description" content={seo.metaDescription} />
{/* Structured Data */}
<script type="application/ld+json">{JSON.stringify(toolSchema)}</script>
<script type="application/ld+json">{JSON.stringify(breadcrumbSchema)}</script>
{faqSchema && (
<script type="application/ld+json">{JSON.stringify(faqSchema)}</script>
)}
</Helmet>
{/* Tool Interface */}
{children}
{/* 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>
)}
{/* FAQ Section */}
<FAQSection faqs={seo.faqs} />
{/* Related Tools */}
<RelatedTools currentSlug={slug} />
</div>
</>
);
}