ui?ux mejorada, comparativa cosecha-cosecha

This commit is contained in:
2025-10-01 03:51:18 -06:00
parent bf370de372
commit 9bd96e6d69
8 changed files with 1353 additions and 60 deletions

View File

@@ -0,0 +1,176 @@
<template>
<div class="flex flex-col">
<!-- Toolbar con selector de cosechas -->
<UDashboardToolbar>
<template #left>
<div class="flex items-center gap-4">
<span class="text-sm text-[var(--brand-text-muted)]">Seleccionar cosechas a comparar:</span>
</div>
</template>
<template #right>
<div class="flex items-center gap-3">
<USwitch
v-model="pageSections.totales"
size="xs"
label="Totales"
/>
<USwitch
v-model="pageSections.evolucion"
size="xs"
label="Evolución"
/>
<USwitch
v-model="pageSections.porTipo"
size="xs"
label="Por Tipo"
/>
</div>
</template>
</UDashboardToolbar>
<div class="flex flex-col gap-8 p-6">
<!-- Loading State -->
<UCard v-if="loading && !ingresosStore.hasData" class="brand-card border border-transparent">
<div class="flex flex-col items-center justify-center gap-4 py-10 text-[var(--brand-text-muted)]">
<div class="flex items-center gap-3">
<span class="inline-flex h-8 w-8 animate-spin rounded-full border-2 border-[#c08040] border-t-transparent align-middle" aria-hidden="true" />
<span class="text-sm uppercase tracking-[0.3em]">Cargando datos...</span>
</div>
</div>
</UCard>
<!-- Error State -->
<div v-else-if="error" class="rounded-lg border border-red-500/40 bg-red-500/18 p-4 text-sm text-red-200">
<p>Error al cargar datos: {{ error }}</p>
</div>
<!-- Main Content -->
<template v-else>
<!-- Selector de Cosechas -->
<UCard class="brand-card border border-transparent">
<template #header>
<div class="flex items-center gap-2">
<UIcon name="i-lucide-calendar-range" class="size-5 text-[#c08040]" />
<h3 class="text-base font-semibold text-[var(--brand-text)]">Cosechas a Comparar</h3>
</div>
</template>
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-3">
<label
v-for="cosecha in cosechasDisponibles"
:key="cosecha.id"
class="flex items-center gap-2 p-3 rounded-lg border cursor-pointer transition-all"
:class="cosechasSeleccionadas.includes(cosecha.id)
? 'border-[#c08040] bg-[#c08040]/10'
: 'border-[var(--brand-border)] hover:border-[#c08040]/50'"
>
<input
type="checkbox"
:value="cosecha.id"
v-model="cosechasSeleccionadas"
class="rounded border-[var(--brand-border)] text-[#c08040] focus:ring-[#c08040]"
/>
<div class="flex flex-col">
<span class="text-sm font-medium text-[var(--brand-text)]">{{ cosecha.label }}</span>
<span class="text-xs text-[var(--brand-text-muted)]">{{ cosecha.periodo }}</span>
</div>
</label>
</div>
</UCard>
<!-- Resumen General por Cosecha -->
<div v-if="cosechasSeleccionadas.length > 0 && pageSections.totales">
<ComparativaCosechasTotales :ingresos="ingresos" :cosechas-seleccionadas="cosechasSeleccionadas" />
</div>
<!-- Comparativa por Tipo de Café -->
<div v-if="cosechasSeleccionadas.length > 0 && pageSections.porTipo">
<ComparativaCosechasPorTipo :ingresos="ingresos" :cosechas-seleccionadas="cosechasSeleccionadas" />
</div>
<!-- Evolución Temporal Comparada -->
<div v-if="cosechasSeleccionadas.length > 0 && pageSections.evolucion">
<ComparativaCosechasEvolucion :ingresos="ingresos" :cosechas-seleccionadas="cosechasSeleccionadas" />
</div>
<!-- Empty State -->
<UCard v-if="cosechasSeleccionadas.length === 0" class="brand-card border border-transparent">
<div class="flex flex-col items-center justify-center gap-4 py-16 text-[var(--brand-text-muted)]">
<UIcon name="i-lucide-bar-chart-2" class="size-16 opacity-50" />
<p class="text-sm">Selecciona al menos una cosecha para ver las comparativas</p>
</div>
</UCard>
</template>
</div>
</div>
</template>
<script setup lang="ts">
import { useTableDataStore } from '~/stores/tableDataFactory'
import type { IngresoRecord } from '~/composables/useIngresosMetrics'
definePageMeta({
layout: 'informe',
title: 'Comparativa Cosechas'
})
// Definir secciones específicas de esta página
const pageSections = ref({
totales: true,
evolucion: true,
porTipo: true
})
// Definición de cosechas disponibles
const cosechasDisponibles = [
{ id: 'cosecha-20-21', label: 'Cosecha 20-21', periodo: 'Sep 2020 - Sep 2021', fechaInicio: '2020-09-25', fechaFin: '2021-09-24' },
{ id: 'cosecha-21-22', label: 'Cosecha 21-22', periodo: 'Sep 2021 - Sep 2022', fechaInicio: '2021-09-25', fechaFin: '2022-09-24' },
{ id: 'cosecha-22-23', label: 'Cosecha 22-23', periodo: 'Sep 2022 - Sep 2023', fechaInicio: '2022-09-25', fechaFin: '2023-09-24' },
{ id: 'cosecha-23-24', label: 'Cosecha 23-24', periodo: 'Sep 2023 - Sep 2024', fechaInicio: '2023-09-25', fechaFin: '2024-09-24' },
{ id: 'cosecha-24-25', label: 'Cosecha 24-25', periodo: 'Sep 2024 - Sep 2025', fechaInicio: '2024-09-25', fechaFin: '2025-09-09' },
{ id: 'cosecha-25-26', label: 'Cosecha 25-26', periodo: 'Sep 2025 - Hoy', fechaInicio: '2025-09-10', fechaFin: new Date().toISOString().split('T')[0] }
]
// Cosechas seleccionadas (por defecto las 3 más recientes)
const cosechasSeleccionadas = ref<string[]>(['cosecha-23-24', 'cosecha-24-25'])
// Store de ingresos
const ingresosStore = useTableDataStore('ingresos')
const loading = ref(false)
const error = ref<string | null>(null)
// Cargar datos
onMounted(async () => {
try {
loading.value = true
error.value = null
await ingresosStore.fetch()
} catch (e) {
error.value = e instanceof Error ? e.message : 'Error desconocido'
} finally {
loading.value = false
}
})
// Datos de ingresos
const ingresos = computed(() => {
return ingresosStore.data.map(row => {
const ingreso: IngresoRecord = {
id: row.id as number,
created_at: row.created_at as string,
peso_neto: row.peso_neto as number,
precio: row.precio as number,
tipo: row.tipo as string,
cliente: row.cliente as string,
peso_seco: row.peso_seco as number | undefined,
pagado: row.pagado as boolean | undefined
}
return ingreso
})
})
// Exportar cosechas para los componentes
provide('cosechasDisponibles', cosechasDisponibles)
</script>