fix: add internal links

This commit is contained in:
Carl-Gerhard Lindesvärd
2025-12-02 13:59:53 +01:00
parent a7a357eb0f
commit 50ef4c0d94
38 changed files with 652 additions and 10 deletions

View File

@@ -78,6 +78,8 @@ export default async function Page({
'@type': 'Article',
headline: article?.data.title,
datePublished: article?.data.date.toISOString(),
dateModified:
article?.data.updated?.toISOString() || article?.data.date.toISOString(),
author: {
'@type': 'Person',
name: author.name,
@@ -132,9 +134,16 @@ export default async function Page({
</div>
<div className="col">
<p className="font-medium">{author.name}</p>
<p className="text-muted-foreground text-sm">
{article?.data.date.toLocaleDateString()}
</p>
<div className="row gap-2">
<p className="text-muted-foreground text-sm">
{article?.data.date.toLocaleDateString()}
</p>
{article?.data.updated && (
<p className="text-muted-foreground text-sm italic">
Updated on {article?.data.updated.toLocaleDateString()}
</p>
)}
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,80 @@
import { FeatureCardContainer } from '@/components/feature-card';
import { Section, SectionHeader } from '@/components/section';
import type { RelatedLinks } from '@/lib/compare';
import { ArrowRightIcon, BookOpenIcon, GitCompareIcon } from 'lucide-react';
import Link from 'next/link';
interface RelatedLinksProps {
relatedLinks?: RelatedLinks;
}
export function RelatedLinksSection({ relatedLinks }: RelatedLinksProps) {
if (
!relatedLinks ||
(!relatedLinks.articles?.length && !relatedLinks.alternatives?.length)
) {
return null;
}
return (
<Section className="container">
<SectionHeader
title="Related resources"
description="Explore more comparisons and guides to help you choose the right analytics tool"
variant="sm"
/>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mt-12">
{relatedLinks.articles && relatedLinks.articles.length > 0 && (
<div className="col gap-4">
<div className="row gap-2 items-center mb-2">
<BookOpenIcon className="size-5 text-muted-foreground" />
<h3 className="text-lg font-semibold">Articles</h3>
</div>
<div className="col gap-3">
{relatedLinks.articles.map((article) => (
<Link key={article.url} href={article.url}>
<FeatureCardContainer className="hover:border-primary/30 transition-colors">
<div className="row gap-3 items-center">
<div className="col gap-1 flex-1 min-w-0">
<h4 className="text-base font-semibold group-hover:text-primary transition-colors">
{article.title}
</h4>
</div>
<ArrowRightIcon className="opacity-0 group-hover:opacity-100 size-4 shrink-0 text-muted-foreground group-hover:text-primary group-hover:translate-x-1 transition-all duration-300" />
</div>
</FeatureCardContainer>
</Link>
))}
</div>
</div>
)}
{relatedLinks.alternatives && relatedLinks.alternatives.length > 0 && (
<div className="col gap-4">
<div className="row gap-2 items-center mb-2">
<GitCompareIcon className="size-5 text-muted-foreground" />
<h3 className="text-lg font-semibold">Other comparisons</h3>
</div>
<div className="col gap-3">
{relatedLinks.alternatives.map((alternative) => (
<Link key={alternative.url} href={alternative.url}>
<FeatureCardContainer className="hover:border-primary/30 transition-colors">
<div className="row gap-3 items-center">
<div className="col gap-1 flex-1 min-w-0">
<h4 className="text-base font-semibold group-hover:text-primary transition-colors">
{alternative.name} alternative
</h4>
</div>
<ArrowRightIcon className="opacity-0 group-hover:opacity-100 size-4 shrink-0 text-muted-foreground group-hover:text-primary group-hover:translate-x-1 transition-all duration-300" />
</div>
</FeatureCardContainer>
</Link>
))}
</div>
</div>
)}
</div>
</Section>
);
}

View File

@@ -17,6 +17,7 @@ import { ComparisonTable } from './_components/comparison-table';
import { FeaturesShowcase } from './_components/features-showcase';
import { MigrationSection } from './_components/migration-section';
import { PricingSection } from './_components/pricing-section';
import { RelatedLinksSection } from './_components/related-links';
import { TechnicalComparison } from './_components/technical-comparison';
import { UseCases } from './_components/use-cases';
import { WhoShouldChoose } from './_components/who-should-choose';
@@ -213,6 +214,10 @@ export default async function ComparePage({
<CompareFaq faqs={data.faqs} pageUrl={pageUrl} />
</div>
{data.related_links && (
<RelatedLinksSection relatedLinks={data.related_links} />
)}
<CtaBanner
title={'Ready to make the switch?'}
description="Test OpenPanel free for 30 days, you'll not be charged anything unless you upgrade to a paid plan."

View File

@@ -9,6 +9,7 @@ import { getOgImageUrl, getPageMetadata } from '@/lib/metadata';
import { formatEventsCount } from '@/lib/utils';
import { PRICING } from '@openpanel/payments/prices';
import type { Metadata } from 'next';
import Link from 'next/link';
import Script from 'next/script';
const title = 'OpenPanel Cloud Pricing';
@@ -58,6 +59,7 @@ export default function SupporterPage() {
</HeroContainer>
<Pricing />
<PricingTable />
<ComparisonSection />
<Testimonials />
<Faq />
<CtaBanner />
@@ -107,3 +109,25 @@ function PricingTable() {
</Section>
);
}
function ComparisonSection() {
return (
<Section className="container">
<SectionHeader
title="How do we compare?"
description={
<>
See how OpenPanel stacks up against other analytics tools in our{' '}
<Link
href="/articles/open-source-web-analytics"
className="underline hover:text-primary transition-colors"
>
comprehensive comparison of open source web analytics tools
</Link>
.
</>
}
/>
</Section>
);
}

View File

@@ -31,7 +31,7 @@ const faqData = [
{
question: 'How does OpenPanel compare to other analytics tools?',
answer:
'We have a dedicated compare page where you can see how OpenPanel compares to other analytics tools. You can find it [here](/compare).',
'We have a dedicated compare page where you can see how OpenPanel compares to other analytics tools. You can find it [here](/compare). You can also read our comprehensive guide on the [best open source web analytics tools](/articles/open-source-web-analytics).',
},
{
question: 'How does OpenPanel compare to Mixpanel?',