All checks were successful
Build Docker Image / build (push) Successful in 3m40s
- Reorder topnav components: search bar before submenu - GlobalSearchBar: use page navigation instead of dropdowns - Remove icons from MainNavigation and SubNavigation - Create ListMapLayout universal component for list+map pages - Migrate catalog pages (offers, suppliers, hubs) to ListMapLayout
156 lines
5.0 KiB
Vue
156 lines
5.0 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-32 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-32 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>
|
|
<div class="flex items-center gap-1">
|
|
<input
|
|
v-model="quantity"
|
|
type="number"
|
|
min="1"
|
|
:placeholder="$t('search.quantity_placeholder')"
|
|
class="w-16 bg-transparent outline-none text-sm"
|
|
@change="syncQuantityToStore"
|
|
/>
|
|
<select v-model="unit" class="bg-transparent outline-none text-sm text-base-content/70" @change="syncQuantityToStore">
|
|
<option value="t">{{ $t('units.t') }}</option>
|
|
<option value="kg">{{ $t('units.kg') }}</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Destination field (clickable, navigates to /select-location) -->
|
|
<div
|
|
class="flex flex-col px-4 py-2 min-w-32 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; unit?: string; 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 unit = ref(searchStore.searchForm.unit || 't')
|
|
|
|
const syncQuantityToStore = () => {
|
|
if (quantity.value) {
|
|
searchStore.setQuantity(String(quantity.value))
|
|
}
|
|
searchStore.setUnit(unit.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,
|
|
unit: unit.value,
|
|
locationUuid: locationUuid.value
|
|
})
|
|
}
|
|
|
|
// Watch store changes to sync quantity
|
|
watch(() => searchStore.searchForm.quantity, (val) => {
|
|
if (val) {
|
|
quantity.value = Number(val)
|
|
}
|
|
}, { immediate: true })
|
|
|
|
watch(() => searchStore.searchForm.unit, (val) => {
|
|
if (val) {
|
|
unit.value = val
|
|
}
|
|
}, { immediate: true })
|
|
</script>
|