southernmonsarysupply/components/material-catalog.tsx

160 lines
5.1 KiB
TypeScript

"use client";
import Image from "next/image";
import Link from "next/link";
import { useState } from "react";
import type { MaterialItem } from "@/data/site-content";
type MaterialCatalogProps = {
heroImage: string;
intro: string;
deliveryNote: string;
materials: MaterialItem[];
};
export function MaterialCatalog({
heroImage,
intro,
deliveryNote,
materials,
}: MaterialCatalogProps) {
const [selectedSubcategories, setSelectedSubcategories] = useState<string[]>(
[],
);
const subcategories = Array.from(
new Set(materials.map((material) => material.subcategory)),
).sort((left, right) => left.localeCompare(right));
const filteredMaterials =
selectedSubcategories.length === 0
? materials
: materials.filter((material) =>
selectedSubcategories.includes(material.subcategory),
);
function toggleFilter(subcategory: string) {
setSelectedSubcategories((current) =>
current.includes(subcategory)
? current.filter((value) => value !== subcategory)
: [...current, subcategory],
);
}
return (
<section id="catalog" className="section catalog-section">
<div className="container catalog-shell">
<aside className="catalog-sidebar" aria-label="Material filters">
<div>
<span className="eyebrow">Filter materials</span>
<h2>Sort by category</h2>
<p>{deliveryNote}</p>
</div>
<div className="filter-group">
<span className="filter-label">Subcategories</span>
<div className="filter-chips">
{subcategories.map((subcategory) => (
<button
key={subcategory}
type="button"
className={`filter-chip ${selectedSubcategories.includes(subcategory) ? "active" : ""
}`}
onClick={() => toggleFilter(subcategory)}
>
{subcategory}
</button>
))}
</div>
</div>
<button
type="button"
className="button button-secondary invert button-block"
onClick={() => setSelectedSubcategories([])}
>
Clear filters
</button>
</aside>
<div className="catalog-main">
<div className="catalog-hero">
<div className="catalog-hero-media">
<Image
src={heroImage}
alt="Material category hero"
fill
sizes="(max-width: 1024px) 100vw, 60vw"
quality={72}
className="cover-image"
/>
</div>
<div className="catalog-hero-content">
<span className="eyebrow">Project-ready inventory</span>
<h2>Materials organized for faster quoting.</h2>
<p>{intro}</p>
</div>
</div>
<div className="catalog-toolbar">
<p className="catalog-count">
Showing {filteredMaterials.length}{" "}
{filteredMaterials.length === 1 ? "material" : "materials"}
</p>
<div className="selected-filters" aria-live="polite">
{selectedSubcategories.map((subcategory) => (
<span key={subcategory} className="selected-chip">
{subcategory}
</span>
))}
</div>
</div>
{filteredMaterials.length > 0 ? (
<div className="material-grid">
{filteredMaterials.map((material) => (
<article
key={material.slug}
className="material-card material-card--catalog reveal"
>
<div className="material-card-media">
<Image
src={material.image}
alt={material.name}
fill
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 25vw"
quality={68}
className="cover-image"
/>
</div>
<div className="material-card-content">
<span className="material-card-tag">{material.subcategory}</span>
<h3>{material.name}</h3>
<p>{material.description}</p>
<div className="material-card-meta">
<span className="unit">
{material.purchaseUnit}
</span>
<Link
href={`/contact?material=${material.slug}`}
className="text-link"
>
Request quote
</Link>
</div>
</div>
</article>
))}
</div>
) : (
<div className="catalog-empty">
<h3>No materials match that filter yet.</h3>
<p>Clear the filters to see the full inventory list again.</p>
</div>
)}
</div>
</div>
</section>
);
}