Files
BelgianBitcoinEmbassy/frontend/app/events/[id]/page.tsx
bbe 78271ea110 feat: organizers, meetups UI, Plausible analytics, and migration tooling
- Add organizer model/API, admin and public organizer pages, meetup cards
- Refresh events/home/contact; add calendar dialog and carousel components
- Optional Plausible via NEXT_PUBLIC_PLAUSIBLE_* env vars in root layout
- Prisma migration, seed updates, baseline-and-migrate script

Made-with: Cursor
2026-04-04 21:55:34 +02:00

93 lines
2.5 KiB
TypeScript

import type { Metadata } from "next";
import EventDetailClient from "./EventDetailClient";
import { EventJsonLd, BreadcrumbJsonLd } from "@/components/public/JsonLd";
import { apiUrl } from "@/lib/api-base";
async function fetchEvent(id: string) {
try {
const res = await fetch(apiUrl(`/meetups/${id}`), {
next: { revalidate: 300 },
});
if (!res.ok) return null;
return res.json();
} catch {
return null;
}
}
interface Props {
params: Promise<{ id: string }>;
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { id } = await params;
const event = await fetchEvent(id);
if (!event) {
return { title: "Event Not Found" };
}
const orgLabel = event.organizer?.name || "Belgian Bitcoin Embassy";
const description =
event.description?.slice(0, 160) ||
`Bitcoin meetup: ${event.title}${event.location ? ` in ${event.location}` : ""}. Organized by ${orgLabel}.`;
const ogImage = event.imageId
? `/media/${event.imageId}`
: `/og?title=${encodeURIComponent(event.title)}&type=event`;
return {
title: event.title,
description,
openGraph: {
type: "article",
title: event.title,
description,
images: [{ url: ogImage, width: 1200, height: 630, alt: event.title }],
},
twitter: {
card: "summary_large_image",
title: event.title,
description,
images: [ogImage],
},
alternates: { canonical: `/events/${id}` },
};
}
export default async function EventDetailPage({ params }: Props) {
const { id } = await params;
const event = await fetchEvent(id);
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || "https://belgianbitcoinembassy.org";
return (
<>
{event && (
<>
<EventJsonLd
name={event.title}
description={event.description}
startDate={event.date}
location={event.location}
url={`${siteUrl}/events/${id}`}
imageUrl={event.imageId ? `${siteUrl}/media/${event.imageId}` : undefined}
organizerName={event.organizer?.name}
organizerUrl={
event.organizer?.slug
? `${siteUrl}/events/organizer/${event.organizer.slug}`
: undefined
}
/>
<BreadcrumbJsonLd
items={[
{ name: "Home", href: "/" },
{ name: "Events", href: "/events" },
{ name: event.title, href: `/events/${id}` },
]}
/>
</>
)}
<EventDetailClient id={id} />
</>
);
}