Fertig
This commit is contained in:
159
components/material-catalog.tsx
Normal file
159
components/material-catalog.tsx
Normal file
@@ -0,0 +1,159 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user