Fix RajaOngkir address integration bugs and styling issues

This commit is contained in:
Dwindi Ramadhana
2026-06-03 22:56:02 +07:00
parent 21ece27b9b
commit fb1a6c40ef
7 changed files with 263 additions and 21 deletions

View File

@@ -18,6 +18,7 @@ interface Address {
email?: string;
phone?: string;
is_default: boolean;
formatted_address?: string;
}
interface AddressSelectorProps {
@@ -148,14 +149,22 @@ export function AddressSelector({
)}
{/* Address */}
<p className="text-sm text-gray-600 mt-2">
{address.address_1}
{address.address_2 && `, ${address.address_2}`}
</p>
<p className="text-sm text-gray-600">
{address.city}, {address.state} {address.postcode}
</p>
<p className="text-sm text-gray-600">{address.country}</p>
<div className="text-sm text-gray-600 mt-2">
{address.formatted_address ? (
<p className="whitespace-pre-wrap">{address.formatted_address}</p>
) : (
<>
<p>
{address.address_1}
{address.address_2 && `, ${address.address_2}`}
</p>
<p>
{address.city}, {address.state} {address.postcode}
</p>
<p>{address.country}</p>
</>
)}
</div>
</div>
</div>
))}

View File

@@ -28,6 +28,7 @@ interface CheckoutField {
interface DynamicCheckoutFieldProps {
field: CheckoutField;
value: string;
valueLabel?: string;
onChange: (value: string) => void;
countryOptions?: { value: string; label: string }[];
stateOptions?: { value: string; label: string }[];
@@ -41,6 +42,7 @@ interface SearchOption {
export function DynamicCheckoutField({
field,
value,
valueLabel,
onChange,
countryOptions = [],
stateOptions = [],
@@ -54,9 +56,11 @@ export function DynamicCheckoutField({
return;
}
// If we have a value but no options yet, we might need to load it
// This handles pre-selected values
}, [field.type, field.search_endpoint, value]);
// If we have a value and a label, inject it into searchOptions so it renders properly when mounted
if (value && valueLabel && searchOptions.length === 0) {
setSearchOptions([{ value, label: valueLabel }]);
}
}, [field.type, field.search_endpoint, value, valueLabel]);
// Handle API search for searchable_select
const handleApiSearch = async (searchTerm: string) => {

View File

@@ -345,10 +345,18 @@ export default function Addresses() {
<div className="text-sm text-gray-700 space-y-1 mb-4">
<p className="font-medium">{address.first_name} {address.last_name}</p>
{address.company && <p>{address.company}</p>}
<p>{address.address_1}</p>
{address.address_2 && <p>{address.address_2}</p>}
<p>{address.city}, {address.state} {address.postcode}</p>
<p>{address.country}</p>
{address.formatted_address ? (
<p className="whitespace-pre-wrap">{address.formatted_address}</p>
) : (
<>
<p>{address.address_1}</p>
{address.address_2 && <p>{address.address_2}</p>}
{[address.city, address.state, address.postcode].filter(Boolean).length > 0 && (
<p>{[address.city, address.state, address.postcode].filter(Boolean).join(', ')}</p>
)}
<p>{address.country}</p>
</>
)}
{address.phone && <p className="pt-2">Phone: {address.phone}</p>}
{address.email && <p>Email: {address.email}</p>}
</div>
@@ -429,6 +437,7 @@ export default function Addresses() {
key={field.key}
field={field}
value={getFieldValue(field.key)}
valueLabel={getFieldValue(field.key + '_label')}
onChange={(v) => setFieldValue(field.key, v)}
countryOptions={countryOptions}
stateOptions={stateOptions}

View File

@@ -33,6 +33,7 @@ interface SavedAddress {
email?: string;
phone?: string;
is_default: boolean;
formatted_address?: string;
}
export default function Checkout() {
@@ -389,7 +390,13 @@ export default function Checkout() {
state: addressData.state,
city: addressData.city,
postcode: addressData.postcode,
destination_id: undefined,
// Include custom fields for shipping calculation (e.g., RajaOngkir destination_id)
...Object.fromEntries(
(shipToDifferentAddress ? shippingCustomFields : billingCustomFields).map(f => [
f.key.replace(/^(shipping_|billing_)/, ''),
customFieldData[f.key] || ''
])
),
},
items,
});
@@ -795,7 +802,16 @@ export default function Checkout() {
<div className="bg-primary/5 border-2 border-primary rounded-lg p-4 text-sm">
<p className="font-semibold">{sel.label}{sel.is_default && <span className="ml-2 text-xs bg-green-100 text-green-700 px-1.5 rounded">Default</span>}</p>
<p>{sel.first_name} {sel.last_name}</p>
<p className="text-gray-600">{sel.address_1}, {sel.city}, {sel.state} {sel.postcode}</p>
{sel.formatted_address ? (
<p className="text-gray-600 whitespace-pre-wrap">{sel.formatted_address}</p>
) : (
<div className="text-gray-600">
<p>{sel.address_1}</p>
{[sel.city, sel.state, sel.postcode].filter(Boolean).length > 0 && (
<p>{[sel.city, sel.state, sel.postcode].filter(Boolean).join(', ')}</p>
)}
</div>
)}
</div>
) : null;
})()}
@@ -866,7 +882,7 @@ export default function Checkout() {
</div>
)}
{billingCustomFields.map(field => (
<DynamicCheckoutField key={field.key} field={field} value={customFieldData[field.key] || ''} onChange={v => handleCustomFieldChange(field.key, v)} countryOptions={countryOptions} stateOptions={billingStateOptions} />
<DynamicCheckoutField key={field.key} field={field} value={customFieldData[field.key] || ''} valueLabel={customFieldData[`${field.key}_label`]} onChange={v => handleCustomFieldChange(field.key, v)} countryOptions={countryOptions} stateOptions={billingStateOptions} />
))}
</>
)}
@@ -908,7 +924,16 @@ export default function Checkout() {
<div className="bg-primary/5 border-2 border-primary rounded-lg p-4 text-sm">
<p className="font-semibold">{sel.label}</p>
<p>{sel.first_name} {sel.last_name}</p>
<p className="text-gray-600">{sel.address_1}, {sel.city} {sel.postcode}</p>
{sel.formatted_address ? (
<p className="text-gray-600 whitespace-pre-wrap">{sel.formatted_address}</p>
) : (
<div className="text-gray-600">
<p>{sel.address_1}</p>
{[sel.city, sel.state, sel.postcode].filter(Boolean).length > 0 && (
<p>{[sel.city, sel.state, sel.postcode].filter(Boolean).join(', ')}</p>
)}
</div>
)}
</div>
) : null;
})()}
@@ -926,7 +951,7 @@ export default function Checkout() {
{getShippingField('shipping_country') && <div><label className="block text-sm font-medium mb-1">Country</label><SearchableSelect options={countryOptions} value={shippingData.country} onChange={v => setShippingData({ ...shippingData, country: v })} placeholder="Select country" disabled={countries.length === 1} /></div>}
{getShippingField('shipping_state') && <div><label className="block text-sm font-medium mb-1">State</label>{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 })} className="w-full border rounded-lg px-4 py-2" />}</div>}
{getShippingField('shipping_postcode') && <div><label className="block text-sm font-medium mb-1">Postcode</label><input type="text" value={shippingData.postcode} onChange={e => setShippingData({ ...shippingData, postcode: e.target.value })} className="w-full border rounded-lg px-4 py-2" /></div>}
{shippingCustomFields.map(field => <DynamicCheckoutField key={field.key} field={field} value={customFieldData[field.key] || ''} onChange={v => handleCustomFieldChange(field.key, v)} countryOptions={countryOptions} stateOptions={shippingStateOptions} />)}
{shippingCustomFields.map(field => <DynamicCheckoutField key={field.key} field={field} value={customFieldData[field.key] || ''} valueLabel={customFieldData[`${field.key}_label`]} onChange={v => handleCustomFieldChange(field.key, v)} countryOptions={countryOptions} stateOptions={shippingStateOptions} />)}
</div>
)}
</>