trptk/app/concerts/page.tsx
2026-02-24 17:14:07 +01:00

115 lines
3.1 KiB
TypeScript

import type { Metadata } from "next";
import { defineQuery } from "next-sanity";
import { sanity } from "@/lib/sanity";
import { AnimatedText } from "@/components/AnimatedText";
import {
ConcertTable,
getDisplayTitle,
type ConcertData,
} from "@/components/concert/ConcertTable";
export const revalidate = 86400;
export const metadata: Metadata = {
title: "Concerts",
description:
"Upcoming and past TRPTK concerts. Find live performances by TRPTK artists near you.",
};
const CONCERT_PROJECTION = `{
_id,
title,
subtitle,
date,
time,
locationName,
city,
country,
"artists": artists[]->{ _id, name, "slug": slug.current },
ticketUrl
}`;
const UPCOMING_CONCERTS_QUERY = defineQuery(`
*[_type == "concert" && date >= $today]
${CONCERT_PROJECTION}
| order(date asc, time asc)
`);
const PAST_CONCERTS_QUERY = defineQuery(`
*[_type == "concert" && date < $today]
${CONCERT_PROJECTION}
| order(date desc, time desc)
`);
export default async function ConcertsPage() {
const today = new Date().toISOString().slice(0, 10);
const [upcoming, past] = await Promise.all([
sanity.fetch<ConcertData[]>(UPCOMING_CONCERTS_QUERY, { today }),
sanity.fetch<ConcertData[]>(PAST_CONCERTS_QUERY, { today }),
]);
const jsonLd = upcoming.length
? upcoming.map((concert) => ({
"@context": "https://schema.org",
"@type": "MusicEvent",
name: getDisplayTitle(concert),
startDate: `${concert.date}T${concert.time}:00`,
url: "https://trptk.com/concerts",
...(concert.locationName && {
location: {
"@type": "Place",
name: concert.locationName,
address: {
"@type": "PostalAddress",
...(concert.city && { addressLocality: concert.city }),
...(concert.country && {
addressCountry: concert.country.toUpperCase(),
}),
},
},
}),
...(concert.ticketUrl && {
offers: {
"@type": "Offer",
url: concert.ticketUrl,
},
}),
...(concert.artists?.length && {
performer: concert.artists.map((a) => ({
"@type": "MusicGroup",
name: a.name,
})),
}),
}))
: null;
return (
<>
{jsonLd && (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(jsonLd).replace(/</g, "\\u003c"),
}}
/>
)}
<main className="mx-auto my-auto max-w-300 px-6 py-12 font-silka md:px-8 md:py-16 2xl:max-w-400">
{upcoming.length > 0 && (
<section className="mb-16">
<AnimatedText text="Upcoming concerts" as="h2" className="mb-4 font-argesta text-3xl" />
<ConcertTable concerts={upcoming} />
</section>
)}
{past.length > 0 && (
<section>
<AnimatedText text="Past concerts" as="h2" className="mb-4 font-argesta text-3xl" />
<ConcertTable concerts={past} past />
</section>
)}
</main>
</>
);
}