mirror of
https://github.com/crater-invoice/crater.git
synced 2025-10-27 11:41:09 -04:00
217 lines
6.1 KiB
Vue
217 lines
6.1 KiB
Vue
<template>
|
|
<div ref="searchBar" class="hidden rounded md:block relative dark:text-white">
|
|
<div>
|
|
<BaseInput
|
|
v-model="name"
|
|
placeholder="Search..."
|
|
container-class="!rounded"
|
|
class="h-8 md:h-9 !rounded"
|
|
@input="onSearch"
|
|
>
|
|
<template #left>
|
|
<BaseIcon name="SearchIcon" class="text-gray-400" />
|
|
</template>
|
|
<template #right>
|
|
<SpinnerIcon v-if="isSearching" class="h-5 text-primary-500" />
|
|
</template>
|
|
</BaseInput>
|
|
</div>
|
|
<transition
|
|
enter-active-class="transition duration-200 ease-out"
|
|
enter-from-class="translate-y-1 opacity-0"
|
|
enter-to-class="translate-y-0 opacity-100"
|
|
leave-active-class="transition duration-150 ease-in"
|
|
leave-from-class="translate-y-0 opacity-100"
|
|
leave-to-class="translate-y-1 opacity-0"
|
|
>
|
|
<div
|
|
v-if="isShow"
|
|
class="
|
|
scrollbar-thin
|
|
scrollbar-thumb-rounded-full
|
|
scrollbar-thumb-gray-300
|
|
scrollbar-track-gray-100
|
|
overflow-y-auto
|
|
bg-white
|
|
rounded-md
|
|
mt-2
|
|
shadow-lg
|
|
p-3
|
|
absolute
|
|
w-[300px]
|
|
h-[200px]
|
|
right-0
|
|
dark:border-white/10
|
|
dark:text-white
|
|
dark:bg-gray-800/[.95]
|
|
dark:shadow-glass
|
|
dark:backdrop-blur-xl
|
|
"
|
|
>
|
|
<div
|
|
v-if="
|
|
usersStore.userList.length < 1 && usersStore.customerList.length < 1
|
|
"
|
|
class="
|
|
flex
|
|
items-center
|
|
justify-center
|
|
text-gray-400 text-base
|
|
flex-col
|
|
mt-4
|
|
"
|
|
>
|
|
<BaseIcon name="ExclamationCircleIcon" class="text-gray-400" />
|
|
|
|
{{ $t('global_search.no_results_found') }}
|
|
</div>
|
|
<div v-else>
|
|
<div v-if="usersStore.customerList.length > 0">
|
|
<label class="text-sm text-gray-400 mb-0.5 block px-2 uppercase">
|
|
{{ $t('global_search.customers') }}
|
|
</label>
|
|
<div
|
|
v-for="(customer, index) in usersStore.customerList"
|
|
:key="index"
|
|
class="p-2 hover:bg-gray-100 cursor-pointer rounded-md dark:hover:bg-gray-700/40"
|
|
>
|
|
<router-link
|
|
:to="{ path: `/admin/customers/${customer.id}/view` }"
|
|
class="flex items-center"
|
|
>
|
|
<span
|
|
class="
|
|
flex
|
|
items-center
|
|
justify-center
|
|
w-9
|
|
h-9
|
|
mr-3
|
|
text-base
|
|
font-semibold
|
|
bg-gray-200
|
|
rounded-full
|
|
text-primary-500
|
|
dark:bg-gray-600
|
|
dark:text-primary-400
|
|
"
|
|
>
|
|
{{ initGenerator(customer.name) }}
|
|
</span>
|
|
<div class="flex flex-col">
|
|
<span class="text-sm">{{ customer.name }}</span>
|
|
<span
|
|
v-if="customer.contact_name"
|
|
class="text-xs text-gray-400"
|
|
>
|
|
{{ customer.contact_name }}
|
|
</span>
|
|
<span v-else class="text-xs text-gray-400">{{
|
|
customer.email
|
|
}}</span>
|
|
</div>
|
|
</router-link>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="usersStore.userList.length > 0" class="mt-2">
|
|
<label
|
|
class="text-sm text-gray-400 mb-2 block px-2 mb-0.5 uppercase"
|
|
>
|
|
{{ $t('global_search.users') }}
|
|
</label>
|
|
<div
|
|
v-for="(user, index) in usersStore.userList"
|
|
:key="index"
|
|
class="p-2 hover:bg-gray-100 cursor-pointer rounded-md dark:hover:bg-gray-700/40"
|
|
>
|
|
<router-link
|
|
:to="{ path: `/admin/users/${user.id}/edit` }"
|
|
class="flex items-center"
|
|
>
|
|
<span
|
|
class="
|
|
flex
|
|
items-center
|
|
justify-center
|
|
w-9
|
|
h-9
|
|
mr-3
|
|
text-base
|
|
font-semibold
|
|
bg-gray-200
|
|
rounded-full
|
|
text-primary-500
|
|
dark:text-primary-400
|
|
dark:bg-gray-600
|
|
"
|
|
>
|
|
{{ initGenerator(user.name) }}
|
|
</span>
|
|
<div class="flex flex-col">
|
|
<span class="text-sm">{{ user.name }}</span>
|
|
<span class="text-xs text-gray-400">{{ user.email }}</span>
|
|
</div>
|
|
</router-link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</transition>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, watch } from 'vue'
|
|
import { useUsersStore } from '@/scripts/admin/stores/users'
|
|
import { onClickOutside } from '@vueuse/core'
|
|
import { useRoute } from 'vue-router'
|
|
import SpinnerIcon from '@/scripts/components/icons/SpinnerIcon.vue'
|
|
import { debounce } from 'lodash'
|
|
|
|
const usersStore = useUsersStore()
|
|
|
|
const isShow = ref(false)
|
|
const name = ref('')
|
|
const searchBar = ref(null)
|
|
const isSearching = ref(false)
|
|
const route = useRoute()
|
|
|
|
watch(route, () => {
|
|
isShow.value = false
|
|
name.value = ''
|
|
})
|
|
|
|
onSearch = debounce(onSearch, 500)
|
|
|
|
onClickOutside(searchBar, () => {
|
|
isShow.value = false
|
|
name.value = ''
|
|
})
|
|
|
|
function onSearch() {
|
|
let data = {
|
|
search: name.value,
|
|
}
|
|
|
|
if (name.value) {
|
|
isSearching.value = true
|
|
usersStore.searchUsers(data).then(() => {
|
|
isShow.value = true
|
|
})
|
|
isSearching.value = false
|
|
}
|
|
if (name.value === '') {
|
|
isShow.value = false
|
|
}
|
|
}
|
|
|
|
function initGenerator(name) {
|
|
if (name) {
|
|
const nameSplit = name.split(' ')
|
|
const initials = nameSplit[0].charAt(0).toUpperCase()
|
|
return initials
|
|
}
|
|
}
|
|
</script>
|