bug fix + add feature

fix salah url ketika sidebar collapsed
menambahkan type select pada reusable searchparam
This commit is contained in:
faiztyanirh 2026-02-13 22:05:08 +07:00
parent b03c0565f1
commit 4f61832f19
5 changed files with 117 additions and 14 deletions

View File

@ -2,6 +2,8 @@ export function useSearch(searchFields, searchApiFunction) {
let searchQuery = $state(initializeSearchQuery(searchFields));
let isLoading = $state(false);
let searchData = $state([]);
let selectOptions = $state({});
let loadingOptions = $state({});
function initializeSearchQuery(fields) {
const query = {};
@ -30,12 +32,38 @@ export function useSearch(searchFields, searchApiFunction) {
searchQuery = initializeSearchQuery(searchFields);
}
async function fetchOptions(field) {
if (!field.optionsEndpoint) return;
if (selectOptions[field.key]?.length) return;
loadingOptions[field.key] = true;
try {
const response = await fetch(field.optionsEndpoint);
const json = await response.json();
selectOptions[field.key] = json.data.map(item => ({
value: item[field.valueKey ?? 'value'],
label: item[field.labelKey ?? 'label'],
}));
} catch (err) {
console.error("Failed fetching options:", err);
selectOptions[field.key] = [];
} finally {
loadingOptions[field.key] = false;
}
}
return {
get searchQuery() { return searchQuery; },
set searchQuery(value) { searchQuery = value; },
get searchData() { return searchData; },
get isLoading() { return isLoading; },
get loadingOptions() { return loadingOptions; },
get selectOptions() { return selectOptions; },
handleSearch,
handleReset
handleReset,
fetchOptions,
};
}

View File

@ -16,7 +16,8 @@ export const searchFields = [
{
key: "LocType",
label: "Location Type",
type: "text"
type: "select",
optionsEndpoint: `https://clqms01-api.services-summit.my.id/api/valueset/location_type`,
},
];

View File

@ -20,7 +20,10 @@
</script>
{#snippet searchParamSnippet()}
<ReusableSearchParam {searchFields} bind:searchQuery={search.searchQuery} onSearch={search.handleSearch} onReset={search.handleReset} isLoading={search.isLoading}/>
<ReusableSearchParam {searchFields}
bind:searchQuery={search.searchQuery} onSearch={search.handleSearch} onReset={search.handleReset} isLoading={search.isLoading}
selectOptions={search.selectOptions} loadingOptions={search.loadingOptions} fetchOptions={search.fetchOptions}
/>
{/snippet}
<div

View File

@ -66,7 +66,7 @@
{/snippet}
</Collapsible.Root>
{:else}
<Popover.Root open={openPopovers[index]} onOpenChange={(open) => openPopovers[index] = open}>
<Popover.Root open={openPopovers[item.url]} onOpenChange={(open) => openPopovers[item.url] = open}>
<Sidebar.MenuItem>
{#snippet trigger(props)}
<Popover.Trigger {...props}>
@ -89,8 +89,8 @@
</div>
<Separator />
{#each item.submenus || [] as submenu}
<a href={submenu.url}
onclick={() => openPopovers[index] = false}
<a href={`${item.url}${submenu.url}`}
onclick={() => openPopovers[item.url] = false}
class="flex items-center rounded-md px-2 py-1.5 text-sm hover:bg-accent"
class:bg-accent={$page.url.pathname === submenu.url}
>
@ -99,7 +99,7 @@
{/each}
{:else}
<a href={item.url}
onclick={() => openPopovers[index] = false}
onclick={() => openPopovers[item.url] = false}
class="flex items-center rounded-md px-2 py-1.5 text-sm font-semibold hover:bg-accent"
class:bg-accent={$page.url.pathname === item.url}
>

View File

@ -7,10 +7,20 @@
import * as Select from "$lib/components/ui/select/index.js";
let props = $props();
let loadedFields = $state(new Set());
function handleOpenSelect(field) {
if (loadedFields.has(field.key)) return;
loadedFields.add(field.key);
props.fetchOptions(field);
}
$inspect(props.searchQuery)
</script>
<div class="w-full">
<div class="space-y-2">
<div class="w-full space-y-2">
{#each props.searchFields as field}
{#if field.type === "text"}
<div class="space-y-2">
@ -22,20 +32,81 @@
<ReusableCalendar title={field.label} bind:value={props.searchQuery[field.key]}/>
</div>
{:else if field.type === "select"}
<div class="space-y-2">
<div class="w-full space-y-2">
<Label for={field.key}>{field.label}</Label>
<Select.Root bind:value={props.searchQuery[field.key]}>
<Select.Trigger id={field.key}>
<Select.Value placeholder={field.placeholder} />
<Select.Root type="single" bind:value={props.searchQuery[field.key]}
onOpenChange={() => {
props.fetchOptions(field)
}}
>
<Select.Trigger id={field.key} class="w-full flex justify-between">
{
props.selectOptions[field.key]?.find(
opt => opt.value === props.searchQuery[field.key]
)?.label ?? "Choose"
}
</Select.Trigger>
<Select.Content>
{#each field.options as opt}
<Select.Content class="w-full">
<Select.Item value="">- None -</Select.Item>
{#each props.selectOptions[field.key] as opt}
<Select.Item value={opt.value}>
{opt.label}
</Select.Item>
{/each}
</Select.Content>
</Select.Root>
<!-- === -->
<!-- <Select.Root type="single" bind:value={props.searchQuery[field.key]}
onOpenChange={(open) => {
if (open && optionsEndpoint) {
formState.fetchOptions(
{ key, optionsEndpoint, dependsOn, endpointParamKey, valueKey, labelKey },
formState.form
);
}
}}
>
<Select.Trigger class="w-full truncate">
{selectedLabel}
</Select.Trigger>
<Select.Content>
{#if formState.loadingOptions[key]}
<Select.Item disabled value="loading">Loading...</Select.Item>
{:else}
{#if !required}
<Select.Item value="">- None -</Select.Item>
{/if}
{#each selectOptions as option}
<Select.Item value={option.value}>
{option.label}
</Select.Item>
{/each}
{/if}
</Select.Content>
</Select.Root> -->
<!-- === -->
<!-- <Select.Root bind:value={props.searchQuery[field.key]}>
<Select.Trigger id={field.key}>
<Select.Value placeholder={`Select ${field.label}`} />
</Select.Trigger>
<Select.Content>
{#if props.loadingOptions[field.key]}
<Select.Item value="" disabled>
Loading...
</Select.Item>
{:else}
<Select.Item value="">
All {field.label}
</Select.Item>
{#each props.selectOptions[field.key] || [] as option}
<Select.Item value={option.value}>
{option.label}
</Select.Item>
{/each}
{/if}
</Select.Content>
</Select.Root> -->
</div>
{/if}
{/each}