130 lines
3.6 KiB
Vue
130 lines
3.6 KiB
Vue
<template>
|
|
<div class="flex flex-col md:flex-row gap-4">
|
|
<!-- Selector de clientes -->
|
|
<div class="flex-1">
|
|
<label class="text-xs text-[var(--brand-text-muted)] block mb-1">Seleccionar clientes</label>
|
|
<UInputMenu
|
|
v-model="selectedClientesObjects"
|
|
v-model:open="isOpen"
|
|
v-model:search-term="searchTerm"
|
|
:items="clienteOptions"
|
|
multiple
|
|
:filter-fields="['name', 'ubicacion']"
|
|
placeholder="Escribe al menos 3 caracteres para buscar..."
|
|
:disabled="props.clientes.length === 0"
|
|
icon="i-lucide-users"
|
|
:ui="{
|
|
trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform duration-200'
|
|
}"
|
|
>
|
|
<template #item-label="{ item }">
|
|
<span class="font-medium">{{ item.name }}</span>
|
|
<span v-if="item.ubicacion" class="text-xs text-[var(--brand-text-muted)] ml-2">
|
|
{{ item.ubicacion }}
|
|
</span>
|
|
</template>
|
|
</UInputMenu>
|
|
</div>
|
|
|
|
<div class="flex items-end">
|
|
<UButton
|
|
:ui="{ base: 'bg-[#c08040] text-[#1b1209] border border-[#d99a56] hover:bg-[#d99a56] hover:border-[#f0c07c]' }"
|
|
@click="clearSelection"
|
|
size="sm"
|
|
>
|
|
Limpiar
|
|
</UButton>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, ref, watch, toRaw } from 'vue'
|
|
|
|
interface Cliente {
|
|
id: number
|
|
name: string
|
|
cedula?: number
|
|
ubicacion?: string
|
|
telefono?: string
|
|
}
|
|
|
|
interface Props {
|
|
clientes: Cliente[]
|
|
selectedIds: number[]
|
|
}
|
|
|
|
const props = defineProps<Props>()
|
|
|
|
const emit = defineEmits<{
|
|
'update:selectedIds': [value: number[]]
|
|
}>()
|
|
|
|
const isOpen = ref(false)
|
|
const searchTerm = ref('')
|
|
|
|
// Opciones procesadas para UInputMenu
|
|
const clienteOptions = computed(() => {
|
|
// Si el searchTerm tiene menos de 3 caracteres, retornar array vacío
|
|
if (searchTerm.value.length < 3 && searchTerm.value.length > 0) {
|
|
return []
|
|
}
|
|
|
|
const filtered = props.clientes
|
|
.map(c => toRaw(c))
|
|
.filter(c => c.name && c.name.trim() !== '')
|
|
.sort((a, b) => a.name.localeCompare(b.name))
|
|
.map(c => ({
|
|
id: c.id,
|
|
name: c.name,
|
|
ubicacion: c.ubicacion || '',
|
|
label: c.name // InputMenu requiere label
|
|
}))
|
|
|
|
console.log('Search term:', searchTerm.value)
|
|
console.log('Total clientes:', props.clientes.length)
|
|
console.log('Filtered cliente options:', filtered.length)
|
|
|
|
return filtered
|
|
})
|
|
|
|
// v-model para UInputMenu trabaja con objetos completos cuando es multiple
|
|
const selectedClientesObjects = computed({
|
|
get: () => {
|
|
// Para el get, siempre mostrar los seleccionados independientemente del searchTerm
|
|
const allOptions = props.clientes
|
|
.map(c => toRaw(c))
|
|
.filter(c => c.name && c.name.trim() !== '')
|
|
.map(c => ({
|
|
id: c.id,
|
|
name: c.name,
|
|
ubicacion: c.ubicacion || '',
|
|
label: c.name
|
|
}))
|
|
|
|
return allOptions.filter(c => props.selectedIds.includes(c.id))
|
|
},
|
|
set: (value: typeof clienteOptions.value) => {
|
|
const ids = value.map(c => c.id)
|
|
emit('update:selectedIds', ids)
|
|
console.log('Selected IDs updated:', ids)
|
|
}
|
|
})
|
|
|
|
function clearSelection() {
|
|
emit('update:selectedIds', [])
|
|
searchTerm.value = ''
|
|
console.log('Cliente selection cleared')
|
|
}
|
|
|
|
// Watch para logging
|
|
watch(() => props.selectedIds, (newIds) => {
|
|
console.log('Selected cliente IDs:', newIds)
|
|
}, { deep: true })
|
|
|
|
// Watch para ver cuando cambian los clientes
|
|
watch(() => props.clientes, (newClientes) => {
|
|
console.log('Clientes prop changed, length:', newClientes?.length)
|
|
}, { deep: true, immediate: true })
|
|
</script>
|