Honor Products.noShippingCost across cart, checkout, and product pages

- api/checkout/route.ts: authoritative shipping charge is 0 whenever
  every cart line opts out via noShippingCost, regardless of the
  free-shipping threshold.
- Cart/checkout order summaries: the whole "Versand" line (cost, free-
  shipping note, delivery time) is hidden entirely rather than showing
  "Kostenlos" — that's a different state from hitting the threshold.
- ProductSpotlight/Pricing/TodoKartenHero: "zzgl. Versand" and delivery-
  time hints drop for an exempted product's own page.
- /versand + its shared modal: one clarifying sentence that digital
  products are exempt.
- Widerrufsformular link: opens inline in a new tab (no forced download),
  arrow icon changed from a download glyph to a plain right arrow to
  match.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Marco
2026-07-29 22:48:33 +00:00
parent bffc7d39a2
commit a3f843ad15
13 changed files with 145 additions and 70 deletions
+10
View File
@@ -37,6 +37,16 @@ export function computeSubtotal(items: CartLine[]): number {
return items.reduce((sum, { entry, product }) => sum + entry.qty * effectivePrice(entry, product), 0);
}
// A cart only needs a shipping line at all if at least one item doesn't
// opt out via Products.noShippingCost (e.g. a purely digital download) —
// mirrors api/checkout/route.ts's own hasShippableItem check, which is
// the actual charged amount; this is only the storefront's estimate/
// display before that. A single non-exempt item still triggers normal
// shipping for the whole cart, this never partially discounts it.
export function cartHasShippableItem(items: CartLine[]): boolean {
return items.some(({ product }) => !product.noShippingCost);
}
export type CartTotals = {
subtotal: number;
/** compareAtPrice-based per-product savings — already excluded from