chore: initial import

This commit is contained in:
2025-11-09 00:26:00 -06:00
commit 67fb60e6ca
76 changed files with 3925 additions and 0 deletions

View File

@@ -0,0 +1,95 @@
import Head from 'next/head';
import SiteLayout from '../../components/SiteLayout';
import { buildMediaUrl, fetchProduct, fetchProducts } from '../../lib/api';
import ProductCard from '../../components/ProductCard';
import { useCart } from '../../context/CartContext';
export default function ProductPage({ product, suggestions }) {
const { addItem } = useCart();
const imageUrl = buildMediaUrl(product.hero_image) || product.imageUrl;
const price = Number(product.price || 0).toFixed(2);
return (
<SiteLayout>
<Head>
<title>{product.name} · Full Moon Bakehouse</title>
<meta name="description" content={product.short_description || product.description || ''} />
</Head>
<div className="wrapper">
<section className="product-detail">
<div className="product-detail__gallery">
<img src={imageUrl} alt={product.name} />
</div>
<div className="product-detail__info">
<button className="ghost-btn" onClick={() => history.back()} style={{ width: 'fit-content' }}>
Back to products
</button>
<h1>{product.name}</h1>
<div className="product-detail__price">${price}</div>
{product.short_description ? (
<p className="product-detail__description">{product.short_description}</p>
) : null}
{product.description ? (
<p className="product-detail__description">{product.description}</p>
) : null}
<div className="product-detail__meta">
<span>Inventory: {product.inventory_quantity ?? '—'} available</span>
<span>Max {product.max_per_order ?? '—'} per order</span>
{product.allergens ? <span>Allergens: {product.allergens}</span> : null}
{product.ingredients ? <span>Ingredients: {product.ingredients}</span> : null}
</div>
<div className="product-card__actions">
<button className="btn btn-primary" type="button" onClick={() => addItem(product)}>
Add to cart
</button>
{Number.isFinite(product.max_per_order) ? (
<span className="cart-note">Max {product.max_per_order} per order</span>
) : null}
</div>
</div>
</section>
{suggestions.length ? (
<section className="section" style={{ paddingTop: 0 }}>
<div className="section-header">
<div>
<h2>More from this week&apos;s bake</h2>
<p>Round out your order with another loaf, bagel pack, or sweet.</p>
</div>
</div>
<div className="product-grid">
{suggestions.map((item) => (
<ProductCard key={item.id || item.slug} product={item} />
))}
</div>
</section>
) : null}
</div>
</SiteLayout>
);
}
export async function getServerSideProps({ params }) {
const { slug } = params;
try {
const product = await fetchProduct(slug);
const products = await fetchProducts();
const suggestions = products
.filter((item) => item.slug !== slug)
.slice(0, 4)
.map((item) => ({ ...item, imageUrl: buildMediaUrl(item.hero_image), isSample: false }));
return {
props: {
product: { ...product, imageUrl: buildMediaUrl(product.hero_image), isSample: false },
suggestions,
},
};
} catch (error) {
console.warn('Product detail fallback', error);
return { notFound: true };
}
}