Feat: agregar indicador visual de cambios pendientes en filtros
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 45s
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 45s
Implementación de sistema de tracking para detectar cuando los filtros han cambiado pero no se han aplicado a los datos mostrados. Indicadores visuales implementados: - Alerta amarilla prominente con animación pulse y ping - Borde y sombra amarilla en el card de filtros - Botón 'Actualizar' cambia a amarillo con emoji de advertencia - Botón rápido 'Actualizar ahora' dentro de la alerta - Animaciones llamativas para captar la atención El sistema compara los filtros actuales con los últimos aplicados y muestra indicadores visuales evidentes cuando hay diferencias.
This commit is contained in:
@@ -99,7 +99,14 @@
|
|||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
<template v-else-if="data">
|
<template v-else-if="data">
|
||||||
<!-- Card de Filtros -->
|
<!-- Card de Filtros -->
|
||||||
<UCard class="brand-card border border-transparent">
|
<UCard
|
||||||
|
:class="[
|
||||||
|
'brand-card border transition-all duration-300',
|
||||||
|
hasPendingChanges
|
||||||
|
? 'border-yellow-500 shadow-lg shadow-yellow-500/50 ring-2 ring-yellow-400/50'
|
||||||
|
: 'border-transparent'
|
||||||
|
]"
|
||||||
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="flex flex-col gap-3">
|
<div class="flex flex-col gap-3">
|
||||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||||
@@ -114,6 +121,40 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Alerta de cambios pendientes -->
|
||||||
|
<UAlert
|
||||||
|
v-if="hasPendingChanges"
|
||||||
|
color="warning"
|
||||||
|
variant="solid"
|
||||||
|
icon="i-lucide-alert-circle"
|
||||||
|
class="animate-pulse"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<span class="font-bold">¡Cambios pendientes!</span>
|
||||||
|
<span class="inline-flex h-2 w-2 rounded-full bg-yellow-400 animate-ping"></span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #description>
|
||||||
|
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-3">
|
||||||
|
<span>Los filtros han cambiado pero no se han aplicado a los datos mostrados. Haz clic en "Actualizar" para aplicar los cambios.</span>
|
||||||
|
<UButton
|
||||||
|
:loading="loading"
|
||||||
|
:disabled="loading"
|
||||||
|
color="yellow"
|
||||||
|
variant="solid"
|
||||||
|
size="xs"
|
||||||
|
@click="loadData"
|
||||||
|
>
|
||||||
|
<template #leading>
|
||||||
|
<UIcon name="i-lucide-refresh-cw" :class="{ 'animate-spin': loading }" />
|
||||||
|
</template>
|
||||||
|
Actualizar ahora
|
||||||
|
</UButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UAlert>
|
||||||
|
|
||||||
<!-- Alerta roja cuando incluye anulados -->
|
<!-- Alerta roja cuando incluye anulados -->
|
||||||
<UAlert
|
<UAlert
|
||||||
v-if="includeAnulados"
|
v-if="includeAnulados"
|
||||||
@@ -143,14 +184,18 @@
|
|||||||
<UButton
|
<UButton
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
:ui="{ base: 'bg-[#c08040] text-[#1b1209] border border-[#d99a56] hover:bg-[#d99a56] hover:border-[#f0c07c] disabled:opacity-50 disabled:cursor-not-allowed' }"
|
:ui="{
|
||||||
|
base: hasPendingChanges
|
||||||
|
? 'bg-yellow-500 text-black border border-yellow-600 hover:bg-yellow-400 hover:border-yellow-500 disabled:opacity-50 disabled:cursor-not-allowed animate-pulse'
|
||||||
|
: 'bg-[#c08040] text-[#1b1209] border border-[#d99a56] hover:bg-[#d99a56] hover:border-[#f0c07c] disabled:opacity-50 disabled:cursor-not-allowed'
|
||||||
|
}"
|
||||||
size="sm"
|
size="sm"
|
||||||
@click="loadData"
|
@click="loadData"
|
||||||
>
|
>
|
||||||
<template #leading>
|
<template #leading>
|
||||||
<UIcon name="i-lucide-refresh-cw" :class="{ 'animate-spin': loading }" />
|
<UIcon name="i-lucide-refresh-cw" :class="{ 'animate-spin': loading }" />
|
||||||
</template>
|
</template>
|
||||||
Actualizar
|
{{ hasPendingChanges ? 'Actualizar ⚠️' : 'Actualizar' }}
|
||||||
</UButton>
|
</UButton>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -230,6 +275,13 @@ const selectedPreset = ref<PresetValue>('cosecha-25-26')
|
|||||||
const fechaDesde = ref<string | null>(null)
|
const fechaDesde = ref<string | null>(null)
|
||||||
const fechaHasta = ref<string | null>(null)
|
const fechaHasta = ref<string | null>(null)
|
||||||
|
|
||||||
|
// Filtros aplicados (los que se usaron en la última carga de datos)
|
||||||
|
const appliedFilters = ref<{
|
||||||
|
fechaDesde: string | null
|
||||||
|
fechaHasta: string | null
|
||||||
|
includeAnulados: boolean
|
||||||
|
} | null>(null)
|
||||||
|
|
||||||
// Computed
|
// Computed
|
||||||
const rangoLegible = computed(() => {
|
const rangoLegible = computed(() => {
|
||||||
if (!fechaDesde.value && !fechaHasta.value) return 'Sin filtro de fecha'
|
if (!fechaDesde.value && !fechaHasta.value) return 'Sin filtro de fecha'
|
||||||
@@ -238,6 +290,19 @@ const rangoLegible = computed(() => {
|
|||||||
return `${f} → ${t}`
|
return `${f} → ${t}`
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Detectar si hay cambios pendientes sin aplicar
|
||||||
|
const hasPendingChanges = computed(() => {
|
||||||
|
// Si no hay datos cargados, no hay cambios pendientes
|
||||||
|
if (!appliedFilters.value) return false
|
||||||
|
|
||||||
|
// Comparar filtros actuales con los aplicados
|
||||||
|
return (
|
||||||
|
fechaDesde.value !== appliedFilters.value.fechaDesde ||
|
||||||
|
fechaHasta.value !== appliedFilters.value.fechaHasta ||
|
||||||
|
includeAnulados.value !== appliedFilters.value.includeAnulados
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
// Format currency helper
|
// Format currency helper
|
||||||
const formatCurrency = (value: number) => {
|
const formatCurrency = (value: number) => {
|
||||||
if (!value) return 'L 0.00'
|
if (!value) return 'L 0.00'
|
||||||
@@ -278,6 +343,13 @@ async function loadData() {
|
|||||||
hour: '2-digit',
|
hour: '2-digit',
|
||||||
minute: '2-digit'
|
minute: '2-digit'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Guardar los filtros aplicados
|
||||||
|
appliedFilters.value = {
|
||||||
|
fechaDesde: fechaDesde.value,
|
||||||
|
fechaHasta: fechaHasta.value,
|
||||||
|
includeAnulados: includeAnulados.value
|
||||||
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
error.value = err.message || 'Error al cargar datos'
|
error.value = err.message || 'Error al cargar datos'
|
||||||
console.error('Error loading panorama data:', err)
|
console.error('Error loading panorama data:', err)
|
||||||
|
|||||||
Reference in New Issue
Block a user