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
This commit is contained in:
42
frontend/app/events/organizer/[slug]/page.tsx
Normal file
42
frontend/app/events/organizer/[slug]/page.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import type { Metadata } from "next";
|
||||
import { notFound } from "next/navigation";
|
||||
import OrganizerEventsClient from "./OrganizerEventsClient";
|
||||
import { apiUrl } from "@/lib/api-base";
|
||||
|
||||
async function fetchOrganizer(slug: string) {
|
||||
try {
|
||||
const res = await fetch(apiUrl(`/organizers/by-slug/${encodeURIComponent(slug)}`), {
|
||||
next: { revalidate: 300 },
|
||||
});
|
||||
if (!res.ok) return null;
|
||||
return res.json() as Promise<{ id: string; name: string; slug: string }>;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
interface Props {
|
||||
params: Promise<{ slug: string }>;
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
||||
const { slug } = await params;
|
||||
const org = await fetchOrganizer(slug);
|
||||
if (!org) {
|
||||
return { title: "Organizer not found" };
|
||||
}
|
||||
return {
|
||||
title: `Events by ${org.name}`,
|
||||
description: `Upcoming and past Bitcoin events organized by ${org.name} in Belgium.`,
|
||||
alternates: { canonical: `/events/organizer/${slug}` },
|
||||
};
|
||||
}
|
||||
|
||||
export default async function OrganizerArchivePage({ params }: Props) {
|
||||
const { slug } = await params;
|
||||
const org = await fetchOrganizer(slug);
|
||||
if (!org) {
|
||||
notFound();
|
||||
}
|
||||
return <OrganizerEventsClient slug={slug} organizerName={org.name} />;
|
||||
}
|
||||
Reference in New Issue
Block a user