feat(cart, checkout, bestellbestaetigung): show total savings in the order summary
"Du sparst -X,XX €" line under Zwischensumme, summing (compareAtPrice - price) × qty across items with a discount — only shown when > 0, same conditional-render convention as the rest of the sale-pricing UI (badge/strikethrough already gated on discountPercent() !== null).
This commit is contained in:
@@ -5,7 +5,7 @@ import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import type { CartItem } from "../../lib/cart";
|
||||
import { useProducts } from "../../lib/products";
|
||||
import { formatPrice, formatDate } from "../../lib/format";
|
||||
import { formatPrice, formatDate, discountPercent } from "../../lib/format";
|
||||
import { Reveal } from "../../components/Reveal";
|
||||
import { CheckoutSteps } from "../../components/CheckoutSteps";
|
||||
import { ORDER_KEY, type OrderSnapshot } from "../../lib/order";
|
||||
@@ -96,6 +96,10 @@ export function BestellbestaetigungContent() {
|
||||
.filter((row): row is { entry: CartItem; product: NonNullable<(typeof row)["product"]> } => Boolean(row.product));
|
||||
|
||||
const subtotal = items.reduce((sum, { entry, product }) => sum + entry.qty * product.price, 0);
|
||||
const totalSavings = items.reduce((sum, { entry, product }) => {
|
||||
const discount = discountPercent(product.price, product.compareAtPrice);
|
||||
return discount !== null ? sum + entry.qty * (product.compareAtPrice! - product.price) : sum;
|
||||
}, 0);
|
||||
const total = subtotal + order.shippingCost;
|
||||
|
||||
return (
|
||||
@@ -194,6 +198,13 @@ export function BestellbestaetigungContent() {
|
||||
<span className="flex-1" />
|
||||
<span className="text-body-sm text-text-primary">{formatPrice(subtotal)}</span>
|
||||
</div>
|
||||
{totalSavings > 0 && (
|
||||
<div className="flex items-center w-full">
|
||||
<span className="text-body-sm text-success">Du sparst</span>
|
||||
<span className="flex-1" />
|
||||
<span className="font-bold text-body-sm text-success">-{formatPrice(totalSavings)}</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center w-full">
|
||||
<span className="text-body-sm text-text-primary">Versand</span>
|
||||
<span className="flex-1" />
|
||||
|
||||
@@ -40,6 +40,10 @@ export function CartContent({
|
||||
.filter((row): row is { entry: typeof cart[number]; product: NonNullable<(typeof row)["product"]> } => Boolean(row.product));
|
||||
|
||||
const subtotal = items.reduce((sum, { entry, product }) => sum + entry.qty * product.price, 0);
|
||||
const totalSavings = items.reduce((sum, { entry, product }) => {
|
||||
const discount = discountPercent(product.price, product.compareAtPrice);
|
||||
return discount !== null ? sum + entry.qty * (product.compareAtPrice! - product.price) : sum;
|
||||
}, 0);
|
||||
const shipping =
|
||||
items.length === 0 || (freeShippingThreshold !== null && subtotal >= freeShippingThreshold)
|
||||
? 0
|
||||
@@ -187,6 +191,14 @@ export function CartContent({
|
||||
<span className="text-body-sm text-text-primary">{formatPrice(subtotal)}</span>
|
||||
</div>
|
||||
|
||||
{totalSavings > 0 && (
|
||||
<div className="flex items-center w-full">
|
||||
<span className="text-body-sm text-success">Du sparst</span>
|
||||
<span className="flex-1" />
|
||||
<span className="font-bold text-body-sm text-success">-{formatPrice(totalSavings)}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col gap-0.5 w-full">
|
||||
<div className="flex items-center w-full">
|
||||
<span className="flex items-center gap-1.5 text-body-sm text-text-primary">
|
||||
|
||||
@@ -5,7 +5,7 @@ import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { useCart, clearCart } from "../../lib/cart";
|
||||
import { useProducts } from "../../lib/products";
|
||||
import { formatPrice } from "../../lib/format";
|
||||
import { formatPrice, discountPercent } from "../../lib/format";
|
||||
import { Reveal } from "../../components/Reveal";
|
||||
import { VersandModal } from "../../components/VersandModal";
|
||||
import { CheckoutSteps } from "../../components/CheckoutSteps";
|
||||
@@ -50,6 +50,10 @@ export function CheckoutContent({
|
||||
.filter((row): row is { entry: typeof cart[number]; product: NonNullable<(typeof row)["product"]> } => Boolean(row.product));
|
||||
|
||||
const subtotal = items.reduce((sum, { entry, product }) => sum + entry.qty * product.price, 0);
|
||||
const totalSavings = items.reduce((sum, { entry, product }) => {
|
||||
const discount = discountPercent(product.price, product.compareAtPrice);
|
||||
return discount !== null ? sum + entry.qty * (product.compareAtPrice! - product.price) : sum;
|
||||
}, 0);
|
||||
const selectedShipping = shippingMethods.find((m) => m.id === shippingMethodId) ?? null;
|
||||
const freeShipping =
|
||||
selectedShipping?.freeShippingThreshold !== null &&
|
||||
@@ -343,6 +347,14 @@ export function CheckoutContent({
|
||||
<span className="text-body-sm text-text-primary">{formatPrice(subtotal)}</span>
|
||||
</div>
|
||||
|
||||
{totalSavings > 0 && (
|
||||
<div className="flex items-center w-full">
|
||||
<span className="text-body-sm text-success">Du sparst</span>
|
||||
<span className="flex-1" />
|
||||
<span className="font-bold text-body-sm text-success">-{formatPrice(totalSavings)}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col gap-0.5 w-full">
|
||||
<div className="flex items-center w-full">
|
||||
<span className="flex items-center gap-1.5 text-body-sm text-text-primary">
|
||||
|
||||
Reference in New Issue
Block a user