import { useParams, Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Helmet } from 'react-helmet-async';
import { CheckCircle, XCircle, MinusCircle, ArrowRight, Swords, Trophy, ExternalLink } from 'lucide-react';
import AdSlot from '@/components/layout/AdSlot';
import { getComparisonPage, getComparisonPagesByTool, type ComparisonFeature } from '@/config/comparisonData';
import { getToolSEO } from '@/config/seoData';
import { getSiteOrigin, buildSocialImageUrl, getOgLocale, generateWebPage, generateFAQ } from '@/utils/seo';
function FeatureIcon({ value }: { value: boolean | 'partial' }) {
if (value === true) return ;
if (value === 'partial') return ;
return ;
}
function FeatureLabel({ value }: { value: boolean | 'partial' }) {
if (value === true) return Yes;
if (value === 'partial') return Partial;
return No;
}
export default function ComparisonPage() {
const { slug } = useParams<{ slug: string }>();
const { t, i18n } = useTranslation();
const comparison = slug ? getComparisonPage(slug) : undefined;
if (!comparison) {
return (
{t('pages.comparison.notFound')}
{t('pages.comparison.browseTools')}
);
}
const ourTool = getToolSEO(comparison.ourToolSlug);
const origin = getSiteOrigin(typeof window !== 'undefined' ? window.location.origin : '');
const canonicalUrl = `${origin}/compare/${comparison.slug}`;
const socialImageUrl = buildSocialImageUrl(origin);
const currentOgLocale = getOgLocale(i18n.language);
const prefix = `pages.comparison.${comparison.i18nKey}`;
const title = t(`${prefix}.title`);
const metaDescription = t(`${prefix}.metaDescription`);
const faqItems = t(`${prefix}.faqs`, { returnObjects: true }) as Array<{ q: string; a: string }>;
const faqs = Array.isArray(faqItems) ? faqItems : [];
const webPageSchema = generateWebPage({
name: title,
description: metaDescription,
url: canonicalUrl,
});
const faqSchema = faqs.length > 0
? generateFAQ(faqs.map((f) => ({ question: f.q, answer: f.a })))
: null;
// Related comparisons
const relatedComparisons = comparison.relatedComparisonSlugs
.map((s) => getComparisonPage(s))
.filter(Boolean);
// Related tools
const relatedTools = comparison.relatedToolSlugs
.map((s) => getToolSEO(s))
.filter(Boolean);
return (
<>
{title} | {t('common.appName')}
{faqSchema && (
)}
{/* Hero */}
{t('pages.comparison.badge')}
{t(`${prefix}.heading`)}
{t(`${prefix}.subtitle`)}
{/* Feature Comparison Table */}
{t('pages.comparison.featureComparison')}
{/* Table header */}
{t('pages.comparison.feature')}
{t('common.appName')}
{comparison.competitorName}
{/* Feature rows */}
{comparison.features.map((feature: ComparisonFeature, idx: number) => (
{t(`pages.comparison.features.${feature.key}`)}
))}
{/* Our Advantages */}
{t(`${prefix}.advantagesTitle`)}
{(() => {
const advantages = t(`${prefix}.advantages`, { returnObjects: true }) as string[];
return Array.isArray(advantages) ? (
{advantages.map((adv, idx) => (
-
{adv}
))}
) : null;
})()}
{/* Verdict */}
{t('pages.comparison.verdictTitle')}
{/* CTA */}
{ourTool ? t(`tools.${ourTool.i18nKey}.title`) : comparison.ourToolSlug}
{t('pages.comparison.ctaSubtext')}
{/* FAQ */}
{faqs.length > 0 && (
{t('pages.comparison.faqTitle')}
{faqs.map((faq, idx) => (
{faq.q}
{faq.a}
))}
)}
{/* Related Comparisons */}
{relatedComparisons.length > 0 && (
{t('pages.comparison.relatedComparisons')}
{relatedComparisons.map((comp) => (
{t(`pages.comparison.${comp!.i18nKey}.heading`)}
))}
)}
{/* Related Tools */}
{relatedTools.length > 0 && (
{t('pages.comparison.relatedTools')}
{relatedTools.map((tool) => (
{t(`tools.${tool!.i18nKey}.title`)}
{t(`tools.${tool!.i18nKey}.shortDesc`)}
))}
)}
>
);
}