Fix: Corregir implementación de InputMenu según documentación oficial
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 52s

Correcciones aplicadas:
- Usar ignore-filter para control manual del filtrado
- Implementar label-key y value-key correctamente
- Usar slot #item-label en lugar de #item para personalización
- Usar slot #empty para mensajes cuando no hay resultados
- Mapear items a formato InputMenuItem con label y value
- Usar clases de Nuxt UI (text-muted) en lugar de variables CSS
- Simplificar lógica eliminando control manual del estado open

Funcionalidad:
- Filtrado solo se activa con mínimo 4 caracteres
- Búsqueda por nombre y cédula
- Selección múltiple con tags visuales
- Formato de cédula mantenido
This commit is contained in:
2025-10-30 13:16:17 -06:00
parent c2bf441db7
commit 466e3923d7

View File

@@ -4,29 +4,39 @@
<UInputMenu <UInputMenu
v-model="selectedClientes" v-model="selectedClientes"
v-model:search-term="searchQuery" v-model:search-term="searchQuery"
v-model:open="isMenuOpen"
:items="filteredItems" :items="filteredItems"
:loading="loading" :loading="loading"
multiple multiple
icon="i-lucide-search" label-key="name"
placeholder="Buscar clientes por nombre o cédula (mínimo 4 caracteres)..."
value-key="id" value-key="id"
icon="i-lucide-search"
placeholder="Buscar clientes por nombre o cédula..."
ignore-filter
> >
<template #item="{ item }"> <template #item-label="{ item }">
<div class="flex-1 min-w-0"> <div class="flex flex-col gap-0.5">
<div class="font-medium truncate"> <span class="font-medium">{{ item.name }}</span>
{{ item.name }} <span v-if="item.cedula" class="text-xs text-muted">
</div> {{ formatCedula(item.cedula) }}
<div class="text-xs text-[var(--brand-text-muted)]"> </span>
<span v-if="item.cedula">{{ formatCedula(item.cedula) }}</span> </div>
</div> </template>
<template #empty>
<div class="text-center py-2">
<span v-if="searchQuery.length < 4" class="text-muted text-sm">
Escribe al menos 4 caracteres para buscar
</span>
<span v-else class="text-muted text-sm">
No se encontraron clientes
</span>
</div> </div>
</template> </template>
</UInputMenu> </UInputMenu>
<!-- Selected count and clear all --> <!-- Selected count and clear all -->
<div v-if="selectedClientes.length > 0" class="flex items-center justify-between text-sm"> <div v-if="selectedClientes.length > 0" class="flex items-center justify-between text-sm">
<span class="text-[var(--brand-text-muted)]"> <span class="text-muted">
{{ selectedClientes.length }} cliente{{ selectedClientes.length !== 1 ? 's' : '' }} seleccionado{{ selectedClientes.length !== 1 ? 's' : '' }} {{ selectedClientes.length }} cliente{{ selectedClientes.length !== 1 ? 's' : '' }} seleccionado{{ selectedClientes.length !== 1 ? 's' : '' }}
</span> </span>
<UButton <UButton
@@ -42,6 +52,8 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import type { InputMenuItem } from '@nuxt/ui'
interface Cliente { interface Cliente {
id: number id: number
name: string name: string
@@ -61,7 +73,6 @@ const emit = defineEmits<{
const clientes = ref<Cliente[]>([]) const clientes = ref<Cliente[]>([])
const loading = ref(false) const loading = ref(false)
const searchQuery = ref('') const searchQuery = ref('')
const isMenuOpen = ref(false)
// Computed - Sync with props // Computed - Sync with props
const selectedClientes = computed({ const selectedClientes = computed({
@@ -74,7 +85,7 @@ const selectedClientes = computed({
}) })
// Computed - Filter items based on search (min 4 characters) // Computed - Filter items based on search (min 4 characters)
const filteredItems = computed(() => { const filteredItems = computed((): InputMenuItem[] => {
const query = searchQuery.value.trim() const query = searchQuery.value.trim()
// Only show items if search has at least 4 characters // Only show items if search has at least 4 characters
@@ -84,19 +95,18 @@ const filteredItems = computed(() => {
const lowerQuery = query.toLowerCase() const lowerQuery = query.toLowerCase()
return clientes.value.filter(c => return clientes.value
c.name.toLowerCase().includes(lowerQuery) || .filter(c =>
c.cedula?.includes(query) c.name.toLowerCase().includes(lowerQuery) ||
) c.cedula?.includes(query)
}) )
.map(c => ({
// Watch searchQuery to control menu opening id: c.id,
watch(searchQuery, (newValue) => { name: c.name,
if (newValue.trim().length >= 4 && filteredItems.value.length > 0) { cedula: c.cedula,
isMenuOpen.value = true label: c.name,
} else { value: c.id
isMenuOpen.value = false }))
}
}) })
// Methods // Methods