a50524832e
- Removed the inline "Nur noch wenige verfügbar" text hint from AddToCartButton/AddToCartInlineButton (was making card heights vary in every grid that renders them — RelatedProducts, ProductSpotlight's CTA row) — now only shown via the same image-overlaid pill badge Ausverkauft/discount already use (position: absolute, doesn't affect layout). Added that badge to RelatedProducts.tsx and todo-cards' Pricing.tsx, which didn't have it before. - RelatedProducts cards now also show "inkl. X% MwSt." (was missing entirely) - VatBreakdown rows are now flex rows with a spacer instead of plain text, so every € amount right-aligns to the same edge regardless of how many digits the rate itself has (was visibly staggered with mixed 7%/19% rates) - Cart's manual discount-code field only renders when Payload actually has at least one active code right now (lib/discountServer.ts's new hasActiveDiscountCode()) — no point showing an open field that could never validate. An already-applied code (e.g. from an older session) still always shows its own result row regardless. - Checkout's "1. Rechnungsadresse" no longer offers a Packstation option — a Packstation isn't a valid billing address for an invoice. Only a plain street address now; Packstation is only offered on the separate, optional "Abweichende Lieferadresse" section, which already had its own address/Packstation toggle. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
68 lines
2.2 KiB
TypeScript
68 lines
2.2 KiB
TypeScript
"use client";
|
|
|
|
// Survives navigating away from /checkout and back (e.g. to double-check
|
|
// something in /cart) — same localStorage approach as lib/cart.ts/
|
|
// lib/discount.ts, but plain read/write functions rather than
|
|
// useSyncExternalStore: CheckoutContent is this draft's only reader, so
|
|
// there's no cross-component subscription to keep in sync the way the cart
|
|
// needs (Navbar + CartContent + CheckoutContent all read it at once).
|
|
// Deliberately excludes `password` — that field stays a plain uncontrolled
|
|
// input, never persisted.
|
|
const DRAFT_KEY = "ep_checkout_draft";
|
|
|
|
export type CheckoutDraft = {
|
|
firstName: string;
|
|
lastName: string;
|
|
email: string;
|
|
// Rechnungsadresse is always a plain street address now — no
|
|
// deliveryMethod/packstationNumber/postNumber here, only on the
|
|
// shipping* override fields below (see CheckoutContent.tsx).
|
|
street: string;
|
|
zip: string;
|
|
city: string;
|
|
country: string;
|
|
hasDifferentShippingAddress: boolean;
|
|
shippingFirstName: string;
|
|
shippingLastName: string;
|
|
shippingDeliveryMethod: "address" | "packstation";
|
|
shippingStreet: string;
|
|
shippingPackstationNumber: string;
|
|
shippingPostNumber: string;
|
|
shippingZip: string;
|
|
shippingCity: string;
|
|
shippingCountry: string;
|
|
newsletterOptIn: boolean;
|
|
shippingMethodId: number | null;
|
|
paymentMethodId: number | null;
|
|
};
|
|
|
|
export function readCheckoutDraft(): Partial<CheckoutDraft> | null {
|
|
if (typeof window === "undefined") return null;
|
|
try {
|
|
const raw = window.localStorage.getItem(DRAFT_KEY);
|
|
return raw ? JSON.parse(raw) : null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function writeCheckoutDraft(draft: CheckoutDraft) {
|
|
try {
|
|
window.localStorage.setItem(DRAFT_KEY, JSON.stringify(draft));
|
|
} catch {
|
|
// localStorage unavailable (private browsing etc.) — the draft just
|
|
// doesn't persist this time, same best-effort fallback as
|
|
// lib/order.ts's sessionStorage write.
|
|
}
|
|
}
|
|
|
|
// Called once an order actually completes (handleSubmit) — a leftover
|
|
// draft from a finished purchase would otherwise pre-fill the next one.
|
|
export function clearCheckoutDraft() {
|
|
try {
|
|
window.localStorage.removeItem(DRAFT_KEY);
|
|
} catch {
|
|
// ignore
|
|
}
|
|
}
|