115 lines
3.1 KiB
TypeScript
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>
|
|
</>
|
|
);
|
|
}
|