fix(checkout): fix disabled country/state and add public countries API
Issues fixed:
1. Country field was disabled when API failed (length 0)
- Changed: disabled={countries.length <= 1} → disabled={countries.length === 1}
- Only disables in single-country mode now
2. State field was disabled when no preloaded states
- Changed: Falls back to text input instead of disabled SearchableSelect
- Allows manual state entry for countries without state list
3. /countries API required admin permission
- Added public /countries endpoint to CheckoutController
- Uses permission_callback __return_true for customer checkout access
- Returns countries, states, and default_country
This commit is contained in:
@@ -628,18 +628,27 @@ export default function Checkout() {
|
|||||||
value={billingData.country}
|
value={billingData.country}
|
||||||
onChange={(v) => setBillingData({ ...billingData, country: v })}
|
onChange={(v) => setBillingData({ ...billingData, country: v })}
|
||||||
placeholder="Select country"
|
placeholder="Select country"
|
||||||
disabled={countries.length <= 1}
|
disabled={countries.length === 1}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium mb-2">State / Province *</label>
|
<label className="block text-sm font-medium mb-2">State / Province *</label>
|
||||||
<SearchableSelect
|
{billingStateOptions.length > 0 ? (
|
||||||
options={billingStateOptions}
|
<SearchableSelect
|
||||||
value={billingData.state}
|
options={billingStateOptions}
|
||||||
onChange={(v) => setBillingData({ ...billingData, state: v })}
|
value={billingData.state}
|
||||||
placeholder={billingStateOptions.length ? "Select state" : "N/A"}
|
onChange={(v) => setBillingData({ ...billingData, state: v })}
|
||||||
disabled={!billingStateOptions.length}
|
placeholder="Select state"
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={billingData.state}
|
||||||
|
onChange={(e) => setBillingData({ ...billingData, state: e.target.value })}
|
||||||
|
placeholder="Enter state/province"
|
||||||
|
className="w-full border rounded-lg px-4 py-2"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium mb-2">Postcode / ZIP *</label>
|
<label className="block text-sm font-medium mb-2">Postcode / ZIP *</label>
|
||||||
@@ -801,18 +810,27 @@ export default function Checkout() {
|
|||||||
value={shippingData.country}
|
value={shippingData.country}
|
||||||
onChange={(v) => setShippingData({ ...shippingData, country: v })}
|
onChange={(v) => setShippingData({ ...shippingData, country: v })}
|
||||||
placeholder="Select country"
|
placeholder="Select country"
|
||||||
disabled={countries.length <= 1}
|
disabled={countries.length === 1}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium mb-2">State / Province *</label>
|
<label className="block text-sm font-medium mb-2">State / Province *</label>
|
||||||
<SearchableSelect
|
{shippingStateOptions.length > 0 ? (
|
||||||
options={shippingStateOptions}
|
<SearchableSelect
|
||||||
value={shippingData.state}
|
options={shippingStateOptions}
|
||||||
onChange={(v) => setShippingData({ ...shippingData, state: v })}
|
value={shippingData.state}
|
||||||
placeholder={shippingStateOptions.length ? "Select state" : "N/A"}
|
onChange={(v) => setShippingData({ ...shippingData, state: v })}
|
||||||
disabled={!shippingStateOptions.length}
|
placeholder="Select state"
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={shippingData.state}
|
||||||
|
onChange={(e) => setShippingData({ ...shippingData, state: e.target.value })}
|
||||||
|
placeholder="Enter state/province"
|
||||||
|
className="w-full border rounded-lg px-4 py-2"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium mb-2">Postcode / ZIP *</label>
|
<label className="block text-sm font-medium mb-2">Postcode / ZIP *</label>
|
||||||
|
|||||||
@@ -32,6 +32,12 @@ class CheckoutController {
|
|||||||
'callback' => [ new self(), 'get_fields' ],
|
'callback' => [ new self(), 'get_fields' ],
|
||||||
'permission_callback' => [ \WooNooW\Api\Permissions::class, 'anon_or_wp_nonce' ],
|
'permission_callback' => [ \WooNooW\Api\Permissions::class, 'anon_or_wp_nonce' ],
|
||||||
]);
|
]);
|
||||||
|
// Public countries endpoint for customer checkout form
|
||||||
|
register_rest_route($namespace, '/countries', [
|
||||||
|
'methods' => 'GET',
|
||||||
|
'callback' => [ new self(), 'get_countries' ],
|
||||||
|
'permission_callback' => '__return_true', // Public - needed for checkout
|
||||||
|
]);
|
||||||
// Public order view endpoint for thank you page
|
// Public order view endpoint for thank you page
|
||||||
register_rest_route($namespace, '/checkout/order/(?P<id>\d+)', [
|
register_rest_route($namespace, '/checkout/order/(?P<id>\d+)', [
|
||||||
'methods' => 'GET',
|
'methods' => 'GET',
|
||||||
@@ -729,4 +735,42 @@ class CheckoutController {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get countries and states for checkout form
|
||||||
|
* Public endpoint - no authentication required
|
||||||
|
*/
|
||||||
|
public function get_countries(): array {
|
||||||
|
$wc_countries = WC()->countries;
|
||||||
|
|
||||||
|
// Get allowed selling countries
|
||||||
|
$allowed = $wc_countries->get_allowed_countries();
|
||||||
|
|
||||||
|
// Format for frontend
|
||||||
|
$countries = [];
|
||||||
|
foreach ($allowed as $code => $name) {
|
||||||
|
$countries[] = [
|
||||||
|
'code' => $code,
|
||||||
|
'name' => $name,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get states for all allowed countries
|
||||||
|
$states = [];
|
||||||
|
foreach (array_keys($allowed) as $country_code) {
|
||||||
|
$country_states = $wc_countries->get_states($country_code);
|
||||||
|
if (!empty($country_states) && is_array($country_states)) {
|
||||||
|
$states[$country_code] = $country_states;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get default country
|
||||||
|
$default_country = $wc_countries->get_base_country();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'countries' => $countries,
|
||||||
|
'states' => $states,
|
||||||
|
'default_country' => $default_country,
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user