85 lines
2.0 KiB
Vue
85 lines
2.0 KiB
Vue
<script setup lang="ts">
|
|
import { ref, watch } from 'vue'
|
|
import { Link } from '@inertiajs/vue3'
|
|
|
|
defineProps<{
|
|
density?: null | 'default' | 'comfortable' | 'compact';
|
|
}>()
|
|
|
|
interface UserOption {
|
|
value: number
|
|
title: string
|
|
}
|
|
|
|
const search = ref('')
|
|
const selectedUser = ref<UserOption | null>(null)
|
|
const items = ref<UserOption[]>([])
|
|
const loading = ref(false)
|
|
|
|
let debounceTimeout: ReturnType<typeof setTimeout> | undefined
|
|
|
|
async function fetchUsers(query: string) {
|
|
if (!query) {
|
|
items.value = []
|
|
return
|
|
}
|
|
|
|
loading.value = true
|
|
try {
|
|
const response = await fetch(`/search/users?q=${encodeURIComponent(query)}`)
|
|
if (!response.ok) throw new Error('Failed to fetch users')
|
|
items.value = await response.json()
|
|
} catch (error) {
|
|
console.error('User search failed:', error)
|
|
items.value = []
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
watch(search, (newQuery) => {
|
|
clearTimeout(debounceTimeout)
|
|
debounceTimeout = setTimeout(() => {
|
|
fetchUsers(newQuery)
|
|
}, 300)
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="user-search-box">
|
|
<v-autocomplete
|
|
v-model="selectedUser"
|
|
v-model:search="search"
|
|
:items="items"
|
|
:loading="loading"
|
|
item-title="title"
|
|
item-value="value"
|
|
return-object
|
|
label="Search users"
|
|
placeholder="Type a name..."
|
|
prepend-inner-icon="mdi-magnify"
|
|
:density="density ?? 'default'"
|
|
variant="outlined"
|
|
no-filter
|
|
clearable
|
|
hide-details
|
|
hide-no-data
|
|
autocomplete="off"
|
|
>
|
|
<template #item="{ item }">
|
|
<Link :href="`/u/${item.title}`" class="user-search-result">
|
|
<v-list-item :title="item.title" />
|
|
</Link>
|
|
</template>
|
|
</v-autocomplete>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.user-search-box {
|
|
display: block;
|
|
align-self: center;
|
|
width: 100%;
|
|
}
|
|
</style>
|