Files
BelgianBitcoinEmbassy/frontend/components/public/JsonLd.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

206 lines
4.8 KiB
TypeScript

interface JsonLdProps {
data: Record<string, unknown>;
}
export function JsonLd({ data }: JsonLdProps) {
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
/>
);
}
const siteUrl =
process.env.NEXT_PUBLIC_SITE_URL || "https://belgianbitcoinembassy.org";
export function OrganizationJsonLd() {
return (
<JsonLd
data={{
"@context": "https://schema.org",
"@type": "Organization",
name: "Belgian Bitcoin Embassy",
url: siteUrl,
logo: `${siteUrl}/og-default.png`,
description:
"Belgium's sovereign Bitcoin community. Monthly meetups, education, and curated Nostr content.",
sameAs: ["https://t.me/belgianbitcoinembassy"],
address: {
"@type": "PostalAddress",
addressLocality: "Antwerp",
addressCountry: "BE",
},
}}
/>
);
}
export function WebSiteJsonLd() {
return (
<JsonLd
data={{
"@context": "https://schema.org",
"@type": "WebSite",
name: "Belgian Bitcoin Embassy",
url: siteUrl,
description:
"Belgium's sovereign Bitcoin community. Monthly meetups, education, and curated Nostr content.",
publisher: {
"@type": "Organization",
name: "Belgian Bitcoin Embassy",
logo: { "@type": "ImageObject", url: `${siteUrl}/og-default.png` },
},
}}
/>
);
}
interface BlogPostingJsonLdProps {
title: string;
description: string;
slug: string;
publishedAt?: string;
authorName?: string;
}
export function BlogPostingJsonLd({
title,
description,
slug,
publishedAt,
authorName,
}: BlogPostingJsonLdProps) {
return (
<JsonLd
data={{
"@context": "https://schema.org",
"@type": "BlogPosting",
headline: title,
description,
url: `${siteUrl}/blog/${slug}`,
...(publishedAt ? { datePublished: publishedAt } : {}),
author: {
"@type": "Person",
name: authorName || "Belgian Bitcoin Embassy",
},
publisher: {
"@type": "Organization",
name: "Belgian Bitcoin Embassy",
logo: { "@type": "ImageObject", url: `${siteUrl}/og-default.png` },
},
image: `${siteUrl}/og?title=${encodeURIComponent(title)}&type=blog`,
mainEntityOfPage: {
"@type": "WebPage",
"@id": `${siteUrl}/blog/${slug}`,
},
}}
/>
);
}
interface EventJsonLdProps {
name: string;
description?: string;
startDate: string;
location?: string;
url: string;
imageUrl?: string;
organizerName?: string;
organizerUrl?: string;
}
export function EventJsonLd({
name,
description,
startDate,
location,
url,
imageUrl,
organizerName,
organizerUrl,
}: EventJsonLdProps) {
const orgName = organizerName || "Belgian Bitcoin Embassy";
const orgUrl = organizerUrl || siteUrl;
return (
<JsonLd
data={{
"@context": "https://schema.org",
"@type": "Event",
name,
description: description || `Bitcoin meetup: ${name}`,
startDate,
eventAttendanceMode: "https://schema.org/OfflineEventAttendanceMode",
eventStatus: "https://schema.org/EventScheduled",
...(location
? {
location: {
"@type": "Place",
name: location,
address: {
"@type": "PostalAddress",
addressLocality: location,
addressCountry: "BE",
},
},
}
: {}),
organizer: {
"@type": "Organization",
name: orgName,
url: orgUrl,
},
image:
imageUrl || `${siteUrl}/og?title=${encodeURIComponent(name)}&type=event`,
url,
}}
/>
);
}
interface FaqJsonLdProps {
items: { question: string; answer: string }[];
}
export function FaqPageJsonLd({ items }: FaqJsonLdProps) {
if (items.length === 0) return null;
return (
<JsonLd
data={{
"@context": "https://schema.org",
"@type": "FAQPage",
mainEntity: items.map((item) => ({
"@type": "Question",
name: item.question,
acceptedAnswer: {
"@type": "Answer",
text: item.answer,
},
})),
}}
/>
);
}
interface BreadcrumbItem {
name: string;
href: string;
}
export function BreadcrumbJsonLd({ items }: { items: BreadcrumbItem[] }) {
return (
<JsonLd
data={{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
itemListElement: items.map((item, index) => ({
"@type": "ListItem",
position: index + 1,
name: item.name,
item: `${siteUrl}${item.href}`,
})),
}}
/>
);
}