// --- FINANCE MODULE V3 (Mobile Patched) ---
window.fTxs = JSON.parse(localStorage.getItem('hf5_txs')||'[]');
window.expenseChartInst = null;
window.saveTxs = function(){ localStorage.setItem('hf5_txs', JSON.stringify(window.fTxs)); };
window.toggleCategory = function() {
var type = document.getElementById('fin-type').value;
var cat = document.getElementById('fin-cat');
if(type === 'income') {
cat.style.display = 'none';
} else {
cat.style.display = 'block';
}
};
window.addTransaction = function() {
var type = document.getElementById('fin-type').value;
var amt = parseFloat(document.getElementById('fin-amount').value);
var desc = document.getElementById('fin-desc').value.trim();
var cat = document.getElementById('fin-cat').value;
if(!amt || amt <= 0 || !desc) { alert('Ingresa un monto válido y una descripción.'); return; }
window.fTxs.push({
id: Date.now().toString(),
type: type,
amount: amt,
desc: desc,
category: type === 'expense' ? cat : 'Ingreso',
date: new Date().toLocaleDateString('es-CL')
});
window.saveTxs();
document.getElementById('fin-amount').value = '';
document.getElementById('fin-desc').value = '';
window.renderFin();
};
window.delTransaction = function(id) {
window.fTxs = window.fTxs.filter(function(t){ return t.id !== id; });
window.saveTxs();
window.renderFin();
};
window.updateChart = function() {
var ctx = document.getElementById('expenseChart');
if(!ctx || typeof Chart === 'undefined') return;
var catData = {};
var hasExpenses = false;
window.fTxs.forEach(function(t) {
if(t.type === 'expense') {
var c = t.category || 'Otros';
catData[c] = (catData[c] || 0) + t.amount;
hasExpenses = true;
}
});
var labels = Object.keys(catData);
var data = Object.values(catData);
var colors = ['#0EA5E9', '#F59E0B', '#E11D48', '#8B5CF6', '#10B981', '#64748B'];
if(window.expenseChartInst) {
window.expenseChartInst.destroy();
}
if(!hasExpenses) {
window.expenseChartInst = new Chart(ctx, {
type: 'doughnut',
data: { labels: ['Sin Gastos'], datasets: [{ data: [1], backgroundColor: ['#E2E8F0'] }] },
options: { responsive: true, maintainAspectRatio: false, plugins: { tooltip: {enabled: false}, legend: {display: false} }, cutout: '75%' }
});
return;
}
window.expenseChartInst = new Chart(ctx, {
type: 'doughnut',
data: {
labels: labels,
datasets: [{
data: data,
backgroundColor: colors.slice(0, labels.length),
borderWidth: 2,
borderColor: '#FFFFFF'
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
cutout: '65%',
plugins: {
legend: { position: 'bottom', labels: { color: '#64748B', font: {size: 11, family: 'system-ui'}, usePointStyle: true, boxWidth: 8, padding: 10 } },
tooltip: {
callbacks: {
label: function(context) {
var value = context.raw;
return ' $' + value.toLocaleString('es-CL');
}
}
}
}
}
});
};
window.renderFin = function() {
var total = 0;
var listHTML = '';
var arr = window.fTxs.slice().reverse();
for(var i=0; i
Android: Chrome → Menu ⋮ → Instalar app
✓ Con la app instalada las notificaciones funcionan en segundo plano.