Files
webapp/app/components/search/GlobalSearchBar.vue
Ruslan Bakiev d6865d2129
Some checks failed
Build Docker Image / build (push) Failing after 1m29s
UI improvements: filters, map layout, search bar
- Add hubCountries query and country filter for hubs page
- Add getAvailableProducts query for offers (only products with active offers)
- Add sourceLatitude/sourceLongitude to orders GraphQL
- Fix ListMapLayout with position fixed for proper map height
- GlobalSearchBar: make fields wider, remove unit selector
- Remove status/isVerified filters from suppliers/offers (backend handles this)
2026-01-08 10:42:59 +07:00

141 lines
4.4 KiB
Vue

<template>
<div class="bg-base-100 py-4 px-4 lg:px-6">
<div class="flex items-center justify-center">
<form
@submit.prevent="handleSearch"
class="flex items-center bg-base-100 rounded-full border border-base-300 shadow-sm hover:shadow-md transition-shadow"
>
<!-- Product field (clickable, navigates to /goods) -->
<div
class="flex flex-col px-4 py-2 min-w-48 pl-6 rounded-l-full hover:bg-base-200/50 border-r border-base-300 cursor-pointer"
@click="goToProductSelection"
>
<label class="text-xs font-semibold text-base-content/60 mb-0.5">
{{ $t('search.product') }}
</label>
<div class="text-sm" :class="productDisplay ? 'text-base-content' : 'text-base-content/50'">
{{ productDisplay || $t('search.product_placeholder') }}
</div>
</div>
<!-- Quantity field (editable) -->
<div class="flex flex-col px-4 py-2 min-w-48 hover:bg-base-200/50 border-r border-base-300">
<label class="text-xs font-semibold text-base-content/60 mb-0.5">
{{ $t('search.quantity') }}
</label>
<input
v-model="quantity"
type="number"
min="1"
:placeholder="$t('search.quantity_placeholder')"
class="w-full bg-transparent outline-none text-sm"
@change="syncQuantityToStore"
/>
</div>
<!-- Destination field (clickable, navigates to /select-location) -->
<div
class="flex flex-col px-4 py-2 min-w-48 hover:bg-base-200/50 cursor-pointer"
@click="goToLocationSelection"
>
<label class="text-xs font-semibold text-base-content/60 mb-0.5">
{{ $t('search.destination') }}
</label>
<div class="text-sm" :class="locationDisplay ? 'text-base-content' : 'text-base-content/50'">
{{ locationDisplay || $t('search.destination_placeholder') }}
</div>
</div>
<!-- Search button -->
<button
type="submit"
class="btn btn-primary btn-circle ml-2 mr-1"
:disabled="!canSearch"
>
<Icon name="lucide:search" size="18" />
</button>
</form>
</div>
</div>
</template>
<script setup lang="ts">
const emit = defineEmits<{
search: [params: { productUuid?: string; quantity?: number; locationUuid?: string }]
}>()
const router = useRouter()
const localePath = useLocalePath()
const searchStore = useSearchStore()
// Read from searchStore
const productDisplay = computed(() => searchStore.searchForm.product || '')
const productUuid = computed(() => searchStore.searchForm.productUuid || '')
const locationDisplay = computed(() => searchStore.searchForm.location || '')
const locationUuid = computed(() => searchStore.searchForm.locationUuid || '')
// Quantity - local state synced with store
const quantity = ref<number | undefined>(
searchStore.searchForm.quantity ? Number(searchStore.searchForm.quantity) : undefined
)
const syncQuantityToStore = () => {
if (quantity.value) {
searchStore.setQuantity(String(quantity.value))
}
}
// Navigation to selection pages
const goToProductSelection = () => {
navigateTo(localePath('/goods'))
}
const goToLocationSelection = () => {
navigateTo(localePath('/select-location'))
}
// Can search - need at least product selected
const canSearch = computed(() => {
return !!productUuid.value
})
// Search handler - navigate to /request
const handleSearch = () => {
if (!canSearch.value) return
// Sync quantity to store
syncQuantityToStore()
const query: Record<string, string | undefined> = {
productUuid: productUuid.value || undefined,
product: productDisplay.value || undefined,
quantity: quantity.value ? String(quantity.value) : undefined,
locationUuid: locationUuid.value || undefined,
location: locationDisplay.value || undefined
}
// Remove undefined/empty values
Object.keys(query).forEach(key => {
if (!query[key]) delete query[key]
})
router.push({
path: localePath('/request'),
query: query as Record<string, string>
})
emit('search', {
productUuid: productUuid.value,
quantity: quantity.value,
locationUuid: locationUuid.value
})
}
// Watch store changes to sync quantity
watch(() => searchStore.searchForm.quantity, (val) => {
if (val) {
quantity.value = Number(val)
}
}, { immediate: true })
</script>