c5500bcc97
Completes the frontend half of the Payload backend's variant/inventory/
tracking work (see that repo's own commit):
- **Cart**: CartItem gained an optional `variant?: string` field — every
function that used to match a line by `id` alone (addToCart/
removeFromCart/setQuantity) now matches by `(id, variant)` together via
a shared sameLine() helper, so two lines for the same product with
different variants stay separate entries. `variant` undefined on both
sides (the no-variants case) still matches by simple equality, so every
pre-existing call site keeps working unchanged.
- **Selection UI**: AddToCartInlineButton renders a <select> above the
button when given a non-empty `variants` prop (ProductGrid/
RelatedProducts pass product.variants straight through); defaults to
the first variant.
- **Pricing**: cartTotals.ts's new effectivePrice(entry, product) — a
variant's priceOverride wins over the base product price. Every cart/
checkout/order-confirmation total and per-line price display now goes
through this instead of reading product.price directly (fixes both a
wrong-price bug and a duplicate-React-key bug the old `key={product.id}`
pattern would have had the moment two variants of one product were both
in the cart).
- **Checkout**: re-validates the requested variant server-side (same
"never trust the client" reasoning as price re-derivation) — a variant
name that doesn't exist on that product fails the whole checkout.
variantName snapshots onto orders.items, shown as a parenthetical next
to the product name on the confirmation email, both invoice PDF types,
and the order-detail page.
- **Cross-device cart**: Customers.cart[].variantName (synced via
/api/account/cart) carries the selection through a login/logout cycle,
not just the current session.
Also adds tracking-number display: /konto/bestellungen/[orderNumber]
shows a clickable link when orders.trackingNumber is set, built by a new
app/lib/tracking.ts that mirrors the Payload backend's own copy
byte-for-byte close (same carrier set/URL patterns) so what a customer
sees here matches exactly what the order-shipped email already links to.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
27 lines
1.3 KiB
TypeScript
27 lines
1.3 KiB
TypeScript
// Mirrors the Payload backend's own src/lib/tracking.ts — same carrier
|
|
// set/labels/URL patterns, kept in sync by hand (two separate
|
|
// deployments, no shared package). Used to render a clickable tracking
|
|
// link on /konto/bestellungen/[orderNumber]; the backend's copy builds
|
|
// the same link for the order-shipped email.
|
|
export const CARRIER_LABELS: Record<string, string> = {
|
|
dhl: "DHL",
|
|
dpd: "DPD",
|
|
hermes: "Hermes",
|
|
ups: "UPS",
|
|
gls: "GLS",
|
|
other: "Sonstiger Versanddienstleister",
|
|
};
|
|
|
|
const CARRIER_TRACKING_URL: Record<string, (trackingNumber: string) => string> = {
|
|
dhl: (n) => `https://www.dhl.de/de/privatkunden/dhl-sendungsverfolgung.html?piececode=${encodeURIComponent(n)}`,
|
|
dpd: (n) => `https://tracking.dpd.de/status/de_DE/parcel/${encodeURIComponent(n)}`,
|
|
hermes: (n) => `https://www.myhermes.de/empfangen/sendungsverfolgung/sendungsinformation/#${encodeURIComponent(n)}`,
|
|
ups: (n) => `https://www.ups.com/track?loc=de_DE&tracknum=${encodeURIComponent(n)}`,
|
|
gls: (n) => `https://www.gls-pakete.de/sendungsverfolgung?trackingNumber=${encodeURIComponent(n)}`,
|
|
};
|
|
|
|
export function buildTrackingUrl(carrier: string | null | undefined, trackingNumber: string | null | undefined): string | null {
|
|
if (!carrier || !trackingNumber) return null;
|
|
return CARRIER_TRACKING_URL[carrier]?.(trackingNumber) ?? null;
|
|
}
|