Files
hotschpotsh/Pottery-website/pages/Collections.tsx
2026-03-23 19:00:17 -05:00

144 lines
6.8 KiB
TypeScript

import React from 'react';
import { AnimatePresence, motion } from 'framer-motion';
import { useStore } from '../src/context/StoreContext';
import { CollectionItem } from '../types';
import SEO, { SITE_URL } from '../components/SEO';
const Collections: React.FC = () => {
const { products } = useStore();
const [selectedProduct, setSelectedProduct] = React.useState<CollectionItem | null>(null);
React.useEffect(() => {
if (!selectedProduct) {
return undefined;
}
const handleEscape = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
setSelectedProduct(null);
}
};
window.addEventListener('keydown', handleEscape);
return () => window.removeEventListener('keydown', handleEscape);
}, [selectedProduct]);
const collectionsSchema = {
"@context": "https://schema.org",
"@type": "CollectionPage",
"name": "Handcrafted Ceramic Collection | KNUTH Ceramics",
"description": "Browse KNUTH Ceramics' handcrafted stoneware collection. Each piece is wheel-thrown in Corpus Christi, Texas, inspired by the Gulf Coast. Vases, bowls, tableware, and more from $45.",
"url": `${SITE_URL}/collections`,
"breadcrumb": {
"@type": "BreadcrumbList",
"itemListElement": [
{ "@type": "ListItem", "position": 1, "name": "Home", "item": `${SITE_URL}/` },
{ "@type": "ListItem", "position": 2, "name": "Collections", "item": `${SITE_URL}/collections` }
]
},
"numberOfItems": products.length,
"itemListElement": products.map((p, i) => ({
"@type": "ListItem",
"position": i + 1,
"url": `${SITE_URL}/collections/${p.slug}`,
"name": p.title
}))
};
return (
<>
<SEO
title="Handcrafted Ceramic Collection | KNUTH Ceramics"
description="Browse KNUTH Ceramics' handcrafted stoneware collection. Each piece is wheel-thrown in Corpus Christi, Texas. Vases, bowls, and tableware from $45 — made in the Gulf Coast tradition."
canonical={`${SITE_URL}/collections`}
schema={collectionsSchema}
/>
<section className="pt-32 pb-24 px-6 md:px-12 bg-stone-50 dark:bg-stone-900 min-h-screen">
<div className="max-w-[1920px] mx-auto">
{/* Header */}
<div className="mb-24 text-center">
<motion.h1
initial={{ y: 20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ delay: 0.5, duration: 0.8 }}
className="font-display text-5xl md:text-7xl font-light mb-6 text-text-main dark:text-white"
>
Collection
</motion.h1>
<motion.p
initial={{ y: 20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ delay: 0.7, duration: 0.8 }}
className="font-body text-stone-500 max-w-xl mx-auto text-lg font-light leading-relaxed"
>
A gallery of handmade objects. Select any piece to view it larger, then close when you are done.
</motion.p>
</div>
{/* Grid */}
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-x-6 gap-y-10 lg:gap-x-10 px-4">
{products.map((collection, index) => (
<motion.button
key={collection.id}
type="button"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: index * 0.1 }}
onClick={() => setSelectedProduct(collection)}
className="group block cursor-pointer text-left"
>
<div className="relative overflow-hidden mb-6 aspect-[4/5] bg-stone-100">
<div className="absolute inset-0 bg-black/0 group-hover:bg-black/5 transition-colors duration-500 z-10" />
<img
src={collection.image}
alt={collection.title}
className="w-full h-full object-cover transition-transform duration-700 ease-out group-hover:scale-105"
/>
</div>
</motion.button>
))}
</div>
</div>
</section>
<AnimatePresence>
{selectedProduct && (
<motion.div
className="fixed inset-0 z-[70] bg-black/80 backdrop-blur-sm flex items-center justify-center p-4 sm:p-8"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={() => setSelectedProduct(null)}
>
<motion.div
className="relative flex w-full max-w-6xl items-center justify-center"
initial={{ scale: 0.96, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.96, opacity: 0 }}
transition={{ duration: 0.2 }}
onClick={(event) => event.stopPropagation()}
>
<img
src={selectedProduct.image}
alt={selectedProduct.title}
className="max-h-[92vh] max-w-full object-contain"
/>
<button
type="button"
onClick={() => setSelectedProduct(null)}
className="absolute right-3 top-3 text-4xl font-normal leading-none text-red-500 transition-colors hover:text-red-400"
aria-label="Close image preview"
>
x
</button>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</>
);
};
export default Collections;