142 lines
4.6 KiB
TypeScript
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>
|
|
);
|
|
}
|