From 56bb60af8df83ce2590e9b0f187d065a85f78b86 Mon Sep 17 00:00:00 2001 From: muraschal Date: Thu, 25 Jun 2026 19:11:36 +0200 Subject: [PATCH 1/2] feat(web): public sample-report gallery at /reports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the audit output visible before a user runs it — the classic show-dont-tell activation lever, and indexable SEO content. - reports.ts: typed Report model, seeded with the REAL #97 self-audit (scorecard, not-applicable-with-reasons, headline findings linking the real issues #81-84, the CHECKSUMS cross-audit dedup exhibit). Adding a real run = appending one object. - reports-page.tsx + 4 routes (index + detail, both langs), reusing the landing-page primitives; localized metadata, hreflang, CollectionPage / Report / BreadcrumbList JSON-LD. - footer nav link + sitemap entries. Honesty: only real #97 data — no invented findings, no fabricated repos. Closes #140. Part of #131. Co-Authored-By: Claude Opus 4.8 (1M context) --- web/app/(de)/de/reports/[slug]/page.tsx | 94 ++++++ web/app/(de)/de/reports/page.tsx | 64 ++++ web/app/(en)/reports/[slug]/page.tsx | 93 +++++ web/app/(en)/reports/page.tsx | 63 ++++ web/app/sitemap.ts | 20 ++ web/components/reports-page.tsx | 431 ++++++++++++++++++++++++ web/components/site-chrome.tsx | 4 + web/lib/i18n.ts | 239 ++++++++++++- web/lib/reports.ts | 121 +++++++ 9 files changed, 1127 insertions(+), 2 deletions(-) create mode 100644 web/app/(de)/de/reports/[slug]/page.tsx create mode 100644 web/app/(de)/de/reports/page.tsx create mode 100644 web/app/(en)/reports/[slug]/page.tsx create mode 100644 web/app/(en)/reports/page.tsx create mode 100644 web/components/reports-page.tsx create mode 100644 web/lib/reports.ts diff --git a/web/app/(de)/de/reports/[slug]/page.tsx b/web/app/(de)/de/reports/[slug]/page.tsx new file mode 100644 index 0000000..79da78e --- /dev/null +++ b/web/app/(de)/de/reports/[slug]/page.tsx @@ -0,0 +1,94 @@ +import type { Metadata } from "next"; +import { notFound } from "next/navigation"; +import { ReportDetailPage } from "@/components/reports-page"; +import { reportProseFor, reportVerdict, t } from "@/lib/i18n"; +import { getReport, REPORTS, reportIssueUrl } from "@/lib/reports"; +import { SITE_URL } from "@/lib/site"; + +const LANG = "de" as const; + +export const dynamicParams = false; + +export function generateStaticParams() { + return REPORTS.map((r) => ({ slug: r.slug })); +} + +export async function generateMetadata({ + params, +}: { + params: Promise<{ slug: string }>; +}): Promise { + const { slug } = await params; + const report = getReport(slug); + const prose = reportProseFor(LANG, slug); + if (!report || !prose) return {}; + const path = `/de/reports/${slug}`; + const enPath = `/reports/${slug}`; + return { + title: `${prose.title} — auditor-Bericht`, + description: prose.summary, + alternates: { + canonical: path, + languages: { en: enPath, de: path, "x-default": enPath }, + }, + openGraph: { + type: "article", + url: path, + title: prose.title, + description: prose.summary, + publishedTime: report.date, + images: [`${SITE_URL}/de/opengraph-image`], + }, + }; +} + +export default async function Page({ + params, +}: { + params: Promise<{ slug: string }>; +}) { + const { slug } = await params; + const report = getReport(slug); + const prose = reportProseFor(LANG, slug); + if (!report || !prose) notFound(); + + const path = `/de/reports/${slug}`; + const jsonLd = { + "@context": "https://schema.org", + "@type": "Report", + "@id": `${SITE_URL}${path}`, + url: `${SITE_URL}${path}`, + headline: prose.title, + name: prose.title, + description: reportVerdict(LANG, report.verdictKey) || prose.summary, + inLanguage: LANG, + datePublished: report.date, + about: { "@type": "SoftwareSourceCode", name: report.target, codeRepository: report.targetUrl }, + author: { "@type": "Organization", name: "auditor", url: SITE_URL }, + publisher: { "@type": "Organization", name: "auditor", url: SITE_URL }, + isBasedOn: reportIssueUrl(report.tracker), + mainEntityOfPage: `${SITE_URL}${path}`, + }; + const breadcrumb = { + "@context": "https://schema.org", + "@type": "BreadcrumbList", + itemListElement: [ + { "@type": "ListItem", position: 1, name: "auditor", item: `${SITE_URL}/de` }, + { "@type": "ListItem", position: 2, name: t(LANG).repIndexKicker, item: `${SITE_URL}/de/reports` }, + { "@type": "ListItem", position: 3, name: prose.title, item: `${SITE_URL}${path}` }, + ], + }; + return ( + <> + +