Add optional Firma/USt-IdNr. fields to checkout and profile
B2B checkout fields, split out from the e-invoicing migration and picked back up now that it's shipped. Both fields are independently optional, format-validated (shared regex in lib/vatId.ts, mirrored server-side in api/checkout and api/account/profile), persisted in the checkout draft, and saved as a customer profile default that pre-fills future checkouts. Order/customer snapshot fields land in a companion Payload backend commit. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -102,6 +102,13 @@ export function CheckoutContent({
|
||||
const [firstName, setFirstName] = useState(savedProfile?.firstName ?? "");
|
||||
const [lastName, setLastName] = useState(savedProfile?.lastName ?? "");
|
||||
const [email, setEmail] = useState(savedProfile?.email ?? customerEmail ?? "");
|
||||
// Optional B2B fields — sit right next to Rechnungsadresse (not a
|
||||
// separately gated "order as a business" toggle) since each is
|
||||
// independently optional (see Customers.ts/Orders.ts's own comment on
|
||||
// why neither implies the other). Prefilled from the saved profile, same
|
||||
// as every other Card 1 field.
|
||||
const [companyName, setCompanyName] = useState(savedProfile?.companyName ?? "");
|
||||
const [vatId, setVatId] = useState(savedProfile?.vatId ?? "");
|
||||
// Always a plain street address — a Packstation isn't a valid Rechnungs-
|
||||
// adresse (an invoice needs a real postal address). Packstation is only
|
||||
// ever offered on the separate, optional shipping-address override below.
|
||||
@@ -143,6 +150,8 @@ export function CheckoutContent({
|
||||
if (draft.firstName) setFirstName(draft.firstName);
|
||||
if (draft.lastName) setLastName(draft.lastName);
|
||||
if (draft.email) setEmail(draft.email);
|
||||
if (draft.companyName) setCompanyName(draft.companyName);
|
||||
if (draft.vatId) setVatId(draft.vatId);
|
||||
if (draft.street) setStreet(draft.street);
|
||||
if (draft.zip) setZip(draft.zip);
|
||||
if (draft.city) setCity(draft.city);
|
||||
@@ -171,6 +180,8 @@ export function CheckoutContent({
|
||||
firstName,
|
||||
lastName,
|
||||
email,
|
||||
companyName,
|
||||
vatId,
|
||||
street,
|
||||
zip,
|
||||
city,
|
||||
@@ -194,6 +205,8 @@ export function CheckoutContent({
|
||||
firstName,
|
||||
lastName,
|
||||
email,
|
||||
companyName,
|
||||
vatId,
|
||||
street,
|
||||
zip,
|
||||
city,
|
||||
@@ -317,6 +330,8 @@ export function CheckoutContent({
|
||||
firstName,
|
||||
lastName,
|
||||
email,
|
||||
companyName: companyName || undefined,
|
||||
vatId: vatId || undefined,
|
||||
// Deliberately still read from FormData, not state — password is the
|
||||
// one address-card field that stays uncontrolled/unpersisted (see
|
||||
// lib/checkoutDraft.ts's own comment on why).
|
||||
@@ -516,6 +531,32 @@ export function CheckoutContent({
|
||||
<FormField label="Vorname" name="firstName" type="text" value={firstName} onChange={(e) => setFirstName(e.target.value)} placeholder="Max" autoComplete="given-name" required />
|
||||
<FormField label="Nachname" name="lastName" type="text" value={lastName} onChange={(e) => setLastName(e.target.value)} placeholder="Mustermann" autoComplete="family-name" required />
|
||||
</div>
|
||||
{/* Optional B2B fields — both independently optional (see
|
||||
Orders.ts's own comment: a sole proprietor might give a VAT
|
||||
ID with no separate "company name", and vice versa), so
|
||||
neither is required just because the other is filled in. */}
|
||||
<div className="flex flex-col sm:flex-row gap-4 w-full">
|
||||
<FormField
|
||||
label="Firma (optional)"
|
||||
name="companyName"
|
||||
type="text"
|
||||
value={companyName}
|
||||
onChange={(e) => setCompanyName(e.target.value)}
|
||||
placeholder="Muster GmbH"
|
||||
autoComplete="organization"
|
||||
/>
|
||||
<FormField
|
||||
label="USt-IdNr. (optional)"
|
||||
name="vatId"
|
||||
type="text"
|
||||
value={vatId}
|
||||
onChange={(e) => setVatId(e.target.value)}
|
||||
placeholder="DE123456789"
|
||||
autoComplete="off"
|
||||
pattern="[A-Za-z]{2}[A-Za-z0-9]{2,12}"
|
||||
title="EU-Format: 2 Buchstaben Länderpräfix + bis zu 12 alphanumerische Zeichen, z. B. DE123456789."
|
||||
/>
|
||||
</div>
|
||||
{/* w-[calc(50%-0.5rem)] at sm: — exactly matches Vorname's
|
||||
actual rendered width in the 2-col row above (each half of
|
||||
a gap-4 flex row), instead of stretching full-width. */}
|
||||
|
||||
Reference in New Issue
Block a user