sper mejoras de UI
This commit is contained in:
293
nuxt4-app/app/components/ingresos/FiltrosActivos.vue
Normal file
293
nuxt4-app/app/components/ingresos/FiltrosActivos.vue
Normal file
@@ -0,0 +1,293 @@
|
||||
<template>
|
||||
<UCard class="brand-card border border-transparent">
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<UIcon name="i-lucide-filter" class="size-4 text-[#c08040]" />
|
||||
<h3 class="text-sm font-semibold text-[var(--brand-text)]">Filtros Activos</h3>
|
||||
<span class="text-xs text-[var(--brand-text-muted)]">({{ totalFiltros }})</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<UButton
|
||||
size="xs"
|
||||
variant="ghost"
|
||||
color="neutral"
|
||||
@click="showFiltros"
|
||||
icon="i-lucide-plus"
|
||||
>
|
||||
Agregar filtros
|
||||
</UButton>
|
||||
<UButton
|
||||
v-if="totalFiltros > 0"
|
||||
size="xs"
|
||||
variant="ghost"
|
||||
color="neutral"
|
||||
@click="limpiarTodos"
|
||||
icon="i-lucide-x"
|
||||
>
|
||||
Limpiar todos
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="totalFiltros === 0" class="text-center py-4 text-sm text-[var(--brand-text-muted)]">
|
||||
No hay filtros activos
|
||||
</div>
|
||||
|
||||
<div v-else class="flex flex-wrap gap-2">
|
||||
<!-- Clientes -->
|
||||
<div
|
||||
v-for="clienteId in selectedClienteIds"
|
||||
:key="`cliente-${clienteId}`"
|
||||
class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-medium bg-blue-500/10 text-blue-400 border border-blue-500/30 transition-all hover:bg-blue-500/20"
|
||||
>
|
||||
<UIcon name="i-lucide-user" class="size-3" />
|
||||
<span>{{ getNombreCliente(clienteId) }}</span>
|
||||
<button
|
||||
@click="removeCliente(clienteId)"
|
||||
class="ml-1 hover:text-blue-300 transition-colors"
|
||||
>
|
||||
<UIcon name="i-lucide-x" class="size-3" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Botón para agregar más clientes -->
|
||||
<button
|
||||
v-if="selectedClienteIds.length > 0"
|
||||
@click="showClienteSelector"
|
||||
class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-medium bg-blue-500/5 text-blue-400 border border-blue-500/20 border-dashed transition-all hover:bg-blue-500/10 hover:border-blue-500/40"
|
||||
title="Agregar más clientes"
|
||||
>
|
||||
<UIcon name="i-lucide-user-plus" class="size-3" />
|
||||
<span>Agregar cliente</span>
|
||||
</button>
|
||||
|
||||
<!-- Fechas -->
|
||||
<div
|
||||
v-if="fechaDesde || fechaHasta"
|
||||
class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-medium bg-purple-500/10 text-purple-400 border border-purple-500/30 transition-all hover:bg-purple-500/20"
|
||||
>
|
||||
<UIcon name="i-lucide-calendar" class="size-3" />
|
||||
<span>{{ fechaDesde || '—' }} → {{ fechaHasta || '—' }}</span>
|
||||
<button
|
||||
@click="removeFechas"
|
||||
class="ml-1 hover:text-purple-300 transition-colors"
|
||||
>
|
||||
<UIcon name="i-lucide-x" class="size-3" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Tipos -->
|
||||
<div
|
||||
v-for="tipo in selectedTipos"
|
||||
:key="`tipo-${tipo}`"
|
||||
class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-medium bg-orange-500/10 text-orange-400 border border-orange-500/30 transition-all hover:bg-orange-500/20"
|
||||
>
|
||||
<UIcon name="i-lucide-coffee" class="size-3" />
|
||||
<span>{{ getLabelTipo(tipo) }}</span>
|
||||
<button
|
||||
@click="removeTipo(tipo)"
|
||||
class="ml-1 hover:text-orange-300 transition-colors"
|
||||
>
|
||||
<UIcon name="i-lucide-x" class="size-3" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Estados -->
|
||||
<div
|
||||
v-for="estado in selectedEstados"
|
||||
:key="`estado-${estado}`"
|
||||
class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-medium bg-green-500/10 text-green-400 border border-green-500/30 transition-all hover:bg-green-500/20"
|
||||
>
|
||||
<UIcon name="i-lucide-check-circle" class="size-3" />
|
||||
<span>{{ getLabelEstado(estado) }}</span>
|
||||
<button
|
||||
@click="removeEstado(estado)"
|
||||
class="ml-1 hover:text-green-300 transition-colors"
|
||||
>
|
||||
<UIcon name="i-lucide-x" class="size-3" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Ubicaciones -->
|
||||
<div
|
||||
v-for="ubicacion in selectedUbicaciones"
|
||||
:key="`ubicacion-${ubicacion}`"
|
||||
class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-medium bg-cyan-500/10 text-cyan-400 border border-cyan-500/30 transition-all hover:bg-cyan-500/20"
|
||||
>
|
||||
<UIcon name="i-lucide-map-pin" class="size-3" />
|
||||
<span>{{ ubicacion }}</span>
|
||||
<button
|
||||
@click="removeUbicacion(ubicacion)"
|
||||
class="ml-1 hover:text-cyan-300 transition-colors"
|
||||
>
|
||||
<UIcon name="i-lucide-x" class="size-3" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Calidades -->
|
||||
<div
|
||||
v-for="calidad in selectedCalidades"
|
||||
:key="`calidad-${calidad}`"
|
||||
class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-medium bg-yellow-500/10 text-yellow-400 border border-yellow-500/30 transition-all hover:bg-yellow-500/20"
|
||||
>
|
||||
<UIcon name="i-lucide-star" class="size-3" />
|
||||
<span>{{ calidad }}</span>
|
||||
<button
|
||||
@click="removeCalidad(calidad)"
|
||||
class="ml-1 hover:text-yellow-300 transition-colors"
|
||||
>
|
||||
<UIcon name="i-lucide-x" class="size-3" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Incluir Anulados -->
|
||||
<div
|
||||
v-if="includeAnulados"
|
||||
class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-medium bg-red-500/10 text-red-400 border border-red-500/30 transition-all hover:bg-red-500/20"
|
||||
>
|
||||
<UIcon name="i-lucide-alert-triangle" class="size-3" />
|
||||
<span>Con anulados</span>
|
||||
<button
|
||||
@click="removeAnulados"
|
||||
class="ml-1 hover:text-red-300 transition-colors"
|
||||
>
|
||||
<UIcon name="i-lucide-x" class="size-3" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Sin Filtros -->
|
||||
<div
|
||||
v-if="noFilter"
|
||||
class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-medium bg-gray-500/10 text-gray-400 border border-gray-500/30 transition-all hover:bg-gray-500/20"
|
||||
>
|
||||
<UIcon name="i-lucide-ban" class="size-3" />
|
||||
<span>Sin filtros</span>
|
||||
<button
|
||||
@click="removeNoFilter"
|
||||
class="ml-1 hover:text-gray-300 transition-colors"
|
||||
>
|
||||
<UIcon name="i-lucide-x" class="size-3" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</UCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
clientes: any[]
|
||||
selectedClienteIds: number[]
|
||||
fechaDesde: string | null
|
||||
fechaHasta: string | null
|
||||
selectedTipos: string[]
|
||||
selectedEstados: string[]
|
||||
selectedUbicaciones: string[]
|
||||
selectedCalidades: string[]
|
||||
includeAnulados: boolean
|
||||
noFilter: boolean
|
||||
tiposOptions: any[]
|
||||
estadosOptions: any[]
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:selectedClienteIds': [value: number[]]
|
||||
'update:fechaDesde': [value: string | null]
|
||||
'update:fechaHasta': [value: string | null]
|
||||
'update:selectedPreset': [value: string]
|
||||
'update:selectedTipos': [value: string[]]
|
||||
'update:selectedEstados': [value: string[]]
|
||||
'update:selectedUbicaciones': [value: string[]]
|
||||
'update:selectedCalidades': [value: string[]]
|
||||
'update:includeAnulados': [value: boolean]
|
||||
'update:noFilter': [value: boolean]
|
||||
'showClienteSelector': []
|
||||
'showFiltros': []
|
||||
}>()
|
||||
|
||||
const totalFiltros = computed(() => {
|
||||
let count = 0
|
||||
count += props.selectedClienteIds.length
|
||||
count += (props.fechaDesde || props.fechaHasta) ? 1 : 0
|
||||
count += props.selectedTipos.length
|
||||
count += props.selectedEstados.length
|
||||
count += props.selectedUbicaciones.length
|
||||
count += props.selectedCalidades.length
|
||||
count += props.includeAnulados ? 1 : 0
|
||||
count += props.noFilter ? 1 : 0
|
||||
return count
|
||||
})
|
||||
|
||||
function showClienteSelector() {
|
||||
emit('showClienteSelector')
|
||||
}
|
||||
|
||||
function showFiltros() {
|
||||
emit('showFiltros')
|
||||
}
|
||||
|
||||
function getNombreCliente(id: number): string {
|
||||
const cliente = props.clientes.find(c => c.id === id)
|
||||
return cliente?.name || `Cliente ${id}`
|
||||
}
|
||||
|
||||
function getLabelTipo(value: string): string {
|
||||
const tipo = props.tiposOptions.find(t => t.value === value)
|
||||
return tipo?.label || value
|
||||
}
|
||||
|
||||
function getLabelEstado(value: string): string {
|
||||
const estado = props.estadosOptions.find(e => e.value === value)
|
||||
return estado?.label || value
|
||||
}
|
||||
|
||||
function removeCliente(id: number) {
|
||||
emit('update:selectedClienteIds', props.selectedClienteIds.filter(cid => cid !== id))
|
||||
}
|
||||
|
||||
function removeFechas() {
|
||||
emit('update:fechaDesde', null)
|
||||
emit('update:fechaHasta', null)
|
||||
emit('update:selectedPreset', '')
|
||||
}
|
||||
|
||||
function removeTipo(tipo: string) {
|
||||
emit('update:selectedTipos', props.selectedTipos.filter(t => t !== tipo))
|
||||
}
|
||||
|
||||
function removeEstado(estado: string) {
|
||||
emit('update:selectedEstados', props.selectedEstados.filter(e => e !== estado))
|
||||
}
|
||||
|
||||
function removeUbicacion(ubicacion: string) {
|
||||
emit('update:selectedUbicaciones', props.selectedUbicaciones.filter(u => u !== ubicacion))
|
||||
}
|
||||
|
||||
function removeCalidad(calidad: string) {
|
||||
emit('update:selectedCalidades', props.selectedCalidades.filter(c => c !== calidad))
|
||||
}
|
||||
|
||||
function removeAnulados() {
|
||||
emit('update:includeAnulados', false)
|
||||
}
|
||||
|
||||
function removeNoFilter() {
|
||||
emit('update:noFilter', false)
|
||||
}
|
||||
|
||||
function limpiarTodos() {
|
||||
emit('update:selectedClienteIds', [])
|
||||
emit('update:fechaDesde', null)
|
||||
emit('update:fechaHasta', null)
|
||||
emit('update:selectedPreset', '')
|
||||
emit('update:selectedTipos', [])
|
||||
emit('update:selectedEstados', [])
|
||||
emit('update:selectedUbicaciones', [])
|
||||
emit('update:selectedCalidades', [])
|
||||
emit('update:includeAnulados', false)
|
||||
emit('update:noFilter', false)
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user