diff --git a/harvester-app/src/backend/casosUso/formulas/aplicarFormula.js b/harvester-app/src/backend/casosUso/formulas/aplicarFormula.js index f8bf9f6b..5b776ce1 100644 --- a/harvester-app/src/backend/casosUso/formulas/aplicarFormula.js +++ b/harvester-app/src/backend/casosUso/formulas/aplicarFormula.js @@ -162,5 +162,8 @@ function indiceALetraColumna(indice) { } module.exports = { - aplicarFormula + aplicarFormula, + encontrarColumnaVacia, + traducirFormulaEstructurada, + indiceALetraColumna, }; diff --git a/harvester-app/src/backend/casosUso/formulas/filtrarDatos.js b/harvester-app/src/backend/casosUso/formulas/filtrarDatos.js new file mode 100644 index 00000000..0c26c0b2 --- /dev/null +++ b/harvester-app/src/backend/casosUso/formulas/filtrarDatos.js @@ -0,0 +1,152 @@ +const { HyperFormula } = require('hyperformula'); +const { encontrarColumnaVacia } = require('./aplicarFormula.js') + +const opciones = { + licenseKey: 'gpl-v3', +}; + +/** + * Aplica una fórmula estructurada a una hoja de Excel almacenada en localStorage. + * @param {string} nombreFormula - Nombre de la fórmula a aplicar. + * @param {string} formulaEstructurada - Fórmula estructurada a aplicar. + * @param {string|null} tractorSeleccionado - Nombre de la hoja donde se aplicará la fórmula (opcional). + * @returns {Object} Resultado de la aplicación de la fórmula. + */ + +function filtrarDatos(filtro, datosExcel, tractorSeleccionado){ + if(filtro.length==0){ + return { resultados: datosExcel }; + } + + // Lógica para filtrar los datos según el filtro + + const hojas = datosExcel.hojas; + + // Si no se especifica hoja, usar la primera disponible + let datos = []; + let tractorUsado = ''; + + if (tractorSeleccionado && hojas[tractorSeleccionado]) { + datos = hojas[tractorSeleccionado]; + tractorUsado = tractorSeleccionado; + } else { + // Tomar la primera hoja disponible + tractorUsado = Object.keys(hojas)[0]; + datos = hojas[tractorUsado]; + } + + // Verificar que datos sea un array + if (!Array.isArray(datos)) { + throw new Error('Formato de datos inválido: se esperaba un array'); + } + + if (datos.length === 0) { + throw new Error('No hay datos disponibles en la hoja seleccionada'); + } + + // Obtener encabezados + const encabezados = datos[0]; + + // Encontrar la primera columna vacía usando método seguro + const indiceColumnaVacio = encontrarColumnaVacia(datos); + + + + const hyperFormulaInstance = HyperFormula.buildFromArray(datos, opciones); + const sheetId = hyperFormulaInstance.getSheetId('Sheet1'); + + const columnaCondicion = nombreColumnaAIndice(filtro[0].Datos, encabezados); + if(columnaCondicion.error) { + return { error: true, columnaNoEncontrada: columnaCondicion.columnaNoEncontrada }; + } + + const filas = datos.length; + + const condicionFiltro = extraerCondicionFiltro(filtro[0].Datos); + + for(let columna = indiceColumnaVacio; columna < indiceColumnaVacio*2+1; columna+=1) { + hyperFormulaInstance.setCellContents({ row: 0, col: columna, sheet: sheetId }, encabezados[columna-indiceColumnaVacio]); + const columnaLetra = numeroAColumnaExcel(columna-indiceColumnaVacio+1); + hyperFormulaInstance.setCellContents({ row: 1, col: columna, sheet: 0 }, `=FILTER(${columnaLetra}2:${columnaLetra}${filas}, ${columnaCondicion}2: ${columnaCondicion}${filas}${condicionFiltro})`); + } + + const resultadosFiltrados = []; + for(let fila = 0; fila < filas; fila+=1) { + const resultadoColumna = []; + for(let columna = indiceColumnaVacio; columna < indiceColumnaVacio*2+1; columna+=1) { + const valorCelda = hyperFormulaInstance.getCellValue({ row: fila, col: columna, sheet: sheetId }); + if (valorCelda != null && valorCelda != undefined) { + resultadoColumna.push(valorCelda); + } + } + if (resultadoColumna.length > 0) { + resultadosFiltrados.push(resultadoColumna); + } + } + + const resultados = { + hojas: { + [tractorSeleccionado]: resultadosFiltrados + } + }; + + + return { + resultados, + }; + +} + + +function numeroAColumnaExcel(num) { + let columna = ''; + while (num > 0) { + const residuo = (num - 1) % 26; + columna = String.fromCharCode(65 + residuo) + columna; + num = Math.floor((num - 1) / 26); + } + return columna; +} + + + +/** + * @function traducirFormulaEstructurada + * @param {string} formula - Fórmula estructurada a traducir. + * @param {Array} encabezados - Encabezados de la hoja de Excel. + * @param {number} filaActiva - Fila activa para la traducción. + * @returns {string} Fórmula traducida a formato clásico de Excel. + */ + +function nombreColumnaAIndice(filtro, encabezados) { + // Buscar el primer match de [@NombreColumna] + const match = filtro.match(/\[@([^\]]+)\]/); + if (!match) { + return { error: true, columnasNoEncontradas: ['No se encontró ninguna columna en el filtro'] }; + } + const nombreColumna = match[1]; + const columna = encabezados.indexOf(nombreColumna); + if (columna === -1) { + return { error: true, columnaNoEncontrada: [nombreColumna] }; + } + return numeroAColumnaExcel(columna + 1); // +1 porque A=1, B=2, etc. +} + + +/** + * Extrae la condición de una fórmula tipo =FILTER([@Columna]condición) + * @param {string} filtro - Fórmula tipo =FILTER([@Columna]condición) + * @returns {string|null} La condición (por ejemplo, '>1' o '="Muy rápido"'), o null si no se encuentra. + */ +function extraerCondicionFiltro(filtro) { + // Busca la referencia estructurada y lo que sigue después de ella, hasta el paréntesis de cierre + const match = filtro.match(/\[@[^\]]+\]([^)]+)/); + if (!match) return null; + return match[1].trim(); +} + + + +module.exports = { + filtrarDatos, +} \ No newline at end of file diff --git a/harvester-app/src/framework/utils/css/paginas/analisis/generarReporte.css b/harvester-app/src/framework/utils/css/paginas/analisis/generarReporte.css index 9d4b7041..ccdd6780 100644 --- a/harvester-app/src/framework/utils/css/paginas/analisis/generarReporte.css +++ b/harvester-app/src/framework/utils/css/paginas/analisis/generarReporte.css @@ -564,7 +564,7 @@ body, gap: 10px; align-items: center; justify-content: flex-start; - height: 650px; + height: wrap-content; position: relative; } diff --git a/harvester-app/src/framework/utils/css/paginas/inicio/moduloTractores.css b/harvester-app/src/framework/utils/css/paginas/inicio/moduloTractores.css index 9146493f..4c94664b 100644 --- a/harvester-app/src/framework/utils/css/paginas/inicio/moduloTractores.css +++ b/harvester-app/src/framework/utils/css/paginas/inicio/moduloTractores.css @@ -55,6 +55,7 @@ input:focus { flex-shrink: 0; position: relative; display: none; + height: 100%; } .secundario { background: #e8e8e8; @@ -96,10 +97,26 @@ input:focus { border: none; } + +.tractores-contenido{ + height: 100%; /* Ajusta según lo que necesites */ + max-height: 34.5em; + overflow-y: auto; + scrollbar-width: thin; /* Firefox */ + scrollbar-color: #999 transparent; /* Firefox */ + display: flex; + flex-direction: column; + gap: 12px; + align-items: flex-start; + justify-content: flex-start; + align-self: stretch; + flex-shrink: 0; + position: relative; +} + .distribuidores-contenido, -.tractores-contenido, .columnas-contenido { - max-height: 300px; /* Ajusta según lo que necesites */ + height: 40em; /* Ajusta según lo que necesites */ overflow-y: auto; scrollbar-width: thin; /* Firefox */ scrollbar-color: #999 transparent; /* Firefox */ diff --git a/harvester-app/src/framework/utils/scripts/paginas/analisis/formulas/crearCuadroFormulas.js b/harvester-app/src/framework/utils/scripts/paginas/analisis/formulas/crearCuadroFormulas.js index 25119706..94907684 100644 --- a/harvester-app/src/framework/utils/scripts/paginas/analisis/formulas/crearCuadroFormulas.js +++ b/harvester-app/src/framework/utils/scripts/paginas/analisis/formulas/crearCuadroFormulas.js @@ -2,11 +2,12 @@ const { eliminarCuadroFormulas } = require('./eliminarCuadroFormulas'); const { mostrarAlerta } = require(`${rutaBase}/src/framework/vistas/includes/componentes/moleculas/alertaSwal/alertaSwal`); const { filtrarYRenderizarFormulas, actualizarCaracteresBuscador } = require(`${rutaBase}/src/framework/utils/scripts/paginas/analisis/formulas/filtrarYRenderizarFormulas.js`); const { aplicarFormula } = require(`${rutaBase}/src/backend/casosUso/formulas/aplicarFormula.js`); -const { actualizarGraficaConColumna } = require(`${rutaBase}/src/framework/utils/scripts/paginas/analisis/graficas/actualizarGraficaConColumna.js`); +const { filtrarDatos } = require(`${rutaBase}/src/backend/casosUso/formulas/filtrarDatos.js`); const { procesarDatosUniversal } = require(`${rutaBase}/src/framework/utils/scripts/paginas/analisis/graficas/procesarDatosUniversal.js`); const { obtenerParametrosTractor } = require(`${rutaBase}/src/framework/utils/scripts/paginas/analisis/formulas/obtenerParametrosTractor.js`); const { retirarDatos } = require(`${rutaBase}/src/framework/utils/scripts/paginas/analisis/graficas/retirarDatos.js`); -const { crearGrafica } = require(`${rutaBase}/src/framework/utils/scripts/paginas/analisis/graficas/crearGrafica.js`); +const { crearMenuParametros } = require(`${rutaBase}/src/framework/utils/scripts/paginas/analisis/formulas/crearMenuParametros.js`); +const { crearMenuFiltros } = require(`${rutaBase}/src/framework/utils/scripts/paginas/analisis/formulas/crearMenuFiltros.js`); const Chart = require('chart.js/auto'); /** @@ -35,6 +36,11 @@ async function crearCuadroFormulas(graficaId, formulasDisponibles, datosOriginal mensajeInicial = 'No hay fórmulas disponibles.'; } + const filtrosDisponibles = formulasDisponibles.filter(formula => { + return formula.Datos.toLowerCase().includes('filter'); + }); + + cuadroFormulas.innerHTML = `

Fórmulas

@@ -45,6 +51,11 @@ async function crearCuadroFormulas(graficaId, formulasDisponibles, datosOriginal
+
+

Filtros

+
+
+

Aplicar Fórmula

@@ -64,12 +75,14 @@ async function crearCuadroFormulas(graficaId, formulasDisponibles, datosOriginal
+ `; - const contenedoesSeleccion = cuadroFormulas.querySelectorAll('.opciones-carta'); + const contenedoresSeleccion = cuadroFormulas.querySelectorAll('.opciones-carta'); //ToDo: Escalar en número de variables dependiendo de las variables en las fórmulas - crearMenuDesplegable(contenedoesSeleccion[0], 'A', columnasActualizadas, graficaId, datosOriginalesFormulas, tractorSeleccionado); + crearMenuParametros(contenedoresSeleccion[0], columnasActualizadas, graficaId, datosOriginalesFormulas, tractorSeleccionado, filtrosDisponibles, contenedoresSeleccion[1]); + crearMenuFiltros(contenedoresSeleccion[1], filtrosDisponibles, graficaId); // Configurar búsqueda de fórmulas const campoBusqueda = cuadroFormulas.querySelector('.search-section'); @@ -88,6 +101,10 @@ async function crearCuadroFormulas(graficaId, formulasDisponibles, datosOriginal botonAplicarFormula.addEventListener('click', () => { + const filtroAplicado = filtrosDisponibles.filter(filtro => { + return contenedoresSeleccion[1].querySelector('.opcion-texto').value == filtro.Nombre; + }); + const textoAplicar = botonAplicarFormula.querySelector('div'); if (textoAplicar) { textoAplicar.textContent = 'Aplicando...'; @@ -96,13 +113,21 @@ async function crearCuadroFormulas(graficaId, formulasDisponibles, datosOriginal const formulaSeleccionada = contenedorBusqueda.querySelector('.formula-seleccionada'); if (!formulaSeleccionada) { mostrarAlerta('Error', 'Debes buscar y seleccionar una fórmula antes de aplicar.', 'error'); + if (textoAplicar) textoAplicar.textContent = 'Aplicar Fórmula'; return; } - // Verificar que hay datos disponibles - const datosExcel = localStorage.getItem('datosFiltradosExcel'); - if (!datosExcel) { + + const datosFiltrados = filtrarDatos(filtroAplicado, JSON.parse(localStorage.getItem('datosFiltradosExcel')), tractorSeleccionado); + if (datosFiltrados.error) { + mostrarAlerta(`Columna no encontrada: ${datosFiltrados.columnaNoEncontrada}`, 'Asegúrate de seleccionar todas las columnas necesarias para aplicar este filtro.', 'error'); + if (textoAplicar) textoAplicar.textContent = 'Aplicar Fórmula'; + return; + } + + if (!datosFiltrados) { mostrarAlerta('Error', 'No hay datos de Excel cargados. Por favor, carga un archivo Excel primero.', 'error'); + if (textoAplicar) textoAplicar.textContent = 'Aplicar Fórmula'; return; } @@ -112,6 +137,7 @@ async function crearCuadroFormulas(graficaId, formulasDisponibles, datosOriginal if (!inputRadio) { mostrarAlerta('Error', 'Error al obtener los datos de la fórmula seleccionada.', 'error'); + if (textoAplicar) textoAplicar.textContent = 'Aplicar Fórmula'; return; } @@ -122,6 +148,7 @@ async function crearCuadroFormulas(graficaId, formulasDisponibles, datosOriginal // Verificar que los datos están completos if (!datosFormula || datosFormula.trim() === '') { mostrarAlerta('Error', 'Los datos de la fórmula están vacíos o incompletos.', 'error'); + if (textoAplicar) textoAplicar.textContent = 'Aplicar Fórmula'; return; } @@ -131,15 +158,16 @@ async function crearCuadroFormulas(graficaId, formulasDisponibles, datosOriginal if (!graficaDiv) { mostrarAlerta('Error', 'No se encontró la gráfica asociada.', 'error'); + if (textoAplicar) textoAplicar.textContent = 'Aplicar Fórmula'; return; } try { let resultadoFormula; if (tractorSeleccionado.length != 0) { - resultadoFormula = aplicarFormula(nombreFormula, datosFormula, tractorSeleccionado, JSON.parse(datosExcel)); + resultadoFormula = aplicarFormula(nombreFormula, datosFormula, tractorSeleccionado, datosFiltrados.resultados); } else { - resultadoFormula = aplicarFormula(nombreFormula, datosFormula, null, JSON.parse(datosExcel)); + resultadoFormula = aplicarFormula(nombreFormula, datosFormula, null, datosFiltrados.resultados); } let contadorErrores = 0; const resultados = resultadoFormula.resultados; @@ -215,7 +243,7 @@ async function crearCuadroFormulas(graficaId, formulasDisponibles, datosOriginal if (textoAplicar) textoAplicar.textContent = 'Aplicar Fórmula'; } }, 100); - } + } }); // Configurar evento de búsqueda (filtrado local) @@ -251,51 +279,7 @@ async function crearCuadroFormulas(graficaId, formulasDisponibles, datosOriginal return datosOriginalesFormulas; } -/** - * Crea un menú desplegable para seleccionar columnas. - * @param {HTMLDivElement} contenedor - Contenedor donde se agregará el menú desplegable. - * @param {string} letra - Letra identificadora del menú. - * @param {string[]} columnas - Lista de columnas disponibles. - * @param {number} graficaId - ID de la gráfica asociada. - * @returns {void} - */ -function crearMenuDesplegable(contenedor, letra, columnas, graficaId, datosOriginalesFormulas, tractorSeleccionado) { - const nuevoMenu = document.createElement('div'); - nuevoMenu.className = 'opcion'; - const seleccionValores = document.createElement('select'); - seleccionValores.className = 'opcion-texto'; - seleccionValores.innerHTML = '' - columnas.forEach((texto) => { - seleccionValores.innerHTML = `${seleccionValores.innerHTML} - ` - }); - - // Agregar evento de cambio para actualizar la gráfica - seleccionValores.addEventListener('change', (evento) => { - const columnaSeleccionada = evento.target.value; - if (columnaSeleccionada && columnaSeleccionada !== '') { - actualizarGraficaConColumna(graficaId, columnaSeleccionada, datosOriginalesFormulas, tractorSeleccionado); - } else { - // Si se deselecciona, resetear la gráfica a estado inicial - const graficaDiv = document.getElementById(`previsualizacion-grafica-${graficaId}`); - if (graficaDiv) { - const canvas = graficaDiv.querySelector('canvas'); - const contexto = canvas.getContext('2d'); - const graficaExistente = Chart.getChart(contexto); - - if (graficaExistente) { - graficaExistente.destroy(); - const nuevaGrafica = crearGrafica(contexto, 'line'); - nuevaGrafica.options.plugins.title.text = ''; - nuevaGrafica.update(); - } - } - } - }); - nuevoMenu.appendChild(seleccionValores); - contenedor.appendChild(nuevoMenu); -} module.exports = { crearCuadroFormulas diff --git a/harvester-app/src/framework/utils/scripts/paginas/analisis/formulas/crearMenuFiltros.js b/harvester-app/src/framework/utils/scripts/paginas/analisis/formulas/crearMenuFiltros.js new file mode 100644 index 00000000..448f3ccc --- /dev/null +++ b/harvester-app/src/framework/utils/scripts/paginas/analisis/formulas/crearMenuFiltros.js @@ -0,0 +1,48 @@ +const Chart = require('chart.js/auto'); +const { crearGrafica } = require(`${rutaBase}/src/framework/utils/scripts/paginas/analisis/graficas/crearGrafica.js`); + +/** + * Crea un menú desplegable para seleccionar columnas. + * @param {HTMLDivElement} contenedor - Contenedor donde se agregará el menú desplegable. + * @param {string[]} filtros - Lista de filtros disponibles. + * @param {number} graficaId - ID de la gráfica asociada. + * @returns {void} + */ +function crearMenuFiltros(contenedor, filtros, graficaId) { + const nuevoMenu = document.createElement('div'); + nuevoMenu.className = 'opcion'; + const seleccionValores = document.createElement('select'); + seleccionValores.className = 'opcion-texto'; + seleccionValores.innerHTML = '' + filtros.forEach((filtro) => { + seleccionValores.innerHTML = `${seleccionValores.innerHTML} + ` + }); + + // Agregar evento de cambio para actualizar la gráfica + seleccionValores.addEventListener('change', () => { + // Si se deselecciona, resetear la gráfica a estado inicial + const graficaDiv = document.getElementById(`previsualizacion-grafica-${graficaId}`); + if (graficaDiv) { + const canvas = graficaDiv.querySelector('canvas'); + const contexto = canvas.getContext('2d'); + const graficaExistente = Chart.getChart(contexto); + + if (graficaExistente) { + const tipo = graficaExistente.config.type; + const titulo = graficaExistente.options.plugins.title.text; + graficaExistente.destroy(); + const nuevaGrafica = crearGrafica(contexto, tipo); + nuevaGrafica.options.plugins.title.text = titulo; + nuevaGrafica.update(); + } + } + }); + + nuevoMenu.appendChild(seleccionValores); + contenedor.appendChild(nuevoMenu); +} + +module.exports = { + crearMenuFiltros, +} \ No newline at end of file diff --git a/harvester-app/src/framework/utils/scripts/paginas/analisis/formulas/crearMenuParametros.js b/harvester-app/src/framework/utils/scripts/paginas/analisis/formulas/crearMenuParametros.js new file mode 100644 index 00000000..e0734732 --- /dev/null +++ b/harvester-app/src/framework/utils/scripts/paginas/analisis/formulas/crearMenuParametros.js @@ -0,0 +1,63 @@ +const { actualizarGraficaConColumna } = require(`${rutaBase}/src/framework/utils/scripts/paginas/analisis/graficas/actualizarGraficaConColumna.js`); +const { crearGrafica } = require(`${rutaBase}/src/framework/utils/scripts/paginas/analisis/graficas/crearGrafica.js`); +const { filtrarDatos } = require(`${rutaBase}/src/backend/casosUso/formulas/filtrarDatos.js`); +const Chart = require('chart.js/auto'); + +/** + * Crea un menú desplegable para seleccionar columnas. + * @param {HTMLDivElement} contenedor - Contenedor donde se agregará el menú desplegable. + * @param {string[]} columnas - Lista de columnas disponibles. + * @param {number} graficaId - ID de la gráfica asociada. + * @returns {void} + */ +function crearMenuParametros(contenedor, columnas, graficaId, datosOriginalesFormulas, tractorSeleccionado, filtrosDisponibles, contenedorFiltros) { + const nuevoMenu = document.createElement('div'); + nuevoMenu.className = 'opcion'; + const seleccionValores = document.createElement('select'); + seleccionValores.className = 'opcion-texto'; + seleccionValores.innerHTML = '' + columnas.forEach((texto) => { + seleccionValores.innerHTML = `${seleccionValores.innerHTML} + ` + }); + + // Agregar evento de cambio para actualizar la gráfica + seleccionValores.addEventListener('change', (evento) => { + + const filtroAplicado = filtrosDisponibles.filter(filtro => { + return contenedorFiltros.querySelector('.opcion-texto').value == filtro.Nombre; + }); + + const datosFiltrados = filtrarDatos(filtroAplicado, JSON.parse(localStorage.getItem('datosFiltradosExcel')), tractorSeleccionado); + + const columnaSeleccionada = evento.target.value; + if (columnaSeleccionada && columnaSeleccionada !== '') { + actualizarGraficaConColumna(graficaId, columnaSeleccionada, datosOriginalesFormulas, tractorSeleccionado, datosFiltrados.resultados); + } else { + // Si se deselecciona, resetear la gráfica a estado inicial + const graficaDiv = document.getElementById(`previsualizacion-grafica-${graficaId}`); + if (graficaDiv) { + + const canvas = graficaDiv.querySelector('canvas'); + const contexto = canvas.getContext('2d'); + const graficaExistente = Chart.getChart(contexto); + + if (graficaExistente) { + const tipo = graficaExistente.config.type; + const titulo = graficaExistente.options.plugins.title.text; + graficaExistente.destroy(); + const nuevaGrafica = crearGrafica(contexto, tipo); + nuevaGrafica.options.plugins.title.text = titulo; + nuevaGrafica.update(); + } + } + } + }); + + nuevoMenu.appendChild(seleccionValores); + contenedor.appendChild(nuevoMenu); +} + +module.exports = { + crearMenuParametros, +} \ No newline at end of file diff --git a/harvester-app/src/framework/utils/scripts/paginas/analisis/formulas/filtrarYRenderizarFormulas.js b/harvester-app/src/framework/utils/scripts/paginas/analisis/formulas/filtrarYRenderizarFormulas.js index 7137e51b..380d6d66 100644 --- a/harvester-app/src/framework/utils/scripts/paginas/analisis/formulas/filtrarYRenderizarFormulas.js +++ b/harvester-app/src/framework/utils/scripts/paginas/analisis/formulas/filtrarYRenderizarFormulas.js @@ -29,12 +29,19 @@ function filtrarYRenderizarFormulas(contenedor, terminoBusqueda = '', formulasDi // Limpiar contenedor contenedor.innerHTML = ''; + + formulasDisponibles = formulasDisponibles.filter(formula => { + return !formula.Datos.toLowerCase().includes('filter'); + }); + + // Si no hay fórmulas cargadas, mostrar mensaje específico if (formulasDisponibles.length === 0) { contenedor.innerHTML = '
No hay fórmulas creadas.
Ve al módulo de fórmulas para crear una.
'; return; } + let formulasFiltradas = formulasDisponibles; // Solo filtrar si hay un término de búsqueda válido diff --git a/harvester-app/src/framework/utils/scripts/paginas/analisis/graficas/actualizarGraficaConColumna.js b/harvester-app/src/framework/utils/scripts/paginas/analisis/graficas/actualizarGraficaConColumna.js index 26caa9c3..b8634da5 100644 --- a/harvester-app/src/framework/utils/scripts/paginas/analisis/graficas/actualizarGraficaConColumna.js +++ b/harvester-app/src/framework/utils/scripts/paginas/analisis/graficas/actualizarGraficaConColumna.js @@ -8,7 +8,7 @@ const { mostrarAlerta } = require(`${rutaBase}/src/framework/vistas/includes/com * @param {string} nombreColumna - Nombre de la columna seleccionada. * @returns {void} */ -function actualizarGraficaConColumna(graficaId, nombreColumna, datosOriginalesFormulas, tractorSeleccionado) { +function actualizarGraficaConColumna(graficaId, nombreColumna, datosOriginalesFormulas, tractorSeleccionado, datosExcel) { // Obtener la gráfica const graficaDiv = document.getElementById(`previsualizacion-grafica-${graficaId}`); if (!graficaDiv) { @@ -28,7 +28,7 @@ function actualizarGraficaConColumna(graficaId, nombreColumna, datosOriginalesFo } // Obtener la hoja seleccionada del localStorage - const datos = localStorage.getItem('datosFiltradosExcel'); + const datos = datosExcel;//localStorage.getItem('datosFiltradosExcel'); if (!datos) { mostrarAlerta('Error', 'No hay datos cargados para mostrar en la gráfica.', 'error'); @@ -39,7 +39,7 @@ function actualizarGraficaConColumna(graficaId, nombreColumna, datosOriginalesFo let datosHoja = null; // Parsear los datos del localStorage - const datosParseados = JSON.parse(datos); + const datosParseados = datos; // Determinar qué hoja usar if (tractorSeleccionado && tractorSeleccionado.trim() !== '') { diff --git a/harvester-app/src/framework/utils/scripts/paginas/analisis/graficas/retirarDatos.js b/harvester-app/src/framework/utils/scripts/paginas/analisis/graficas/retirarDatos.js index 3adb9799..524d023e 100644 --- a/harvester-app/src/framework/utils/scripts/paginas/analisis/graficas/retirarDatos.js +++ b/harvester-app/src/framework/utils/scripts/paginas/analisis/graficas/retirarDatos.js @@ -56,22 +56,57 @@ function retirarDatos(graficaId, datosOriginalesFormulas) { } else { graficaExistente.options.plugins.title.text = 'Gráfica sin datos - Aplica una fórmula para ver resultados'; } - - graficaExistente.options.plugins.title.display = false; - - // Actualizar la gráfica - graficaExistente.update(); - - // Eliminar los datos originales del mapa - datosOriginalesFormulas.delete(graficaId); - - // Restablecer los menús desplegables de parámetros si existen - const cuadroFormulas = document.querySelector('.contenedor-formulas'); - if (cuadroFormulas) { - const selectoresParametros = cuadroFormulas.querySelectorAll('.opcion-texto'); - selectoresParametros.forEach(selector => { - selector.value = ''; - }); + + try { + // Verificar si hay datos originales para esta gráfica + const datosOriginales = datosOriginalesFormulas.get(graficaId); + + if (!datosOriginales) { + mostrarAlerta('Información', 'Esta gráfica no tiene fórmula aplicada para retirar.', 'info'); + return; + } + + // Obtener el título actual del input de la tarjeta + const tarjetaGrafica = document.getElementById(graficaId.toString()); + const tituloInput = tarjetaGrafica ? tarjetaGrafica.querySelector('.titulo-grafica') : null; + const tituloPersonalizado = tituloInput ? tituloInput.value : ''; + + // Restaurar gráfica a estado inicial + graficaExistente.data.labels = ['Sin datos']; + graficaExistente.data.datasets[0].data = [0]; + graficaExistente.data.datasets[0].label = 'Datos'; + + // Restaurar título: usar el título personalizado del input o el mensaje por defecto + if (tituloPersonalizado && tituloPersonalizado.trim() !== '') { + graficaExistente.options.plugins.title.text = tituloPersonalizado; + } else { + graficaExistente.options.plugins.title.text = 'Gráfica sin datos - Aplica una fórmula para ver resultados'; + } + + // Configurar etiquetas: ocultar según el tipo de gráfica + const tipoActual = graficaExistente.config.type; + graficaExistente.options.plugins.datalabels.display = tipoActual !== 'line'; + graficaExistente.options.plugins.title.display = true; + + // Actualizar la gráfica + graficaExistente.update(); + + // Eliminar los datos originales del mapa + datosOriginalesFormulas.delete(graficaId); + + // Restablecer los menús desplegables de parámetros si existen + const cuadroFormulas = document.querySelector('.contenedor-formulas'); + if (cuadroFormulas) { + const selectoresParametros = cuadroFormulas.querySelectorAll('.opcion-texto'); + selectoresParametros.forEach(selector => { + selector.value = ''; + }); + } + + mostrarAlerta('Éxito', 'Los datos han sido retirados correctamente de la gráfica.', 'success'); + + } catch (error) { + mostrarAlerta('Error', `Error inesperado al retirar la fórmula: ${error.message}`, 'error'); } mostrarAlerta('Éxito', 'Los datos han sido retirados correctamente de la gráfica.', 'success'); diff --git a/harvester-app/src/framework/utils/scripts/paginas/formulas/crearFormula.js b/harvester-app/src/framework/utils/scripts/paginas/formulas/crearFormula.js index 66aff1a5..2168f169 100644 --- a/harvester-app/src/framework/utils/scripts/paginas/formulas/crearFormula.js +++ b/harvester-app/src/framework/utils/scripts/paginas/formulas/crearFormula.js @@ -1,5 +1,5 @@ -// RF67 Crear Fórmula - https://codeandco-wiki.netlify.app/docs/proyectos/tractores/documentacion/requisitos/RF67 -// RF69 Guardar Fórmula - https://codeandco-wiki.netlify.app/docs/proyectos/tractores/documentacion/requisitos/RF69 +// RF24 Crear Fórmula - https://codeandco-wiki.netlify.app/docs/proyectos/tractores/documentacion/requisitos/RF24 +// RF22 Guardar Fórmula - https://codeandco-wiki.netlify.app/docs/proyectos/tractores/documentacion/requisitos/RF22 const { LONGITUD_MAXIMA_FORMULA, LONGITUD_MAXIMA_NOMBRE_FORMULA } = require(`${rutaBase}src/framework/utils/scripts/constantes.js`); @@ -65,13 +65,13 @@ async function inicializarCrearFormula() { if (nombreArchivo === null || nombreArchivo === undefined) { const selectorAnidado = document.getElementById('main-function'); - const listaOpcionesEliminar = ['IF', 'IFERROR', 'VLOOKUP'] + const listaOpcionesEliminar = ['IF', 'IFERROR'] listaOpcionesEliminar.forEach(elemento => { const elemntoEliminar = selectorAnidado.querySelector(`option[value='${elemento}']`) selectorAnidado.removeChild(elemntoEliminar) }); - mostrarAlerta('No hay un archivo cargado.', 'No se podran crear fórmulas (SI, SI.ERROR y BUSCAV).', 'warning'); + mostrarAlerta('No hay un archivo cargado.', 'No se podran crear fórmulas (SI, SI.ERROR).', 'warning'); return; } } @@ -160,7 +160,7 @@ async function procesarFormula() { btnGuardar.disabled = false; return; } - const formula = cuadroTextoGenerado.split(':')[1].trim(); + const formula = cuadroTextoGenerado.split('°')[2].trim(); try { const respuesta = await guardarFormula(nombreFormula, formula); @@ -217,11 +217,8 @@ function definirEstructura(elementoElegido, contenedor) { agregarArgumento('Valor', 'iferror-value', contenedor, true); agregarArgumento('Valor si error', 'iferror-iferror', contenedor, true); break; - case 'VLOOKUP': - agregarArgumento('Valor buscado', 'vlookup-lookupvalue', contenedor, true); - agregarArgumento('Matriz tabla', 'vlookup-tablearray', contenedor); - agregarArgumento('Indicador de columnas', 'vlookup-colindexnum', contenedor); - agregarArgumento('Coincidencia (0=exacta, 1=aproximada)', 'vlookup-rangelookup', contenedor); + case 'FILTER': + agregarCriterio('Condición', 'if-condition', contenedor); break; case 'ARITHMETIC': agregarArgumento('Expresión', 'arithmetic-expression', contenedor); @@ -346,7 +343,7 @@ function agregarFuncionAnidada(boton) { - + `; @@ -538,7 +535,7 @@ function generarFormulaCompleja() { } const formula = construirFormulaDesdeContenedor(contenedor, seleccionFuncionPrincipal.value); - document.getElementById('resultado').innerText = `Fórmula generada (en inglés para HyperFormula):\n=${formula}`; + document.getElementById('resultado').innerText = `°Fórmula generada (en inglés para HyperFormula)°\n=${formula}`; } /** @@ -576,11 +573,8 @@ function construirFormulaDesdeContenedor(contenedor, nombreFuncion) { argumentos.push(procesarArgumento(elementosArgumentos[0])); argumentos.push(procesarArgumento(elementosArgumentos[1])); break; - case 'VLOOKUP': - argumentos.push(procesarArgumento(elementosArgumentos[0])); - argumentos.push(obtenerValor(elementosArgumentos[1].querySelector('input'))); - argumentos.push(obtenerValor(elementosArgumentos[2].querySelector('input'))); - argumentos.push(obtenerValor(elementosArgumentos[3].querySelector('input'))); + case 'FILTER': + argumentos.push(construirCondicion(elementosArgumentos[0], false)); break; case 'ARITHMETIC': return obtenerValor(elementosArgumentos[0].querySelector('input')); @@ -673,7 +667,7 @@ function traducirFuncion(nombre) { 'CONTAR.SI': 'COUNTIF', 'CONTAR.SI.CONJUNTO': 'COUNTIFS', 'SI.ERROR': 'IFERROR', - 'BUSCARV': 'VLOOKUP', + 'FILTRO': 'FILTER', 'ARITHMETIC': 'ARITHMETIC' }; return map[nombre] || nombre; diff --git a/harvester-app/src/framework/vistas/paginas/formulas/crearFormula.ejs b/harvester-app/src/framework/vistas/paginas/formulas/crearFormula.ejs index 7c3fbf00..dd141c12 100644 --- a/harvester-app/src/framework/vistas/paginas/formulas/crearFormula.ejs +++ b/harvester-app/src/framework/vistas/paginas/formulas/crearFormula.ejs @@ -40,7 +40,7 @@ - + diff --git a/harvester-app/src/framework/vistas/paginas/iniciarSesion.ejs b/harvester-app/src/framework/vistas/paginas/iniciarSesion.ejs index 4afe7d4b..8b79e3f1 100644 --- a/harvester-app/src/framework/vistas/paginas/iniciarSesion.ejs +++ b/harvester-app/src/framework/vistas/paginas/iniciarSesion.ejs @@ -41,8 +41,9 @@