101 lines
3.0 KiB
TypeScript
101 lines
3.0 KiB
TypeScript
"use client";
|
|
|
|
// import { PortfolioImage } from "@/generated/prisma";
|
|
// Possibly unused: no references found in `src/app` or `src/components`.
|
|
import { cn } from "@/lib/utils";
|
|
import type { ArtworkWithRelations } from "@/types/Artwork";
|
|
import Image from "next/image";
|
|
import Link from "next/link";
|
|
import { useSearchParams } from "next/navigation";
|
|
import type { CSSProperties } from "react";
|
|
import { useState } from "react";
|
|
import { Button } from "../ui/button";
|
|
|
|
// Client-side artwork gallery with incremental pagination.
|
|
export default function ArtworkGallery({
|
|
initialArtworks,
|
|
initialCursor,
|
|
}: {
|
|
initialArtworks: ArtworkWithRelations[];
|
|
initialCursor: string | null;
|
|
}) {
|
|
const searchParams = useSearchParams();
|
|
const published = searchParams.get("published") ?? "all";
|
|
|
|
const [artworks, setArtworks] = useState(initialArtworks);
|
|
const [cursor, setCursor] = useState<string | null>(initialCursor);
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
const loadMore = async () => {
|
|
if (!cursor || loading) return;
|
|
setLoading(true);
|
|
|
|
try {
|
|
const qs = new URLSearchParams();
|
|
qs.set("published", published);
|
|
qs.set("cursor", cursor);
|
|
qs.set("take", "48");
|
|
|
|
const res = await fetch(`/api/artworks/page?${qs.toString()}`, {
|
|
method: "GET",
|
|
cache: "no-store",
|
|
});
|
|
|
|
if (!res.ok) throw new Error("Failed to load more artworks");
|
|
|
|
const data: { items: ArtworkWithRelations[]; nextCursor: string | null } =
|
|
await res.json();
|
|
|
|
setArtworks((prev) => [...prev, ...data.items]);
|
|
setCursor(data.nextCursor);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="w-full flex flex-col gap-4">
|
|
<div className="flex flex-wrap gap-4">
|
|
{artworks.map((artwork) => (
|
|
<div key={artwork.id} style={{ width: 200, height: 200 }}>
|
|
<Link href={`/artworks/${artwork.id}`}>
|
|
<div
|
|
className={cn(
|
|
"overflow-hidden transition-all duration-100",
|
|
"w-full h-full",
|
|
"hover:border-2 border-transparent"
|
|
)}
|
|
style={{
|
|
'--tw-border-opacity': 1,
|
|
} as CSSProperties}
|
|
>
|
|
<div
|
|
className={cn(
|
|
"relative w-full h-full"
|
|
)}
|
|
>
|
|
<Image
|
|
src={`/api/image/thumbnail/${artwork.file.fileKey}.webp`}
|
|
alt={artwork.altText ?? artwork.name ?? "Image"}
|
|
fill
|
|
className={cn("object-cover"
|
|
)}
|
|
loading="lazy"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
</div>
|
|
))}
|
|
</div>
|
|
{cursor ? (
|
|
<div className="flex justify-center">
|
|
<Button onClick={loadMore} disabled={loading}>
|
|
{loading ? "Loading..." : "Load more"}
|
|
</Button>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|