From 979e219cb215211a99a78cb1631d48a9f2f40a94 Mon Sep 17 00:00:00 2001 From: josedario87 Date: Sat, 20 Dec 2025 11:13:30 -0600 Subject: [PATCH] =?UTF-8?q?Feat:=20Agregar=20p=C3=A1gina=20Comparativa=20U?= =?UTF-8?q?VA=20vs=20Carretas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Nuevo endpoint API para ejecutar Card 94 de Metabase - Página con filtros de fecha, cards de totales y tabla completa - Colores de rendimiento: verde (95-105%), amarillo, rojo - Enlace agregado al sidebar --- nuxt4-app/app/components/app/AppSidebar.vue | 6 + .../app/pages/comparativa-uva-carretas.vue | 292 ++++++++++++++++++ .../metabase/comparativa-uva-carretas.post.ts | 107 +++++++ 3 files changed, 405 insertions(+) create mode 100644 nuxt4-app/app/pages/comparativa-uva-carretas.vue create mode 100644 nuxt4-app/server/api/metabase/comparativa-uva-carretas.post.ts diff --git a/nuxt4-app/app/components/app/AppSidebar.vue b/nuxt4-app/app/components/app/AppSidebar.vue index 0d5f52c..c313a97 100644 --- a/nuxt4-app/app/components/app/AppSidebar.vue +++ b/nuxt4-app/app/components/app/AppSidebar.vue @@ -309,6 +309,12 @@ const navigationPrimary = computed(() => [ to: '/comparativa-cosechas', active: route.path === '/comparativa-cosechas' }, + { + label: 'UVA vs Carretas', + icon: 'i-lucide-scale', + to: '/comparativa-uva-carretas', + active: route.path === '/comparativa-uva-carretas' + }, { label: 'Explorador de datos', icon: 'i-lucide-table', diff --git a/nuxt4-app/app/pages/comparativa-uva-carretas.vue b/nuxt4-app/app/pages/comparativa-uva-carretas.vue new file mode 100644 index 0000000..c55ad0b --- /dev/null +++ b/nuxt4-app/app/pages/comparativa-uva-carretas.vue @@ -0,0 +1,292 @@ + + + diff --git a/nuxt4-app/server/api/metabase/comparativa-uva-carretas.post.ts b/nuxt4-app/server/api/metabase/comparativa-uva-carretas.post.ts new file mode 100644 index 0000000..ab30986 --- /dev/null +++ b/nuxt4-app/server/api/metabase/comparativa-uva-carretas.post.ts @@ -0,0 +1,107 @@ +/** + * API endpoint for Comparativa Ingreso UVA vs Salida Carretas + * Executes Card 94 from Metabase and returns processed data + */ + +const CARD_ID = 94 // Comparativa Ingreso UVA vs Salida (Nov-Dic 2025) + +export default defineEventHandler(async (event) => { + const body = await readBody(event) + + const { + fecha_desde = null, + fecha_hasta = null + } = body + + try { + console.log(`[Comparativa UVA] Executing card ${CARD_ID}`) + + // Execute the card without parameters (dates are hardcoded in the card) + const result = await executeCardQuery(CARD_ID, []) + + if (!result.data?.rows || !result.data?.cols) { + console.warn('[Comparativa UVA] No data returned from Metabase') + return { + datos: [], + totales: { + ingreso_qq: 0, + ingreso_lb: 0, + salida_lb: 0, + diferencia_lb: 0, + rendimiento_promedio: 0 + } + } + } + + // Transform rows to objects + const cols = result.data.cols + let datos = result.data.rows.map((row: any[]) => { + const obj: Record = {} + cols.forEach((col: any, index: number) => { + obj[col.name] = row[index] + }) + return obj + }) + + console.log(`[Comparativa UVA] Retrieved ${datos.length} rows`) + + // Filter by dates if provided + if (fecha_desde || fecha_hasta) { + datos = datos.filter((row: any) => { + if (!row.fecha) return false + + const rowDate = new Date(row.fecha) + + if (fecha_desde) { + const desde = new Date(fecha_desde) + if (rowDate < desde) return false + } + + if (fecha_hasta) { + const hasta = new Date(fecha_hasta) + if (rowDate > hasta) return false + } + + return true + }) + + console.log(`[Comparativa UVA] After date filter: ${datos.length} rows`) + } + + // Calculate totals + const totales = datos.reduce((acc: any, row: any) => { + acc.ingreso_qq += Number(row.ingreso_uva_qq) || 0 + acc.ingreso_lb += Number(row.ingreso_uva_lb) || 0 + acc.salida_lb += Number(row.salida_total_lb) || 0 + acc.diferencia_lb += Number(row.diferencia_lb) || 0 + return acc + }, { + ingreso_qq: 0, + ingreso_lb: 0, + salida_lb: 0, + diferencia_lb: 0 + }) + + // Calculate weighted average rendimiento + totales.rendimiento_promedio = totales.ingreso_lb > 0 + ? Math.round((totales.salida_lb / totales.ingreso_lb) * 1000) / 10 + : 0 + + // Round totals + totales.ingreso_qq = Math.round(totales.ingreso_qq * 100) / 100 + totales.ingreso_lb = Math.round(totales.ingreso_lb) + totales.salida_lb = Math.round(totales.salida_lb) + totales.diferencia_lb = Math.round(totales.diferencia_lb) + + return { + datos, + totales + } + } catch (error: any) { + console.error('[Comparativa UVA] Failed to execute query:', error) + throw createError({ + statusCode: error.statusCode || 500, + statusMessage: error.statusMessage || 'Failed to execute comparativa UVA query' + }) + } +})