cotizador/assets/js/app.js

296 lines
12 KiB
JavaScript

// Configuración global
const CONFIG = {
apis: {
brasil: {
endpoint: '/api/brasil.php',
currency: 'BRL',
currencySymbol: 'R$',
label: 'Precio Reales'
},
chile: {
endpoint: '/api/chile.php',
currency: 'CLP',
currencySymbol: '$',
label: 'Precio Pesos Chilenos'
}
},
exchangeRateAPI: 'https://api.exchangerate-api.com/v4/latest/'
};
// Estado de la aplicación
let productos = [];
let cotizacion = 0;
let cotizacionUsd = 0;
let paisActual = '';
// Inicializar aplicación
function inicializarApp(pais) {
paisActual = pais;
obtenerCotizacion();
configurarEventos();
}
// Cargar productos desde la API
async function cargarDesdeArchivo() {
if (typeof LayoutManager !== 'undefined') {
LayoutManager.showLoading();
}
const endpoint = CONFIG.apis[paisActual].endpoint;
try {
const response = await fetch(endpoint);
productos = await response.json();
if (typeof LayoutManager !== 'undefined') {
LayoutManager.updateLastUpdate();
}
cargarListado();
if (typeof LayoutManager !== 'undefined') {
LayoutManager.hideLoading();
}
} catch (error) {
console.error('No se pudo cargar productos desde ' + endpoint, error);
productos = [];
cargarListado();
if (typeof LayoutManager !== 'undefined') {
LayoutManager.hideLoading();
}
}
}
// Obtener cotización de la moneda
async function obtenerCotizacion() {
const currency = CONFIG.apis[paisActual].currency;
const apiUrl = CONFIG.exchangeRateAPI + currency;
try {
const response = await fetch(apiUrl);
const data = await response.json();
if (data.rates && data.rates.ARS) {
cotizacion = data.rates.ARS;
cotizacionUsd = 1 / data.rates.USD;
document.getElementById('cotizacionMoneda').textContent = cotizacion.toFixed(2);
document.getElementById('cotizacionUsd').textContent = cotizacionUsd.toFixed(2);
await cargarDesdeArchivo();
} else {
document.getElementById('cotizacionMoneda').textContent = 'No disponible';
}
} catch (error) {
console.error('Error al obtener cotización:', error);
document.getElementById('cotizacionMoneda').textContent = 'Error al obtener cotización';
await cargarDesdeArchivo();
}
}
// Renderizar la tabla de productos
function cargarListado() {
let html = '';
let totalDiferenciaGlobal = 0;
// Agrupar productos por responsable
const responsables = {};
productos.forEach(producto => {
if (!responsables[producto.responsable]) {
responsables[producto.responsable] = [];
}
responsables[producto.responsable].push(producto);
});
// Renderizar por responsable
for (const responsable in responsables) {
let totalDiferenciaResponsable = 0;
let totalPrecioExtranjeroResponsable = 0;
html += `
<tr class="bg-gray-100">
<td colspan="6" class="px-6 py-3 text-center font-bold text-gray-700">${responsable}</td>
</tr>`;
responsables[responsable].forEach(producto => {
const diferencia = ((producto.precioBra * cotizacion) - producto.precioAr);
totalDiferenciaResponsable += diferencia;
totalDiferenciaGlobal += diferencia;
totalPrecioExtranjeroResponsable += producto.precioBra;
const colorClass = diferencia < 0 ? 'text-green-600' : 'text-red-600';
html += `
<tr class="hover:bg-gray-50">
<td class="px-3 sm:px-4 lg:px-6 py-3 sm:py-4">
<input type="text" class="w-full px-2 sm:px-3 py-1 text-xs sm:text-sm border border-gray-300 rounded focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 articuloInput" data-id="${producto.id}" value="${producto.articulo}">
</td>
<td class="px-3 sm:px-4 lg:px-6 py-3 sm:py-4">
<input type="number" class="w-full px-2 sm:px-3 py-1 text-xs sm:text-sm border border-gray-300 rounded focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 precioArInput" data-id="${producto.id}" step="0.1" value="${producto.precioAr}">
</td>
<td class="px-3 sm:px-4 lg:px-6 py-3 sm:py-4">
<input type="number" class="w-full px-2 sm:px-3 py-1 text-xs sm:text-sm border border-gray-300 rounded focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 precioExtranjeroInput" data-id="${producto.id}" step="0.1" value="${producto.precioBra}">
</td>
<td class="px-3 sm:px-4 lg:px-6 py-3 sm:py-4">
<span class="diferencia font-bold text-xs sm:text-sm ${colorClass}" data-id="${producto.id}">${diferencia.toLocaleString('es-AR')}</span>
</td>
<td class="px-3 sm:px-4 lg:px-6 py-3 sm:py-4 hidden lg:table-cell">${producto.responsable}</td>
<td class="px-3 sm:px-4 lg:px-6 py-3 sm:py-4">
<button class="bg-red-500 hover:bg-red-600 text-white px-2 sm:px-3 py-1 rounded text-xs sm:text-sm font-medium transition eliminarBtn" data-id="${producto.id}">
<span class="hidden sm:inline">Eliminar</span>
<span class="sm:hidden">✕</span>
</button>
</td>
</tr>`;
});
// Total por responsable
html += `
<tr class="bg-gray-100">
<td colspan="2" class="px-3 sm:px-4 lg:px-6 py-2 sm:py-3 text-right font-bold text-xs sm:text-sm text-gray-700">Total ${responsable}</td>
<td class="px-3 sm:px-4 lg:px-6 py-2 sm:py-3 text-right font-bold text-xs sm:text-sm text-gray-700">${(totalPrecioExtranjeroResponsable * cotizacion).toLocaleString('es-AR')}</td>
<td class="px-3 sm:px-4 lg:px-6 py-2 sm:py-3 text-right font-bold text-xs sm:text-sm text-gray-700">${totalDiferenciaResponsable.toLocaleString('es-AR')}</td>
<td colspan="2"></td>
</tr>`;
}
document.getElementById('priceList').innerHTML = html;
const totalElement = document.getElementById('totalDiferencia');
totalElement.textContent = totalDiferenciaGlobal.toLocaleString('es-AR');
totalElement.className = `px-3 sm:px-4 lg:px-6 py-3 sm:py-4 font-bold text-sm sm:text-base lg:text-lg ${totalDiferenciaGlobal < 0 ? 'text-green-600' : 'text-red-600'}`;
// Reconfigurar eventos después de renderizar
configurarEventosTabla();
}
// Guardar productos en la API
async function guardarProductos() {
const endpoint = CONFIG.apis[paisActual].endpoint;
try {
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(productos)
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (data.error) {
console.error('Error del servidor:', data.error);
if (typeof LayoutManager !== 'undefined') {
LayoutManager.showToast('Error al guardar: ' + data.error, 'error');
} else {
alert('Error al guardar: ' + data.error);
}
} else {
console.log('✓ Guardado exitoso:', data.mensaje);
if (typeof LayoutManager !== 'undefined') {
LayoutManager.showToast('Productos guardados correctamente', 'success');
}
}
} catch (error) {
console.error('Error al guardar los datos:', error);
if (typeof LayoutManager !== 'undefined') {
LayoutManager.showToast('Error de conexión al guardar', 'error');
} else {
alert('Error al guardar los productos. Verifica la consola.');
}
}
}
// Agregar nuevo artículo
function agregarArticulo(nombre, precioAr, precioExtranjero, responsable) {
const nuevoId = productos.length ? Math.max(...productos.map(p => p.id)) + 1 : 1;
productos.push({
id: nuevoId,
articulo: nombre,
precioAr: parseFloat(precioAr),
precioBra: parseFloat(precioExtranjero),
responsable: responsable
});
cargarListado();
guardarProductos();
}
// Eliminar artículo
function eliminarArticulo(id) {
productos = productos.filter(producto => producto.id !== id);
cargarListado();
guardarProductos();
}
// Configurar eventos de la tabla
function configurarEventosTabla() {
// Actualizar precios al editar
const inputs = document.querySelectorAll('.articuloInput, .precioArInput, .precioExtranjeroInput');
inputs.forEach(input => {
input.addEventListener('input', function() {
const id = parseInt(this.dataset.id);
const producto = productos.find(p => p.id === id);
const articulo = document.querySelector(`.articuloInput[data-id="${id}"]`).value;
const precioAr = parseFloat(document.querySelector(`.precioArInput[data-id="${id}"]`).value);
const precioExtranjero = parseFloat(document.querySelector(`.precioExtranjeroInput[data-id="${id}"]`).value);
if (!isNaN(precioAr) && !isNaN(precioExtranjero)) {
producto.articulo = articulo;
producto.precioAr = precioAr;
producto.precioBra = precioExtranjero;
const diferencia = ((precioExtranjero * cotizacion) - precioAr);
const diferenciaElement = document.querySelector(`.diferencia[data-id="${id}"]`);
diferenciaElement.textContent = diferencia.toLocaleString('es-AR');
diferenciaElement.className = `diferencia font-bold ${diferencia < 0 ? 'text-green-600' : 'text-red-600'}`;
guardarProductos();
}
});
});
// Botones de eliminar
const deleteButtons = document.querySelectorAll('.eliminarBtn');
deleteButtons.forEach(button => {
button.addEventListener('click', function() {
const id = parseInt(this.dataset.id);
if (confirm('¿Estás seguro de que deseas eliminar este artículo?')) {
eliminarArticulo(id);
}
});
});
}
// Configurar eventos principales
function configurarEventos() {
const form = document.getElementById('formAgregarArticulo');
if (form) {
form.addEventListener('submit', function(event) {
event.preventDefault();
const nombre = document.getElementById('articulo').value;
const precioAr = document.getElementById('precioAr').value;
const precioExtranjero = document.getElementById('precioExtranjero').value;
const responsable = document.getElementById('responsable').value;
if (nombre && precioAr && precioExtranjero && responsable) {
agregarArticulo(nombre, precioAr, precioExtranjero, responsable);
document.getElementById('articulo').value = '';
document.getElementById('precioAr').value = '';
document.getElementById('precioExtranjero').value = '';
document.getElementById('responsable').value = '';
} else {
alert('Por favor ingrese todos los campos.');
}
});
}
}