Deactivated product shows as sold out, StepArrow to black, newsletter checkmarks fixed
- /todo-cards's detail page (the only page resolving a product regardless of active status) now renders both CTAs as "Ausverkauft" and disabled when the product is deactivated, instead of fully buyable — variants are dropped too, since AddToCartButton's outOfStock prop is otherwise ignored whenever variants is non-empty. - StepArrow: brand orange didn't fit here after all, switched to near-black (#222221) per feedback. - Newsletter/"Impulse & Tipps" checklist checkmarks (WeeklyImpulsesHero.tsx, WeeklyBenefits.tsx) were still the old un-recolorable icon-check.svg — same inline-SVG orange-checkmark fix as todo-cards/Challenge, plus items-start instead of items-center on WeeklyImpulsesHero's list for top-alignment consistency.
This commit is contained in:
@@ -7,14 +7,16 @@
|
||||
// 2. Its color lives in a `var(--stroke-0, #C9C9C9)` CSS custom property
|
||||
// that's scoped to the SVG file's own document when loaded via <img
|
||||
// src>/next/image — un-recolorable from the host page's CSS, so it could
|
||||
// never actually become the brand orange used everywhere else (buttons,
|
||||
// checkmarks, icons). Inline SVG sidesteps that entirely.
|
||||
// never actually become a solid dark color like the rest of this file's
|
||||
// icons. Inline SVG sidesteps that entirely. Stroke color tried brand
|
||||
// orange first, then switched to near-black (#222221, same as
|
||||
// icon-check.svg's original fill) per feedback the same day.
|
||||
export function StepArrow({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg width="40" height="16" viewBox="0 0 40 16" fill="none" aria-hidden="true" className={className}>
|
||||
<path
|
||||
d="M1 8H33M26 14.5L34.5 8L26 1.5"
|
||||
stroke="#f6a701"
|
||||
stroke="#222221"
|
||||
strokeWidth="2.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
|
||||
@@ -8,6 +8,17 @@ const benefits = [
|
||||
{ title: "Motivation & Erinnerung", desc: "Ein freundlicher Schub in die richtige Richtung." },
|
||||
];
|
||||
|
||||
// Inline, brand-orange stroke — same fix/reasoning as
|
||||
// WeeklyImpulsesHero.tsx's own IconCheck (icon-check.svg's fill can't be
|
||||
// recolored from outside the SVG when loaded via <img src>/next/image).
|
||||
function IconCheck() {
|
||||
return (
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" className="size-5 shrink-0 mt-0.5">
|
||||
<path d="M4 10.5l4.5 4.5L16 5.5" stroke="#f6a701" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function WeeklyBenefits() {
|
||||
return (
|
||||
<section className="w-full bg-bg-base flex flex-col lg:flex-row gap-10 lg:gap-16 items-center py-12 md:py-16 px-[var(--layout-padding-x)]">
|
||||
@@ -31,7 +42,7 @@ export function WeeklyBenefits() {
|
||||
<ul className="flex flex-col gap-4 items-start w-full">
|
||||
{benefits.map((b) => (
|
||||
<li key={b.title} className="flex gap-[0.625rem] items-start w-full">
|
||||
<Image alt="" src="/icon-check.svg" width={20} height={20} className="size-5 shrink-0 mt-0.5" />
|
||||
<IconCheck />
|
||||
<div className="flex flex-col gap-0.5 items-start flex-1 min-w-0">
|
||||
<p className="font-semibold text-body text-text-primary">{b.title}</p>
|
||||
<p className="text-body-sm text-text-muted">{b.desc}</p>
|
||||
|
||||
@@ -16,6 +16,17 @@ function LockIcon() {
|
||||
);
|
||||
}
|
||||
|
||||
// Inline, brand-orange stroke — icon-check.svg's fill lives in an internal
|
||||
// CSS var that can't be recolored from outside the SVG when loaded via
|
||||
// <img src>/next/image, same fix/reasoning as todo-cards's own IconCheck.
|
||||
function IconCheck() {
|
||||
return (
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" className="size-5 shrink-0 mt-0.5">
|
||||
<path d="M4 10.5l4.5 4.5L16 5.5" stroke="#f6a701" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
const checklist = [
|
||||
"Jeden Mittwoch neue Impulse & Tipps",
|
||||
"Kurz & knackig – in 5 Minuten gelesen",
|
||||
@@ -89,8 +100,8 @@ export function WeeklyImpulsesHero() {
|
||||
|
||||
<ul className="flex flex-col gap-3 items-start w-full">
|
||||
{checklist.map((item) => (
|
||||
<li key={item} className="flex gap-[0.625rem] items-center w-full">
|
||||
<Image alt="" src="/icon-check.svg" width={20} height={20} className="size-5 shrink-0" />
|
||||
<li key={item} className="flex gap-[0.625rem] items-start w-full">
|
||||
<IconCheck />
|
||||
<span className="flex-1 text-body text-text-primary">{item}</span>
|
||||
</li>
|
||||
))}
|
||||
|
||||
@@ -28,8 +28,17 @@ export async function Pricing() {
|
||||
const discount = discountPercent(product.price, product.compareAtPrice);
|
||||
const taxRate = effectiveTaxRate(product, defaultTaxRate);
|
||||
// Same "any vs. every" split as ProductGrid.tsx/ProductSpotlight.tsx.
|
||||
const fullyOutOfStock = product.variants.length > 0 ? product.variants.every((v) => v.outOfStock) : product.outOfStock;
|
||||
const anyLowStock = product.variants.length > 0 ? product.variants.some((v) => v.lowStock) : product.lowStock;
|
||||
// A deactivated product (product.active === false) isn't caught by
|
||||
// either — the shop grid/spotlight filter those out before they'd ever
|
||||
// reach this page, but this page resolves a product regardless of
|
||||
// active status (existing links to it should still 200, see
|
||||
// mapPayloadProduct's own comment in lib/payload.ts) — so it needs its
|
||||
// own explicit check here to read as "Ausverkauft" rather than fully
|
||||
// buyable (fixed 2026-07-24, feedback: deactivated should look
|
||||
// sold-out on its own detail page).
|
||||
const fullyOutOfStock =
|
||||
!product.active || (product.variants.length > 0 ? product.variants.every((v) => v.outOfStock) : product.outOfStock);
|
||||
const anyLowStock = product.active && (product.variants.length > 0 ? product.variants.some((v) => v.lowStock) : product.lowStock);
|
||||
|
||||
return (
|
||||
<section className="w-full bg-bg-base px-[var(--layout-padding-x)] py-8">
|
||||
@@ -100,12 +109,17 @@ export async function Pricing() {
|
||||
this is an add-to-cart step, not the actual payment step, so
|
||||
a payment-security reassurance is premature here and just
|
||||
duplicates the one shown later at checkout. */}
|
||||
{/* variants={[]} when deactivated — AddToCartButton's own
|
||||
outOfStock prop is ignored whenever variants is non-empty (it
|
||||
defers to each variant's own outOfStock flag instead, see its
|
||||
currentlyOutOfStock calc), so a deactivated product with
|
||||
in-stock variants would otherwise still render as buyable. */}
|
||||
<AddToCartButton
|
||||
label="In den Warenkorb"
|
||||
className="w-full inline-flex items-center justify-center px-6 py-[0.8125rem] rounded-sm bg-brand hover:bg-brand-hover active:scale-[0.97] transition-all font-bold text-body text-text-primary text-center focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand focus-visible:ring-offset-2 focus-visible:ring-offset-bg-muted"
|
||||
outOfStock={product.outOfStock}
|
||||
outOfStock={!product.active || product.outOfStock}
|
||||
maxQty={product.maxQty}
|
||||
variants={product.variants}
|
||||
variants={product.active ? product.variants : []}
|
||||
/>
|
||||
</div>
|
||||
</Reveal>
|
||||
|
||||
@@ -41,7 +41,9 @@ export async function TodoKartenHero() {
|
||||
const discount = product ? discountPercent(product.price, product.compareAtPrice) : null;
|
||||
const taxRate = product ? effectiveTaxRate(product, defaultTaxRate) : null;
|
||||
// Same "any vs. every" split as ProductGrid.tsx/Pricing.tsx.
|
||||
const anyLowStock = product ? (product.variants.length > 0 ? product.variants.some((v) => v.lowStock) : product.lowStock) : false;
|
||||
const anyLowStock = product
|
||||
? product.active && (product.variants.length > 0 ? product.variants.some((v) => v.lowStock) : product.lowStock)
|
||||
: false;
|
||||
|
||||
return (
|
||||
<section className="bg-bg-base w-full overflow-hidden">
|
||||
@@ -133,7 +135,15 @@ export async function TodoKartenHero() {
|
||||
</div>
|
||||
|
||||
{product && (
|
||||
<AddToCartButton label="ToDo-Karten bestellen" outOfStock={product.outOfStock} maxQty={product.maxQty} variants={product.variants} />
|
||||
// variants={[]} when deactivated — see Pricing.tsx's own
|
||||
// comment on why AddToCartButton's outOfStock prop alone
|
||||
// isn't enough once variants is non-empty.
|
||||
<AddToCartButton
|
||||
label="ToDo-Karten bestellen"
|
||||
outOfStock={!product.active || product.outOfStock}
|
||||
maxQty={product.maxQty}
|
||||
variants={product.active ? product.variants : []}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Reveal>
|
||||
|
||||
Reference in New Issue
Block a user