trptk/components/concert/ConcertTable.tsx
2026-02-24 17:14:07 +01:00

122 lines
3.6 KiB
TypeScript

import { IconButtonMiniLink } from "@/components/IconButtonMini";
import { IoTicketOutline } from "react-icons/io5";
export type ConcertData = {
_id: string;
title?: string;
subtitle?: string;
date: string;
time: string;
locationName?: string;
city?: string;
country?: string;
artists?: { _id: string; name?: string; slug?: string }[];
ticketUrl?: string;
};
function formatConcertDate(dateString: string) {
return new Intl.DateTimeFormat("en-GB", {
day: "numeric",
month: "short",
year: "numeric",
}).format(new Date(dateString + "T00:00:00"));
}
function getCountryCode(code?: string): string | undefined {
if (!code) return undefined;
return code.toUpperCase();
}
/** City + country code, e.g. "Utrecht (NL)" */
function formatCityCountry(city?: string, country?: string): string | undefined {
const code = getCountryCode(country);
if (city && code) return `${city} (${code})`;
if (city) return city;
if (code) return `(${code})`;
return undefined;
}
export function getDisplayTitle(concert: ConcertData): string {
if (concert.title) return concert.title;
if (concert.artists?.length) {
return concert.artists
.map((a) => a.name)
.filter(Boolean)
.join(", ");
}
return "Concert";
}
export function ConcertRow({
concert,
past,
}: {
concert: ConcertData;
past?: boolean;
}) {
const displayTitle = getDisplayTitle(concert);
const cityCountry = formatCityCountry(concert.city, concert.country);
return (
<tr>
<td className="py-4 pr-4 align-middle whitespace-nowrap text-lightsec dark:text-darksec">
<div className="text-lighttext dark:text-darktext">{formatConcertDate(concert.date)}</div>
<div className="text-sm">{concert.time}</div>
</td>
<td className="py-4 pr-4 align-middle text-lighttext dark:text-darktext">
<div className="line-clamp-1 font-silkasb">{displayTitle}</div>
{concert.subtitle && (
<div className="line-clamp-1 text-lightsec dark:text-darksec">{concert.subtitle}</div>
)}
{cityCountry && (
<div className="line-clamp-1 text-lightsec md:hidden dark:text-darksec">{cityCountry}</div>
)}
</td>
<td className="hidden py-4 pr-4 align-middle md:table-cell">
{concert.locationName && (
<div className="line-clamp-1 text-lighttext dark:text-darktext">{concert.locationName}</div>
)}
{cityCountry && <div className="line-clamp-1 text-lightsec dark:text-darksec">{cityCountry}</div>}
</td>
<td className="py-4 align-middle">
{!past && concert.ticketUrl && (
<IconButtonMiniLink
href={concert.ticketUrl}
target="_blank"
rel="noopener noreferrer"
aria-label="Buy tickets"
className="inline-flex items-center justify-center !p-1.5"
>
<IoTicketOutline className="size-4" />
</IconButtonMiniLink>
)}
</td>
</tr>
);
}
export function ConcertTable({
concerts,
past,
}: {
concerts: ConcertData[];
past?: boolean;
}) {
if (concerts.length === 0) return null;
return (
<table className="w-full table-fixed text-left text-sm">
<colgroup>
<col className="w-28 md:w-32" />
<col />
<col className="hidden md:table-column md:w-56" />
<col className="w-10" />
</colgroup>
<tbody className="divide-y-1 divide-lighttext/10 dark:divide-darktext/10">
{concerts.map((concert) => (
<ConcertRow key={concert._id} concert={concert} past={past} />
))}
</tbody>
</table>
);
}