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

142 lines
4.6 KiB
TypeScript

"use client";
import Link from "next/link";
import Image from "next/image";
import { HiOutlineArrowDownTray } from "react-icons/hi2";
import { urlFor } from "@/lib/sanityImage";
import type { SanityImageSource } from "@sanity/image-url";
export type DownloadFormat = {
variant_title: string;
sku: string;
download_url: string;
};
export type UpcomingFormat = {
variant_title: string;
sku: string;
};
export type DownloadCardData = {
product_title: string;
albumCover: SanityImageSource | null;
albumArtist: string | null;
slug: string | null;
catalogNo: string | null;
releaseDate: string | null;
genre: string[];
instrumentation: string[];
formats: DownloadFormat[];
};
export type UpcomingCardData = {
product_title: string;
albumCover: SanityImageSource | null;
albumArtist: string | null;
slug: string | null;
catalogNo: string | null;
releaseDate: string | null;
genre: string[];
instrumentation: string[];
release_date: string;
formats: UpcomingFormat[];
};
function formatReleaseMonth(dateString?: string) {
if (!dateString) return null;
return new Intl.DateTimeFormat("en-US", {
month: "short",
year: "numeric",
}).format(new Date(dateString));
}
const downloadPillClass = [
"inline-flex items-center gap-1.5",
"text-sm text-lightsec dark:text-darksec",
"hover:text-trptkblue dark:hover:text-white",
"transition-all duration-200 ease-in-out",
].join(" ");
export function DownloadCard({ item }: { item: DownloadCardData }) {
const coverSrc = item.albumCover ? urlFor(item.albumCover).url() : null;
const href = item.slug ? `/release/${item.slug}` : "#";
return (
<div className="group flex flex-col rounded-xl shadow-lg ring-1 ring-lightline transition-all duration-300 ease-in-out hover:ring-lightline-hover dark:ring-darkline dark:hover:ring-darkline-hover">
<div className="block">
<div className="relative aspect-square w-full overflow-hidden rounded-xl">
{coverSrc ? (
<Image
src={coverSrc}
alt={`Album cover for ${item.product_title}`}
fill
className="object-cover"
sizes="(max-width: 560px) calc(100vw - 32px), 450px"
/>
) : (
<div className="absolute inset-0 bg-lightline-mid dark:bg-darkline-mid" />
)}
</div>
<div className="p-4 pb-0 sm:p-5 sm:pb-0">
<h3 className="mb-2 break-words">{item.product_title}</h3>
{item.albumArtist && (
<h4 className="text-sm break-words text-lightsec dark:text-darksec">
{item.albumArtist}
</h4>
)}
</div>
</div>
{/* Format download buttons */}
<div className="mt-auto flex flex-col gap-1 p-4 sm:p-5">
{item.formats.map((fmt) => (
<a key={fmt.sku} href={fmt.download_url} className={downloadPillClass}>
<HiOutlineArrowDownTray className="shrink-0" />
<span className="truncate">{fmt.variant_title}</span>
</a>
))}
</div>
</div>
);
}
export function UpcomingCard({ item }: { item: UpcomingCardData }) {
const coverSrc = item.albumCover ? urlFor(item.albumCover).url() : null;
const href = item.slug ? `/release/${item.slug}` : "#";
return (
<Link
href={href}
className="group relative rounded-xl shadow-lg ring-1 ring-lightline transition-all duration-300 ease-in-out hover:text-trptkblue hover:ring-lightline-hover dark:ring-darkline dark:hover:text-white dark:hover:ring-darkline-hover"
>
<div className="relative aspect-square w-full overflow-hidden rounded-t-xl opacity-60">
{coverSrc ? (
<Image
src={coverSrc}
alt={`Album cover for ${item.product_title}`}
fill
className="object-cover"
sizes="(max-width: 560px) calc(100vw - 32px), 450px"
/>
) : (
<div className="absolute inset-0 bg-lightline-mid dark:bg-darkline-mid" />
)}
</div>
<div className="p-4 sm:p-5 sm:pb-15">
<h3 className="mb-2 break-words">{item.product_title}</h3>
{item.albumArtist && (
<h4 className="text-sm break-words text-lightsec dark:text-darksec">
{item.albumArtist}
</h4>
)}
</div>
<div className="absolute right-4 bottom-4 left-4 hidden items-center justify-between text-lightsec opacity-50 sm:right-5 sm:bottom-5 sm:left-5 sm:flex dark:text-darksec">
<span className="text-sm">{item.catalogNo}</span>
<span className="text-sm">{formatReleaseMonth(item.release_date)}</span>
</div>
</Link>
);
}