diff --git a/src/app/compare/[slug]/page.tsx b/src/app/compare/[slug]/page.tsx index cf793b7..c6e1116 100644 --- a/src/app/compare/[slug]/page.tsx +++ b/src/app/compare/[slug]/page.tsx @@ -1,3 +1,4 @@ +import type { Metadata } from "next"; import { notFound } from "next/navigation"; import { compareNav } from "@/lib/compare-nav"; import { MARKETPLACE_URL } from "@/lib/links"; @@ -7,6 +8,24 @@ export function generateStaticParams() { return compareNav.map((c) => ({ slug: c.slug })); } +export async function generateMetadata({ + params, +}: { + params: Promise<{ slug: string }>; +}): Promise { + const { slug } = await params; + const comparison = compareNav.find((c) => c.slug === slug); + if (!comparison) notFound(); + + return { + title: `${comparison.title} — BugDrop`, + description: comparison.description, + alternates: { + canonical: `/compare/${slug}`, + }, + }; +} + export default async function ComparePage({ params, }: { diff --git a/src/app/compare/page.tsx b/src/app/compare/page.tsx index 35e05a5..41d76a3 100644 --- a/src/app/compare/page.tsx +++ b/src/app/compare/page.tsx @@ -1,9 +1,13 @@ import Link from "next/link"; +import type { Metadata } from "next"; import { compareNav } from "@/lib/compare-nav"; -export const metadata = { +export const metadata: Metadata = { title: "Compare — BugDrop", description: "See how BugDrop compares to other feedback tools.", + alternates: { + canonical: "/compare", + }, }; export default function CompareIndex() { diff --git a/src/app/docs/[slug]/page.tsx b/src/app/docs/[slug]/page.tsx index d117db3..87c7f26 100644 --- a/src/app/docs/[slug]/page.tsx +++ b/src/app/docs/[slug]/page.tsx @@ -1,3 +1,4 @@ +import type { Metadata } from "next"; import { notFound } from "next/navigation"; import { docsNav } from "@/lib/docs-nav"; import Link from "next/link"; @@ -8,6 +9,23 @@ export function generateStaticParams() { .map((doc) => ({ slug: doc.slug })); } +export async function generateMetadata({ + params, +}: { + params: Promise<{ slug: string }>; +}): Promise { + const { slug } = await params; + const doc = docsNav.find((d) => d.slug === slug); + if (!doc) notFound(); + + return { + title: `${doc.title} — BugDrop Docs`, + alternates: { + canonical: `/docs/${slug}`, + }, + }; +} + export default async function DocPage({ params, }: { diff --git a/src/app/docs/getting-started/page.tsx b/src/app/docs/getting-started/page.tsx new file mode 100644 index 0000000..34cafbe --- /dev/null +++ b/src/app/docs/getting-started/page.tsx @@ -0,0 +1,5 @@ +import { permanentRedirect } from "next/navigation"; + +export default function GettingStartedRedirect() { + permanentRedirect("/docs"); +} diff --git a/src/app/docs/page.tsx b/src/app/docs/page.tsx index 8f0fcf0..2e42faf 100644 --- a/src/app/docs/page.tsx +++ b/src/app/docs/page.tsx @@ -1,10 +1,14 @@ import Link from "next/link"; +import type { Metadata } from "next"; import { PlayCircle } from "lucide-react"; import { DEMO_PATH } from "@/lib/links"; -export const metadata = { +export const metadata: Metadata = { title: "Docs — BugDrop", description: "BugDrop documentation.", + alternates: { + canonical: "/docs", + }, }; export default function DocsIndex() { diff --git a/src/app/page.tsx b/src/app/page.tsx index b8541dc..9430334 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,4 +1,5 @@ import Script from "next/script"; +import type { Metadata } from "next"; import { Hero } from "@/components/landing/hero"; import { HowItWorks } from "@/components/landing/how-it-works"; import { LiveDemoCta } from "@/components/landing/live-demo-cta"; @@ -20,6 +21,12 @@ import { WIDGET_URL, } from "@/lib/links"; +export const metadata: Metadata = { + alternates: { + canonical: "/", + }, +}; + const structuredData = { "@context": "https://schema.org", "@type": "SoftwareApplication", diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts index a9d9748..f71d362 100644 --- a/src/app/sitemap.ts +++ b/src/app/sitemap.ts @@ -1,22 +1,36 @@ import type { MetadataRoute } from "next"; +import { compareNav } from "@/lib/compare-nav"; +import { docsNav } from "@/lib/docs-nav"; +import { useCasesNav } from "@/lib/use-cases-nav"; export default function sitemap(): MetadataRoute.Sitemap { const base = "https://bugdrop.dev"; + const lastModified = new Date(); return [ - { url: base, lastModified: new Date(), changeFrequency: "weekly", priority: 1 }, - { url: `${base}/docs`, lastModified: new Date(), changeFrequency: "weekly", priority: 0.8 }, - { url: `${base}/docs/installation`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.7 }, - { url: `${base}/docs/configuration`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.7 }, - { url: `${base}/docs/styling`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.7 }, - { url: `${base}/docs/faq`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.6 }, - { url: `${base}/use-cases`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.8 }, - { url: `${base}/use-cases/open-source`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.6 }, - { url: `${base}/use-cases/internal-tools`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.6 }, - { url: `${base}/use-cases/client-projects`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.6 }, - { url: `${base}/compare`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.8 }, - { url: `${base}/compare/userback`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.7 }, - { url: `${base}/compare/canny`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.7 }, - { url: `${base}/compare/sentry-user-feedback`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.7 }, + { url: base, lastModified, changeFrequency: "weekly", priority: 1 }, + { url: `${base}/docs`, lastModified, changeFrequency: "weekly", priority: 0.8 }, + ...docsNav + .filter((doc) => doc.slug !== "") + .map((doc) => ({ + url: `${base}/docs/${doc.slug}`, + lastModified, + changeFrequency: "monthly" as const, + priority: doc.slug === "faq" ? 0.6 : 0.7, + })), + { url: `${base}/use-cases`, lastModified, changeFrequency: "monthly", priority: 0.8 }, + ...useCasesNav.map((useCase) => ({ + url: `${base}/use-cases/${useCase.slug}`, + lastModified, + changeFrequency: "monthly" as const, + priority: 0.6, + })), + { url: `${base}/compare`, lastModified, changeFrequency: "monthly", priority: 0.8 }, + ...compareNav.map((comparison) => ({ + url: `${base}/compare/${comparison.slug}`, + lastModified, + changeFrequency: "monthly" as const, + priority: 0.7, + })), ]; } diff --git a/src/app/use-cases/[slug]/page.tsx b/src/app/use-cases/[slug]/page.tsx index 317e610..7e100e7 100644 --- a/src/app/use-cases/[slug]/page.tsx +++ b/src/app/use-cases/[slug]/page.tsx @@ -1,3 +1,4 @@ +import type { Metadata } from "next"; import { notFound } from "next/navigation"; import { useCasesNav } from "@/lib/use-cases-nav"; import { MARKETPLACE_URL } from "@/lib/links"; @@ -7,6 +8,24 @@ export function generateStaticParams() { return useCasesNav.map((uc) => ({ slug: uc.slug })); } +export async function generateMetadata({ + params, +}: { + params: Promise<{ slug: string }>; +}): Promise { + const { slug } = await params; + const useCase = useCasesNav.find((uc) => uc.slug === slug); + if (!useCase) notFound(); + + return { + title: `${useCase.title} — BugDrop Use Cases`, + description: useCase.description, + alternates: { + canonical: `/use-cases/${slug}`, + }, + }; +} + export default async function UseCasePage({ params, }: { diff --git a/src/app/use-cases/page.tsx b/src/app/use-cases/page.tsx index 88cfa35..326cef8 100644 --- a/src/app/use-cases/page.tsx +++ b/src/app/use-cases/page.tsx @@ -1,9 +1,13 @@ import Link from "next/link"; +import type { Metadata } from "next"; import { useCasesNav } from "@/lib/use-cases-nav"; -export const metadata = { +export const metadata: Metadata = { title: "Use Cases — BugDrop", description: "See how teams use BugDrop.", + alternates: { + canonical: "/use-cases", + }, }; export default function UseCasesIndex() {