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:
Dwindi Ramadhana
2026-01-08 14:02:13 +07:00
parent 6694d9e0c4
commit 274c3d35e1
2 changed files with 78 additions and 16 deletions

View File

@@ -628,18 +628,27 @@ export default function Checkout() {
value={billingData.country}
onChange={(v) => setBillingData({ ...billingData, country: v })}
placeholder="Select country"
disabled={countries.length <= 1}
disabled={countries.length === 1}
/>
</div>
<div>
<label className="block text-sm font-medium mb-2">State / Province *</label>
<SearchableSelect
options={billingStateOptions}
value={billingData.state}
onChange={(v) => setBillingData({ ...billingData, state: v })}
placeholder={billingStateOptions.length ? "Select state" : "N/A"}
disabled={!billingStateOptions.length}
/>
{billingStateOptions.length > 0 ? (
<SearchableSelect
options={billingStateOptions}
value={billingData.state}
onChange={(v) => setBillingData({ ...billingData, state: v })}
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>
<label className="block text-sm font-medium mb-2">Postcode / ZIP *</label>
@@ -801,18 +810,27 @@ export default function Checkout() {
value={shippingData.country}
onChange={(v) => setShippingData({ ...shippingData, country: v })}
placeholder="Select country"
disabled={countries.length <= 1}
disabled={countries.length === 1}
/>
</div>
<div>
<label className="block text-sm font-medium mb-2">State / Province *</label>
<SearchableSelect
options={shippingStateOptions}
value={shippingData.state}
onChange={(v) => setShippingData({ ...shippingData, state: v })}
placeholder={shippingStateOptions.length ? "Select state" : "N/A"}
disabled={!shippingStateOptions.length}
/>
{shippingStateOptions.length > 0 ? (
<SearchableSelect
options={shippingStateOptions}
value={shippingData.state}
onChange={(v) => setShippingData({ ...shippingData, state: v })}
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>
<label className="block text-sm font-medium mb-2">Postcode / ZIP *</label>