feat(checkout): fix searchable select API search and add billing destination

Fixes:
1. SearchableSelect now supports onSearch prop for API-based search
   - Added onSearch and isSearching props
   - shouldFilter disabled when onSearch provided
2. DynamicCheckoutField connects handleApiSearch to SearchableSelect
3. RAJAONGKIR_INTEGRATION.md adds both billing and shipping destination_id

This enables the destination search field to actually call the API
when user types, instead of just filtering local (empty) options.
This commit is contained in:
Dwindi Ramadhana
2026-01-08 14:47:54 +07:00
parent f6b778c7fc
commit f518d7e589
3 changed files with 40 additions and 9 deletions

View File

@@ -162,7 +162,9 @@ export function DynamicCheckoutField({
document.dispatchEvent(event);
}
}}
placeholder={isSearching ? 'Searching...' : (field.placeholder || `Search ${field.label}...`)}
onSearch={handleApiSearch}
isSearching={isSearching}
placeholder={field.placeholder || `Search ${field.label}...`}
emptyLabel={
isSearching
? 'Searching...'

View File

@@ -28,6 +28,9 @@ interface Props {
emptyLabel?: string;
className?: string;
disabled?: boolean;
// For API-based search
onSearch?: (searchTerm: string) => void;
isSearching?: boolean;
}
export function SearchableSelect({
@@ -38,12 +41,26 @@ export function SearchableSelect({
emptyLabel = "No results found.",
className,
disabled = false,
onSearch,
isSearching = false,
}: Props) {
const [open, setOpen] = React.useState(false);
const [searchValue, setSearchValue] = React.useState("");
const selected = options.find((o) => o.value === value);
React.useEffect(() => { if (disabled && open) setOpen(false); }, [disabled, open]);
// Handle search input changes
const handleSearchChange = (value: string) => {
setSearchValue(value);
if (onSearch) {
onSearch(value);
}
};
// Determine if we should use local filtering or API-based search
const shouldFilter = !onSearch;
return (
<Popover open={disabled ? false : open} onOpenChange={(o) => !disabled && setOpen(o)}>
<PopoverTrigger asChild>
@@ -69,10 +86,16 @@ export function SearchableSelect({
align="start"
sideOffset={4}
>
<Command shouldFilter>
<CommandInput placeholder="Search..." />
<Command shouldFilter={shouldFilter}>
<CommandInput
placeholder="Search..."
value={searchValue}
onValueChange={handleSearchChange}
/>
<CommandList>
<CommandEmpty>{emptyLabel}</CommandEmpty>
<CommandEmpty>
{isSearching ? "Searching..." : emptyLabel}
</CommandEmpty>
{options.map((opt) => (
<CommandItem
key={opt.value}
@@ -80,6 +103,7 @@ export function SearchableSelect({
onSelect={() => {
onChange?.(opt.value);
setOpen(false);
setSearchValue("");
}}
>
<Check