feat(selection): lock single-option boxes, drop the about link
build / build (push) Successful in 1m38s
build / build (push) Successful in 1m38s
Recovered work in progress that sat uncommitted since 2026-05: a SelectionBox renders as a plain div instead of a NuxtLink when locked, so a variant with only one choice reads as decided rather than clickable, and it reports aria-checked in that state. useProductContent gained the content lookups the details and index pages now use, the cookie banner says Schließen instead of Akzeptieren, the Über uns header entry is gone, and ProductCard's hover shadow no longer relies on a short-circuit that could yield a non-array.
This commit is contained in:
+41
-31
@@ -77,48 +77,40 @@
|
||||
{{ product?.cover?.copyText?.paper }}
|
||||
</p>
|
||||
|
||||
<div class="flex flex-col-reverse lg:flex-row-reverse gap-4 xl:gap-12">
|
||||
<div class="flex flex-col-reverse lg:flex-row-reverse lg:items-center gap-4 xl:gap-12">
|
||||
<div class="lg:w-1/2 xl:w-2/5 xl:pt-8">
|
||||
<div class="rounded-lg bg-white bg-opacity-100 shadow-md mb-16 relative">
|
||||
<!-- Pages Variant Selection -->
|
||||
<section
|
||||
v-if="productVariantsPages?.length"
|
||||
class="relative overflow-hidden flex flex-col xl:flex-row-reverse gap-4 xl:gap-12 py-6 px-6 xl:px-8 xl:pt-20"
|
||||
>
|
||||
<h2 class="hidden lg:block pointer-events-none absolute left-8 top-4 text-right xl:text-left pt-3 tracking-tight opacity-70 text-xl font-semibold">
|
||||
Seiten
|
||||
</h2>
|
||||
<div class="flex gap-4 z-10">
|
||||
<!-- Pages Variant Selection (locked chip when only one option) -->
|
||||
<section v-if="productVariantsPages?.length" class="flex flex-col gap-3 py-6 px-6 xl:px-8">
|
||||
<h2 class="tracking-tight opacity-70 text-lg font-semibold">Seiten</h2>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<SelectionBox
|
||||
v-for="pages in productVariantsPages"
|
||||
:key="pages.id"
|
||||
:label="pages.name"
|
||||
:path="pages.productSlug ? `/details/${pages.productSlug}` : ''"
|
||||
:is-active="pages.active"
|
||||
:locked="productVariantsPages.length === 1"
|
||||
:aria-disabled="isAddingToCart || isChangingVariant"
|
||||
@click="handleVariantClick('pages', pages)"
|
||||
@click="productVariantsPages.length > 1 && handleVariantClick('pages', pages)"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<hr v-if="productVariantsPages?.length" class="border-t-[1px] border-black opacity-40" />
|
||||
|
||||
<!-- Ruling Variant Selection -->
|
||||
<section
|
||||
v-if="productVariantsRuling?.length"
|
||||
class="relative overflow-hidden flex flex-col xl:flex-row-reverse gap-4 xl:gap-12 py-6 px-6 xl:px-8 xl:pt-20"
|
||||
>
|
||||
<h2 class="hidden lg:block pointer-events-none absolute left-8 top-4 text-right xl:text-left pt-3 tracking-tight opacity-70 text-xl font-semibold">
|
||||
Layout
|
||||
</h2>
|
||||
<div class="flex gap-4 z-10">
|
||||
<section v-if="productVariantsRuling?.length" class="flex flex-col gap-3 py-6 px-6 xl:px-8">
|
||||
<h2 class="tracking-tight opacity-70 text-lg font-semibold">Layout</h2>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<SelectionBox
|
||||
v-for="ruling in productVariantsRuling"
|
||||
:key="ruling.id"
|
||||
:label="ruling.name"
|
||||
:path="ruling.productSlug ? `/details/${ruling.productSlug}` : ''"
|
||||
:is-active="ruling.active"
|
||||
:locked="productVariantsRuling.length === 1"
|
||||
:aria-disabled="isAddingToCart || isChangingVariant"
|
||||
@click="handleVariantClick('ruling', ruling)"
|
||||
@click="productVariantsRuling.length > 1 && handleVariantClick('ruling', ruling)"
|
||||
>
|
||||
<img v-if="ruling.iconUrl" :alt="ruling.name" :src="ruling.iconUrl" />
|
||||
</SelectionBox>
|
||||
@@ -126,15 +118,10 @@
|
||||
</section>
|
||||
<hr v-if="productVariantsRuling?.length" class="border-t-[1px] border-black opacity-40" />
|
||||
|
||||
<!-- Cover Variant Selection -->
|
||||
<section
|
||||
v-if="productVariantsCover?.length"
|
||||
class="flex flex-col relative xl:flex-row-reverse gap-4 xl:gap-12 py-6 px-6 xl:px-8 xl:pt-20 overflow-hidden"
|
||||
>
|
||||
<h2 class="hidden lg:block pointer-events-none absolute left-8 top-4 text-right xl:text-left pt-3 tracking-tight opacity-70 text-xl font-semibold">
|
||||
Einband
|
||||
</h2>
|
||||
<div class="flex gap-4 z-10">
|
||||
<!-- Cover Variant Selection (only when a real choice exists) -->
|
||||
<section v-if="productVariantsCover?.length > 1" class="flex flex-col gap-3 py-6 px-6 xl:px-8">
|
||||
<h2 class="tracking-tight opacity-70 text-lg font-semibold">Einband</h2>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<SelectionBox
|
||||
v-for="cover in productVariantsCover"
|
||||
:key="cover.id"
|
||||
@@ -148,7 +135,18 @@
|
||||
</SelectionBox>
|
||||
</div>
|
||||
</section>
|
||||
<hr v-if="productVariantsCover?.length" class="border-t-[1px] border-black opacity-40" />
|
||||
<hr v-if="productVariantsCover?.length > 1" class="border-t-[1px] border-black opacity-40" />
|
||||
|
||||
<!-- Fixed product facts (e.g. Journaling Booklet) as pre-selected locked chips -->
|
||||
<template v-for="fact in buyBoxFacts" :key="fact.label">
|
||||
<section class="flex flex-col gap-3 py-6 px-6 xl:px-8">
|
||||
<h2 class="tracking-tight opacity-70 text-lg font-semibold">{{ fact.label }}</h2>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<SelectionBox :label="fact.value" :path="''" :locked="true" :aria-label="`${fact.label}: ${fact.value}`" />
|
||||
</div>
|
||||
</section>
|
||||
<hr class="border-t-[1px] border-black opacity-40" />
|
||||
</template>
|
||||
|
||||
<!-- Price and Add to Cart -->
|
||||
<section v-if="product?.totalProductPrice" class="flex flex-col lg:flex-row gap-6 py-6 px-6 lg:px-8 items-center">
|
||||
@@ -186,7 +184,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col items-center lg:w-1/2 xl:w-3/5 xl:pt-2 mx-auto">
|
||||
<div class="flex flex-col items-center lg:w-1/2 xl:w-3/5 lg:py-12 xl:py-16 mx-auto">
|
||||
<a v-if="productImage" :href="productImage.url" class="flex items-center justify-center mx-8" target="_blank" rel="noopener noreferrer">
|
||||
<img :src="productImage.formats?.medium?.url" :alt="`Overhead product shot of MUELLERPRINTS. Paperwork ${product?.name}`" class="object-contain" />
|
||||
</a>
|
||||
@@ -421,6 +419,18 @@ const productVariantsPatterns = computed(() => {
|
||||
}));
|
||||
});
|
||||
|
||||
// Fixed/standalone products (no selectable pages & ruling, e.g. Journaling Booklet)
|
||||
// surface their key specs as pre-selected locked chips in the buy box.
|
||||
const buyBoxFacts = computed(() => {
|
||||
if (productVariantsPages.value.length || productVariantsRuling.value.length) return [];
|
||||
const s = (technicalSpecs.value ?? {}) as Record<string, string>;
|
||||
const facts: { label: string; value: string }[] = [];
|
||||
if (s.Umfang) facts.push({ label: "Umfang", value: s.Umfang });
|
||||
if (s.Layout) facts.push({ label: "Layout", value: s.Layout });
|
||||
if (s.Einband) facts.push({ label: "Einband", value: s.Bindung ? `${s.Einband} · ${s.Bindung}` : s.Einband });
|
||||
return facts;
|
||||
});
|
||||
|
||||
// Testimonials data
|
||||
const testimonials: Testimonial[] = [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user