fix: add required email and country fields to checkout
- Add email field (required) for order confirmation - Add phone field in contact info section - Add country dropdown with regional options - Add validation for email format and required fields - Add checkoutEmailUpdate mutation call before completing - Use selected country instead of hardcoded RS - Add translations for new fields (EN, SR, DE, FR)
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
CHECKOUT_SHIPPING_ADDRESS_UPDATE,
|
||||
CHECKOUT_BILLING_ADDRESS_UPDATE,
|
||||
CHECKOUT_COMPLETE,
|
||||
CHECKOUT_EMAIL_UPDATE,
|
||||
} from "@/lib/saleor/mutations/Checkout";
|
||||
import type { Checkout } from "@/types/saleor";
|
||||
|
||||
@@ -38,6 +39,13 @@ interface CheckoutCompleteResponse {
|
||||
};
|
||||
}
|
||||
|
||||
interface EmailUpdateResponse {
|
||||
checkoutEmailUpdate?: {
|
||||
checkout?: Checkout;
|
||||
errors?: Array<{ message: string }>;
|
||||
};
|
||||
}
|
||||
|
||||
interface AddressForm {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
@@ -45,7 +53,9 @@ interface AddressForm {
|
||||
streetAddress2: string;
|
||||
city: string;
|
||||
postalCode: string;
|
||||
country: string;
|
||||
phone: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
export default function CheckoutPage() {
|
||||
@@ -66,7 +76,9 @@ export default function CheckoutPage() {
|
||||
streetAddress2: "",
|
||||
city: "",
|
||||
postalCode: "",
|
||||
country: "RS",
|
||||
phone: "",
|
||||
email: "",
|
||||
});
|
||||
const [billingAddress, setBillingAddress] = useState<AddressForm>({
|
||||
firstName: "",
|
||||
@@ -75,7 +87,9 @@ export default function CheckoutPage() {
|
||||
streetAddress2: "",
|
||||
city: "",
|
||||
postalCode: "",
|
||||
country: "RS",
|
||||
phone: "",
|
||||
email: "",
|
||||
});
|
||||
|
||||
const lines = getLines();
|
||||
@@ -89,7 +103,7 @@ export default function CheckoutPage() {
|
||||
|
||||
const handleShippingChange = (field: keyof AddressForm, value: string) => {
|
||||
setShippingAddress((prev) => ({ ...prev, [field]: value }));
|
||||
if (sameAsShipping) {
|
||||
if (sameAsShipping && field !== "email") {
|
||||
setBillingAddress((prev) => ({ ...prev, [field]: value }));
|
||||
}
|
||||
};
|
||||
@@ -98,6 +112,10 @@ export default function CheckoutPage() {
|
||||
setBillingAddress((prev) => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
const handleEmailChange = (value: string) => {
|
||||
setShippingAddress((prev) => ({ ...prev, email: value }));
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -106,17 +124,45 @@ export default function CheckoutPage() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!shippingAddress.email || !shippingAddress.email.includes("@")) {
|
||||
setError(t("errorEmailRequired"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!shippingAddress.firstName || !shippingAddress.lastName || !shippingAddress.streetAddress1 || !shippingAddress.city || !shippingAddress.postalCode || !shippingAddress.phone) {
|
||||
setError(t("errorFieldsRequired"));
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const emailResult = await saleorClient.mutate<EmailUpdateResponse>({
|
||||
mutation: CHECKOUT_EMAIL_UPDATE,
|
||||
variables: {
|
||||
checkoutId: checkout.id,
|
||||
email: shippingAddress.email,
|
||||
},
|
||||
});
|
||||
|
||||
if (emailResult.data?.checkoutEmailUpdate?.errors && emailResult.data.checkoutEmailUpdate.errors.length > 0) {
|
||||
throw new Error(emailResult.data.checkoutEmailUpdate.errors[0].message);
|
||||
}
|
||||
|
||||
const shippingResult = await saleorClient.mutate<ShippingAddressUpdateResponse>({
|
||||
mutation: CHECKOUT_SHIPPING_ADDRESS_UPDATE,
|
||||
variables: {
|
||||
checkoutId: checkout.id,
|
||||
shippingAddress: {
|
||||
...shippingAddress,
|
||||
country: "RS",
|
||||
firstName: shippingAddress.firstName,
|
||||
lastName: shippingAddress.lastName,
|
||||
streetAddress1: shippingAddress.streetAddress1,
|
||||
streetAddress2: shippingAddress.streetAddress2,
|
||||
city: shippingAddress.city,
|
||||
postalCode: shippingAddress.postalCode,
|
||||
country: shippingAddress.country,
|
||||
phone: shippingAddress.phone,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -130,8 +176,14 @@ export default function CheckoutPage() {
|
||||
variables: {
|
||||
checkoutId: checkout.id,
|
||||
billingAddress: {
|
||||
...billingAddress,
|
||||
country: "RS",
|
||||
firstName: billingAddress.firstName,
|
||||
lastName: billingAddress.lastName,
|
||||
streetAddress1: billingAddress.streetAddress1,
|
||||
streetAddress2: billingAddress.streetAddress2,
|
||||
city: billingAddress.city,
|
||||
postalCode: billingAddress.postalCode,
|
||||
country: billingAddress.country,
|
||||
phone: billingAddress.phone,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -227,6 +279,36 @@ export default function CheckoutPage() {
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
|
||||
<div>
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
<div className="border-b border-border pb-6">
|
||||
<h2 className="text-xl font-serif mb-4">{t("contactInfo")}</h2>
|
||||
<div className="grid grid-cols-1 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">{t("email")}</label>
|
||||
<input
|
||||
type="email"
|
||||
required
|
||||
value={shippingAddress.email}
|
||||
onChange={(e) => handleEmailChange(e.target.value)}
|
||||
className="w-full border border-border px-4 py-2 rounded"
|
||||
placeholder="email@example.com"
|
||||
/>
|
||||
<p className="text-xs text-foreground-muted mt-1">{t("emailRequired")}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">{t("phone")}</label>
|
||||
<input
|
||||
type="tel"
|
||||
required
|
||||
value={shippingAddress.phone}
|
||||
onChange={(e) => handleShippingChange("phone", e.target.value)}
|
||||
className="w-full border border-border px-4 py-2 rounded"
|
||||
placeholder="+381..."
|
||||
/>
|
||||
<p className="text-xs text-foreground-muted mt-1">{t("phoneRequired")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-b border-border pb-6">
|
||||
<h2 className="text-xl font-serif mb-4">{t("shippingAddress")}</h2>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
@@ -250,6 +332,35 @@ export default function CheckoutPage() {
|
||||
className="w-full border border-border px-4 py-2 rounded"
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<label className="block text-sm font-medium mb-1">{t("country")}</label>
|
||||
<select
|
||||
required
|
||||
value={shippingAddress.country}
|
||||
onChange={(e) => handleShippingChange("country", e.target.value)}
|
||||
className="w-full border border-border px-4 py-2 rounded"
|
||||
>
|
||||
<option value="RS">Serbia (Srbija)</option>
|
||||
<option value="BA">Bosnia and Herzegovina</option>
|
||||
<option value="ME">Montenegro</option>
|
||||
<option value="HR">Croatia</option>
|
||||
<option value="SI">Slovenia</option>
|
||||
<option value="MK">North Macedonia</option>
|
||||
<option value="AL">Albania</option>
|
||||
<option value="XK">Kosovo</option>
|
||||
<option value="BG">Bulgaria</option>
|
||||
<option value="RO">Romania</option>
|
||||
<option value="HU">Hungary</option>
|
||||
<option value="DE">Germany</option>
|
||||
<option value="AT">Austria</option>
|
||||
<option value="CH">Switzerland</option>
|
||||
<option value="FR">France</option>
|
||||
<option value="GB">United Kingdom</option>
|
||||
<option value="US">United States</option>
|
||||
<option value="CA">Canada</option>
|
||||
<option value="AU">Australia</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<label className="block text-sm font-medium mb-1">{t("streetAddress")}</label>
|
||||
<input
|
||||
@@ -289,16 +400,6 @@ export default function CheckoutPage() {
|
||||
className="w-full border border-border px-4 py-2 rounded"
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<label className="block text-sm font-medium mb-1">{t("phone")}</label>
|
||||
<input
|
||||
type="tel"
|
||||
required
|
||||
value={shippingAddress.phone}
|
||||
onChange={(e) => handleShippingChange("phone", e.target.value)}
|
||||
className="w-full border border-border px-4 py-2 rounded"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user