Blog categories (hasMany), shipping-address contact fields, product SKU on invoices
- Posts.categories is now hasMany — blog list/detail/live-preview render a comma-joined list instead of a single category. - Checkout's "Abweichende Lieferadresse" gains optional Firma + Kontakt- E-Mail/Telefon fields (handed to the shipping carrier, not used for customer communication). - Customer profile can now store its own shipping address (mirroring Customers.ts's new "Lieferadresse" tab), prefilling the checkout override instead of always starting blank. - Product-level optional SKU (previously only on variants) snapshots onto each order item and shows up on invoices (visual + EN16931 XML), the confirmation email, and the account order detail page.
This commit is contained in:
@@ -243,19 +243,32 @@ export function CheckoutContent({
|
||||
const [zip, setZip] = useState(savedProfile?.zip ?? "");
|
||||
const [city, setCity] = useState(savedProfile?.city ?? "");
|
||||
const [country, setCountry] = useState(savedProfile?.country ?? "Deutschland");
|
||||
// Optional package destination distinct from the billing address above —
|
||||
// no savedProfile fallback (a customer's saved profile has only ever had
|
||||
// one address), just an empty draft-only section.
|
||||
const [hasDifferentShippingAddress, setHasDifferentShippingAddress] = useState(false);
|
||||
const [shippingFirstName, setShippingFirstName] = useState("");
|
||||
const [shippingLastName, setShippingLastName] = useState("");
|
||||
const [shippingDeliveryMethod, setShippingDeliveryMethod] = useState<"address" | "packstation">("address");
|
||||
const [shippingStreet, setShippingStreet] = useState("");
|
||||
const [shippingPackstationNumber, setShippingPackstationNumber] = useState("");
|
||||
const [shippingPostNumber, setShippingPostNumber] = useState("");
|
||||
const [shippingZip, setShippingZip] = useState("");
|
||||
const [shippingCity, setShippingCity] = useState("");
|
||||
const [shippingCountry, setShippingCountry] = useState("Deutschland");
|
||||
// Optional package destination distinct from the billing address above.
|
||||
// Seeded from savedProfile's own "Lieferadresse" tab (Customers.ts),
|
||||
// same precedence as Card 1's billing fields above: draft (if any)
|
||||
// overwrites this in the hydration effect below, savedProfile is only
|
||||
// the pre-hydration/SSR-safe fallback. Unlike Card 1, savedProfile *can*
|
||||
// supply these now (see Customers.ts's "Lieferadresse" tab) — this used
|
||||
// to always start empty since the saved profile had only one address.
|
||||
const [hasDifferentShippingAddress, setHasDifferentShippingAddress] = useState(savedProfile?.hasDifferentShippingAddress ?? false);
|
||||
const [shippingFirstName, setShippingFirstName] = useState(savedProfile?.shippingFirstName ?? "");
|
||||
const [shippingLastName, setShippingLastName] = useState(savedProfile?.shippingLastName ?? "");
|
||||
const [shippingDeliveryMethod, setShippingDeliveryMethod] = useState<"address" | "packstation">(
|
||||
savedProfile?.shippingDeliveryMethod ?? "address",
|
||||
);
|
||||
const [shippingStreet, setShippingStreet] = useState(savedProfile?.shippingStreet ?? "");
|
||||
const [shippingPackstationNumber, setShippingPackstationNumber] = useState(savedProfile?.shippingPackstationNumber ?? "");
|
||||
const [shippingPostNumber, setShippingPostNumber] = useState(savedProfile?.shippingPostNumber ?? "");
|
||||
const [shippingZip, setShippingZip] = useState(savedProfile?.shippingZip ?? "");
|
||||
const [shippingCity, setShippingCity] = useState(savedProfile?.shippingCity ?? "");
|
||||
const [shippingCountry, setShippingCountry] = useState(savedProfile?.shippingCountry ?? "Deutschland");
|
||||
// Optional, mirrors companyName above — no shipping-side vatId though,
|
||||
// a VAT ID is a billing/invoice concept, not a shipping one. Contact
|
||||
// email/phone have no billing-side equivalent at all: they're handed to
|
||||
// the shipping carrier, not used for any customer communication.
|
||||
const [shippingCompanyName, setShippingCompanyName] = useState(savedProfile?.shippingCompanyName ?? "");
|
||||
const [shippingContactEmail, setShippingContactEmail] = useState(savedProfile?.shippingContactEmail ?? "");
|
||||
const [shippingContactPhone, setShippingContactPhone] = useState(savedProfile?.shippingContactPhone ?? "");
|
||||
const [newsletterOptIn, setNewsletterOptIn] = useState(false);
|
||||
// Live VIES status for the USt-IdNr. field — only meaningful once the
|
||||
// goods' destination (shipping override country when set, billing
|
||||
@@ -301,6 +314,9 @@ export function CheckoutContent({
|
||||
if (draft.shippingZip) setShippingZip(draft.shippingZip);
|
||||
if (draft.shippingCity) setShippingCity(draft.shippingCity);
|
||||
if (draft.shippingCountry) setShippingCountry(draft.shippingCountry);
|
||||
if (draft.shippingCompanyName) setShippingCompanyName(draft.shippingCompanyName);
|
||||
if (draft.shippingContactEmail) setShippingContactEmail(draft.shippingContactEmail);
|
||||
if (draft.shippingContactPhone) setShippingContactPhone(draft.shippingContactPhone);
|
||||
if (typeof draft.newsletterOptIn === "boolean") setNewsletterOptIn(draft.newsletterOptIn);
|
||||
if (draft.shippingMethodId != null) setShippingMethodId(draft.shippingMethodId);
|
||||
if (draft.paymentMethodId != null) setPaymentMethodId(draft.paymentMethodId);
|
||||
@@ -331,6 +347,9 @@ export function CheckoutContent({
|
||||
shippingZip,
|
||||
shippingCity,
|
||||
shippingCountry,
|
||||
shippingCompanyName,
|
||||
shippingContactEmail,
|
||||
shippingContactPhone,
|
||||
newsletterOptIn,
|
||||
shippingMethodId,
|
||||
paymentMethodId,
|
||||
@@ -356,6 +375,9 @@ export function CheckoutContent({
|
||||
shippingZip,
|
||||
shippingCity,
|
||||
shippingCountry,
|
||||
shippingCompanyName,
|
||||
shippingContactEmail,
|
||||
shippingContactPhone,
|
||||
newsletterOptIn,
|
||||
shippingMethodId,
|
||||
paymentMethodId,
|
||||
@@ -558,6 +580,9 @@ export function CheckoutContent({
|
||||
shippingZip: hasDifferentShippingAddress ? shippingZip : undefined,
|
||||
shippingCity: hasDifferentShippingAddress ? shippingCity : undefined,
|
||||
shippingCountry: hasDifferentShippingAddress ? shippingCountry : undefined,
|
||||
shippingCompanyName: hasDifferentShippingAddress ? shippingCompanyName || undefined : undefined,
|
||||
shippingContactEmail: hasDifferentShippingAddress ? shippingContactEmail || undefined : undefined,
|
||||
shippingContactPhone: hasDifferentShippingAddress ? shippingContactPhone || undefined : undefined,
|
||||
newsletterOptIn,
|
||||
};
|
||||
|
||||
@@ -1048,6 +1073,15 @@ export function CheckoutContent({
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<FormField
|
||||
label="Firma (optional)"
|
||||
type="text"
|
||||
value={shippingCompanyName}
|
||||
onChange={(e) => setShippingCompanyName(e.target.value)}
|
||||
placeholder="Muster GmbH"
|
||||
autoComplete="off"
|
||||
wrapperClassName="w-full"
|
||||
/>
|
||||
<div className="w-full sm:w-[calc(50%-0.5rem)] flex flex-col gap-2 items-start">
|
||||
<span className="text-label text-text-muted">Lieferart</span>
|
||||
<div className="flex w-full rounded-sm border border-border overflow-hidden">
|
||||
@@ -1165,6 +1199,30 @@ export function CheckoutContent({
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
{/* Both optional and independent — handed to the shipping
|
||||
carrier, not used for any customer communication (that
|
||||
stays the account email above). Useful when the
|
||||
recipient at this address isn't reachable via the
|
||||
account holder's own email/phone. */}
|
||||
<div className="flex flex-col sm:flex-row gap-4 w-full">
|
||||
<FormField
|
||||
label="Kontakt-E-Mail (optional)"
|
||||
type="email"
|
||||
value={shippingContactEmail}
|
||||
onChange={(e) => setShippingContactEmail(e.target.value)}
|
||||
placeholder="empfang@beispiel.de"
|
||||
autoComplete="off"
|
||||
/>
|
||||
<FormField
|
||||
label="Telefonnummer (optional)"
|
||||
type="tel"
|
||||
value={shippingContactPhone}
|
||||
onChange={(e) => setShippingContactPhone(e.target.value)}
|
||||
placeholder="+49 30 123456"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-label text-text-muted">Werden nur dem Versanddienstleister übergeben, z. B. für Lieferbenachrichtigungen.</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user