From 5a1944b216f80e2aef29bd47f80c671cf246123a Mon Sep 17 00:00:00 2001 From: Erol Haagenrud Date: Mon, 20 Apr 2026 08:41:51 +0200 Subject: [PATCH] =?UTF-8?q?F=C3=B8r=20aleggsbilde-interface?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/golfbaner/[slug]/opengraph-image.tsx | 35 ++++++++++++- frontend/src/app/golfbaner/[slug]/page.tsx | 52 ++++++++++++++++--- 2 files changed, 78 insertions(+), 9 deletions(-) diff --git a/frontend/src/app/golfbaner/[slug]/opengraph-image.tsx b/frontend/src/app/golfbaner/[slug]/opengraph-image.tsx index dbe54f4..9c877e3 100755 --- a/frontend/src/app/golfbaner/[slug]/opengraph-image.tsx +++ b/frontend/src/app/golfbaner/[slug]/opengraph-image.tsx @@ -1,5 +1,6 @@ import { ImageResponse } from "next/og"; import { API_URL } from "@/config/constants"; +import { resolveFacilityAlias } from "@/app/facilityAliases"; import { buildAbsoluteUrl } from "@/app/seo"; export const size = { @@ -13,14 +14,44 @@ type OpenGraphImageProps = { params: Promise<{ slug: string }>; }; +type OpenGraphFacility = { + name: string; + city?: string | null; + county?: string | null; + image_url?: string | null; +}; + async function getFacility(slug: string) { const res = await fetch(`${API_URL}/facilities/${slug}`, { cache: "no-store" }); - return res.json(); + if (!res.ok) { + return null; + } + + const facility = (await res.json()) as Partial | null; + if (!facility?.name) { + return null; + } + + return facility as OpenGraphFacility; +} + +async function getFacilityForSlug(slug: string) { + const facility = await getFacility(slug); + if (facility) { + return facility; + } + + const canonicalSlug = await resolveFacilityAlias(slug); + if (!canonicalSlug || canonicalSlug === slug) { + return null; + } + + return getFacility(canonicalSlug); } export default async function OpenGraphImage({ params }: OpenGraphImageProps) { const { slug } = await params; - const facility = await getFacility(slug); + const facility = await getFacilityForSlug(slug); const title = facility?.name || "TeeOff"; const location = [facility?.city, facility?.county].filter(Boolean).join(" · ") || "Norske golfbaner"; diff --git a/frontend/src/app/golfbaner/[slug]/page.tsx b/frontend/src/app/golfbaner/[slug]/page.tsx index bc5a4e0..8e83025 100755 --- a/frontend/src/app/golfbaner/[slug]/page.tsx +++ b/frontend/src/app/golfbaner/[slug]/page.tsx @@ -1,5 +1,8 @@ import type { Metadata } from "next"; +import { notFound, permanentRedirect } from "next/navigation"; import { API_URL } from "@/config/constants"; +import { resolveFacilityAlias } from "@/app/facilityAliases"; +import type { FacilityRecord } from "@/app/facilityData"; import { createBreadcrumbJsonLd, createFacilityJsonLd, @@ -13,17 +16,47 @@ type GolfCoursePageProps = { params: Promise<{ slug: string }>; }; +type FacilityPageData = FacilityRecord & { + address?: string | null; + [key: string]: unknown; +}; + async function getFacility(slug: string) { const res = await fetch(`${API_URL}/facilities/${slug}`, { cache: "no-store" }); - return res.json(); + if (!res.ok) { + return null; + } + + const facility = (await res.json()) as Partial | null; + if (typeof facility?.id !== "number" || !facility?.name || !facility?.slug) { + return null; + } + + return facility as FacilityPageData; +} + +async function getCanonicalSlug(slug: string) { + const canonicalSlug = await resolveFacilityAlias(slug); + if (!canonicalSlug || canonicalSlug === slug) { + return null; + } + + return canonicalSlug; } export async function generateMetadata({ params }: GolfCoursePageProps): Promise { const { slug } = await params; try { - const facility = await getFacility(slug); - if (!facility || facility.error) { + let facility = await getFacility(slug); + if (!facility) { + const canonicalSlug = await getCanonicalSlug(slug); + if (canonicalSlug) { + facility = await getFacility(canonicalSlug); + } + } + + if (!facility) { return createPageMetadata({ title: "Golfbane ikke funnet", description: "Golfbanen du prøvde å åpne finnes ikke på TeeOff.", @@ -37,8 +70,8 @@ export async function generateMetadata({ params }: GolfCoursePageProps): Promise return createPageMetadata({ title, description: trimDescription(facility.description) || fallbackDescription, - path: `/golfbaner/${slug}`, - image: `/golfbaner/${slug}/opengraph-image`, + path: `/golfbaner/${facility.slug}`, + image: `/golfbaner/${facility.slug}/opengraph-image`, }); } catch { return createPageMetadata({ @@ -53,8 +86,13 @@ export default async function GolfCoursePage({ params }: GolfCoursePageProps) { const { slug } = await params; const facility = await getFacility(slug); - if (!facility || facility.error) { - return
Fant ikke golfbanen...
; + if (!facility) { + const canonicalSlug = await getCanonicalSlug(slug); + if (canonicalSlug) { + permanentRedirect(`/golfbaner/${canonicalSlug}`); + } + + notFound(); } const facilityJsonLd = createFacilityJsonLd(facility);