Nye-TeeOff/frontend/src/app/golfbaner/[slug]/opengraph-image.tsx

165 lines
4.2 KiB
TypeScript
Raw Normal View History

2026-04-13 13:18:25 +02:00
import { ImageResponse } from "next/og";
import { API_URL } from "@/config/constants";
2026-04-20 08:41:51 +02:00
import { resolveFacilityAlias } from "@/app/facilityAliases";
2026-04-13 13:18:25 +02:00
import { buildAbsoluteUrl } from "@/app/seo";
export const size = {
width: 1200,
height: 630,
};
export const contentType = "image/png";
type OpenGraphImageProps = {
params: Promise<{ slug: string }>;
};
2026-04-20 08:41:51 +02:00
type OpenGraphFacility = {
name: string;
city?: string | null;
county?: string | null;
image_url?: string | null;
};
2026-04-13 13:18:25 +02:00
async function getFacility(slug: string) {
const res = await fetch(`${API_URL}/facilities/${slug}`, { cache: "no-store" });
2026-04-20 08:41:51 +02:00
if (!res.ok) {
return null;
}
const facility = (await res.json()) as Partial<OpenGraphFacility> | 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);
2026-04-13 13:18:25 +02:00
}
export default async function OpenGraphImage({ params }: OpenGraphImageProps) {
const { slug } = await params;
2026-04-20 08:41:51 +02:00
const facility = await getFacilityForSlug(slug);
2026-04-13 13:18:25 +02:00
const title = facility?.name || "TeeOff";
2026-04-14 16:31:28 +02:00
const location = [facility?.city, facility?.county].filter(Boolean).join(" · ") || "Norske golfbaner";
2026-04-13 13:18:25 +02:00
const imageUrl = facility?.image_url ? buildAbsoluteUrl(facility.image_url) : null;
return new ImageResponse(
(
<div
style={{
display: "flex",
position: "relative",
width: "100%",
height: "100%",
overflow: "hidden",
background: "linear-gradient(135deg, #1f2b24 0%, #3b4a3f 100%)",
color: "white",
}}
>
{imageUrl ? (
<img
src={imageUrl}
alt={title}
width={1200}
height={630}
style={{
position: "absolute",
inset: 0,
width: "100%",
height: "100%",
objectFit: "cover",
}}
/>
) : null}
<div
style={{
position: "absolute",
inset: 0,
background:
"linear-gradient(180deg, rgba(17,32,21,0.15) 0%, rgba(17,32,21,0.82) 72%, rgba(17,32,21,0.92) 100%)",
}}
/>
<div
style={{
position: "relative",
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
width: "100%",
height: "100%",
padding: "52px 58px",
}}
>
<div
style={{
2026-04-26 11:29:35 +02:00
display: "flex",
2026-04-13 13:18:25 +02:00
alignSelf: "flex-start",
borderRadius: 999,
background: "rgba(139, 195, 74, 0.92)",
color: "#112015",
padding: "12px 22px",
fontSize: 22,
fontWeight: 800,
letterSpacing: "0.18em",
textTransform: "uppercase",
}}
>
TeeOff
</div>
<div style={{ display: "flex", flexDirection: "column", gap: 18, maxWidth: 900 }}>
<div
style={{
display: "flex",
fontSize: 28,
fontWeight: 700,
letterSpacing: "0.08em",
textTransform: "uppercase",
color: "#9ecb66",
}}
>
Golfbane
</div>
<div
style={{
display: "flex",
fontSize: 66,
lineHeight: 1.02,
fontWeight: 900,
letterSpacing: "-0.04em",
}}
>
{title}
</div>
<div
style={{
display: "flex",
fontSize: 30,
fontWeight: 600,
color: "rgba(255,255,255,0.88)",
}}
>
{location}
</div>
</div>
</div>
</div>
),
size,
);
}