Simplify crossSell to a single hand-picked product; badge everywhere; gallery cursor

- CrossSellBlock: dropped mode (automatic/manual) + hasMany products
  down to one required `product` relationship, same filterOptions-
  excludes-self dropdown as Products.relatedProduct — the "automatic,
  same category" mode added complexity a plain dropdown already covered
  better, and the frontend's card was always designed around one
  recommendation, not a grid (reported 2026-08-29).
- New shared ProductBadge component (app/components/ProductBadge.tsx),
  used by both ProductBlocks.tsx (PDP) and ProductCard.tsx (shop grid,
  cart's RelatedProducts, wishlist grid) — Products.badge ("Neu") only
  showed on the PDP before, now consistent everywhere a product's image
  renders a badge.
- ProductGallery's arrow/thumbnail buttons get an explicit cursor-pointer
  class.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013Eg6h91yngXmSnM51wxXM8
This commit is contained in:
Marco
2026-08-29 23:53:41 +00:00
parent 48e73796b1
commit 31b8e89ea4
5 changed files with 72 additions and 103 deletions
+3 -4
View File
@@ -1384,7 +1384,7 @@ export type ProductBlock =
// needs getProductsByIds), done in ProductBlocks.tsx's own outer
// Promise.all, mirroring how that file already special-cases
// testimonialsRef for the exact same reason.
| { blockType: "crossSell"; id: string; mode: "automatic" | "manual"; productIds: number[] };
| { blockType: "crossSell"; id: string; productId: number };
type PayloadProductBlock =
| { blockType: "productHero"; id: string; body?: unknown | null }
@@ -1409,7 +1409,7 @@ type PayloadProductBlock =
bullets?: { id: string; text: string }[];
}
| { blockType: "faq"; id: string; items: { id: string; question: string; answer: unknown }[] }
| { blockType: "crossSell"; id: string; mode?: "automatic" | "manual" | null; products?: (number | { id: number })[] | null };
| { blockType: "crossSell"; id: string; product: number | { id: number } };
function mapPayloadProductBlock(block: PayloadProductBlock): ProductBlock {
switch (block.blockType) {
@@ -1445,8 +1445,7 @@ function mapPayloadProductBlock(block: PayloadProductBlock): ProductBlock {
return {
blockType: "crossSell",
id: block.id,
mode: block.mode ?? "automatic",
productIds: (block.products ?? []).map((p) => (typeof p === "object" ? p.id : p)),
productId: typeof block.product === "object" ? block.product.id : block.product,
};
default:
return block;