14 blog post schedule

This commit is contained in:
Timo Knuth
2026-01-26 16:43:47 +01:00
parent 702e2710de
commit 2771faf3ba
44 changed files with 3561 additions and 2174 deletions

View File

@@ -0,0 +1,69 @@
import { notFound } from "next/navigation";
import Link from "next/link";
import Script from "next/script";
import { pillarMeta } from "@/lib/pillar-data";
import { getPostsByPillar } from "@/lib/content";
import type { PillarKey } from "@/lib/types";
import { pillarPageSchema, faqPageSchema } from "@/lib/schema";
import { FAQSection } from "@/components/aeo/FAQSection";
import { AnswerBox } from "@/components/aeo/AnswerBox";
export function generateMetadata({ params }: { params: { pillar: string } }) {
const meta = pillarMeta.find(p => p.key === params.pillar);
if (!meta) return {};
return {
title: `${meta.title} - Ultimate Guide | QR Master`,
description: meta.description
};
}
export function generateStaticParams() {
return pillarMeta.map((pillar) => ({
pillar: pillar.key,
}));
}
export default function PillarPage({ params }: { params: { pillar: PillarKey } }) {
const meta = pillarMeta.find(p => p.key === params.pillar);
if (!meta) return notFound();
const posts = getPostsByPillar(meta.key);
const jsonLd = pillarPageSchema(meta, posts);
const faqLd = meta.miniFaq ? faqPageSchema(meta.miniFaq) : null;
return (
<main className="container mx-auto max-w-5xl py-12 px-4 space-y-10">
<Script id="ld-pillar" type="application/ld+json" strategy="afterInteractive"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
{faqLd && (
<Script id="ld-pillar-faq" type="application/ld+json" strategy="afterInteractive"
dangerouslySetInnerHTML={{ __html: JSON.stringify(faqLd) }} />
)}
<header className="space-y-4 max-w-3xl">
<div className="text-sm font-medium text-gray-500 hover:text-gray-700">
<Link href="/learn">Learn</Link> <span className="text-gray-900">{meta.title}</span>
</div>
<h1 className="text-4xl md:text-5xl font-extrabold text-gray-900">{meta.title}</h1>
<p className="text-xl text-gray-600">{meta.description}</p>
</header>
<AnswerBox html={meta.quickAnswer} />
<section className="space-y-6">
<h2 className="text-2xl font-bold text-gray-900">Guides & Articles</h2>
<div className="grid md:grid-cols-2 gap-6">
{posts.map(p => (
<Link key={p.slug} href={`/blog/${p.slug}`} className="group block rounded-xl border border-gray-200 bg-white p-6 shadow-sm hover:shadow-md hover:border-blue-200 transition-all">
<div className="text-xs text-gray-400 mb-2">{p.date}</div>
<div className="text-lg font-bold text-gray-900 mb-2 group-hover:text-blue-700">{p.title}</div>
<div className="text-sm text-gray-600 line-clamp-2">{p.description}</div>
</Link>
))}
</div>
</section>
{!!meta.miniFaq?.length && <FAQSection items={meta.miniFaq} title={`${meta.title} FAQ`} />}
</main>
);
}

View File

@@ -0,0 +1,54 @@
import Link from "next/link";
import { pillarMeta } from "@/lib/pillar-data";
import { getPublishedPosts } from "@/lib/content";
export const metadata = {
title: "Learn QR Code Mastery | QR Master Hub",
description: "Guides, use cases, tracking deep-dives, and security best practices for dynamic QR codes.",
};
export default function LearnHubPage() {
const posts = getPublishedPosts();
// Sort by date descending
const topLatest = [...posts].sort((a, b) => (new Date(a.datePublished).getTime() < new Date(b.datePublished).getTime() ? 1 : -1)).slice(0, 6);
return (
<main className="container mx-auto max-w-5xl py-12 px-4 space-y-12">
<header className="space-y-4 max-w-3xl">
<h1 className="text-4xl md:text-5xl font-extrabold text-gray-900 tracking-tight">QR Code Knowledge Hub</h1>
<p className="text-xl text-gray-600">
Master the art of QR codes. Explore our expert guides on generation, tracking, security, and marketing strategies.
</p>
</header>
<section>
<h2 className="text-2xl font-bold text-gray-900 mb-6">Topic Pillars</h2>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
{pillarMeta.sort((a, b) => a.order - b.order).map(p => (
<Link key={p.key} href={`/learn/${p.key}`} className="group block h-full rounded-2xl border border-gray-200 bg-white p-6 shadow-sm hover:shadow-md hover:border-blue-200 transition-all">
<div className="text-sm font-semibold text-blue-600 uppercase tracking-wide mb-2 opacity-80 group-hover:opacity-100">Pillar</div>
<div className="text-2xl font-bold text-gray-900 mb-2 group-hover:text-blue-700">{p.title}</div>
<div className="text-gray-600">{p.description}</div>
</Link>
))}
</div>
</section>
<section>
<h2 className="text-2xl font-bold text-gray-900 mb-6">Latest Guides</h2>
<div className="grid md:grid-cols-2 gap-6">
{topLatest.map(p => (
<Link key={p.slug} href={`/blog/${p.slug}`} className="group block rounded-2xl border border-gray-200 bg-white p-6 shadow-sm hover:shadow-md hover:border-blue-200 transition-all">
<div className="flex justify-between items-center mb-3">
<div className="text-xs font-semibold px-2 py-1 rounded bg-gray-100 text-gray-600">{p.pillar?.toUpperCase() || 'GUIDE'}</div>
<div className="text-xs text-gray-400">{p.date}</div>
</div>
<div className="text-xl font-bold text-gray-900 mb-2 group-hover:text-blue-700 line-clamp-2">{p.title}</div>
<div className="text-gray-600 line-clamp-2">{p.description}</div>
</Link>
))}
</div>
</section>
</main>
);
}