91 lines
4.1 KiB
Vue
91 lines
4.1 KiB
Vue
<script setup>
|
|
import { FwbInput } from 'flowbite-vue';
|
|
import axios from 'axios';
|
|
import { debounce } from 'lodash';
|
|
import { SearchIcon } from '@/Utilities/Icons';
|
|
import { onMounted, onUnmounted, ref, watch } from 'vue';
|
|
import { Link } from '@inertiajs/vue3';
|
|
|
|
const props = defineProps({
|
|
open: { type: Boolean, default: false },
|
|
});
|
|
const emit = defineEmits(['update:open']);
|
|
|
|
const query = ref('');
|
|
const result = ref({ clients: [], client_cases: [] });
|
|
const isOpen = ref(props.open);
|
|
|
|
watch(() => props.open, (v) => { isOpen.value = v; if (v) focusInput(); });
|
|
watch(isOpen, (v) => emit('update:open', v));
|
|
|
|
const searching = debounce((value) => {
|
|
if (!value || !value.trim()) { result.value = { clients: [], client_cases: [] }; return; }
|
|
axios.get(route('search'), { params: { query: value, limit: 8, tag: '' } })
|
|
.then(res => { result.value = res.data; })
|
|
.catch(() => {})
|
|
}, 250);
|
|
|
|
watch(() => query.value, (val) => searching(val));
|
|
|
|
const inputWrap = ref(null);
|
|
const focusInput = () => setTimeout(() => inputWrap.value?.querySelector('input')?.focus(), 0);
|
|
|
|
function onKeydown(e) {
|
|
if (e.key === 'Escape') { isOpen.value = false; }
|
|
}
|
|
onMounted(() => window.addEventListener('keydown', onKeydown));
|
|
onUnmounted(() => window.removeEventListener('keydown', onKeydown));
|
|
</script>
|
|
|
|
<template>
|
|
<teleport to="body">
|
|
<transition name="fade">
|
|
<div v-if="isOpen" class="fixed inset-0 z-50">
|
|
<!-- Backdrop -->
|
|
<div class="absolute inset-0 bg-black/30" @click="isOpen = false"></div>
|
|
|
|
<!-- Dialog (click outside closes) -->
|
|
<div class="absolute inset-0 flex items-start sm:items-start justify-center p-4 pt-8 sm:pt-16" @click.self="isOpen = false">
|
|
<div class="w-full max-w-2xl bg-white rounded-lg shadow-xl overflow-hidden">
|
|
<div class="p-3 border-b" ref="inputWrap">
|
|
<FwbInput v-model="query" placeholder="Išči po naročnikih in primerih..." size="md" class="w-full">
|
|
<template #prefix>
|
|
<SearchIcon />
|
|
</template>
|
|
</FwbInput>
|
|
</div>
|
|
<div class="max-h-[60vh] overflow-auto">
|
|
<div v-if="!query" class="p-6 text-sm text-gray-500">Začni tipkati za iskanje. Namig: pritisni Ctrl+K kjerkoli.</div>
|
|
<div v-else>
|
|
<div class="px-4 py-2 text-xs text-gray-500">Naročniki</div>
|
|
<ul>
|
|
<li v-for="client in result.clients" :key="client.client_uuid">
|
|
<Link :href="route('client.show', {uuid: client.client_uuid})" class="block px-4 py-2 hover:bg-gray-50" @click="isOpen=false">
|
|
{{ client.full_name }}
|
|
</Link>
|
|
</li>
|
|
</ul>
|
|
|
|
<div class="px-4 py-2 mt-2 text-xs text-gray-500">Primeri</div>
|
|
<ul>
|
|
<li v-for="clientcase in result.client_cases" :key="clientcase.case_uuid">
|
|
<Link :href="route('clientCase.show', {uuid: clientcase.case_uuid})" class="block px-4 py-2 hover:bg-gray-50" @click="isOpen=false">
|
|
{{ clientcase.full_name }}
|
|
</Link>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</transition>
|
|
</teleport>
|
|
|
|
<!-- no inline trigger here; AppLayout provides the button and opens this modal -->
|
|
</template>
|
|
|
|
<style>
|
|
.fade-enter-active, .fade-leave-active { transition: opacity .15s; }
|
|
.fade-enter-from, .fade-leave-to { opacity: 0; }
|
|
</style> |