Fix out-of-stock CTA regressions: grid alignment, preview accuracy, label length

- Replace the invisible reserved-space trick with items-start on every
  product grid (ProductGrid/MerklisteGrid/RelatedProducts) — the
  reservation looked worse in practice (visible dead space under
  in-stock cards' buttons) than letting an out-of-stock card simply be
  taller than its siblings.
- New renderBackInStockHtml() in lib/emailTemplates.ts, used by the
  Live Preview instead of the generic order-status renderer — that one
  showed a fake order number and "Bestellung ansehen", neither of
  which apply to a back-in-stock mail (no order exists). CTA is now
  "Zum Produkt", matching the real backend send.
- Shortened NotifyMeForm's button label ("Benachrichtigen") — the
  longer version wrapped to two lines on narrow single-column cards.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Marco
2026-08-01 20:12:23 +00:00
parent 70ee518e1d
commit 87388479de
9 changed files with 99 additions and 106 deletions
+31 -41
View File
@@ -4,7 +4,7 @@ import { useEffect, useRef, useState } from "react";
import Image from "next/image";
import { addToCart, useCart } from "../lib/cart";
import { useCartFly } from "./CartFly";
import { NotifyMeForm, NotifyMeFormReservedSpace } from "./NotifyMeForm";
import { NotifyMeForm } from "./NotifyMeForm";
// Exported so consumers like RelatedProducts.tsx can delay their own
// follow-up UI changes (e.g. swapping out this exact card) until after
@@ -113,46 +113,36 @@ export function AddToCartInlineButton({
))}
</select>
)}
{/* grid + [grid-area:1/1] stack — NotifyMeFormReservedSpace (an
invisible, non-interactive twin of NotifyMeForm's markup) always
contributes its height here, even for an in-stock card that
never shows the real form. Without it, only out-of-stock cards
would be tall enough to need the input+button, and plain CSS
Grid's row-stretch (ProductGrid.tsx has no explicit row height)
would push every sibling card's button down to match whichever
card in the row happens to be out of stock. */}
<div className="relative grid w-full">
<div className="invisible pointer-events-none [grid-area:1/1]">
<NotifyMeFormReservedSpace />
</div>
{/* self-start, not the stretch default — the button's TOP edge is
what must align across cards (ProductGrid.tsx's flex-1 spacer
pins this whole block to a card's bottom already); the extra
reserved height below a single button just stays blank. */}
<div className="[grid-area:1/1] self-start">
{currentlyOutOfStock ? (
<NotifyMeForm productId={numericId} variantName={variants.length > 0 ? (selectedVariant ?? "") : ""} />
) : (
<button
ref={buttonRef}
type="button"
onClick={handleClick}
disabled={disabled}
className={`${base} ${stateClasses}`}
>
<span
className={
"text-body-sm transition-colors " +
(disabled ? "text-text-muted" : added ? "font-semibold text-success" : "text-text-primary")
}
>
{limitReached ? "Maximale Menge im Warenkorb" : added ? "Hinzugefügt ✓" : label}
</span>
<Image alt="" src="/icon-cart-outline.png" width={32} height={30} className="h-[1.875rem] w-8 object-contain" />
</button>
)}
</div>
</div>
{currentlyOutOfStock ? (
// Replaces the button slot entirely rather than stacking below a
// disabled "Ausverkauft" button. An out-of-stock card is taller
// than its in-stock siblings now — ProductGrid.tsx/MerklisteGrid.tsx/
// RelatedProducts.tsx all use `items-start` on their grid (not the
// CSS Grid default `stretch`) specifically so that doesn't cascade
// into pushing every other card's button down to match; an earlier
// attempt reserved the extra height invisibly on every card
// instead, which looked worse in practice (visible dead space
// under in-stock cards' buttons).
<NotifyMeForm productId={numericId} variantName={variants.length > 0 ? (selectedVariant ?? "") : ""} />
) : (
<button
ref={buttonRef}
type="button"
onClick={handleClick}
disabled={disabled}
className={`${base} ${stateClasses}`}
>
<span
className={
"text-body-sm transition-colors " +
(disabled ? "text-text-muted" : added ? "font-semibold text-success" : "text-text-primary")
}
>
{limitReached ? "Maximale Menge im Warenkorb" : added ? "Hinzugefügt ✓" : label}
</span>
<Image alt="" src="/icon-cart-outline.png" width={32} height={30} className="h-[1.875rem] w-8 object-contain" />
</button>
)}
</div>
);
}