first commit
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
---
|
||||
import Layout from "../../layouts/Layout.astro";
|
||||
import AddToCartButton from "../../components/AddToCartButton.astro";
|
||||
import { getProductByHandle, getDefaultRegion } from "../../lib/medusa";
|
||||
import { formatEUR, formatGrundpreis } from "../../lib/format";
|
||||
|
||||
export const prerender = false;
|
||||
|
||||
const { handle } = Astro.params;
|
||||
const region = await getDefaultRegion();
|
||||
const product = handle ? await getProductByHandle(handle, region.id) : null;
|
||||
|
||||
if (!product) {
|
||||
return Astro.redirect("/shop");
|
||||
}
|
||||
|
||||
const infoByVariant = Object.fromEntries(
|
||||
product.variants.map((variant) => [
|
||||
variant.id,
|
||||
{
|
||||
price: variant.calculated_price ? formatEUR(variant.calculated_price.calculated_amount) : "",
|
||||
grundpreis:
|
||||
variant.calculated_price && variant.metadata?.net_weight_grams
|
||||
? formatGrundpreis(variant.calculated_price.calculated_amount, variant.metadata.net_weight_grams)
|
||||
: null,
|
||||
},
|
||||
])
|
||||
);
|
||||
const initial = infoByVariant[product.variants[0]?.id] ?? { price: "", grundpreis: null };
|
||||
const deliveryTime = product.metadata?.delivery_time;
|
||||
|
||||
const gallery = (product.images?.length ? product.images.map((i) => i.url) : product.thumbnail ? [product.thumbnail] : []);
|
||||
---
|
||||
|
||||
<Layout title={product.title} description={product.description ?? product.title}>
|
||||
<section class="mx-auto max-w-6xl px-4 sm:px-6 lg:px-8 py-8 sm:py-12">
|
||||
<nav class="flex items-center gap-2 text-sm text-brand-text-muted">
|
||||
<a href="/shop" class="hover:text-brand-primary transition-colors">Shop</a>
|
||||
<span>/</span>
|
||||
{product.metadata?.category && (
|
||||
<>
|
||||
<span>{product.metadata.category}</span>
|
||||
<span>/</span>
|
||||
</>
|
||||
)}
|
||||
<span class="text-brand-text">{product.title}</span>
|
||||
</nav>
|
||||
|
||||
<div class="mt-8 grid gap-12 sm:grid-cols-2">
|
||||
<div>
|
||||
<div id="main-image" class="aspect-square rounded-2xl bg-brand-surface overflow-hidden">
|
||||
{gallery[0] && <img src={gallery[0]} alt={product.title} class="h-full w-full object-cover" />}
|
||||
</div>
|
||||
{gallery.length > 1 && (
|
||||
<div class="mt-4 flex gap-3">
|
||||
{gallery.map((url, index) => (
|
||||
<button
|
||||
class:list={[
|
||||
"gallery-thumb h-16 w-16 rounded-lg overflow-hidden ring-2 transition-colors",
|
||||
index === 0 ? "ring-brand-primary" : "ring-transparent",
|
||||
]}
|
||||
data-src={url}
|
||||
>
|
||||
<img src={url} alt="" class="h-full w-full object-cover" />
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{product.metadata?.category && (
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.2em] text-brand-accent">{product.metadata.category}</p>
|
||||
)}
|
||||
<h1 class="mt-2 text-3xl font-semibold text-brand-ink">{product.title}</h1>
|
||||
|
||||
<div class="mt-3 flex items-baseline gap-3">
|
||||
<p id="product-price" class="text-2xl font-semibold text-brand-primary">{initial.price}</p>
|
||||
<span class="text-sm text-brand-text-muted">inkl. MwSt.</span>
|
||||
</div>
|
||||
<p id="product-grundpreis" class="mt-1 text-sm text-brand-text-muted">{initial.grundpreis ?? ""}</p>
|
||||
|
||||
<p class="mt-6 text-brand-text whitespace-pre-line">{product.description}</p>
|
||||
|
||||
<div class="mt-8">
|
||||
<AddToCartButton variants={product.variants} regionId={region.id} />
|
||||
</div>
|
||||
|
||||
<div class="mt-8 grid gap-3 sm:grid-cols-2">
|
||||
{deliveryTime && (
|
||||
<p class="flex items-center gap-2 text-sm text-brand-text-muted">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
|
||||
</svg>
|
||||
Lieferzeit: {deliveryTime}
|
||||
</p>
|
||||
)}
|
||||
<p class="flex items-center gap-2 text-sm text-brand-text-muted">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
Regional aus Hamburg
|
||||
</p>
|
||||
<p class="flex items-center gap-2 text-sm text-brand-text-muted">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
Nachhaltig produziert
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script type="application/json" id="variant-info" set:html={JSON.stringify(infoByVariant)} />
|
||||
</Layout>
|
||||
|
||||
<script>
|
||||
const priceEl = document.getElementById("product-price");
|
||||
const grundpreisEl = document.getElementById("product-grundpreis");
|
||||
const infoJson = document.getElementById("variant-info")?.textContent;
|
||||
|
||||
if (priceEl && grundpreisEl && infoJson) {
|
||||
const info: Record<string, { price: string; grundpreis: string | null }> = JSON.parse(infoJson);
|
||||
document.querySelectorAll<HTMLInputElement>('input[name="variant"]').forEach((input) => {
|
||||
input.addEventListener("change", () => {
|
||||
const entry = info[input.value];
|
||||
priceEl.textContent = entry?.price ?? "";
|
||||
grundpreisEl.textContent = entry?.grundpreis ?? "";
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const mainImage = document.querySelector<HTMLImageElement>("#main-image img");
|
||||
document.querySelectorAll<HTMLButtonElement>(".gallery-thumb").forEach((thumb) => {
|
||||
thumb.addEventListener("click", () => {
|
||||
if (mainImage && thumb.dataset.src) mainImage.src = thumb.dataset.src;
|
||||
document.querySelectorAll(".gallery-thumb").forEach((t) => {
|
||||
t.classList.remove("ring-brand-primary");
|
||||
t.classList.add("ring-transparent");
|
||||
});
|
||||
thumb.classList.remove("ring-transparent");
|
||||
thumb.classList.add("ring-brand-primary");
|
||||
});
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user