o continuΓ‘ con
πŸ’° Dashboard
β€”
$ 0
Ingresos
$ 0
Gastos
$ 0
En ARS
$0
En USD
U$0
Transacciones
0
CategorΓ­as
0
Gastos por categorΓ­a
Últimos movimientos
+d.compra+' venta function loadAll(){ return Promise.all([ api('getTransactions',{uid:S.user.uid}), api('getCategories',{uid:S.user.uid}), api('getVacations',{uid:S.user.uid}), api('getAccounts',{uid:S.user.uid}) ]).then(function(results){ S.transactions=results[0].transactions||[]; var bc=results[1].categories||[]; S.categories=defaultCategories().concat(bc.filter(function(c){return c.type==='expense'||c.type==='income';})); S.vacations=results[2].vacations||[]; var ba=results[3].accounts||[]; ba.forEach(function(a){if(!S.accounts.find(function(x){return x.id===a.id;}))S.accounts.push(a);}); saveAccountsLocal(); }).catch(function(){if(!S.categories.length)S.categories=defaultCategories();}); } // ── TABS ───────────────────────────────────────── document.querySelectorAll('.nav-item').forEach(function(btn){btn.addEventListener('click',function(){switchTab(btn.getAttribute('data-tab'),btn);});}); function switchTab(tab,btn){ ['dashboard','transactions','accounts','savings','couple','vacation','settings'].forEach(function(t){var el=document.getElementById('tab-'+t);if(el)el.style.display='none';}); var active=document.getElementById('tab-'+tab);if(active)active.style.display=''; document.querySelectorAll('.nav-item').forEach(function(b){b.classList.remove('active');}); if(btn)btn.classList.add('active');S.activeTab=tab; var titles={dashboard:'Dashboard',transactions:'Movimientos',accounts:'Cuentas',savings:'Ahorros',couple:'Pareja',vacation:'Viajes',settings:'ConfiguraciΓ³n'}; document.getElementById('topbar-title').textContent=titles[tab]||tab; if(tab==='transactions')renderTransactions(); if(tab==='accounts')renderAccounts(); if(tab==='savings')renderSavings(); if(tab==='couple')renderCouple(); if(tab==='vacation')renderVacations(); if(tab==='settings')updateSettingsUI(); } document.getElementById('btn-go-settings').addEventListener('click',function(){switchTab('settings',document.querySelector('[data-tab="settings"]'));}); // ── MODE ────────────────────────────────────────── function updateModeUI(){ if(!S.user)return; var c=S.user.mode==='couple'||S.user.mode==='both',v=S.user.mode==='vacation'||S.user.mode==='both'; document.getElementById('nav-couple').style.display=c?'':'none'; document.getElementById('nav-vacation').style.display=v?'':'none'; var badges='';if(c)badges+='πŸ’‘ Pareja ';if(v)badges+='πŸ–οΈ Vacaciones'; document.getElementById('mode-indicator').innerHTML=badges; document.getElementById('shared-section').style.display=c?'':'none'; document.getElementById('vexp-shared-wrap').style.display=c?'':'none'; } document.getElementById('toggle-couple').addEventListener('change',function(){toggleMode('couple',this.checked);}); document.getElementById('toggle-vacation').addEventListener('change',function(){toggleMode('vacation',this.checked);}); function toggleMode(mode,checked){ if(!S.user)return; var c=S.user.mode==='couple'||S.user.mode==='both',v=S.user.mode==='vacation'||S.user.mode==='both'; if(mode==='couple')c=checked;if(mode==='vacation')v=checked; S.user.mode=c&&v?'both':c?'couple':v?'vacation':'normal'; localStorage.setItem('session',JSON.stringify(S.user));updateModeUI(); api('updateUserMode',{uid:S.user.uid,mode:S.user.mode}).catch(function(){}); } // ── SETTINGS / CATEGORIES ──────────────────────── function updateSettingsUI(){ if(!S.user)return; document.getElementById('settings-name').textContent=S.user.name||'β€”'; document.getElementById('settings-email').textContent=S.user.email||'β€”'; document.getElementById('settings-avatar').textContent=(S.user.name||'?')[0].toUpperCase(); document.getElementById('toggle-couple').checked=S.user.mode==='couple'||S.user.mode==='both'; document.getElementById('toggle-vacation').checked=S.user.mode==='vacation'||S.user.mode==='both'; var list=document.getElementById('categories-list-settings'); list.innerHTML=S.categories.map(function(cat){ return'
'+ '
'+ ''+cat.icon+''+ '
'+cat.name+'
'+ '
'+(cat.type==='income'?'Ingreso':'Gasto')+'
'+ '
'+ ''+ ''+ '
'; }).join(''); list.querySelectorAll('[data-edit-cat]').forEach(function(btn){ btn.addEventListener('click',function(){openEditCat(btn.getAttribute('data-edit-cat'));}); }); } // Edit category function openEditCat(catId){ var cat=S.categories.find(function(c){return c.id===catId;});if(!cat)return; S.editingCatId=catId; document.getElementById('edit-cat-name').value=cat.name; document.getElementById('edit-cat-icon').value=cat.icon; showModal('modal-edit-cat'); } document.getElementById('btn-save-edit-cat').addEventListener('click',function(){ var cat=S.categories.find(function(c){return c.id===S.editingCatId;});if(!cat)return; var name=document.getElementById('edit-cat-name').value.trim(); var icon=document.getElementById('edit-cat-icon').value.trim(); if(!name){toast('IngresΓ‘ un nombre','error');return;} cat.name=name;if(icon)cat.icon=icon; closeModal('modal-edit-cat');updateSettingsUI();renderCatPicker(); toast('CategorΓ­a actualizada βœ“','success'); api('updateCategory',{category:cat}).catch(function(){}); }); document.getElementById('btn-new-cat').addEventListener('click',function(){ S.newCatType='expense'; document.getElementById('cat-type-expense').classList.add('active');document.getElementById('cat-type-income').classList.remove('active'); showModal('modal-new-cat'); }); document.getElementById('cat-type-expense').addEventListener('click',function(){S.newCatType='expense';document.getElementById('cat-type-expense').classList.add('active');document.getElementById('cat-type-income').classList.remove('active');}); document.getElementById('cat-type-income').addEventListener('click',function(){S.newCatType='income';document.getElementById('cat-type-income').classList.add('active');document.getElementById('cat-type-expense').classList.remove('active');}); document.getElementById('btn-save-cat').addEventListener('click',function(){ var name=document.getElementById('cat-name').value.trim(),icon=document.getElementById('cat-icon').value.trim()||'πŸ“¦',color=document.getElementById('cat-color').value; if(!name){toast('IngresΓ‘ un nombre','error');return;} var cat={id:genId(),uid:S.user.uid,name:name,icon:icon,color:color,type:S.newCatType}; S.categories.push(cat);closeModal('modal-new-cat');updateSettingsUI(); toast('CategorΓ­a creada βœ“','success');api('addCategory',{category:cat}).catch(function(){}); }); // ── TC ──────────────────────────────────────────── document.getElementById('btn-open-tc').addEventListener('click',function(){ document.getElementById('tc-oficial').value=S.tc.oficial;document.getElementById('tc-blue').value=S.tc.blue;showModal('modal-tc'); }); document.getElementById('btn-save-tc').addEventListener('click',function(){ S.tc.oficial=parseFloat(document.getElementById('tc-oficial').value)||S.tc.oficial; S.tc.blue=parseFloat(document.getElementById('tc-blue').value)||S.tc.blue; localStorage.setItem('tc',JSON.stringify(S.tc));toast('Tipo de cambio guardado βœ“','success');closeModal('modal-tc'); if(S.activeTab==='savings')renderSavings(); }); // ── MODALS CLOSE ───────────────────────────────── document.querySelectorAll('[data-close]').forEach(function(btn){btn.addEventListener('click',function(){closeModal(btn.getAttribute('data-close'));});}); document.querySelectorAll('.modal-overlay').forEach(function(o){o.addEventListener('click',function(e){if(e.target===o)o.classList.remove('open');});}); // ── ACCOUNTS ───────────────────────────────────── function renderAccounts(){ var c=document.getElementById('accounts-content'); var nonSav=S.accounts.filter(function(a){return!isSavings(a);}); var total=nonSav.reduce(function(s,a){return s+(a.currency==='USD'?a.balance*S.tc.blue:a.balance);},0); var btnHtml='
'+ ''+ ''+ '
'; if(!S.accounts.length){ c.innerHTML='
🏦

Sin cuentas

CreΓ‘ tu primera cuenta para empezar a registrar movimientos.

'+btnHtml; }else{ var totalHtml='

Total disponible (sin ahorros)

$'+fmt(total)+'
En ARS equivalente al TC blue
'; var cards=nonSav.map(function(a){return accCardHTML(a,true);}).join(''); c.innerHTML=btnHtml+totalHtml+cards; } document.getElementById('btn-open-new-account').addEventListener('click',function(){openNewAccount();}); document.getElementById('btn-open-transfer').addEventListener('click',function(){openTransfer();}); c.querySelectorAll('[data-edit-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openEditAccount(btn.getAttribute('data-edit-acc'));});}); c.querySelectorAll('[data-del-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteAccount(btn.getAttribute('data-del-acc'));});}); c.querySelectorAll('[data-acc-history]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();showAccHistory(btn.getAttribute('data-acc-history'));});}); } function accCardHTML(acc,showActions){ var cfg=ACC_TYPES[acc.type]||ACC_TYPES.bank; var icon=acc.icon||cfg.icon; var balStr=acc.currency==='USD'?'U$'+fmt(acc.balance):'$'+fmt(acc.balance); var subStr=acc.currency==='USD'?'β‰ˆ $'+fmt(acc.balance*S.tc.blue)+' ARS':''; var actions=showActions?'
'+ ''+ ''+ ''+ '
':''; return'
'+ ''+ ''+ (subStr?'':'')+ actions+'
'; } function openNewAccount(){ S.editingAccId=null; document.getElementById('acc-modal-title').textContent='Nueva cuenta'; document.getElementById('acc-name').value='';document.getElementById('acc-balance').value=''; document.getElementById('acc-icon').value='';document.getElementById('acc-color').value='#6c63ff'; document.getElementById('acc-type').value='bank';document.getElementById('acc-currency').value='ARS'; document.getElementById('btn-save-account').textContent='Crear cuenta'; showModal('modal-new-account'); } function openEditAccount(accId){ var acc=S.accounts.find(function(a){return a.id===accId;});if(!acc)return; S.editingAccId=accId; document.getElementById('acc-modal-title').textContent='Editar cuenta'; document.getElementById('acc-name').value=acc.name; document.getElementById('acc-balance').value=acc.balance; document.getElementById('acc-icon').value=acc.icon||''; document.getElementById('acc-color').value=acc.color||'#6c63ff'; document.getElementById('acc-type').value=acc.type; document.getElementById('acc-currency').value=acc.currency; document.getElementById('btn-save-account').textContent='Guardar cambios'; showModal('modal-new-account'); } document.getElementById('btn-save-account').addEventListener('click',function(){ var name=document.getElementById('acc-name').value.trim(); var type=document.getElementById('acc-type').value; var balance=parseFloat(document.getElementById('acc-balance').value)||0; var currency=document.getElementById('acc-currency').value; var icon=document.getElementById('acc-icon').value.trim()||(ACC_TYPES[type]||ACC_TYPES.bank).icon; var color=document.getElementById('acc-color').value; if(!name){toast('PonΓ© un nombre a la cuenta','error');return;} if(S.editingAccId){ var acc=S.accounts.find(function(a){return a.id===S.editingAccId;}); if(acc){acc.name=name;acc.type=type;acc.balance=balance;acc.currency=currency;acc.icon=icon;acc.color=color;} }else{ var newAcc={id:genId(),uid:S.user.uid,name:name,type:type,balance:balance,currency:currency,icon:icon,color:color,created_at:new Date().toISOString()}; S.accounts.push(newAcc); api('addAccount',{account:newAcc}).catch(function(){}); } saveAccountsLocal();closeModal('modal-new-account'); if(S.activeTab==='accounts')renderAccounts(); if(S.activeTab==='savings')renderSavings(); populateAccountSelect(); toast((S.editingAccId?'Cuenta actualizada':'Cuenta creada')+' βœ“','success'); if(S.editingAccId)api('updateAccount',{account:S.accounts.find(function(a){return a.id===S.editingAccId;})}).catch(function(){}); }); function deleteAccount(accId){ if(!confirm('ΒΏEliminar esta cuenta? Las transacciones asociadas no se borrarΓ‘n.'))return; S.accounts=S.accounts.filter(function(a){return a.id!==accId;}); saveAccountsLocal(); if(S.activeTab==='accounts')renderAccounts(); if(S.activeTab==='savings')renderSavings(); populateAccountSelect(); toast('Cuenta eliminada','success'); api('deleteAccount',{id:accId}).catch(function(){}); } function showAccHistory(accId){ var acc=S.accounts.find(function(a){return a.id===accId;});if(!acc)return; var txs=S.transactions.filter(function(t){return t.account_id===accId;}); var overlay=document.createElement('div');overlay.className='modal-overlay open';overlay.style.zIndex='600'; var txHtml=txs.length?'
'+txs.map(txHTML).join('')+'
':'
πŸ“­

Sin movimientos

'; overlay.innerHTML=''; overlay.addEventListener('click',function(e){if(e.target===overlay)overlay.remove();}); document.body.appendChild(overlay); document.getElementById('close-acc-history').addEventListener('click',function(){overlay.remove();}); } // Transfer function openTransfer(){ populateTransferSelects(); document.getElementById('transfer-amount').value=''; document.getElementById('transfer-date').value=todayStr(); showModal('modal-transfer'); } function populateTransferSelects(){ var opts=S.accounts.map(function(a){return'';}).join(''); document.getElementById('transfer-from').innerHTML=opts; document.getElementById('transfer-to').innerHTML=opts; } document.getElementById('btn-save-transfer').addEventListener('click',function(){ var fromId=document.getElementById('transfer-from').value; var toId=document.getElementById('transfer-to').value; var amount=parseFloat(document.getElementById('transfer-amount').value); var currency=document.getElementById('transfer-currency').value; var date=document.getElementById('transfer-date').value||todayStr(); if(fromId===toId){toast('ElegΓ­ cuentas distintas','error');return;} if(!amount||amount<=0){toast('IngresΓ‘ un monto vΓ‘lido','error');return;} var from=S.accounts.find(function(a){return a.id===fromId;}); var to=S.accounts.find(function(a){return a.id===toId;}); // Adjust balances var debit=currency===from.currency?amount:(currency==='USD'?amount*S.tc.blue:amount/S.tc.blue); var credit=currency===to.currency?amount:(currency==='USD'?amount*S.tc.blue:amount/S.tc.blue); from.balance=(from.balance||0)-debit; to.balance=(to.balance||0)+credit; saveAccountsLocal(); // Record as two transactions var txOut={id:genId(),uid:S.user.uid,date:date,type:'expense',amount:amount,currency:currency,tc:S.tc.blue,category_id:'c9',account_id:fromId,tags:[],description:'Transferencia β†’ '+to.name,note:'',shared:false,payer:'me',split_me:100,created_at:new Date().toISOString()}; var txIn={id:genId(),uid:S.user.uid,date:date,type:'income',amount:amount,currency:currency,tc:S.tc.blue,category_id:'i4',account_id:toId,tags:[],description:'Transferencia ← '+from.name,note:'',shared:false,payer:'me',split_me:100,created_at:new Date().toISOString()}; S.transactions.unshift(txIn);S.transactions.unshift(txOut); closeModal('modal-transfer');renderAccounts();renderDashboard(); toast('Transferencia realizada βœ“','success'); api('addTransaction',{transaction:txOut}).catch(function(){}); api('addTransaction',{transaction:txIn}).catch(function(){}); }); // ── SAVINGS ────────────────────────────────────── function renderSavings(){ var c=document.getElementById('savings-content'); var savs=S.accounts.filter(function(a){return isSavings(a);}); var tUSD=savs.reduce(function(s,a){return s+(a.currency==='USD'?a.balance:0);},0); var tARS=savs.reduce(function(s,a){return s+(a.currency==='ARS'?a.balance:a.balance*S.tc.blue);},0); var btnHtml=''; if(!savs.length){ c.innerHTML='
🐷

Sin ahorros

CreΓ‘ una cuenta de ahorro para llevar el registro de lo que tenΓ©s guardado.

'+btnHtml; }else{ var summary='

Total ahorrado

$'+fmt(tARS)+'
U$'+fmt(tUSD)+' en dΓ³lares Β· TC blue $'+fmt(S.tc.blue)+'
'; var cards=savs.map(function(a){return accCardHTML(a,true);}).join(''); c.innerHTML=btnHtml+summary+cards; } document.getElementById('btn-open-new-savings').addEventListener('click',function(){ openNewAccount();document.getElementById('acc-type').value='savings'; }); c.querySelectorAll('[data-edit-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openEditAccount(btn.getAttribute('data-edit-acc'));});}); c.querySelectorAll('[data-del-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteAccount(btn.getAttribute('data-del-acc'));});}); c.querySelectorAll('[data-acc-history]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();showAccHistory(btn.getAttribute('data-acc-history'));});}); } // ── POPULATE ACCOUNT SELECT ────────────────────── function populateAccountSelect(){ var sel=document.getElementById('tx-account-id'); var nonSav=S.accounts.filter(function(a){return!isSavings(a);}); sel.innerHTML=nonSav.length ?nonSav.map(function(a){return'';}).join('') :''; } // ── NEW / EDIT TX ──────────────────────────────── document.getElementById('fab').addEventListener('click',function(){openTxModal(null);}); function openTxModal(tx){ S.editingTxId=tx?tx.id:null; document.getElementById('tx-modal-title').textContent=tx?'Editar transacciΓ³n':'Nueva transacciΓ³n'; document.getElementById('btn-save-tx').textContent=tx?'Guardar cambios':'Guardar'; document.getElementById('tx-desc').value=tx?tx.description:''; document.getElementById('tx-amount').value=tx?tx.amount:''; document.getElementById('tx-note').value=tx?tx.note:''; document.getElementById('tx-date').value=tx?tx.date:todayStr(); document.getElementById('tx-currency').value=tx?tx.currency:'ARS'; document.getElementById('tc-field').style.display=(tx&&tx.currency==='USD')||false?'':'none'; if(tx&&tx.currency==='USD')document.getElementById('tx-tc').value=tx.tc; document.getElementById('tx-shared').checked=tx?tx.shared:false; document.getElementById('split-section').style.display=(tx&&tx.shared)?'':'none'; document.getElementById('split-me').value=tx?tx.split_me:50; document.getElementById('split-partner').value=tx?(100-tx.split_me):50; S.txType=tx?tx.type:'expense'; S.selectedCat=tx?tx.category_id:null; S.currentTags=tx?(tx.tags||[]).slice():[]; document.querySelectorAll('.toggle-opt[data-type]').forEach(function(b){b.classList.toggle('active',b.getAttribute('data-type')===S.txType);}); populateAccountSelect(); if(tx&&tx.account_id){var sel=document.getElementById('tx-account-id');for(var i=0;iΓ—'; pill.querySelector('button').addEventListener('click',function(){S.currentTags=S.currentTags.filter(function(t){return t!==tag;});renderTagsWrap();}); wrap.insertBefore(pill,inp); }); } function renderCatPicker(){ var grid=document.getElementById('cat-picker');grid.innerHTML=''; var filtered=S.categories.filter(function(cat){return cat.type===S.txType;}); if(!filtered.length){grid.innerHTML='

Sin categorΓ­as para este tipo

';return;} filtered.forEach(function(cat){ var d=document.createElement('div');d.className='cat-opt'+(S.selectedCat===cat.id?' selected':''); d.innerHTML=''+cat.icon+''+cat.name+''; d.addEventListener('click',function(){S.selectedCat=cat.id;renderCatPicker();});grid.appendChild(d); }); } document.getElementById('btn-save-tx').addEventListener('click',function(){ var desc=document.getElementById('tx-desc').value.trim(); var amount=parseFloat(document.getElementById('tx-amount').value); var currency=document.getElementById('tx-currency').value; var date=document.getElementById('tx-date').value||todayStr(); var tc=currency==='USD'?(parseFloat(document.getElementById('tx-tc').value)||S.tc.blue):1; var note=document.getElementById('tx-note').value.trim(); var shared=document.getElementById('tx-shared').checked; var payer=document.getElementById('tx-payer').value; var splitMe=parseInt(document.getElementById('split-me').value)||50; var accountId=document.getElementById('tx-account-id').value; if(!desc){toast('AgregΓ‘ una descripciΓ³n','error');return;} if(!amount||amount<=0){toast('IngresΓ‘ un monto vΓ‘lido','error');return;} if(!accountId){toast('SeleccionΓ‘ una cuenta','error');return;} var defaultCatId=S.txType==='income'?'i4':'c9'; if(S.editingTxId){ // Editing: reverse old effect on account, apply new var oldTx=S.transactions.find(function(t){return t.id===S.editingTxId;}); if(oldTx){ var oldAcc=S.accounts.find(function(a){return a.id===oldTx.account_id;}); if(oldAcc){var rev=oldTx.type==='income'?-oldTx.amount:oldTx.amount;if(oldTx.currency==='USD'&&oldAcc.currency==='ARS')rev*=oldTx.tc;else if(oldTx.currency==='ARS'&&oldAcc.currency==='USD')rev/=oldTx.tc;oldAcc.balance=(oldAcc.balance||0)+rev;} oldTx.description=desc;oldTx.amount=amount;oldTx.currency=currency;oldTx.tc=tc; oldTx.date=date;oldTx.note=note;oldTx.shared=shared;oldTx.payer=shared?payer:'me'; oldTx.split_me=shared?splitMe:100;oldTx.category_id=S.selectedCat||defaultCatId; oldTx.account_id=accountId;oldTx.tags=S.currentTags.slice(); } var newAcc=S.accounts.find(function(a){return a.id===accountId;}); if(newAcc){var delta=S.txType==='income'?amount:-amount;if(currency==='USD'&&newAcc.currency==='ARS')delta*=tc;else if(currency==='ARS'&&newAcc.currency==='USD')delta/=tc;newAcc.balance=(newAcc.balance||0)+delta;} saveAccountsLocal();closeModal('modal-new-tx');renderDashboard(); if(S.activeTab==='transactions')renderTransactions(); if(S.activeTab==='accounts')renderAccounts(); toast('TransacciΓ³n actualizada βœ“','success'); api('updateTransaction',{transaction:oldTx}).catch(function(){}); }else{ var acc=S.accounts.find(function(a){return a.id===accountId;}); if(acc){var d2=S.txType==='income'?amount:-amount;if(currency==='USD'&&acc.currency==='ARS')d2*=tc;else if(currency==='ARS'&&acc.currency==='USD')d2/=tc;acc.balance=(acc.balance||0)+d2;saveAccountsLocal();api('updateAccountBalance',{id:acc.id,balance:acc.balance}).catch(function(){});} var newTx={id:genId(),uid:S.user.uid,date:date,type:S.txType,amount:amount,currency:currency,tc:tc,category_id:S.selectedCat||defaultCatId,account_id:accountId,tags:S.currentTags.slice(),description:desc,note:note,shared:shared,payer:shared?payer:'me',split_me:shared?splitMe:100,created_at:new Date().toISOString()}; S.transactions.unshift(newTx);closeModal('modal-new-tx');renderDashboard(); if(S.activeTab==='transactions')renderTransactions(); if(S.activeTab==='accounts')renderAccounts(); toast('TransacciΓ³n guardada βœ“','success');api('addTransaction',{transaction:newTx}).catch(function(){}); } }); // ── TRANSACTIONS ────────────────────────────────── document.querySelectorAll('.filter-chip').forEach(function(btn){ btn.addEventListener('click',function(){ S.txFilter=btn.getAttribute('data-filter'); document.querySelectorAll('.filter-chip').forEach(function(b){b.classList.remove('active');});btn.classList.add('active');renderTransactions(); }); }); function renderTransactions(){ var container=document.getElementById('tx-list-container'); var txs=S.transactions.slice(); if(S.txFilter==='income')txs=txs.filter(function(t){return t.type==='income';}); if(S.txFilter==='expense')txs=txs.filter(function(t){return t.type==='expense';}); if(S.txFilter==='shared')txs=txs.filter(function(t){return t.shared;}); if(S.txFilter==='ARS')txs=txs.filter(function(t){return t.currency==='ARS';}); if(S.txFilter==='USD')txs=txs.filter(function(t){return t.currency==='USD';}); if(!txs.length){container.innerHTML='
πŸ“­

Sin movimientos

TocΓ‘ el + para agregar tu primer transacciΓ³n

';return;} container.innerHTML='
'+txs.map(function(tx){return txHTML(tx,true);}).join('')+'
'; container.querySelectorAll('[data-edit-tx]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();var tx=S.transactions.find(function(t){return t.id===btn.getAttribute('data-edit-tx');});if(tx)openTxModal(tx);});}); container.querySelectorAll('[data-del-tx]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteTx(btn.getAttribute('data-del-tx'));});}); } function deleteTx(txId){ if(!confirm('ΒΏEliminar esta transacciΓ³n?'))return; var tx=S.transactions.find(function(t){return t.id===txId;}); if(tx){ var acc=S.accounts.find(function(a){return a.id===tx.account_id;}); if(acc){var rev=tx.type==='income'?-tx.amount:tx.amount;if(tx.currency==='USD'&&acc.currency==='ARS')rev*=tx.tc;else if(tx.currency==='ARS'&&acc.currency==='USD')rev/=tx.tc;acc.balance=(acc.balance||0)+rev;saveAccountsLocal();} } S.transactions=S.transactions.filter(function(t){return t.id!==txId;}); renderTransactions();renderDashboard(); toast('TransacciΓ³n eliminada','success'); api('deleteTransaction',{id:txId}).catch(function(){}); } function txHTML(tx,showActions){ var cat=S.categories.find(function(c){return c.id===tx.category_id;})||{icon:'πŸ“¦',color:'#6b7280'}; var acc=S.accounts.find(function(a){return a.id===tx.account_id;}); var sign=tx.type==='income'?'+':'-',cls=tx.type==='income'?'income':'expense'; var tags=(tx.tags||[]).map(function(t){return'#'+t+'';}).join(''); var badge=tx.shared?'πŸ’‘':''; var accBadge=acc?'':''; var amt=tx.currency==='USD'?'U$'+fmt(tx.amount):'$'+fmt(tx.amount); var equiv=tx.currency==='USD'?'β‰ˆ $'+fmt(tx.amount*tx.tc)+' ARS':''; var actions=showActions?'
':''; return'
'+ '
'+cat.icon+'
'+ '
'+tx.description+'
'+ '
'+fmtDate(tx.date)+' '+accBadge+' '+badge+' '+tags+'
'+ '
'+ '
'+sign+amt+'
'+(equiv?'
'+equiv+'
':'')+'
'+ actions+'
'; } // ── DASHBOARD ───────────────────────────────────── function renderDashboard(){ var now=new Date(),month=now.getMonth(),year=now.getFullYear(); var savIds=S.accounts.filter(isSavings).map(function(a){return a.id;}); var txs=S.transactions.filter(function(t){var d=new Date(t.date);return d.getMonth()===month&&d.getFullYear()===year&&savIds.indexOf(t.account_id)===-1;}); var iARS=0,eARS=0,iUSD=0,eUSD=0; txs.forEach(function(t){var isIn=t.type==='income';if(t.currency==='ARS'){isIn?(iARS+=t.amount):(eARS+=t.amount);}else{isIn?(iUSD+=t.amount):(eUSD+=t.amount);}}); document.getElementById('balance-month-label').textContent=monthName(month)+' '+year; document.getElementById('balance-main-val').textContent='$'+fmt(iARS-eARS)+' ARS'; document.getElementById('dash-income').textContent='+$'+fmt(iARS); document.getElementById('dash-expense').textContent='-$'+fmt(eARS); var tARS=iARS-eARS,tUSD=iUSD-eUSD; var arsEl=document.getElementById('dash-ars');arsEl.textContent=(tARS>=0?'+':'')+' $'+fmt(tARS);arsEl.className='si-val '+(tARS>=0?'green':'red'); var usdEl=document.getElementById('dash-usd');usdEl.textContent=(tUSD>=0?'+':'')+'U$'+fmt(tUSD);usdEl.className='si-val '+(tUSD>=0?'green':'red'); document.getElementById('dash-count').textContent=txs.length; var cats={};txs.forEach(function(t){cats[t.category_id]=true;});document.getElementById('dash-cats').textContent=Object.keys(cats).length; renderPie(txs.filter(function(t){return t.type==='expense';})); var recent=S.transactions.slice(0,5); document.getElementById('dash-recent-list').innerHTML=recent.length?'
'+recent.map(function(t){return txHTML(t,false);}).join('')+'
':'
πŸ’Έ

Sin movimientos

EmpezΓ‘ cargando tu primer gasto

'; } function renderPie(expenses){ var canvas=document.getElementById('pieChart'),legend=document.getElementById('pie-legend'),ctx=canvas.getContext('2d'); var W=canvas.offsetWidth||300,H=220;canvas.width=W;canvas.height=H;ctx.clearRect(0,0,W,H); var catMap={};expenses.forEach(function(t){var val=t.currency==='USD'?t.amount*t.tc:t.amount;catMap[t.category_id]=(catMap[t.category_id]||0)+val;}); var entries=Object.keys(catMap).map(function(k){return[k,catMap[k]];}).sort(function(a,b){return b[1]-a[1];}); var total=entries.reduce(function(s,e){return s+e[1];},0)||1;legend.innerHTML=''; if(!entries.length){ctx.fillStyle='#ffffff22';ctx.beginPath();ctx.arc(W/2,H/2,80,0,Math.PI*2);ctx.fill();ctx.fillStyle='#9ca3af';ctx.textAlign='center';ctx.font='14px Inter';ctx.fillText('Sin datos',W/2,H/2+5);return;} var cx=W/2,cy=H/2,r=Math.min(cx,cy)-20,startAngle=-Math.PI/2; entries.forEach(function(entry){ var cat=S.categories.find(function(c){return c.id===entry[0];})||{color:'#6b7280',name:'Otros',icon:'πŸ“¦'}; var slice=(entry[1]/total)*Math.PI*2; ctx.beginPath();ctx.moveTo(cx,cy);ctx.arc(cx,cy,r,startAngle,startAngle+slice);ctx.closePath();ctx.fillStyle=cat.color;ctx.fill();ctx.strokeStyle='#1a1d27';ctx.lineWidth=2;ctx.stroke(); legend.innerHTML+='
'+cat.icon+' '+cat.name+' '+Math.round(entry[1]/total*100)+'%
'; startAngle+=slice; }); ctx.beginPath();ctx.arc(cx,cy,r*0.5,0,Math.PI*2);ctx.fillStyle='#1a1d27';ctx.fill(); ctx.fillStyle='#fff';ctx.textAlign='center';ctx.font='bold 14px Inter';ctx.fillText('$'+fmt(total),cx,cy+5); } // ── COUPLE ──────────────────────────────────────── document.getElementById('btn-send-partner').addEventListener('click',function(){ var email=document.getElementById('partner-email').value.trim();if(!email){toast('IngresΓ‘ un email','error');return;} api('linkPartner',{uid:S.user.uid,partner_email:email}).then(function(d){ S.user.partner_uid=d.partner_uid;S.user.partner_name=d.partner_name;localStorage.setItem('session',JSON.stringify(S.user)); toast('Β‘Pareja vinculada! βœ“','success');closeModal('modal-link-partner');renderCouple(); }).catch(function(e){toast(e.message,'error');}); }); function renderCouple(){ var c=document.getElementById('couple-content'); if(!S.user.partner_uid){ c.innerHTML='
πŸ‘€

'+S.user.name+'

Sin pareja vinculada

'+ '
πŸ’‘

Modo pareja activado

VinculΓ‘ tu cuenta con la de tu pareja para compartir gastos.

'+ ''; document.getElementById('btn-open-link-partner').addEventListener('click',function(){showModal('modal-link-partner');});return; } var shared=S.transactions.filter(function(t){return t.shared;}); var iOwe=0,theyOwe=0; shared.forEach(function(t){var val=t.currency==='USD'?t.amount*t.tc:t.amount;var my=val*(t.split_me||50)/100;if(t.payer==='me')theyOwe+=(val-my);else iOwe+=my;}); var net=theyOwe-iOwe; c.innerHTML='
πŸ‘€
πŸ’ž
πŸ‘€
'+ '

'+S.user.name+' & '+(S.user.partner_name||'Pareja')+'

Vinculados

'+ '

Balance compartido (ARS)

'+ (net>=0?'+':'')+' $'+fmt(Math.abs(net))+'
'+(net>0?'Te deben':'Le debΓ©s a '+(S.user.partner_name||'tu pareja'))+'
'+ '
Gastos compartidos
'+ '
'+(shared.length?shared.map(function(t){return txHTML(t,false);}).join(''):'
🀝

Sin gastos compartidos

')+'
'; } // ── VACATIONS ───────────────────────────────────── document.getElementById('btn-save-vac').addEventListener('click',function(){ var name=document.getElementById('vac-name').value.trim();if(!name){toast('PonΓ© un nombre al viaje','error');return;} var vac={id:genId(),uid:S.user.uid,name:name,start_date:document.getElementById('vac-start').value,end_date:document.getElementById('vac-end').value,budget:parseFloat(document.getElementById('vac-budget').value)||0,currency:document.getElementById('vac-currency').value,created_at:new Date().toISOString()}; S.vacations.unshift(vac);closeModal('modal-new-vac');renderVacations();toast('Viaje creado βœ“','success');api('addVacation',{vacation:vac}).catch(function(){}); }); document.getElementById('btn-save-vexp').addEventListener('click',function(){ var desc=document.getElementById('vexp-desc').value.trim(),amount=parseFloat(document.getElementById('vexp-amount').value); if(!desc||!amount){toast('CompletΓ‘ los campos','error');return;} var item={id:genId(),vacation_id:S.activeVacId,type:'expense',description:desc,amount:amount,currency:document.getElementById('vexp-currency').value,tc:parseFloat(document.getElementById('vexp-tc').value)||S.tc.blue,shared:document.getElementById('vexp-shared').checked,payer:document.getElementById('vexp-payer').value,split_me:parseInt(document.getElementById('vexp-split-me').value)||50,created_at:new Date().toISOString()}; if(!S.vacItems[S.activeVacId])S.vacItems[S.activeVacId]=[];S.vacItems[S.activeVacId].unshift(item); closeModal('modal-vac-expense');renderVacations();toast('Gasto guardado βœ“','success');api('addVacationItem',{item:item}).catch(function(){}); }); function calcVacSpent(vacId){return(S.vacItems[vacId]||[]).filter(function(i){return i.type==='expense';}).reduce(function(s,i){return s+(i.currency==='USD'?i.amount*(i.tc||S.tc.blue):i.amount);},0);} function renderVacations(){ var c=document.getElementById('vacation-content'); var btnHtml=''; if(!S.vacations.length){c.innerHTML='
✈️

Sin viajes

CreΓ‘ tu primer viaje para empezar.

'+btnHtml;} else{ var cards=S.vacations.map(function(v){var spent=calcVacSpent(v.id),pct=Math.min(100,Math.round(spent/v.budget*100))||0,curr=v.currency==='USD'?'U$':'$'; return'
✈️ '+v.name+'
πŸ“… '+fmtDate(v.start_date)+' β†’ '+fmtDate(v.end_date)+'
'+ '
'+ '
'+curr+fmt(spent)+' gastado'+curr+fmt(v.budget-spent)+' restante
'+ '
'; }).join('');c.innerHTML=btnHtml+cards; } document.getElementById('btn-open-new-vac').addEventListener('click',function(){showModal('modal-new-vac');}); c.querySelectorAll('[data-vac-expense]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();S.activeVacId=btn.getAttribute('data-vac-expense');showModal('modal-vac-expense');});}); c.querySelectorAll('[data-vac-detail]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openVacationDetail(btn.getAttribute('data-vac-detail'));});}); } function openVacationDetail(vacId){ var vac=S.vacations.find(function(v){return v.id===vacId;});if(!vac)return; var items=S.vacItems[vacId]||[],expenses=items.filter(function(i){return i.type==='expense';}),checks=items.filter(function(i){return i.type==='checklist';}); var spent=calcVacSpent(vacId),pct=Math.min(100,Math.round(spent/vac.budget*100))||0,curr=vac.currency==='USD'?'U$':'$'; var overlay=document.createElement('div');overlay.className='modal-overlay open';overlay.style.zIndex='600'; var checkHtml=checks.length?checks.map(function(i){return'
βœ“
'+i.description+'
';}).join(''):'

Sin Γ­tems

'; var expHtml=expenses.length?expenses.map(function(i){return'
✈️
'+i.description+'
'+(i.shared?'πŸ’‘':'')+'
-'+(i.currency==='USD'?'U$':'$')+fmt(i.amount)+'
';}).join(''):'

Sin gastos aΓΊn

'; overlay.innerHTML=''; overlay.addEventListener('click',function(e){if(e.target===overlay)overlay.remove();});document.body.appendChild(overlay); document.getElementById('close-vac-detail').addEventListener('click',function(){overlay.remove();}); document.getElementById('checklist-container').addEventListener('click',function(e){ var item=e.target.closest('.checklist-item');if(!item)return; var box=item.querySelector('.check-box'),txt=item.querySelector('.check-text'),iid=item.getAttribute('data-item-id'); var vi=(S.vacItems[vacId]||[]).find(function(x){return x.id===iid;});if(vi)vi.done=!vi.done;box.classList.toggle('checked');txt.classList.toggle('done'); }); document.getElementById('btn-add-check').addEventListener('click',function(){ var inp=document.getElementById('new-check-txt'),desc=inp.value.trim();if(!desc)return; var ni={id:genId(),vacation_id:vacId,type:'checklist',description:desc,done:false}; if(!S.vacItems[vacId])S.vacItems[vacId]=[];S.vacItems[vacId].push(ni);inp.value=''; var container=document.getElementById('checklist-container');var p=container.querySelector('p');if(p)p.remove(); var div=document.createElement('div');div.className='checklist-item';div.setAttribute('data-item-id',ni.id);div.innerHTML='
βœ“
'+desc+'';container.appendChild(div); api('addVacationItem',{item:ni}).catch(function(){}); }); } window.addEventListener('resize',function(){if(S.activeTab==='dashboard')renderDashboard();}); +d.venta); } }).catch(function(){console.warn('No se pudo obtener el dΓ³lar oficial');}); } function loadAll(){ return Promise.all([ api('getTransactions',{uid:S.user.uid}), api('getCategories',{uid:S.user.uid}), api('getVacations',{uid:S.user.uid}), api('getAccounts',{uid:S.user.uid}) ]).then(function(results){ S.transactions=results[0].transactions||[]; var bc=results[1].categories||[]; S.categories=defaultCategories().concat(bc.filter(function(c){return c.type==='expense'||c.type==='income';})); S.vacations=results[2].vacations||[]; var ba=results[3].accounts||[]; ba.forEach(function(a){if(!S.accounts.find(function(x){return x.id===a.id;}))S.accounts.push(a);}); saveAccountsLocal(); }).catch(function(){if(!S.categories.length)S.categories=defaultCategories();}); } // ── TABS ───────────────────────────────────────── document.querySelectorAll('.nav-item').forEach(function(btn){btn.addEventListener('click',function(){switchTab(btn.getAttribute('data-tab'),btn);});}); function switchTab(tab,btn){ ['dashboard','transactions','accounts','savings','couple','vacation','settings'].forEach(function(t){var el=document.getElementById('tab-'+t);if(el)el.style.display='none';}); var active=document.getElementById('tab-'+tab);if(active)active.style.display=''; document.querySelectorAll('.nav-item').forEach(function(b){b.classList.remove('active');}); if(btn)btn.classList.add('active');S.activeTab=tab; var titles={dashboard:'Dashboard',transactions:'Movimientos',accounts:'Cuentas',savings:'Ahorros',couple:'Pareja',vacation:'Viajes',settings:'ConfiguraciΓ³n'}; document.getElementById('topbar-title').textContent=titles[tab]||tab; if(tab==='transactions')renderTransactions(); if(tab==='accounts')renderAccounts(); if(tab==='savings')renderSavings(); if(tab==='couple')renderCouple(); if(tab==='vacation')renderVacations(); if(tab==='settings')updateSettingsUI(); } document.getElementById('btn-go-settings').addEventListener('click',function(){switchTab('settings',document.querySelector('[data-tab="settings"]'));}); // ── MODE ────────────────────────────────────────── function updateModeUI(){ if(!S.user)return; var c=S.user.mode==='couple'||S.user.mode==='both',v=S.user.mode==='vacation'||S.user.mode==='both'; document.getElementById('nav-couple').style.display=c?'':'none'; document.getElementById('nav-vacation').style.display=v?'':'none'; var badges='';if(c)badges+='πŸ’‘ Pareja ';if(v)badges+='πŸ–οΈ Vacaciones'; document.getElementById('mode-indicator').innerHTML=badges; document.getElementById('shared-section').style.display=c?'':'none'; document.getElementById('vexp-shared-wrap').style.display=c?'':'none'; } document.getElementById('toggle-couple').addEventListener('change',function(){toggleMode('couple',this.checked);}); document.getElementById('toggle-vacation').addEventListener('change',function(){toggleMode('vacation',this.checked);}); function toggleMode(mode,checked){ if(!S.user)return; var c=S.user.mode==='couple'||S.user.mode==='both',v=S.user.mode==='vacation'||S.user.mode==='both'; if(mode==='couple')c=checked;if(mode==='vacation')v=checked; S.user.mode=c&&v?'both':c?'couple':v?'vacation':'normal'; localStorage.setItem('session',JSON.stringify(S.user));updateModeUI(); api('updateUserMode',{uid:S.user.uid,mode:S.user.mode}).catch(function(){}); } // ── SETTINGS / CATEGORIES ──────────────────────── function updateSettingsUI(){ if(!S.user)return; document.getElementById('settings-name').textContent=S.user.name||'β€”'; document.getElementById('settings-email').textContent=S.user.email||'β€”'; document.getElementById('settings-avatar').textContent=(S.user.name||'?')[0].toUpperCase(); document.getElementById('toggle-couple').checked=S.user.mode==='couple'||S.user.mode==='both'; document.getElementById('toggle-vacation').checked=S.user.mode==='vacation'||S.user.mode==='both'; var list=document.getElementById('categories-list-settings'); list.innerHTML=S.categories.map(function(cat){ return'
'+ '
'+ ''+cat.icon+''+ '
'+cat.name+'
'+ '
'+(cat.type==='income'?'Ingreso':'Gasto')+'
'+ '
'+ ''+ ''+ '
'; }).join(''); list.querySelectorAll('[data-edit-cat]').forEach(function(btn){ btn.addEventListener('click',function(){openEditCat(btn.getAttribute('data-edit-cat'));}); }); } // Edit category function openEditCat(catId){ var cat=S.categories.find(function(c){return c.id===catId;});if(!cat)return; S.editingCatId=catId; document.getElementById('edit-cat-name').value=cat.name; document.getElementById('edit-cat-icon').value=cat.icon; showModal('modal-edit-cat'); } document.getElementById('btn-save-edit-cat').addEventListener('click',function(){ var cat=S.categories.find(function(c){return c.id===S.editingCatId;});if(!cat)return; var name=document.getElementById('edit-cat-name').value.trim(); var icon=document.getElementById('edit-cat-icon').value.trim(); if(!name){toast('IngresΓ‘ un nombre','error');return;} cat.name=name;if(icon)cat.icon=icon; closeModal('modal-edit-cat');updateSettingsUI();renderCatPicker(); toast('CategorΓ­a actualizada βœ“','success'); api('updateCategory',{category:cat}).catch(function(){}); }); document.getElementById('btn-new-cat').addEventListener('click',function(){ S.newCatType='expense'; document.getElementById('cat-type-expense').classList.add('active');document.getElementById('cat-type-income').classList.remove('active'); showModal('modal-new-cat'); }); document.getElementById('cat-type-expense').addEventListener('click',function(){S.newCatType='expense';document.getElementById('cat-type-expense').classList.add('active');document.getElementById('cat-type-income').classList.remove('active');}); document.getElementById('cat-type-income').addEventListener('click',function(){S.newCatType='income';document.getElementById('cat-type-income').classList.add('active');document.getElementById('cat-type-expense').classList.remove('active');}); document.getElementById('btn-save-cat').addEventListener('click',function(){ var name=document.getElementById('cat-name').value.trim(),icon=document.getElementById('cat-icon').value.trim()||'πŸ“¦',color=document.getElementById('cat-color').value; if(!name){toast('IngresΓ‘ un nombre','error');return;} var cat={id:genId(),uid:S.user.uid,name:name,icon:icon,color:color,type:S.newCatType}; S.categories.push(cat);closeModal('modal-new-cat');updateSettingsUI(); toast('CategorΓ­a creada βœ“','success');api('addCategory',{category:cat}).catch(function(){}); }); // ── TC ──────────────────────────────────────────── document.getElementById('btn-open-tc').addEventListener('click',function(){ document.getElementById('tc-oficial').value=S.tc.oficial;document.getElementById('tc-blue').value=S.tc.blue;showModal('modal-tc'); }); document.getElementById('btn-save-tc').addEventListener('click',function(){ S.tc.oficial=parseFloat(document.getElementById('tc-oficial').value)||S.tc.oficial; S.tc.blue=parseFloat(document.getElementById('tc-blue').value)||S.tc.blue; localStorage.setItem('tc',JSON.stringify(S.tc));toast('Tipo de cambio guardado βœ“','success');closeModal('modal-tc'); if(S.activeTab==='savings')renderSavings(); }); // ── MODALS CLOSE ───────────────────────────────── document.querySelectorAll('[data-close]').forEach(function(btn){btn.addEventListener('click',function(){closeModal(btn.getAttribute('data-close'));});}); document.querySelectorAll('.modal-overlay').forEach(function(o){o.addEventListener('click',function(e){if(e.target===o)o.classList.remove('open');});}); // ── ACCOUNTS ───────────────────────────────────── function renderAccounts(){ var c=document.getElementById('accounts-content'); var nonSav=S.accounts.filter(function(a){return!isSavings(a);}); var total=nonSav.reduce(function(s,a){return s+(a.currency==='USD'?a.balance*S.tc.blue:a.balance);},0); var btnHtml='
'+ ''+ ''+ '
'; if(!S.accounts.length){ c.innerHTML='
🏦

Sin cuentas

CreΓ‘ tu primera cuenta para empezar a registrar movimientos.

'+btnHtml; }else{ var totalHtml='

Total disponible (sin ahorros)

$'+fmt(total)+'
En ARS equivalente al TC blue
'; var cards=nonSav.map(function(a){return accCardHTML(a,true);}).join(''); c.innerHTML=btnHtml+totalHtml+cards; } document.getElementById('btn-open-new-account').addEventListener('click',function(){openNewAccount();}); document.getElementById('btn-open-transfer').addEventListener('click',function(){openTransfer();}); c.querySelectorAll('[data-edit-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openEditAccount(btn.getAttribute('data-edit-acc'));});}); c.querySelectorAll('[data-del-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteAccount(btn.getAttribute('data-del-acc'));});}); c.querySelectorAll('[data-acc-history]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();showAccHistory(btn.getAttribute('data-acc-history'));});}); } function accCardHTML(acc,showActions){ var cfg=ACC_TYPES[acc.type]||ACC_TYPES.bank; var icon=acc.icon||cfg.icon; var balStr=acc.currency==='USD'?'U$'+fmt(acc.balance):'$'+fmt(acc.balance); var subStr=acc.currency==='USD'?'β‰ˆ $'+fmt(acc.balance*S.tc.blue)+' ARS':''; var actions=showActions?'
'+ ''+ ''+ ''+ '
':''; return''; } function openNewAccount(){ S.editingAccId=null; document.getElementById('acc-modal-title').textContent='Nueva cuenta'; document.getElementById('acc-name').value='';document.getElementById('acc-balance').value=''; document.getElementById('acc-icon').value='';document.getElementById('acc-color').value='#6c63ff'; document.getElementById('acc-type').value='bank';document.getElementById('acc-currency').value='ARS'; document.getElementById('btn-save-account').textContent='Crear cuenta'; showModal('modal-new-account'); } function openEditAccount(accId){ var acc=S.accounts.find(function(a){return a.id===accId;});if(!acc)return; S.editingAccId=accId; document.getElementById('acc-modal-title').textContent='Editar cuenta'; document.getElementById('acc-name').value=acc.name; document.getElementById('acc-balance').value=acc.balance; document.getElementById('acc-icon').value=acc.icon||''; document.getElementById('acc-color').value=acc.color||'#6c63ff'; document.getElementById('acc-type').value=acc.type; document.getElementById('acc-currency').value=acc.currency; document.getElementById('btn-save-account').textContent='Guardar cambios'; showModal('modal-new-account'); } document.getElementById('btn-save-account').addEventListener('click',function(){ var name=document.getElementById('acc-name').value.trim(); var type=document.getElementById('acc-type').value; var balance=parseFloat(document.getElementById('acc-balance').value)||0; var currency=document.getElementById('acc-currency').value; var icon=document.getElementById('acc-icon').value.trim()||(ACC_TYPES[type]||ACC_TYPES.bank).icon; var color=document.getElementById('acc-color').value; if(!name){toast('PonΓ© un nombre a la cuenta','error');return;} if(S.editingAccId){ var acc=S.accounts.find(function(a){return a.id===S.editingAccId;}); if(acc){acc.name=name;acc.type=type;acc.balance=balance;acc.currency=currency;acc.icon=icon;acc.color=color;} }else{ var newAcc={id:genId(),uid:S.user.uid,name:name,type:type,balance:balance,currency:currency,icon:icon,color:color,created_at:new Date().toISOString()}; S.accounts.push(newAcc); api('addAccount',{account:newAcc}).catch(function(){}); } saveAccountsLocal();closeModal('modal-new-account'); if(S.activeTab==='accounts')renderAccounts(); if(S.activeTab==='savings')renderSavings(); populateAccountSelect(); toast((S.editingAccId?'Cuenta actualizada':'Cuenta creada')+' βœ“','success'); if(S.editingAccId)api('updateAccount',{account:S.accounts.find(function(a){return a.id===S.editingAccId;})}).catch(function(){}); }); function deleteAccount(accId){ if(!confirm('ΒΏEliminar esta cuenta? Las transacciones asociadas no se borrarΓ‘n.'))return; S.accounts=S.accounts.filter(function(a){return a.id!==accId;}); saveAccountsLocal(); if(S.activeTab==='accounts')renderAccounts(); if(S.activeTab==='savings')renderSavings(); populateAccountSelect(); toast('Cuenta eliminada','success'); api('deleteAccount',{id:accId}).catch(function(){}); } function showAccHistory(accId){ var acc=S.accounts.find(function(a){return a.id===accId;});if(!acc)return; var txs=S.transactions.filter(function(t){return t.account_id===accId;}); var overlay=document.createElement('div');overlay.className='modal-overlay open';overlay.style.zIndex='600'; var txHtml=txs.length?'
'+txs.map(txHTML).join('')+'
':'
πŸ“­

Sin movimientos

'; overlay.innerHTML=''; overlay.addEventListener('click',function(e){if(e.target===overlay)overlay.remove();}); document.body.appendChild(overlay); document.getElementById('close-acc-history').addEventListener('click',function(){overlay.remove();}); } // Transfer function openTransfer(){ populateTransferSelects(); document.getElementById('transfer-amount').value=''; document.getElementById('transfer-date').value=todayStr(); showModal('modal-transfer'); } function populateTransferSelects(){ var opts=S.accounts.map(function(a){return'';}).join(''); document.getElementById('transfer-from').innerHTML=opts; document.getElementById('transfer-to').innerHTML=opts; } document.getElementById('btn-save-transfer').addEventListener('click',function(){ var fromId=document.getElementById('transfer-from').value; var toId=document.getElementById('transfer-to').value; var amount=parseFloat(document.getElementById('transfer-amount').value); var currency=document.getElementById('transfer-currency').value; var date=document.getElementById('transfer-date').value||todayStr(); if(fromId===toId){toast('ElegΓ­ cuentas distintas','error');return;} if(!amount||amount<=0){toast('IngresΓ‘ un monto vΓ‘lido','error');return;} var from=S.accounts.find(function(a){return a.id===fromId;}); var to=S.accounts.find(function(a){return a.id===toId;}); // Adjust balances var debit=currency===from.currency?amount:(currency==='USD'?amount*S.tc.blue:amount/S.tc.blue); var credit=currency===to.currency?amount:(currency==='USD'?amount*S.tc.blue:amount/S.tc.blue); from.balance=(from.balance||0)-debit; to.balance=(to.balance||0)+credit; saveAccountsLocal(); // Record as two transactions var txOut={id:genId(),uid:S.user.uid,date:date,type:'expense',amount:amount,currency:currency,tc:S.tc.blue,category_id:'c9',account_id:fromId,tags:[],description:'Transferencia β†’ '+to.name,note:'',shared:false,payer:'me',split_me:100,created_at:new Date().toISOString()}; var txIn={id:genId(),uid:S.user.uid,date:date,type:'income',amount:amount,currency:currency,tc:S.tc.blue,category_id:'i4',account_id:toId,tags:[],description:'Transferencia ← '+from.name,note:'',shared:false,payer:'me',split_me:100,created_at:new Date().toISOString()}; S.transactions.unshift(txIn);S.transactions.unshift(txOut); closeModal('modal-transfer');renderAccounts();renderDashboard(); toast('Transferencia realizada βœ“','success'); api('addTransaction',{transaction:txOut}).catch(function(){}); api('addTransaction',{transaction:txIn}).catch(function(){}); }); // ── SAVINGS ────────────────────────────────────── function renderSavings(){ var c=document.getElementById('savings-content'); var savs=S.accounts.filter(function(a){return isSavings(a);}); var tUSD=savs.reduce(function(s,a){return s+(a.currency==='USD'?a.balance:0);},0); var tARS=savs.reduce(function(s,a){return s+(a.currency==='ARS'?a.balance:a.balance*S.tc.blue);},0); var btnHtml=''; if(!savs.length){ c.innerHTML='
🐷

Sin ahorros

CreΓ‘ una cuenta de ahorro para llevar el registro de lo que tenΓ©s guardado.

'+btnHtml; }else{ var summary='

Total ahorrado

$'+fmt(tARS)+'
U$'+fmt(tUSD)+' en dΓ³lares Β· TC blue $'+fmt(S.tc.blue)+'
'; var cards=savs.map(function(a){return accCardHTML(a,true);}).join(''); c.innerHTML=btnHtml+summary+cards; } document.getElementById('btn-open-new-savings').addEventListener('click',function(){ openNewAccount();document.getElementById('acc-type').value='savings'; }); c.querySelectorAll('[data-edit-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openEditAccount(btn.getAttribute('data-edit-acc'));});}); c.querySelectorAll('[data-del-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteAccount(btn.getAttribute('data-del-acc'));});}); c.querySelectorAll('[data-acc-history]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();showAccHistory(btn.getAttribute('data-acc-history'));});}); } // ── POPULATE ACCOUNT SELECT ────────────────────── function populateAccountSelect(){ var sel=document.getElementById('tx-account-id'); var nonSav=S.accounts.filter(function(a){return!isSavings(a);}); sel.innerHTML=nonSav.length ?nonSav.map(function(a){return'';}).join('') :''; } // ── NEW / EDIT TX ──────────────────────────────── document.getElementById('fab').addEventListener('click',function(){openTxModal(null);}); function openTxModal(tx){ S.editingTxId=tx?tx.id:null; document.getElementById('tx-modal-title').textContent=tx?'Editar transacciΓ³n':'Nueva transacciΓ³n'; document.getElementById('btn-save-tx').textContent=tx?'Guardar cambios':'Guardar'; document.getElementById('tx-desc').value=tx?tx.description:''; document.getElementById('tx-amount').value=tx?tx.amount:''; document.getElementById('tx-note').value=tx?tx.note:''; document.getElementById('tx-date').value=tx?tx.date:todayStr(); document.getElementById('tx-currency').value=tx?tx.currency:'ARS'; document.getElementById('tc-field').style.display=(tx&&tx.currency==='USD')||false?'':'none'; if(tx&&tx.currency==='USD')document.getElementById('tx-tc').value=tx.tc; document.getElementById('tx-shared').checked=tx?tx.shared:false; document.getElementById('split-section').style.display=(tx&&tx.shared)?'':'none'; document.getElementById('split-me').value=tx?tx.split_me:50; document.getElementById('split-partner').value=tx?(100-tx.split_me):50; S.txType=tx?tx.type:'expense'; S.selectedCat=tx?tx.category_id:null; S.currentTags=tx?(tx.tags||[]).slice():[]; document.querySelectorAll('.toggle-opt[data-type]').forEach(function(b){b.classList.toggle('active',b.getAttribute('data-type')===S.txType);}); populateAccountSelect(); if(tx&&tx.account_id){var sel=document.getElementById('tx-account-id');for(var i=0;iΓ—'; pill.querySelector('button').addEventListener('click',function(){S.currentTags=S.currentTags.filter(function(t){return t!==tag;});renderTagsWrap();}); wrap.insertBefore(pill,inp); }); } function renderCatPicker(){ var grid=document.getElementById('cat-picker');grid.innerHTML=''; var filtered=S.categories.filter(function(cat){return cat.type===S.txType;}); if(!filtered.length){grid.innerHTML='

Sin categorΓ­as para este tipo

';return;} filtered.forEach(function(cat){ var d=document.createElement('div');d.className='cat-opt'+(S.selectedCat===cat.id?' selected':''); d.innerHTML=''+cat.icon+''+cat.name+''; d.addEventListener('click',function(){S.selectedCat=cat.id;renderCatPicker();});grid.appendChild(d); }); } document.getElementById('btn-save-tx').addEventListener('click',function(){ var desc=document.getElementById('tx-desc').value.trim(); var amount=parseFloat(document.getElementById('tx-amount').value); var currency=document.getElementById('tx-currency').value; var date=document.getElementById('tx-date').value||todayStr(); var tc=currency==='USD'?(parseFloat(document.getElementById('tx-tc').value)||S.tc.blue):1; var note=document.getElementById('tx-note').value.trim(); var shared=document.getElementById('tx-shared').checked; var payer=document.getElementById('tx-payer').value; var splitMe=parseInt(document.getElementById('split-me').value)||50; var accountId=document.getElementById('tx-account-id').value; if(!desc){toast('AgregΓ‘ una descripciΓ³n','error');return;} if(!amount||amount<=0){toast('IngresΓ‘ un monto vΓ‘lido','error');return;} if(!accountId){toast('SeleccionΓ‘ una cuenta','error');return;} var defaultCatId=S.txType==='income'?'i4':'c9'; if(S.editingTxId){ // Editing: reverse old effect on account, apply new var oldTx=S.transactions.find(function(t){return t.id===S.editingTxId;}); if(oldTx){ var oldAcc=S.accounts.find(function(a){return a.id===oldTx.account_id;}); if(oldAcc){var rev=oldTx.type==='income'?-oldTx.amount:oldTx.amount;if(oldTx.currency==='USD'&&oldAcc.currency==='ARS')rev*=oldTx.tc;else if(oldTx.currency==='ARS'&&oldAcc.currency==='USD')rev/=oldTx.tc;oldAcc.balance=(oldAcc.balance||0)+rev;} oldTx.description=desc;oldTx.amount=amount;oldTx.currency=currency;oldTx.tc=tc; oldTx.date=date;oldTx.note=note;oldTx.shared=shared;oldTx.payer=shared?payer:'me'; oldTx.split_me=shared?splitMe:100;oldTx.category_id=S.selectedCat||defaultCatId; oldTx.account_id=accountId;oldTx.tags=S.currentTags.slice(); } var newAcc=S.accounts.find(function(a){return a.id===accountId;}); if(newAcc){var delta=S.txType==='income'?amount:-amount;if(currency==='USD'&&newAcc.currency==='ARS')delta*=tc;else if(currency==='ARS'&&newAcc.currency==='USD')delta/=tc;newAcc.balance=(newAcc.balance||0)+delta;} saveAccountsLocal();closeModal('modal-new-tx');renderDashboard(); if(S.activeTab==='transactions')renderTransactions(); if(S.activeTab==='accounts')renderAccounts(); toast('TransacciΓ³n actualizada βœ“','success'); api('updateTransaction',{transaction:oldTx}).catch(function(){}); }else{ var acc=S.accounts.find(function(a){return a.id===accountId;}); if(acc){var d2=S.txType==='income'?amount:-amount;if(currency==='USD'&&acc.currency==='ARS')d2*=tc;else if(currency==='ARS'&&acc.currency==='USD')d2/=tc;acc.balance=(acc.balance||0)+d2;saveAccountsLocal();api('updateAccountBalance',{id:acc.id,balance:acc.balance}).catch(function(){});} var newTx={id:genId(),uid:S.user.uid,date:date,type:S.txType,amount:amount,currency:currency,tc:tc,category_id:S.selectedCat||defaultCatId,account_id:accountId,tags:S.currentTags.slice(),description:desc,note:note,shared:shared,payer:shared?payer:'me',split_me:shared?splitMe:100,created_at:new Date().toISOString()}; S.transactions.unshift(newTx);closeModal('modal-new-tx');renderDashboard(); if(S.activeTab==='transactions')renderTransactions(); if(S.activeTab==='accounts')renderAccounts(); toast('TransacciΓ³n guardada βœ“','success');api('addTransaction',{transaction:newTx}).catch(function(){}); } }); // ── TRANSACTIONS ────────────────────────────────── document.querySelectorAll('.filter-chip').forEach(function(btn){ btn.addEventListener('click',function(){ S.txFilter=btn.getAttribute('data-filter'); document.querySelectorAll('.filter-chip').forEach(function(b){b.classList.remove('active');});btn.classList.add('active');renderTransactions(); }); }); function renderTransactions(){ var container=document.getElementById('tx-list-container'); var txs=S.transactions.slice(); if(S.txFilter==='income')txs=txs.filter(function(t){return t.type==='income';}); if(S.txFilter==='expense')txs=txs.filter(function(t){return t.type==='expense';}); if(S.txFilter==='shared')txs=txs.filter(function(t){return t.shared;}); if(S.txFilter==='ARS')txs=txs.filter(function(t){return t.currency==='ARS';}); if(S.txFilter==='USD')txs=txs.filter(function(t){return t.currency==='USD';}); if(!txs.length){container.innerHTML='
πŸ“­

Sin movimientos

TocΓ‘ el + para agregar tu primer transacciΓ³n

';return;} container.innerHTML='
'+txs.map(function(tx){return txHTML(tx,true);}).join('')+'
'; container.querySelectorAll('[data-edit-tx]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();var tx=S.transactions.find(function(t){return t.id===btn.getAttribute('data-edit-tx');});if(tx)openTxModal(tx);});}); container.querySelectorAll('[data-del-tx]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteTx(btn.getAttribute('data-del-tx'));});}); } function deleteTx(txId){ if(!confirm('ΒΏEliminar esta transacciΓ³n?'))return; var tx=S.transactions.find(function(t){return t.id===txId;}); if(tx){ var acc=S.accounts.find(function(a){return a.id===tx.account_id;}); if(acc){var rev=tx.type==='income'?-tx.amount:tx.amount;if(tx.currency==='USD'&&acc.currency==='ARS')rev*=tx.tc;else if(tx.currency==='ARS'&&acc.currency==='USD')rev/=tx.tc;acc.balance=(acc.balance||0)+rev;saveAccountsLocal();} } S.transactions=S.transactions.filter(function(t){return t.id!==txId;}); renderTransactions();renderDashboard(); toast('TransacciΓ³n eliminada','success'); api('deleteTransaction',{id:txId}).catch(function(){}); } function txHTML(tx,showActions){ var cat=S.categories.find(function(c){return c.id===tx.category_id;})||{icon:'πŸ“¦',color:'#6b7280'}; var acc=S.accounts.find(function(a){return a.id===tx.account_id;}); var sign=tx.type==='income'?'+':'-',cls=tx.type==='income'?'income':'expense'; var tags=(tx.tags||[]).map(function(t){return'#'+t+'';}).join(''); var badge=tx.shared?'πŸ’‘':''; var accBadge=acc?'':''; var amt=tx.currency==='USD'?'U$'+fmt(tx.amount):'$'+fmt(tx.amount); var equiv=tx.currency==='USD'?'β‰ˆ $'+fmt(tx.amount*tx.tc)+' ARS':''; var actions=showActions?'
':''; return'
'+ '
'+cat.icon+'
'+ '
'+tx.description+'
'+ '
'+fmtDate(tx.date)+' '+accBadge+' '+badge+' '+tags+'
'+ '
'+ '
'+sign+amt+'
'+(equiv?'
'+equiv+'
':'')+'
'+ actions+'
'; } // ── DASHBOARD ───────────────────────────────────── function renderDashboard(){ var now=new Date(),month=now.getMonth(),year=now.getFullYear(); var savIds=S.accounts.filter(isSavings).map(function(a){return a.id;}); var txs=S.transactions.filter(function(t){var d=new Date(t.date);return d.getMonth()===month&&d.getFullYear()===year&&savIds.indexOf(t.account_id)===-1;}); var iARS=0,eARS=0,iUSD=0,eUSD=0; txs.forEach(function(t){var isIn=t.type==='income';if(t.currency==='ARS'){isIn?(iARS+=t.amount):(eARS+=t.amount);}else{isIn?(iUSD+=t.amount):(eUSD+=t.amount);}}); document.getElementById('balance-month-label').textContent=monthName(month)+' '+year; document.getElementById('balance-main-val').textContent='$'+fmt(iARS-eARS)+' ARS'; document.getElementById('dash-income').textContent='+$'+fmt(iARS); document.getElementById('dash-expense').textContent='-$'+fmt(eARS); var tARS=iARS-eARS,tUSD=iUSD-eUSD; var arsEl=document.getElementById('dash-ars');arsEl.textContent=(tARS>=0?'+':'')+' $'+fmt(tARS);arsEl.className='si-val '+(tARS>=0?'green':'red'); var usdEl=document.getElementById('dash-usd');usdEl.textContent=(tUSD>=0?'+':'')+'U$'+fmt(tUSD);usdEl.className='si-val '+(tUSD>=0?'green':'red'); document.getElementById('dash-count').textContent=txs.length; var cats={};txs.forEach(function(t){cats[t.category_id]=true;});document.getElementById('dash-cats').textContent=Object.keys(cats).length; renderPie(txs.filter(function(t){return t.type==='expense';})); var recent=S.transactions.slice(0,5); document.getElementById('dash-recent-list').innerHTML=recent.length?'
'+recent.map(function(t){return txHTML(t,false);}).join('')+'
':'
πŸ’Έ

Sin movimientos

EmpezΓ‘ cargando tu primer gasto

'; } function renderPie(expenses){ var canvas=document.getElementById('pieChart'),legend=document.getElementById('pie-legend'),ctx=canvas.getContext('2d'); var W=canvas.offsetWidth||300,H=220;canvas.width=W;canvas.height=H;ctx.clearRect(0,0,W,H); var catMap={};expenses.forEach(function(t){var val=t.currency==='USD'?t.amount*t.tc:t.amount;catMap[t.category_id]=(catMap[t.category_id]||0)+val;}); var entries=Object.keys(catMap).map(function(k){return[k,catMap[k]];}).sort(function(a,b){return b[1]-a[1];}); var total=entries.reduce(function(s,e){return s+e[1];},0)||1;legend.innerHTML=''; if(!entries.length){ctx.fillStyle='#ffffff22';ctx.beginPath();ctx.arc(W/2,H/2,80,0,Math.PI*2);ctx.fill();ctx.fillStyle='#9ca3af';ctx.textAlign='center';ctx.font='14px Inter';ctx.fillText('Sin datos',W/2,H/2+5);return;} var cx=W/2,cy=H/2,r=Math.min(cx,cy)-20,startAngle=-Math.PI/2; entries.forEach(function(entry){ var cat=S.categories.find(function(c){return c.id===entry[0];})||{color:'#6b7280',name:'Otros',icon:'πŸ“¦'}; var slice=(entry[1]/total)*Math.PI*2; ctx.beginPath();ctx.moveTo(cx,cy);ctx.arc(cx,cy,r,startAngle,startAngle+slice);ctx.closePath();ctx.fillStyle=cat.color;ctx.fill();ctx.strokeStyle='#1a1d27';ctx.lineWidth=2;ctx.stroke(); legend.innerHTML+='
'+cat.icon+' '+cat.name+' '+Math.round(entry[1]/total*100)+'%
'; startAngle+=slice; }); ctx.beginPath();ctx.arc(cx,cy,r*0.5,0,Math.PI*2);ctx.fillStyle='#1a1d27';ctx.fill(); ctx.fillStyle='#fff';ctx.textAlign='center';ctx.font='bold 14px Inter';ctx.fillText('$'+fmt(total),cx,cy+5); } // ── COUPLE ──────────────────────────────────────── document.getElementById('btn-send-partner').addEventListener('click',function(){ var email=document.getElementById('partner-email').value.trim();if(!email){toast('IngresΓ‘ un email','error');return;} api('linkPartner',{uid:S.user.uid,partner_email:email}).then(function(d){ S.user.partner_uid=d.partner_uid;S.user.partner_name=d.partner_name;localStorage.setItem('session',JSON.stringify(S.user)); toast('Β‘Pareja vinculada! βœ“','success');closeModal('modal-link-partner');renderCouple(); }).catch(function(e){toast(e.message,'error');}); }); function renderCouple(){ var c=document.getElementById('couple-content'); if(!S.user.partner_uid){ c.innerHTML='
πŸ‘€

'+S.user.name+'

Sin pareja vinculada

'+ '
πŸ’‘

Modo pareja activado

VinculΓ‘ tu cuenta con la de tu pareja para compartir gastos.

'+ ''; document.getElementById('btn-open-link-partner').addEventListener('click',function(){showModal('modal-link-partner');});return; } var shared=S.transactions.filter(function(t){return t.shared;}); var iOwe=0,theyOwe=0; shared.forEach(function(t){var val=t.currency==='USD'?t.amount*t.tc:t.amount;var my=val*(t.split_me||50)/100;if(t.payer==='me')theyOwe+=(val-my);else iOwe+=my;}); var net=theyOwe-iOwe; c.innerHTML='
πŸ‘€
πŸ’ž
πŸ‘€
'+ '

'+S.user.name+' & '+(S.user.partner_name||'Pareja')+'

Vinculados

'+ '

Balance compartido (ARS)

'+ (net>=0?'+':'')+' $'+fmt(Math.abs(net))+'
'+(net>0?'Te deben':'Le debΓ©s a '+(S.user.partner_name||'tu pareja'))+'
'+ '
Gastos compartidos
'+ '
'+(shared.length?shared.map(function(t){return txHTML(t,false);}).join(''):'
🀝

Sin gastos compartidos

')+'
'; } // ── VACATIONS ───────────────────────────────────── document.getElementById('btn-save-vac').addEventListener('click',function(){ var name=document.getElementById('vac-name').value.trim();if(!name){toast('PonΓ© un nombre al viaje','error');return;} var vac={id:genId(),uid:S.user.uid,name:name,start_date:document.getElementById('vac-start').value,end_date:document.getElementById('vac-end').value,budget:parseFloat(document.getElementById('vac-budget').value)||0,currency:document.getElementById('vac-currency').value,created_at:new Date().toISOString()}; S.vacations.unshift(vac);closeModal('modal-new-vac');renderVacations();toast('Viaje creado βœ“','success');api('addVacation',{vacation:vac}).catch(function(){}); }); document.getElementById('btn-save-vexp').addEventListener('click',function(){ var desc=document.getElementById('vexp-desc').value.trim(),amount=parseFloat(document.getElementById('vexp-amount').value); if(!desc||!amount){toast('CompletΓ‘ los campos','error');return;} var item={id:genId(),vacation_id:S.activeVacId,type:'expense',description:desc,amount:amount,currency:document.getElementById('vexp-currency').value,tc:parseFloat(document.getElementById('vexp-tc').value)||S.tc.blue,shared:document.getElementById('vexp-shared').checked,payer:document.getElementById('vexp-payer').value,split_me:parseInt(document.getElementById('vexp-split-me').value)||50,created_at:new Date().toISOString()}; if(!S.vacItems[S.activeVacId])S.vacItems[S.activeVacId]=[];S.vacItems[S.activeVacId].unshift(item); closeModal('modal-vac-expense');renderVacations();toast('Gasto guardado βœ“','success');api('addVacationItem',{item:item}).catch(function(){}); }); function calcVacSpent(vacId){return(S.vacItems[vacId]||[]).filter(function(i){return i.type==='expense';}).reduce(function(s,i){return s+(i.currency==='USD'?i.amount*(i.tc||S.tc.blue):i.amount);},0);} function renderVacations(){ var c=document.getElementById('vacation-content'); var btnHtml=''; if(!S.vacations.length){c.innerHTML='
✈️

Sin viajes

CreΓ‘ tu primer viaje para empezar.

'+btnHtml;} else{ var cards=S.vacations.map(function(v){var spent=calcVacSpent(v.id),pct=Math.min(100,Math.round(spent/v.budget*100))||0,curr=v.currency==='USD'?'U$':'$'; return'
✈️ '+v.name+'
πŸ“… '+fmtDate(v.start_date)+' β†’ '+fmtDate(v.end_date)+'
'+ '
'+ '
'+curr+fmt(spent)+' gastado'+curr+fmt(v.budget-spent)+' restante
'+ '
'; }).join('');c.innerHTML=btnHtml+cards; } document.getElementById('btn-open-new-vac').addEventListener('click',function(){showModal('modal-new-vac');}); c.querySelectorAll('[data-vac-expense]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();S.activeVacId=btn.getAttribute('data-vac-expense');showModal('modal-vac-expense');});}); c.querySelectorAll('[data-vac-detail]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openVacationDetail(btn.getAttribute('data-vac-detail'));});}); } function openVacationDetail(vacId){ var vac=S.vacations.find(function(v){return v.id===vacId;});if(!vac)return; var items=S.vacItems[vacId]||[],expenses=items.filter(function(i){return i.type==='expense';}),checks=items.filter(function(i){return i.type==='checklist';}); var spent=calcVacSpent(vacId),pct=Math.min(100,Math.round(spent/vac.budget*100))||0,curr=vac.currency==='USD'?'U$':'$'; var overlay=document.createElement('div');overlay.className='modal-overlay open';overlay.style.zIndex='600'; var checkHtml=checks.length?checks.map(function(i){return'
βœ“
'+i.description+'
';}).join(''):'

Sin Γ­tems

'; var expHtml=expenses.length?expenses.map(function(i){return'
✈️
'+i.description+'
'+(i.shared?'πŸ’‘':'')+'
-'+(i.currency==='USD'?'U$':'$')+fmt(i.amount)+'
';}).join(''):'

Sin gastos aΓΊn

'; overlay.innerHTML=''; overlay.addEventListener('click',function(e){if(e.target===overlay)overlay.remove();});document.body.appendChild(overlay); document.getElementById('close-vac-detail').addEventListener('click',function(){overlay.remove();}); document.getElementById('checklist-container').addEventListener('click',function(e){ var item=e.target.closest('.checklist-item');if(!item)return; var box=item.querySelector('.check-box'),txt=item.querySelector('.check-text'),iid=item.getAttribute('data-item-id'); var vi=(S.vacItems[vacId]||[]).find(function(x){return x.id===iid;});if(vi)vi.done=!vi.done;box.classList.toggle('checked');txt.classList.toggle('done'); }); document.getElementById('btn-add-check').addEventListener('click',function(){ var inp=document.getElementById('new-check-txt'),desc=inp.value.trim();if(!desc)return; var ni={id:genId(),vacation_id:vacId,type:'checklist',description:desc,done:false}; if(!S.vacItems[vacId])S.vacItems[vacId]=[];S.vacItems[vacId].push(ni);inp.value=''; var container=document.getElementById('checklist-container');var p=container.querySelector('p');if(p)p.remove(); var div=document.createElement('div');div.className='checklist-item';div.setAttribute('data-item-id',ni.id);div.innerHTML='
βœ“
'+desc+'';container.appendChild(div); api('addVacationItem',{item:ni}).catch(function(){}); }); } window.addEventListener('resize',function(){if(S.activeTab==='dashboard')renderDashboard();}); +d.compra+' Β· venta document.getElementById('btn-save-tc').addEventListener('click',function(){ S.tc.oficial=parseFloat(document.getElementById('tc-oficial').value)||S.tc.oficial; S.tc.blue=parseFloat(document.getElementById('tc-blue').value)||S.tc.blue; localStorage.setItem('tc',JSON.stringify(S.tc));toast('Tipo de cambio guardado βœ“','success');closeModal('modal-tc'); if(S.activeTab==='savings')renderSavings(); }); // ── MODALS CLOSE ───────────────────────────────── document.querySelectorAll('[data-close]').forEach(function(btn){btn.addEventListener('click',function(){closeModal(btn.getAttribute('data-close'));});}); document.querySelectorAll('.modal-overlay').forEach(function(o){o.addEventListener('click',function(e){if(e.target===o)o.classList.remove('open');});}); // ── ACCOUNTS ───────────────────────────────────── function renderAccounts(){ var c=document.getElementById('accounts-content'); var nonSav=S.accounts.filter(function(a){return!isSavings(a);}); var total=nonSav.reduce(function(s,a){return s+(a.currency==='USD'?a.balance*S.tc.blue:a.balance);},0); var btnHtml='
'+ ''+ ''+ '
'; if(!S.accounts.length){ c.innerHTML='
🏦

Sin cuentas

CreΓ‘ tu primera cuenta para empezar a registrar movimientos.

'+btnHtml; }else{ var totalHtml='

Total disponible (sin ahorros)

$'+fmt(total)+'
En ARS equivalente al TC blue
'; var cards=nonSav.map(function(a){return accCardHTML(a,true);}).join(''); c.innerHTML=btnHtml+totalHtml+cards; } document.getElementById('btn-open-new-account').addEventListener('click',function(){openNewAccount();}); document.getElementById('btn-open-transfer').addEventListener('click',function(){openTransfer();}); c.querySelectorAll('[data-edit-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openEditAccount(btn.getAttribute('data-edit-acc'));});}); c.querySelectorAll('[data-del-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteAccount(btn.getAttribute('data-del-acc'));});}); c.querySelectorAll('[data-acc-history]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();showAccHistory(btn.getAttribute('data-acc-history'));});}); } function accCardHTML(acc,showActions){ var cfg=ACC_TYPES[acc.type]||ACC_TYPES.bank; var icon=acc.icon||cfg.icon; var balStr=acc.currency==='USD'?'U$'+fmt(acc.balance):'$'+fmt(acc.balance); var subStr=acc.currency==='USD'?'β‰ˆ $'+fmt(acc.balance*S.tc.blue)+' ARS':''; var actions=showActions?'
'+ ''+ ''+ ''+ '
':''; return''; } function openNewAccount(){ S.editingAccId=null; document.getElementById('acc-modal-title').textContent='Nueva cuenta'; document.getElementById('acc-name').value='';document.getElementById('acc-balance').value=''; document.getElementById('acc-icon').value='';document.getElementById('acc-color').value='#6c63ff'; document.getElementById('acc-type').value='bank';document.getElementById('acc-currency').value='ARS'; document.getElementById('btn-save-account').textContent='Crear cuenta'; showModal('modal-new-account'); } function openEditAccount(accId){ var acc=S.accounts.find(function(a){return a.id===accId;});if(!acc)return; S.editingAccId=accId; document.getElementById('acc-modal-title').textContent='Editar cuenta'; document.getElementById('acc-name').value=acc.name; document.getElementById('acc-balance').value=acc.balance; document.getElementById('acc-icon').value=acc.icon||''; document.getElementById('acc-color').value=acc.color||'#6c63ff'; document.getElementById('acc-type').value=acc.type; document.getElementById('acc-currency').value=acc.currency; document.getElementById('btn-save-account').textContent='Guardar cambios'; showModal('modal-new-account'); } document.getElementById('btn-save-account').addEventListener('click',function(){ var name=document.getElementById('acc-name').value.trim(); var type=document.getElementById('acc-type').value; var balance=parseFloat(document.getElementById('acc-balance').value)||0; var currency=document.getElementById('acc-currency').value; var icon=document.getElementById('acc-icon').value.trim()||(ACC_TYPES[type]||ACC_TYPES.bank).icon; var color=document.getElementById('acc-color').value; if(!name){toast('PonΓ© un nombre a la cuenta','error');return;} if(S.editingAccId){ var acc=S.accounts.find(function(a){return a.id===S.editingAccId;}); if(acc){acc.name=name;acc.type=type;acc.balance=balance;acc.currency=currency;acc.icon=icon;acc.color=color;} }else{ var newAcc={id:genId(),uid:S.user.uid,name:name,type:type,balance:balance,currency:currency,icon:icon,color:color,created_at:new Date().toISOString()}; S.accounts.push(newAcc); api('addAccount',{account:newAcc}).catch(function(){}); } saveAccountsLocal();closeModal('modal-new-account'); if(S.activeTab==='accounts')renderAccounts(); if(S.activeTab==='savings')renderSavings(); populateAccountSelect(); toast((S.editingAccId?'Cuenta actualizada':'Cuenta creada')+' βœ“','success'); if(S.editingAccId)api('updateAccount',{account:S.accounts.find(function(a){return a.id===S.editingAccId;})}).catch(function(){}); }); function deleteAccount(accId){ if(!confirm('ΒΏEliminar esta cuenta? Las transacciones asociadas no se borrarΓ‘n.'))return; S.accounts=S.accounts.filter(function(a){return a.id!==accId;}); saveAccountsLocal(); if(S.activeTab==='accounts')renderAccounts(); if(S.activeTab==='savings')renderSavings(); populateAccountSelect(); toast('Cuenta eliminada','success'); api('deleteAccount',{id:accId}).catch(function(){}); } function showAccHistory(accId){ var acc=S.accounts.find(function(a){return a.id===accId;});if(!acc)return; var txs=S.transactions.filter(function(t){return t.account_id===accId;}); var overlay=document.createElement('div');overlay.className='modal-overlay open';overlay.style.zIndex='600'; var txHtml=txs.length?'
'+txs.map(txHTML).join('')+'
':'
πŸ“­

Sin movimientos

'; overlay.innerHTML=''; overlay.addEventListener('click',function(e){if(e.target===overlay)overlay.remove();}); document.body.appendChild(overlay); document.getElementById('close-acc-history').addEventListener('click',function(){overlay.remove();}); } // Transfer function openTransfer(){ populateTransferSelects(); document.getElementById('transfer-amount').value=''; document.getElementById('transfer-date').value=todayStr(); showModal('modal-transfer'); } function populateTransferSelects(){ var opts=S.accounts.map(function(a){return'';}).join(''); document.getElementById('transfer-from').innerHTML=opts; document.getElementById('transfer-to').innerHTML=opts; } document.getElementById('btn-save-transfer').addEventListener('click',function(){ var fromId=document.getElementById('transfer-from').value; var toId=document.getElementById('transfer-to').value; var amount=parseFloat(document.getElementById('transfer-amount').value); var currency=document.getElementById('transfer-currency').value; var date=document.getElementById('transfer-date').value||todayStr(); if(fromId===toId){toast('ElegΓ­ cuentas distintas','error');return;} if(!amount||amount<=0){toast('IngresΓ‘ un monto vΓ‘lido','error');return;} var from=S.accounts.find(function(a){return a.id===fromId;}); var to=S.accounts.find(function(a){return a.id===toId;}); // Adjust balances var debit=currency===from.currency?amount:(currency==='USD'?amount*S.tc.blue:amount/S.tc.blue); var credit=currency===to.currency?amount:(currency==='USD'?amount*S.tc.blue:amount/S.tc.blue); from.balance=(from.balance||0)-debit; to.balance=(to.balance||0)+credit; saveAccountsLocal(); // Record as two transactions var txOut={id:genId(),uid:S.user.uid,date:date,type:'expense',amount:amount,currency:currency,tc:S.tc.blue,category_id:'c9',account_id:fromId,tags:[],description:'Transferencia β†’ '+to.name,note:'',shared:false,payer:'me',split_me:100,created_at:new Date().toISOString()}; var txIn={id:genId(),uid:S.user.uid,date:date,type:'income',amount:amount,currency:currency,tc:S.tc.blue,category_id:'i4',account_id:toId,tags:[],description:'Transferencia ← '+from.name,note:'',shared:false,payer:'me',split_me:100,created_at:new Date().toISOString()}; S.transactions.unshift(txIn);S.transactions.unshift(txOut); closeModal('modal-transfer');renderAccounts();renderDashboard(); toast('Transferencia realizada βœ“','success'); api('addTransaction',{transaction:txOut}).catch(function(){}); api('addTransaction',{transaction:txIn}).catch(function(){}); }); // ── SAVINGS ────────────────────────────────────── function renderSavings(){ var c=document.getElementById('savings-content'); var savs=S.accounts.filter(function(a){return isSavings(a);}); var tUSD=savs.reduce(function(s,a){return s+(a.currency==='USD'?a.balance:0);},0); var tARS=savs.reduce(function(s,a){return s+(a.currency==='ARS'?a.balance:a.balance*S.tc.blue);},0); var btnHtml=''; if(!savs.length){ c.innerHTML='
🐷

Sin ahorros

CreΓ‘ una cuenta de ahorro para llevar el registro de lo que tenΓ©s guardado.

'+btnHtml; }else{ var summary='

Total ahorrado

$'+fmt(tARS)+'
U$'+fmt(tUSD)+' en dΓ³lares Β· TC blue $'+fmt(S.tc.blue)+'
'; var cards=savs.map(function(a){return accCardHTML(a,true);}).join(''); c.innerHTML=btnHtml+summary+cards; } document.getElementById('btn-open-new-savings').addEventListener('click',function(){ openNewAccount();document.getElementById('acc-type').value='savings'; }); c.querySelectorAll('[data-edit-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openEditAccount(btn.getAttribute('data-edit-acc'));});}); c.querySelectorAll('[data-del-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteAccount(btn.getAttribute('data-del-acc'));});}); c.querySelectorAll('[data-acc-history]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();showAccHistory(btn.getAttribute('data-acc-history'));});}); } // ── POPULATE ACCOUNT SELECT ────────────────────── function populateAccountSelect(){ var sel=document.getElementById('tx-account-id'); var nonSav=S.accounts.filter(function(a){return!isSavings(a);}); sel.innerHTML=nonSav.length ?nonSav.map(function(a){return'';}).join('') :''; } // ── NEW / EDIT TX ──────────────────────────────── document.getElementById('fab').addEventListener('click',function(){openTxModal(null);}); function openTxModal(tx){ S.editingTxId=tx?tx.id:null; document.getElementById('tx-modal-title').textContent=tx?'Editar transacciΓ³n':'Nueva transacciΓ³n'; document.getElementById('btn-save-tx').textContent=tx?'Guardar cambios':'Guardar'; document.getElementById('tx-desc').value=tx?tx.description:''; document.getElementById('tx-amount').value=tx?tx.amount:''; document.getElementById('tx-note').value=tx?tx.note:''; document.getElementById('tx-date').value=tx?tx.date:todayStr(); document.getElementById('tx-currency').value=tx?tx.currency:'ARS'; document.getElementById('tc-field').style.display=(tx&&tx.currency==='USD')||false?'':'none'; if(tx&&tx.currency==='USD')document.getElementById('tx-tc').value=tx.tc; document.getElementById('tx-shared').checked=tx?tx.shared:false; document.getElementById('split-section').style.display=(tx&&tx.shared)?'':'none'; document.getElementById('split-me').value=tx?tx.split_me:50; document.getElementById('split-partner').value=tx?(100-tx.split_me):50; S.txType=tx?tx.type:'expense'; S.selectedCat=tx?tx.category_id:null; S.currentTags=tx?(tx.tags||[]).slice():[]; document.querySelectorAll('.toggle-opt[data-type]').forEach(function(b){b.classList.toggle('active',b.getAttribute('data-type')===S.txType);}); populateAccountSelect(); if(tx&&tx.account_id){var sel=document.getElementById('tx-account-id');for(var i=0;iΓ—'; pill.querySelector('button').addEventListener('click',function(){S.currentTags=S.currentTags.filter(function(t){return t!==tag;});renderTagsWrap();}); wrap.insertBefore(pill,inp); }); } function renderCatPicker(){ var grid=document.getElementById('cat-picker');grid.innerHTML=''; var filtered=S.categories.filter(function(cat){return cat.type===S.txType;}); if(!filtered.length){grid.innerHTML='

Sin categorΓ­as para este tipo

';return;} filtered.forEach(function(cat){ var d=document.createElement('div');d.className='cat-opt'+(S.selectedCat===cat.id?' selected':''); d.innerHTML=''+cat.icon+''+cat.name+''; d.addEventListener('click',function(){S.selectedCat=cat.id;renderCatPicker();});grid.appendChild(d); }); } document.getElementById('btn-save-tx').addEventListener('click',function(){ var desc=document.getElementById('tx-desc').value.trim(); var amount=parseFloat(document.getElementById('tx-amount').value); var currency=document.getElementById('tx-currency').value; var date=document.getElementById('tx-date').value||todayStr(); var tc=currency==='USD'?(parseFloat(document.getElementById('tx-tc').value)||S.tc.blue):1; var note=document.getElementById('tx-note').value.trim(); var shared=document.getElementById('tx-shared').checked; var payer=document.getElementById('tx-payer').value; var splitMe=parseInt(document.getElementById('split-me').value)||50; var accountId=document.getElementById('tx-account-id').value; if(!desc){toast('AgregΓ‘ una descripciΓ³n','error');return;} if(!amount||amount<=0){toast('IngresΓ‘ un monto vΓ‘lido','error');return;} if(!accountId){toast('SeleccionΓ‘ una cuenta','error');return;} var defaultCatId=S.txType==='income'?'i4':'c9'; if(S.editingTxId){ // Editing: reverse old effect on account, apply new var oldTx=S.transactions.find(function(t){return t.id===S.editingTxId;}); if(oldTx){ var oldAcc=S.accounts.find(function(a){return a.id===oldTx.account_id;}); if(oldAcc){var rev=oldTx.type==='income'?-oldTx.amount:oldTx.amount;if(oldTx.currency==='USD'&&oldAcc.currency==='ARS')rev*=oldTx.tc;else if(oldTx.currency==='ARS'&&oldAcc.currency==='USD')rev/=oldTx.tc;oldAcc.balance=(oldAcc.balance||0)+rev;} oldTx.description=desc;oldTx.amount=amount;oldTx.currency=currency;oldTx.tc=tc; oldTx.date=date;oldTx.note=note;oldTx.shared=shared;oldTx.payer=shared?payer:'me'; oldTx.split_me=shared?splitMe:100;oldTx.category_id=S.selectedCat||defaultCatId; oldTx.account_id=accountId;oldTx.tags=S.currentTags.slice(); } var newAcc=S.accounts.find(function(a){return a.id===accountId;}); if(newAcc){var delta=S.txType==='income'?amount:-amount;if(currency==='USD'&&newAcc.currency==='ARS')delta*=tc;else if(currency==='ARS'&&newAcc.currency==='USD')delta/=tc;newAcc.balance=(newAcc.balance||0)+delta;} saveAccountsLocal();closeModal('modal-new-tx');renderDashboard(); if(S.activeTab==='transactions')renderTransactions(); if(S.activeTab==='accounts')renderAccounts(); toast('TransacciΓ³n actualizada βœ“','success'); api('updateTransaction',{transaction:oldTx}).catch(function(){}); }else{ var acc=S.accounts.find(function(a){return a.id===accountId;}); if(acc){var d2=S.txType==='income'?amount:-amount;if(currency==='USD'&&acc.currency==='ARS')d2*=tc;else if(currency==='ARS'&&acc.currency==='USD')d2/=tc;acc.balance=(acc.balance||0)+d2;saveAccountsLocal();api('updateAccountBalance',{id:acc.id,balance:acc.balance}).catch(function(){});} var newTx={id:genId(),uid:S.user.uid,date:date,type:S.txType,amount:amount,currency:currency,tc:tc,category_id:S.selectedCat||defaultCatId,account_id:accountId,tags:S.currentTags.slice(),description:desc,note:note,shared:shared,payer:shared?payer:'me',split_me:shared?splitMe:100,created_at:new Date().toISOString()}; S.transactions.unshift(newTx);closeModal('modal-new-tx');renderDashboard(); if(S.activeTab==='transactions')renderTransactions(); if(S.activeTab==='accounts')renderAccounts(); toast('TransacciΓ³n guardada βœ“','success');api('addTransaction',{transaction:newTx}).catch(function(){}); } }); // ── TRANSACTIONS ────────────────────────────────── document.querySelectorAll('.filter-chip').forEach(function(btn){ btn.addEventListener('click',function(){ S.txFilter=btn.getAttribute('data-filter'); document.querySelectorAll('.filter-chip').forEach(function(b){b.classList.remove('active');});btn.classList.add('active');renderTransactions(); }); }); function renderTransactions(){ var container=document.getElementById('tx-list-container'); var txs=S.transactions.slice(); if(S.txFilter==='income')txs=txs.filter(function(t){return t.type==='income';}); if(S.txFilter==='expense')txs=txs.filter(function(t){return t.type==='expense';}); if(S.txFilter==='shared')txs=txs.filter(function(t){return t.shared;}); if(S.txFilter==='ARS')txs=txs.filter(function(t){return t.currency==='ARS';}); if(S.txFilter==='USD')txs=txs.filter(function(t){return t.currency==='USD';}); if(!txs.length){container.innerHTML='
πŸ“­

Sin movimientos

TocΓ‘ el + para agregar tu primer transacciΓ³n

';return;} container.innerHTML='
'+txs.map(function(tx){return txHTML(tx,true);}).join('')+'
'; container.querySelectorAll('[data-edit-tx]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();var tx=S.transactions.find(function(t){return t.id===btn.getAttribute('data-edit-tx');});if(tx)openTxModal(tx);});}); container.querySelectorAll('[data-del-tx]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteTx(btn.getAttribute('data-del-tx'));});}); } function deleteTx(txId){ if(!confirm('ΒΏEliminar esta transacciΓ³n?'))return; var tx=S.transactions.find(function(t){return t.id===txId;}); if(tx){ var acc=S.accounts.find(function(a){return a.id===tx.account_id;}); if(acc){var rev=tx.type==='income'?-tx.amount:tx.amount;if(tx.currency==='USD'&&acc.currency==='ARS')rev*=tx.tc;else if(tx.currency==='ARS'&&acc.currency==='USD')rev/=tx.tc;acc.balance=(acc.balance||0)+rev;saveAccountsLocal();} } S.transactions=S.transactions.filter(function(t){return t.id!==txId;}); renderTransactions();renderDashboard(); toast('TransacciΓ³n eliminada','success'); api('deleteTransaction',{id:txId}).catch(function(){}); } function txHTML(tx,showActions){ var cat=S.categories.find(function(c){return c.id===tx.category_id;})||{icon:'πŸ“¦',color:'#6b7280'}; var acc=S.accounts.find(function(a){return a.id===tx.account_id;}); var sign=tx.type==='income'?'+':'-',cls=tx.type==='income'?'income':'expense'; var tags=(tx.tags||[]).map(function(t){return'#'+t+'';}).join(''); var badge=tx.shared?'πŸ’‘':''; var accBadge=acc?'':''; var amt=tx.currency==='USD'?'U$'+fmt(tx.amount):'$'+fmt(tx.amount); var equiv=tx.currency==='USD'?'β‰ˆ $'+fmt(tx.amount*tx.tc)+' ARS':''; var actions=showActions?'
':''; return'
'+ '
'+cat.icon+'
'+ '
'+tx.description+'
'+ '
'+fmtDate(tx.date)+' '+accBadge+' '+badge+' '+tags+'
'+ '
'+ '
'+sign+amt+'
'+(equiv?'
'+equiv+'
':'')+'
'+ actions+'
'; } // ── DASHBOARD ───────────────────────────────────── function renderDashboard(){ var now=new Date(),month=now.getMonth(),year=now.getFullYear(); // Excluir solo cuentas de ahorro que existen β€” si account_id estΓ‘ vacΓ­o, incluir igual var savIds=S.accounts.filter(isSavings).map(function(a){return a.id;}); var txs=S.transactions.filter(function(t){ var d=new Date(t.date); var inMonth=d.getMonth()===month&&d.getFullYear()===year; var notSavings=!t.account_id||savIds.indexOf(t.account_id)===-1; return inMonth&¬Savings; }); var iARS=0,eARS=0,iUSD=0,eUSD=0; txs.forEach(function(t){var isIn=t.type==='income';if(t.currency==='ARS'){isIn?(iARS+=t.amount):(eARS+=t.amount);}else{isIn?(iUSD+=t.amount):(eUSD+=t.amount);}}); document.getElementById('balance-month-label').textContent=monthName(month)+' '+year; document.getElementById('balance-main-val').textContent='$'+fmt(iARS-eARS)+' ARS'; document.getElementById('dash-income').textContent='+$'+fmt(iARS); document.getElementById('dash-expense').textContent='-$'+fmt(eARS); var tARS=iARS-eARS,tUSD=iUSD-eUSD; var arsEl=document.getElementById('dash-ars');arsEl.textContent=(tARS>=0?'+':'')+' $'+fmt(tARS);arsEl.className='si-val '+(tARS>=0?'green':'red'); var usdEl=document.getElementById('dash-usd');usdEl.textContent=(tUSD>=0?'+':'')+'U$'+fmt(tUSD);usdEl.className='si-val '+(tUSD>=0?'green':'red'); document.getElementById('dash-count').textContent=txs.length; var cats={};txs.forEach(function(t){cats[t.category_id]=true;});document.getElementById('dash-cats').textContent=Object.keys(cats).length; renderPie(txs.filter(function(t){return t.type==='expense';})); var recent=S.transactions.slice(0,5); document.getElementById('dash-recent-list').innerHTML=recent.length?'
'+recent.map(function(t){return txHTML(t,false);}).join('')+'
':'
πŸ’Έ

Sin movimientos

EmpezΓ‘ cargando tu primer gasto

'; } function renderPie(expenses){ var canvas=document.getElementById('pieChart'),legend=document.getElementById('pie-legend'),ctx=canvas.getContext('2d'); var W=canvas.offsetWidth||300,H=220;canvas.width=W;canvas.height=H;ctx.clearRect(0,0,W,H); var catMap={};expenses.forEach(function(t){var val=t.currency==='USD'?t.amount*t.tc:t.amount;catMap[t.category_id]=(catMap[t.category_id]||0)+val;}); var entries=Object.keys(catMap).map(function(k){return[k,catMap[k]];}).sort(function(a,b){return b[1]-a[1];}); var total=entries.reduce(function(s,e){return s+e[1];},0)||1;legend.innerHTML=''; if(!entries.length){ctx.fillStyle='#ffffff22';ctx.beginPath();ctx.arc(W/2,H/2,80,0,Math.PI*2);ctx.fill();ctx.fillStyle='#9ca3af';ctx.textAlign='center';ctx.font='14px Inter';ctx.fillText('Sin datos',W/2,H/2+5);return;} var cx=W/2,cy=H/2,r=Math.min(cx,cy)-20,startAngle=-Math.PI/2; entries.forEach(function(entry){ var cat=S.categories.find(function(c){return c.id===entry[0];})||{color:'#6b7280',name:'Otros',icon:'πŸ“¦'}; var slice=(entry[1]/total)*Math.PI*2; ctx.beginPath();ctx.moveTo(cx,cy);ctx.arc(cx,cy,r,startAngle,startAngle+slice);ctx.closePath();ctx.fillStyle=cat.color;ctx.fill();ctx.strokeStyle='#1a1d27';ctx.lineWidth=2;ctx.stroke(); legend.innerHTML+='
'+cat.icon+' '+cat.name+' '+Math.round(entry[1]/total*100)+'%
'; startAngle+=slice; }); ctx.beginPath();ctx.arc(cx,cy,r*0.5,0,Math.PI*2);ctx.fillStyle='#1a1d27';ctx.fill(); ctx.fillStyle='#fff';ctx.textAlign='center';ctx.font='bold 14px Inter';ctx.fillText('$'+fmt(total),cx,cy+5); } // ── COUPLE ──────────────────────────────────────── document.getElementById('btn-send-partner').addEventListener('click',function(){ var email=document.getElementById('partner-email').value.trim();if(!email){toast('IngresΓ‘ un email','error');return;} api('linkPartner',{uid:S.user.uid,partner_email:email}).then(function(d){ S.user.partner_uid=d.partner_uid;S.user.partner_name=d.partner_name;localStorage.setItem('session',JSON.stringify(S.user)); toast('Β‘Pareja vinculada! βœ“','success');closeModal('modal-link-partner');renderCouple(); }).catch(function(e){toast(e.message,'error');}); }); function renderCouple(){ var c=document.getElementById('couple-content'); if(!S.user.partner_uid){ c.innerHTML='
πŸ‘€

'+S.user.name+'

Sin pareja vinculada

'+ '
πŸ’‘

Modo pareja activado

VinculΓ‘ tu cuenta con la de tu pareja para compartir gastos.

'+ ''; document.getElementById('btn-open-link-partner').addEventListener('click',function(){showModal('modal-link-partner');});return; } var shared=S.transactions.filter(function(t){return t.shared;}); var iOwe=0,theyOwe=0; shared.forEach(function(t){var val=t.currency==='USD'?t.amount*t.tc:t.amount;var my=val*(t.split_me||50)/100;if(t.payer==='me')theyOwe+=(val-my);else iOwe+=my;}); var net=theyOwe-iOwe; c.innerHTML='
πŸ‘€
πŸ’ž
πŸ‘€
'+ '

'+S.user.name+' & '+(S.user.partner_name||'Pareja')+'

Vinculados

'+ '

Balance compartido (ARS)

'+ (net>=0?'+':'')+' $'+fmt(Math.abs(net))+'
'+(net>0?'Te deben':'Le debΓ©s a '+(S.user.partner_name||'tu pareja'))+'
'+ '
Gastos compartidos
'+ '
'+(shared.length?shared.map(function(t){return txHTML(t,false);}).join(''):'
🀝

Sin gastos compartidos

')+'
'; } // ── VACATIONS ───────────────────────────────────── document.getElementById('btn-save-vac').addEventListener('click',function(){ var name=document.getElementById('vac-name').value.trim();if(!name){toast('PonΓ© un nombre al viaje','error');return;} var vac={id:genId(),uid:S.user.uid,name:name,start_date:document.getElementById('vac-start').value,end_date:document.getElementById('vac-end').value,budget:parseFloat(document.getElementById('vac-budget').value)||0,currency:document.getElementById('vac-currency').value,created_at:new Date().toISOString()}; S.vacations.unshift(vac);closeModal('modal-new-vac');renderVacations();toast('Viaje creado βœ“','success');api('addVacation',{vacation:vac}).catch(function(){}); }); document.getElementById('btn-save-vexp').addEventListener('click',function(){ var desc=document.getElementById('vexp-desc').value.trim(),amount=parseFloat(document.getElementById('vexp-amount').value); if(!desc||!amount){toast('CompletΓ‘ los campos','error');return;} var item={id:genId(),vacation_id:S.activeVacId,type:'expense',description:desc,amount:amount,currency:document.getElementById('vexp-currency').value,tc:parseFloat(document.getElementById('vexp-tc').value)||S.tc.blue,shared:document.getElementById('vexp-shared').checked,payer:document.getElementById('vexp-payer').value,split_me:parseInt(document.getElementById('vexp-split-me').value)||50,created_at:new Date().toISOString()}; if(!S.vacItems[S.activeVacId])S.vacItems[S.activeVacId]=[];S.vacItems[S.activeVacId].unshift(item); closeModal('modal-vac-expense');renderVacations();toast('Gasto guardado βœ“','success');api('addVacationItem',{item:item}).catch(function(){}); }); function calcVacSpent(vacId){return(S.vacItems[vacId]||[]).filter(function(i){return i.type==='expense';}).reduce(function(s,i){return s+(i.currency==='USD'?i.amount*(i.tc||S.tc.blue):i.amount);},0);} function renderVacations(){ var c=document.getElementById('vacation-content'); var btnHtml=''; if(!S.vacations.length){c.innerHTML='
✈️

Sin viajes

CreΓ‘ tu primer viaje para empezar.

'+btnHtml;} else{ var cards=S.vacations.map(function(v){var spent=calcVacSpent(v.id),pct=Math.min(100,Math.round(spent/v.budget*100))||0,curr=v.currency==='USD'?'U$':'$'; return'
✈️ '+v.name+'
πŸ“… '+fmtDate(v.start_date)+' β†’ '+fmtDate(v.end_date)+'
'+ '
'+ '
'+curr+fmt(spent)+' gastado'+curr+fmt(v.budget-spent)+' restante
'+ '
'; }).join('');c.innerHTML=btnHtml+cards; } document.getElementById('btn-open-new-vac').addEventListener('click',function(){showModal('modal-new-vac');}); c.querySelectorAll('[data-vac-expense]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();S.activeVacId=btn.getAttribute('data-vac-expense');showModal('modal-vac-expense');});}); c.querySelectorAll('[data-vac-detail]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openVacationDetail(btn.getAttribute('data-vac-detail'));});}); } function openVacationDetail(vacId){ var vac=S.vacations.find(function(v){return v.id===vacId;});if(!vac)return; var items=S.vacItems[vacId]||[],expenses=items.filter(function(i){return i.type==='expense';}),checks=items.filter(function(i){return i.type==='checklist';}); var spent=calcVacSpent(vacId),pct=Math.min(100,Math.round(spent/vac.budget*100))||0,curr=vac.currency==='USD'?'U$':'$'; var overlay=document.createElement('div');overlay.className='modal-overlay open';overlay.style.zIndex='600'; var checkHtml=checks.length?checks.map(function(i){return'
βœ“
'+i.description+'
';}).join(''):'

Sin Γ­tems

'; var expHtml=expenses.length?expenses.map(function(i){return'
✈️
'+i.description+'
'+(i.shared?'πŸ’‘':'')+'
-'+(i.currency==='USD'?'U$':'$')+fmt(i.amount)+'
';}).join(''):'

Sin gastos aΓΊn

'; overlay.innerHTML=''; overlay.addEventListener('click',function(e){if(e.target===overlay)overlay.remove();});document.body.appendChild(overlay); document.getElementById('close-vac-detail').addEventListener('click',function(){overlay.remove();}); document.getElementById('checklist-container').addEventListener('click',function(e){ var item=e.target.closest('.checklist-item');if(!item)return; var box=item.querySelector('.check-box'),txt=item.querySelector('.check-text'),iid=item.getAttribute('data-item-id'); var vi=(S.vacItems[vacId]||[]).find(function(x){return x.id===iid;});if(vi)vi.done=!vi.done;box.classList.toggle('checked');txt.classList.toggle('done'); }); document.getElementById('btn-add-check').addEventListener('click',function(){ var inp=document.getElementById('new-check-txt'),desc=inp.value.trim();if(!desc)return; var ni={id:genId(),vacation_id:vacId,type:'checklist',description:desc,done:false}; if(!S.vacItems[vacId])S.vacItems[vacId]=[];S.vacItems[vacId].push(ni);inp.value=''; var container=document.getElementById('checklist-container');var p=container.querySelector('p');if(p)p.remove(); var div=document.createElement('div');div.className='checklist-item';div.setAttribute('data-item-id',ni.id);div.innerHTML='
βœ“
'+desc+'';container.appendChild(div); api('addVacationItem',{item:ni}).catch(function(){}); }); } window.addEventListener('resize',function(){if(S.activeTab==='dashboard')renderDashboard();}); +d.compra+' venta function loadAll(){ return Promise.all([ api('getTransactions',{uid:S.user.uid}), api('getCategories',{uid:S.user.uid}), api('getVacations',{uid:S.user.uid}), api('getAccounts',{uid:S.user.uid}) ]).then(function(results){ S.transactions=results[0].transactions||[]; var bc=results[1].categories||[]; S.categories=defaultCategories().concat(bc.filter(function(c){return c.type==='expense'||c.type==='income';})); S.vacations=results[2].vacations||[]; var ba=results[3].accounts||[]; ba.forEach(function(a){if(!S.accounts.find(function(x){return x.id===a.id;}))S.accounts.push(a);}); saveAccountsLocal(); }).catch(function(){if(!S.categories.length)S.categories=defaultCategories();}); } // ── TABS ───────────────────────────────────────── document.querySelectorAll('.nav-item').forEach(function(btn){btn.addEventListener('click',function(){switchTab(btn.getAttribute('data-tab'),btn);});}); function switchTab(tab,btn){ ['dashboard','transactions','accounts','savings','couple','vacation','settings'].forEach(function(t){var el=document.getElementById('tab-'+t);if(el)el.style.display='none';}); var active=document.getElementById('tab-'+tab);if(active)active.style.display=''; document.querySelectorAll('.nav-item').forEach(function(b){b.classList.remove('active');}); if(btn)btn.classList.add('active');S.activeTab=tab; var titles={dashboard:'Dashboard',transactions:'Movimientos',accounts:'Cuentas',savings:'Ahorros',couple:'Pareja',vacation:'Viajes',settings:'ConfiguraciΓ³n'}; document.getElementById('topbar-title').textContent=titles[tab]||tab; if(tab==='transactions')renderTransactions(); if(tab==='accounts')renderAccounts(); if(tab==='savings')renderSavings(); if(tab==='couple')renderCouple(); if(tab==='vacation')renderVacations(); if(tab==='settings')updateSettingsUI(); } document.getElementById('btn-go-settings').addEventListener('click',function(){switchTab('settings',document.querySelector('[data-tab="settings"]'));}); // ── MODE ────────────────────────────────────────── function updateModeUI(){ if(!S.user)return; var c=S.user.mode==='couple'||S.user.mode==='both',v=S.user.mode==='vacation'||S.user.mode==='both'; document.getElementById('nav-couple').style.display=c?'':'none'; document.getElementById('nav-vacation').style.display=v?'':'none'; var badges='';if(c)badges+='πŸ’‘ Pareja ';if(v)badges+='πŸ–οΈ Vacaciones'; document.getElementById('mode-indicator').innerHTML=badges; document.getElementById('shared-section').style.display=c?'':'none'; document.getElementById('vexp-shared-wrap').style.display=c?'':'none'; } document.getElementById('toggle-couple').addEventListener('change',function(){toggleMode('couple',this.checked);}); document.getElementById('toggle-vacation').addEventListener('change',function(){toggleMode('vacation',this.checked);}); function toggleMode(mode,checked){ if(!S.user)return; var c=S.user.mode==='couple'||S.user.mode==='both',v=S.user.mode==='vacation'||S.user.mode==='both'; if(mode==='couple')c=checked;if(mode==='vacation')v=checked; S.user.mode=c&&v?'both':c?'couple':v?'vacation':'normal'; localStorage.setItem('session',JSON.stringify(S.user));updateModeUI(); api('updateUserMode',{uid:S.user.uid,mode:S.user.mode}).catch(function(){}); } // ── SETTINGS / CATEGORIES ──────────────────────── function updateSettingsUI(){ if(!S.user)return; document.getElementById('settings-name').textContent=S.user.name||'β€”'; document.getElementById('settings-email').textContent=S.user.email||'β€”'; document.getElementById('settings-avatar').textContent=(S.user.name||'?')[0].toUpperCase(); document.getElementById('toggle-couple').checked=S.user.mode==='couple'||S.user.mode==='both'; document.getElementById('toggle-vacation').checked=S.user.mode==='vacation'||S.user.mode==='both'; var list=document.getElementById('categories-list-settings'); list.innerHTML=S.categories.map(function(cat){ return'
'+ '
'+ ''+cat.icon+''+ '
'+cat.name+'
'+ '
'+(cat.type==='income'?'Ingreso':'Gasto')+'
'+ '
'+ ''+ ''+ '
'; }).join(''); list.querySelectorAll('[data-edit-cat]').forEach(function(btn){ btn.addEventListener('click',function(){openEditCat(btn.getAttribute('data-edit-cat'));}); }); } // Edit category function openEditCat(catId){ var cat=S.categories.find(function(c){return c.id===catId;});if(!cat)return; S.editingCatId=catId; document.getElementById('edit-cat-name').value=cat.name; document.getElementById('edit-cat-icon').value=cat.icon; showModal('modal-edit-cat'); } document.getElementById('btn-save-edit-cat').addEventListener('click',function(){ var cat=S.categories.find(function(c){return c.id===S.editingCatId;});if(!cat)return; var name=document.getElementById('edit-cat-name').value.trim(); var icon=document.getElementById('edit-cat-icon').value.trim(); if(!name){toast('IngresΓ‘ un nombre','error');return;} cat.name=name;if(icon)cat.icon=icon; closeModal('modal-edit-cat');updateSettingsUI();renderCatPicker(); toast('CategorΓ­a actualizada βœ“','success'); api('updateCategory',{category:cat}).catch(function(){}); }); document.getElementById('btn-new-cat').addEventListener('click',function(){ S.newCatType='expense'; document.getElementById('cat-type-expense').classList.add('active');document.getElementById('cat-type-income').classList.remove('active'); showModal('modal-new-cat'); }); document.getElementById('cat-type-expense').addEventListener('click',function(){S.newCatType='expense';document.getElementById('cat-type-expense').classList.add('active');document.getElementById('cat-type-income').classList.remove('active');}); document.getElementById('cat-type-income').addEventListener('click',function(){S.newCatType='income';document.getElementById('cat-type-income').classList.add('active');document.getElementById('cat-type-expense').classList.remove('active');}); document.getElementById('btn-save-cat').addEventListener('click',function(){ var name=document.getElementById('cat-name').value.trim(),icon=document.getElementById('cat-icon').value.trim()||'πŸ“¦',color=document.getElementById('cat-color').value; if(!name){toast('IngresΓ‘ un nombre','error');return;} var cat={id:genId(),uid:S.user.uid,name:name,icon:icon,color:color,type:S.newCatType}; S.categories.push(cat);closeModal('modal-new-cat');updateSettingsUI(); toast('CategorΓ­a creada βœ“','success');api('addCategory',{category:cat}).catch(function(){}); }); // ── TC ──────────────────────────────────────────── document.getElementById('btn-open-tc').addEventListener('click',function(){ document.getElementById('tc-oficial').value=S.tc.oficial;document.getElementById('tc-blue').value=S.tc.blue;showModal('modal-tc'); }); document.getElementById('btn-save-tc').addEventListener('click',function(){ S.tc.oficial=parseFloat(document.getElementById('tc-oficial').value)||S.tc.oficial; S.tc.blue=parseFloat(document.getElementById('tc-blue').value)||S.tc.blue; localStorage.setItem('tc',JSON.stringify(S.tc));toast('Tipo de cambio guardado βœ“','success');closeModal('modal-tc'); if(S.activeTab==='savings')renderSavings(); }); // ── MODALS CLOSE ───────────────────────────────── document.querySelectorAll('[data-close]').forEach(function(btn){btn.addEventListener('click',function(){closeModal(btn.getAttribute('data-close'));});}); document.querySelectorAll('.modal-overlay').forEach(function(o){o.addEventListener('click',function(e){if(e.target===o)o.classList.remove('open');});}); // ── ACCOUNTS ───────────────────────────────────── function renderAccounts(){ var c=document.getElementById('accounts-content'); var nonSav=S.accounts.filter(function(a){return!isSavings(a);}); var total=nonSav.reduce(function(s,a){return s+(a.currency==='USD'?a.balance*S.tc.blue:a.balance);},0); var btnHtml='
'+ ''+ ''+ '
'; if(!S.accounts.length){ c.innerHTML='
🏦

Sin cuentas

CreΓ‘ tu primera cuenta para empezar a registrar movimientos.

'+btnHtml; }else{ var totalHtml='

Total disponible (sin ahorros)

$'+fmt(total)+'
En ARS equivalente al TC blue
'; var cards=nonSav.map(function(a){return accCardHTML(a,true);}).join(''); c.innerHTML=btnHtml+totalHtml+cards; } document.getElementById('btn-open-new-account').addEventListener('click',function(){openNewAccount();}); document.getElementById('btn-open-transfer').addEventListener('click',function(){openTransfer();}); c.querySelectorAll('[data-edit-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openEditAccount(btn.getAttribute('data-edit-acc'));});}); c.querySelectorAll('[data-del-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteAccount(btn.getAttribute('data-del-acc'));});}); c.querySelectorAll('[data-acc-history]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();showAccHistory(btn.getAttribute('data-acc-history'));});}); } function accCardHTML(acc,showActions){ var cfg=ACC_TYPES[acc.type]||ACC_TYPES.bank; var icon=acc.icon||cfg.icon; var balStr=acc.currency==='USD'?'U$'+fmt(acc.balance):'$'+fmt(acc.balance); var subStr=acc.currency==='USD'?'β‰ˆ $'+fmt(acc.balance*S.tc.blue)+' ARS':''; var actions=showActions?'
'+ ''+ ''+ ''+ '
':''; return''; } function openNewAccount(){ S.editingAccId=null; document.getElementById('acc-modal-title').textContent='Nueva cuenta'; document.getElementById('acc-name').value='';document.getElementById('acc-balance').value=''; document.getElementById('acc-icon').value='';document.getElementById('acc-color').value='#6c63ff'; document.getElementById('acc-type').value='bank';document.getElementById('acc-currency').value='ARS'; document.getElementById('btn-save-account').textContent='Crear cuenta'; showModal('modal-new-account'); } function openEditAccount(accId){ var acc=S.accounts.find(function(a){return a.id===accId;});if(!acc)return; S.editingAccId=accId; document.getElementById('acc-modal-title').textContent='Editar cuenta'; document.getElementById('acc-name').value=acc.name; document.getElementById('acc-balance').value=acc.balance; document.getElementById('acc-icon').value=acc.icon||''; document.getElementById('acc-color').value=acc.color||'#6c63ff'; document.getElementById('acc-type').value=acc.type; document.getElementById('acc-currency').value=acc.currency; document.getElementById('btn-save-account').textContent='Guardar cambios'; showModal('modal-new-account'); } document.getElementById('btn-save-account').addEventListener('click',function(){ var name=document.getElementById('acc-name').value.trim(); var type=document.getElementById('acc-type').value; var balance=parseFloat(document.getElementById('acc-balance').value)||0; var currency=document.getElementById('acc-currency').value; var icon=document.getElementById('acc-icon').value.trim()||(ACC_TYPES[type]||ACC_TYPES.bank).icon; var color=document.getElementById('acc-color').value; if(!name){toast('PonΓ© un nombre a la cuenta','error');return;} if(S.editingAccId){ var acc=S.accounts.find(function(a){return a.id===S.editingAccId;}); if(acc){acc.name=name;acc.type=type;acc.balance=balance;acc.currency=currency;acc.icon=icon;acc.color=color;} }else{ var newAcc={id:genId(),uid:S.user.uid,name:name,type:type,balance:balance,currency:currency,icon:icon,color:color,created_at:new Date().toISOString()}; S.accounts.push(newAcc); api('addAccount',{account:newAcc}).catch(function(){}); } saveAccountsLocal();closeModal('modal-new-account'); if(S.activeTab==='accounts')renderAccounts(); if(S.activeTab==='savings')renderSavings(); populateAccountSelect(); toast((S.editingAccId?'Cuenta actualizada':'Cuenta creada')+' βœ“','success'); if(S.editingAccId)api('updateAccount',{account:S.accounts.find(function(a){return a.id===S.editingAccId;})}).catch(function(){}); }); function deleteAccount(accId){ if(!confirm('ΒΏEliminar esta cuenta? Las transacciones asociadas no se borrarΓ‘n.'))return; S.accounts=S.accounts.filter(function(a){return a.id!==accId;}); saveAccountsLocal(); if(S.activeTab==='accounts')renderAccounts(); if(S.activeTab==='savings')renderSavings(); populateAccountSelect(); toast('Cuenta eliminada','success'); api('deleteAccount',{id:accId}).catch(function(){}); } function showAccHistory(accId){ var acc=S.accounts.find(function(a){return a.id===accId;});if(!acc)return; var txs=S.transactions.filter(function(t){return t.account_id===accId;}); var overlay=document.createElement('div');overlay.className='modal-overlay open';overlay.style.zIndex='600'; var txHtml=txs.length?'
'+txs.map(txHTML).join('')+'
':'
πŸ“­

Sin movimientos

'; overlay.innerHTML=''; overlay.addEventListener('click',function(e){if(e.target===overlay)overlay.remove();}); document.body.appendChild(overlay); document.getElementById('close-acc-history').addEventListener('click',function(){overlay.remove();}); } // Transfer function openTransfer(){ populateTransferSelects(); document.getElementById('transfer-amount').value=''; document.getElementById('transfer-date').value=todayStr(); showModal('modal-transfer'); } function populateTransferSelects(){ var opts=S.accounts.map(function(a){return'';}).join(''); document.getElementById('transfer-from').innerHTML=opts; document.getElementById('transfer-to').innerHTML=opts; } document.getElementById('btn-save-transfer').addEventListener('click',function(){ var fromId=document.getElementById('transfer-from').value; var toId=document.getElementById('transfer-to').value; var amount=parseFloat(document.getElementById('transfer-amount').value); var currency=document.getElementById('transfer-currency').value; var date=document.getElementById('transfer-date').value||todayStr(); if(fromId===toId){toast('ElegΓ­ cuentas distintas','error');return;} if(!amount||amount<=0){toast('IngresΓ‘ un monto vΓ‘lido','error');return;} var from=S.accounts.find(function(a){return a.id===fromId;}); var to=S.accounts.find(function(a){return a.id===toId;}); // Adjust balances var debit=currency===from.currency?amount:(currency==='USD'?amount*S.tc.blue:amount/S.tc.blue); var credit=currency===to.currency?amount:(currency==='USD'?amount*S.tc.blue:amount/S.tc.blue); from.balance=(from.balance||0)-debit; to.balance=(to.balance||0)+credit; saveAccountsLocal(); // Record as two transactions var txOut={id:genId(),uid:S.user.uid,date:date,type:'expense',amount:amount,currency:currency,tc:S.tc.blue,category_id:'c9',account_id:fromId,tags:[],description:'Transferencia β†’ '+to.name,note:'',shared:false,payer:'me',split_me:100,created_at:new Date().toISOString()}; var txIn={id:genId(),uid:S.user.uid,date:date,type:'income',amount:amount,currency:currency,tc:S.tc.blue,category_id:'i4',account_id:toId,tags:[],description:'Transferencia ← '+from.name,note:'',shared:false,payer:'me',split_me:100,created_at:new Date().toISOString()}; S.transactions.unshift(txIn);S.transactions.unshift(txOut); closeModal('modal-transfer');renderAccounts();renderDashboard(); toast('Transferencia realizada βœ“','success'); api('addTransaction',{transaction:txOut}).catch(function(){}); api('addTransaction',{transaction:txIn}).catch(function(){}); }); // ── SAVINGS ────────────────────────────────────── function renderSavings(){ var c=document.getElementById('savings-content'); var savs=S.accounts.filter(function(a){return isSavings(a);}); var tUSD=savs.reduce(function(s,a){return s+(a.currency==='USD'?a.balance:0);},0); var tARS=savs.reduce(function(s,a){return s+(a.currency==='ARS'?a.balance:a.balance*S.tc.blue);},0); var btnHtml=''; if(!savs.length){ c.innerHTML='
🐷

Sin ahorros

CreΓ‘ una cuenta de ahorro para llevar el registro de lo que tenΓ©s guardado.

'+btnHtml; }else{ var summary='

Total ahorrado

$'+fmt(tARS)+'
U$'+fmt(tUSD)+' en dΓ³lares Β· TC blue $'+fmt(S.tc.blue)+'
'; var cards=savs.map(function(a){return accCardHTML(a,true);}).join(''); c.innerHTML=btnHtml+summary+cards; } document.getElementById('btn-open-new-savings').addEventListener('click',function(){ openNewAccount();document.getElementById('acc-type').value='savings'; }); c.querySelectorAll('[data-edit-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openEditAccount(btn.getAttribute('data-edit-acc'));});}); c.querySelectorAll('[data-del-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteAccount(btn.getAttribute('data-del-acc'));});}); c.querySelectorAll('[data-acc-history]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();showAccHistory(btn.getAttribute('data-acc-history'));});}); } // ── POPULATE ACCOUNT SELECT ────────────────────── function populateAccountSelect(){ var sel=document.getElementById('tx-account-id'); var nonSav=S.accounts.filter(function(a){return!isSavings(a);}); sel.innerHTML=nonSav.length ?nonSav.map(function(a){return'';}).join('') :''; } // ── NEW / EDIT TX ──────────────────────────────── document.getElementById('fab').addEventListener('click',function(){openTxModal(null);}); function openTxModal(tx){ S.editingTxId=tx?tx.id:null; document.getElementById('tx-modal-title').textContent=tx?'Editar transacciΓ³n':'Nueva transacciΓ³n'; document.getElementById('btn-save-tx').textContent=tx?'Guardar cambios':'Guardar'; document.getElementById('tx-desc').value=tx?tx.description:''; document.getElementById('tx-amount').value=tx?tx.amount:''; document.getElementById('tx-note').value=tx?tx.note:''; document.getElementById('tx-date').value=tx?tx.date:todayStr(); document.getElementById('tx-currency').value=tx?tx.currency:'ARS'; document.getElementById('tc-field').style.display=(tx&&tx.currency==='USD')||false?'':'none'; if(tx&&tx.currency==='USD')document.getElementById('tx-tc').value=tx.tc; document.getElementById('tx-shared').checked=tx?tx.shared:false; document.getElementById('split-section').style.display=(tx&&tx.shared)?'':'none'; document.getElementById('split-me').value=tx?tx.split_me:50; document.getElementById('split-partner').value=tx?(100-tx.split_me):50; S.txType=tx?tx.type:'expense'; S.selectedCat=tx?tx.category_id:null; S.currentTags=tx?(tx.tags||[]).slice():[]; document.querySelectorAll('.toggle-opt[data-type]').forEach(function(b){b.classList.toggle('active',b.getAttribute('data-type')===S.txType);}); populateAccountSelect(); if(tx&&tx.account_id){var sel=document.getElementById('tx-account-id');for(var i=0;iΓ—'; pill.querySelector('button').addEventListener('click',function(){S.currentTags=S.currentTags.filter(function(t){return t!==tag;});renderTagsWrap();}); wrap.insertBefore(pill,inp); }); } function renderCatPicker(){ var grid=document.getElementById('cat-picker');grid.innerHTML=''; var filtered=S.categories.filter(function(cat){return cat.type===S.txType;}); if(!filtered.length){grid.innerHTML='

Sin categorΓ­as para este tipo

';return;} filtered.forEach(function(cat){ var d=document.createElement('div');d.className='cat-opt'+(S.selectedCat===cat.id?' selected':''); d.innerHTML=''+cat.icon+''+cat.name+''; d.addEventListener('click',function(){S.selectedCat=cat.id;renderCatPicker();});grid.appendChild(d); }); } document.getElementById('btn-save-tx').addEventListener('click',function(){ var desc=document.getElementById('tx-desc').value.trim(); var amount=parseFloat(document.getElementById('tx-amount').value); var currency=document.getElementById('tx-currency').value; var date=document.getElementById('tx-date').value||todayStr(); var tc=currency==='USD'?(parseFloat(document.getElementById('tx-tc').value)||S.tc.blue):1; var note=document.getElementById('tx-note').value.trim(); var shared=document.getElementById('tx-shared').checked; var payer=document.getElementById('tx-payer').value; var splitMe=parseInt(document.getElementById('split-me').value)||50; var accountId=document.getElementById('tx-account-id').value; if(!desc){toast('AgregΓ‘ una descripciΓ³n','error');return;} if(!amount||amount<=0){toast('IngresΓ‘ un monto vΓ‘lido','error');return;} if(!accountId){toast('SeleccionΓ‘ una cuenta','error');return;} var defaultCatId=S.txType==='income'?'i4':'c9'; if(S.editingTxId){ // Editing: reverse old effect on account, apply new var oldTx=S.transactions.find(function(t){return t.id===S.editingTxId;}); if(oldTx){ var oldAcc=S.accounts.find(function(a){return a.id===oldTx.account_id;}); if(oldAcc){var rev=oldTx.type==='income'?-oldTx.amount:oldTx.amount;if(oldTx.currency==='USD'&&oldAcc.currency==='ARS')rev*=oldTx.tc;else if(oldTx.currency==='ARS'&&oldAcc.currency==='USD')rev/=oldTx.tc;oldAcc.balance=(oldAcc.balance||0)+rev;} oldTx.description=desc;oldTx.amount=amount;oldTx.currency=currency;oldTx.tc=tc; oldTx.date=date;oldTx.note=note;oldTx.shared=shared;oldTx.payer=shared?payer:'me'; oldTx.split_me=shared?splitMe:100;oldTx.category_id=S.selectedCat||defaultCatId; oldTx.account_id=accountId;oldTx.tags=S.currentTags.slice(); } var newAcc=S.accounts.find(function(a){return a.id===accountId;}); if(newAcc){var delta=S.txType==='income'?amount:-amount;if(currency==='USD'&&newAcc.currency==='ARS')delta*=tc;else if(currency==='ARS'&&newAcc.currency==='USD')delta/=tc;newAcc.balance=(newAcc.balance||0)+delta;} saveAccountsLocal();closeModal('modal-new-tx');renderDashboard(); if(S.activeTab==='transactions')renderTransactions(); if(S.activeTab==='accounts')renderAccounts(); toast('TransacciΓ³n actualizada βœ“','success'); api('updateTransaction',{transaction:oldTx}).catch(function(){}); }else{ var acc=S.accounts.find(function(a){return a.id===accountId;}); if(acc){var d2=S.txType==='income'?amount:-amount;if(currency==='USD'&&acc.currency==='ARS')d2*=tc;else if(currency==='ARS'&&acc.currency==='USD')d2/=tc;acc.balance=(acc.balance||0)+d2;saveAccountsLocal();api('updateAccountBalance',{id:acc.id,balance:acc.balance}).catch(function(){});} var newTx={id:genId(),uid:S.user.uid,date:date,type:S.txType,amount:amount,currency:currency,tc:tc,category_id:S.selectedCat||defaultCatId,account_id:accountId,tags:S.currentTags.slice(),description:desc,note:note,shared:shared,payer:shared?payer:'me',split_me:shared?splitMe:100,created_at:new Date().toISOString()}; S.transactions.unshift(newTx);closeModal('modal-new-tx');renderDashboard(); if(S.activeTab==='transactions')renderTransactions(); if(S.activeTab==='accounts')renderAccounts(); toast('TransacciΓ³n guardada βœ“','success');api('addTransaction',{transaction:newTx}).catch(function(){}); } }); // ── TRANSACTIONS ────────────────────────────────── document.querySelectorAll('.filter-chip').forEach(function(btn){ btn.addEventListener('click',function(){ S.txFilter=btn.getAttribute('data-filter'); document.querySelectorAll('.filter-chip').forEach(function(b){b.classList.remove('active');});btn.classList.add('active');renderTransactions(); }); }); function renderTransactions(){ var container=document.getElementById('tx-list-container'); var txs=S.transactions.slice(); if(S.txFilter==='income')txs=txs.filter(function(t){return t.type==='income';}); if(S.txFilter==='expense')txs=txs.filter(function(t){return t.type==='expense';}); if(S.txFilter==='shared')txs=txs.filter(function(t){return t.shared;}); if(S.txFilter==='ARS')txs=txs.filter(function(t){return t.currency==='ARS';}); if(S.txFilter==='USD')txs=txs.filter(function(t){return t.currency==='USD';}); if(!txs.length){container.innerHTML='
πŸ“­

Sin movimientos

TocΓ‘ el + para agregar tu primer transacciΓ³n

';return;} container.innerHTML='
'+txs.map(function(tx){return txHTML(tx,true);}).join('')+'
'; container.querySelectorAll('[data-edit-tx]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();var tx=S.transactions.find(function(t){return t.id===btn.getAttribute('data-edit-tx');});if(tx)openTxModal(tx);});}); container.querySelectorAll('[data-del-tx]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteTx(btn.getAttribute('data-del-tx'));});}); } function deleteTx(txId){ if(!confirm('ΒΏEliminar esta transacciΓ³n?'))return; var tx=S.transactions.find(function(t){return t.id===txId;}); if(tx){ var acc=S.accounts.find(function(a){return a.id===tx.account_id;}); if(acc){var rev=tx.type==='income'?-tx.amount:tx.amount;if(tx.currency==='USD'&&acc.currency==='ARS')rev*=tx.tc;else if(tx.currency==='ARS'&&acc.currency==='USD')rev/=tx.tc;acc.balance=(acc.balance||0)+rev;saveAccountsLocal();} } S.transactions=S.transactions.filter(function(t){return t.id!==txId;}); renderTransactions();renderDashboard(); toast('TransacciΓ³n eliminada','success'); api('deleteTransaction',{id:txId}).catch(function(){}); } function txHTML(tx,showActions){ var cat=S.categories.find(function(c){return c.id===tx.category_id;})||{icon:'πŸ“¦',color:'#6b7280'}; var acc=S.accounts.find(function(a){return a.id===tx.account_id;}); var sign=tx.type==='income'?'+':'-',cls=tx.type==='income'?'income':'expense'; var tags=(tx.tags||[]).map(function(t){return'#'+t+'';}).join(''); var badge=tx.shared?'πŸ’‘':''; var accBadge=acc?'':''; var amt=tx.currency==='USD'?'U$'+fmt(tx.amount):'$'+fmt(tx.amount); var equiv=tx.currency==='USD'?'β‰ˆ $'+fmt(tx.amount*tx.tc)+' ARS':''; var actions=showActions?'
':''; return'
'+ '
'+cat.icon+'
'+ '
'+tx.description+'
'+ '
'+fmtDate(tx.date)+' '+accBadge+' '+badge+' '+tags+'
'+ '
'+ '
'+sign+amt+'
'+(equiv?'
'+equiv+'
':'')+'
'+ actions+'
'; } // ── DASHBOARD ───────────────────────────────────── function renderDashboard(){ var now=new Date(),month=now.getMonth(),year=now.getFullYear(); var savIds=S.accounts.filter(isSavings).map(function(a){return a.id;}); var txs=S.transactions.filter(function(t){var d=new Date(t.date);return d.getMonth()===month&&d.getFullYear()===year&&savIds.indexOf(t.account_id)===-1;}); var iARS=0,eARS=0,iUSD=0,eUSD=0; txs.forEach(function(t){var isIn=t.type==='income';if(t.currency==='ARS'){isIn?(iARS+=t.amount):(eARS+=t.amount);}else{isIn?(iUSD+=t.amount):(eUSD+=t.amount);}}); document.getElementById('balance-month-label').textContent=monthName(month)+' '+year; document.getElementById('balance-main-val').textContent='$'+fmt(iARS-eARS)+' ARS'; document.getElementById('dash-income').textContent='+$'+fmt(iARS); document.getElementById('dash-expense').textContent='-$'+fmt(eARS); var tARS=iARS-eARS,tUSD=iUSD-eUSD; var arsEl=document.getElementById('dash-ars');arsEl.textContent=(tARS>=0?'+':'')+' $'+fmt(tARS);arsEl.className='si-val '+(tARS>=0?'green':'red'); var usdEl=document.getElementById('dash-usd');usdEl.textContent=(tUSD>=0?'+':'')+'U$'+fmt(tUSD);usdEl.className='si-val '+(tUSD>=0?'green':'red'); document.getElementById('dash-count').textContent=txs.length; var cats={};txs.forEach(function(t){cats[t.category_id]=true;});document.getElementById('dash-cats').textContent=Object.keys(cats).length; renderPie(txs.filter(function(t){return t.type==='expense';})); var recent=S.transactions.slice(0,5); document.getElementById('dash-recent-list').innerHTML=recent.length?'
'+recent.map(function(t){return txHTML(t,false);}).join('')+'
':'
πŸ’Έ

Sin movimientos

EmpezΓ‘ cargando tu primer gasto

'; } function renderPie(expenses){ var canvas=document.getElementById('pieChart'),legend=document.getElementById('pie-legend'),ctx=canvas.getContext('2d'); var W=canvas.offsetWidth||300,H=220;canvas.width=W;canvas.height=H;ctx.clearRect(0,0,W,H); var catMap={};expenses.forEach(function(t){var val=t.currency==='USD'?t.amount*t.tc:t.amount;catMap[t.category_id]=(catMap[t.category_id]||0)+val;}); var entries=Object.keys(catMap).map(function(k){return[k,catMap[k]];}).sort(function(a,b){return b[1]-a[1];}); var total=entries.reduce(function(s,e){return s+e[1];},0)||1;legend.innerHTML=''; if(!entries.length){ctx.fillStyle='#ffffff22';ctx.beginPath();ctx.arc(W/2,H/2,80,0,Math.PI*2);ctx.fill();ctx.fillStyle='#9ca3af';ctx.textAlign='center';ctx.font='14px Inter';ctx.fillText('Sin datos',W/2,H/2+5);return;} var cx=W/2,cy=H/2,r=Math.min(cx,cy)-20,startAngle=-Math.PI/2; entries.forEach(function(entry){ var cat=S.categories.find(function(c){return c.id===entry[0];})||{color:'#6b7280',name:'Otros',icon:'πŸ“¦'}; var slice=(entry[1]/total)*Math.PI*2; ctx.beginPath();ctx.moveTo(cx,cy);ctx.arc(cx,cy,r,startAngle,startAngle+slice);ctx.closePath();ctx.fillStyle=cat.color;ctx.fill();ctx.strokeStyle='#1a1d27';ctx.lineWidth=2;ctx.stroke(); legend.innerHTML+='
'+cat.icon+' '+cat.name+' '+Math.round(entry[1]/total*100)+'%
'; startAngle+=slice; }); ctx.beginPath();ctx.arc(cx,cy,r*0.5,0,Math.PI*2);ctx.fillStyle='#1a1d27';ctx.fill(); ctx.fillStyle='#fff';ctx.textAlign='center';ctx.font='bold 14px Inter';ctx.fillText('$'+fmt(total),cx,cy+5); } // ── COUPLE ──────────────────────────────────────── document.getElementById('btn-send-partner').addEventListener('click',function(){ var email=document.getElementById('partner-email').value.trim();if(!email){toast('IngresΓ‘ un email','error');return;} api('linkPartner',{uid:S.user.uid,partner_email:email}).then(function(d){ S.user.partner_uid=d.partner_uid;S.user.partner_name=d.partner_name;localStorage.setItem('session',JSON.stringify(S.user)); toast('Β‘Pareja vinculada! βœ“','success');closeModal('modal-link-partner');renderCouple(); }).catch(function(e){toast(e.message,'error');}); }); function renderCouple(){ var c=document.getElementById('couple-content'); if(!S.user.partner_uid){ c.innerHTML='
πŸ‘€

'+S.user.name+'

Sin pareja vinculada

'+ '
πŸ’‘

Modo pareja activado

VinculΓ‘ tu cuenta con la de tu pareja para compartir gastos.

'+ ''; document.getElementById('btn-open-link-partner').addEventListener('click',function(){showModal('modal-link-partner');});return; } var shared=S.transactions.filter(function(t){return t.shared;}); var iOwe=0,theyOwe=0; shared.forEach(function(t){var val=t.currency==='USD'?t.amount*t.tc:t.amount;var my=val*(t.split_me||50)/100;if(t.payer==='me')theyOwe+=(val-my);else iOwe+=my;}); var net=theyOwe-iOwe; c.innerHTML='
πŸ‘€
πŸ’ž
πŸ‘€
'+ '

'+S.user.name+' & '+(S.user.partner_name||'Pareja')+'

Vinculados

'+ '

Balance compartido (ARS)

'+ (net>=0?'+':'')+' $'+fmt(Math.abs(net))+'
'+(net>0?'Te deben':'Le debΓ©s a '+(S.user.partner_name||'tu pareja'))+'
'+ '
Gastos compartidos
'+ '
'+(shared.length?shared.map(function(t){return txHTML(t,false);}).join(''):'
🀝

Sin gastos compartidos

')+'
'; } // ── VACATIONS ───────────────────────────────────── document.getElementById('btn-save-vac').addEventListener('click',function(){ var name=document.getElementById('vac-name').value.trim();if(!name){toast('PonΓ© un nombre al viaje','error');return;} var vac={id:genId(),uid:S.user.uid,name:name,start_date:document.getElementById('vac-start').value,end_date:document.getElementById('vac-end').value,budget:parseFloat(document.getElementById('vac-budget').value)||0,currency:document.getElementById('vac-currency').value,created_at:new Date().toISOString()}; S.vacations.unshift(vac);closeModal('modal-new-vac');renderVacations();toast('Viaje creado βœ“','success');api('addVacation',{vacation:vac}).catch(function(){}); }); document.getElementById('btn-save-vexp').addEventListener('click',function(){ var desc=document.getElementById('vexp-desc').value.trim(),amount=parseFloat(document.getElementById('vexp-amount').value); if(!desc||!amount){toast('CompletΓ‘ los campos','error');return;} var item={id:genId(),vacation_id:S.activeVacId,type:'expense',description:desc,amount:amount,currency:document.getElementById('vexp-currency').value,tc:parseFloat(document.getElementById('vexp-tc').value)||S.tc.blue,shared:document.getElementById('vexp-shared').checked,payer:document.getElementById('vexp-payer').value,split_me:parseInt(document.getElementById('vexp-split-me').value)||50,created_at:new Date().toISOString()}; if(!S.vacItems[S.activeVacId])S.vacItems[S.activeVacId]=[];S.vacItems[S.activeVacId].unshift(item); closeModal('modal-vac-expense');renderVacations();toast('Gasto guardado βœ“','success');api('addVacationItem',{item:item}).catch(function(){}); }); function calcVacSpent(vacId){return(S.vacItems[vacId]||[]).filter(function(i){return i.type==='expense';}).reduce(function(s,i){return s+(i.currency==='USD'?i.amount*(i.tc||S.tc.blue):i.amount);},0);} function renderVacations(){ var c=document.getElementById('vacation-content'); var btnHtml=''; if(!S.vacations.length){c.innerHTML='
✈️

Sin viajes

CreΓ‘ tu primer viaje para empezar.

'+btnHtml;} else{ var cards=S.vacations.map(function(v){var spent=calcVacSpent(v.id),pct=Math.min(100,Math.round(spent/v.budget*100))||0,curr=v.currency==='USD'?'U$':'$'; return'
✈️ '+v.name+'
πŸ“… '+fmtDate(v.start_date)+' β†’ '+fmtDate(v.end_date)+'
'+ '
'+ '
'+curr+fmt(spent)+' gastado'+curr+fmt(v.budget-spent)+' restante
'+ '
'; }).join('');c.innerHTML=btnHtml+cards; } document.getElementById('btn-open-new-vac').addEventListener('click',function(){showModal('modal-new-vac');}); c.querySelectorAll('[data-vac-expense]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();S.activeVacId=btn.getAttribute('data-vac-expense');showModal('modal-vac-expense');});}); c.querySelectorAll('[data-vac-detail]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openVacationDetail(btn.getAttribute('data-vac-detail'));});}); } function openVacationDetail(vacId){ var vac=S.vacations.find(function(v){return v.id===vacId;});if(!vac)return; var items=S.vacItems[vacId]||[],expenses=items.filter(function(i){return i.type==='expense';}),checks=items.filter(function(i){return i.type==='checklist';}); var spent=calcVacSpent(vacId),pct=Math.min(100,Math.round(spent/vac.budget*100))||0,curr=vac.currency==='USD'?'U$':'$'; var overlay=document.createElement('div');overlay.className='modal-overlay open';overlay.style.zIndex='600'; var checkHtml=checks.length?checks.map(function(i){return'
βœ“
'+i.description+'
';}).join(''):'

Sin Γ­tems

'; var expHtml=expenses.length?expenses.map(function(i){return'
✈️
'+i.description+'
'+(i.shared?'πŸ’‘':'')+'
-'+(i.currency==='USD'?'U$':'$')+fmt(i.amount)+'
';}).join(''):'

Sin gastos aΓΊn

'; overlay.innerHTML=''; overlay.addEventListener('click',function(e){if(e.target===overlay)overlay.remove();});document.body.appendChild(overlay); document.getElementById('close-vac-detail').addEventListener('click',function(){overlay.remove();}); document.getElementById('checklist-container').addEventListener('click',function(e){ var item=e.target.closest('.checklist-item');if(!item)return; var box=item.querySelector('.check-box'),txt=item.querySelector('.check-text'),iid=item.getAttribute('data-item-id'); var vi=(S.vacItems[vacId]||[]).find(function(x){return x.id===iid;});if(vi)vi.done=!vi.done;box.classList.toggle('checked');txt.classList.toggle('done'); }); document.getElementById('btn-add-check').addEventListener('click',function(){ var inp=document.getElementById('new-check-txt'),desc=inp.value.trim();if(!desc)return; var ni={id:genId(),vacation_id:vacId,type:'checklist',description:desc,done:false}; if(!S.vacItems[vacId])S.vacItems[vacId]=[];S.vacItems[vacId].push(ni);inp.value=''; var container=document.getElementById('checklist-container');var p=container.querySelector('p');if(p)p.remove(); var div=document.createElement('div');div.className='checklist-item';div.setAttribute('data-item-id',ni.id);div.innerHTML='
βœ“
'+desc+'';container.appendChild(div); api('addVacationItem',{item:ni}).catch(function(){}); }); } window.addEventListener('resize',function(){if(S.activeTab==='dashboard')renderDashboard();}); +d.venta); } }).catch(function(){console.warn('No se pudo obtener el dΓ³lar oficial');}); } function loadAll(){ return Promise.all([ api('getTransactions',{uid:S.user.uid}), api('getCategories',{uid:S.user.uid}), api('getVacations',{uid:S.user.uid}), api('getAccounts',{uid:S.user.uid}) ]).then(function(results){ S.transactions=results[0].transactions||[]; var bc=results[1].categories||[]; S.categories=defaultCategories().concat(bc.filter(function(c){return c.type==='expense'||c.type==='income';})); S.vacations=results[2].vacations||[]; var ba=results[3].accounts||[]; ba.forEach(function(a){if(!S.accounts.find(function(x){return x.id===a.id;}))S.accounts.push(a);}); saveAccountsLocal(); }).catch(function(){if(!S.categories.length)S.categories=defaultCategories();}); } // ── TABS ───────────────────────────────────────── document.querySelectorAll('.nav-item').forEach(function(btn){btn.addEventListener('click',function(){switchTab(btn.getAttribute('data-tab'),btn);});}); function switchTab(tab,btn){ ['dashboard','transactions','accounts','savings','couple','vacation','settings'].forEach(function(t){var el=document.getElementById('tab-'+t);if(el)el.style.display='none';}); var active=document.getElementById('tab-'+tab);if(active)active.style.display=''; document.querySelectorAll('.nav-item').forEach(function(b){b.classList.remove('active');}); if(btn)btn.classList.add('active');S.activeTab=tab; var titles={dashboard:'Dashboard',transactions:'Movimientos',accounts:'Cuentas',savings:'Ahorros',couple:'Pareja',vacation:'Viajes',settings:'ConfiguraciΓ³n'}; document.getElementById('topbar-title').textContent=titles[tab]||tab; if(tab==='transactions')renderTransactions(); if(tab==='accounts')renderAccounts(); if(tab==='savings')renderSavings(); if(tab==='couple')renderCouple(); if(tab==='vacation')renderVacations(); if(tab==='settings')updateSettingsUI(); } document.getElementById('btn-go-settings').addEventListener('click',function(){switchTab('settings',document.querySelector('[data-tab="settings"]'));}); // ── MODE ────────────────────────────────────────── function updateModeUI(){ if(!S.user)return; var c=S.user.mode==='couple'||S.user.mode==='both',v=S.user.mode==='vacation'||S.user.mode==='both'; document.getElementById('nav-couple').style.display=c?'':'none'; document.getElementById('nav-vacation').style.display=v?'':'none'; var badges='';if(c)badges+='πŸ’‘ Pareja ';if(v)badges+='πŸ–οΈ Vacaciones'; document.getElementById('mode-indicator').innerHTML=badges; document.getElementById('shared-section').style.display=c?'':'none'; document.getElementById('vexp-shared-wrap').style.display=c?'':'none'; } document.getElementById('toggle-couple').addEventListener('change',function(){toggleMode('couple',this.checked);}); document.getElementById('toggle-vacation').addEventListener('change',function(){toggleMode('vacation',this.checked);}); function toggleMode(mode,checked){ if(!S.user)return; var c=S.user.mode==='couple'||S.user.mode==='both',v=S.user.mode==='vacation'||S.user.mode==='both'; if(mode==='couple')c=checked;if(mode==='vacation')v=checked; S.user.mode=c&&v?'both':c?'couple':v?'vacation':'normal'; localStorage.setItem('session',JSON.stringify(S.user));updateModeUI(); api('updateUserMode',{uid:S.user.uid,mode:S.user.mode}).catch(function(){}); } // ── SETTINGS / CATEGORIES ──────────────────────── function updateSettingsUI(){ if(!S.user)return; document.getElementById('settings-name').textContent=S.user.name||'β€”'; document.getElementById('settings-email').textContent=S.user.email||'β€”'; document.getElementById('settings-avatar').textContent=(S.user.name||'?')[0].toUpperCase(); document.getElementById('toggle-couple').checked=S.user.mode==='couple'||S.user.mode==='both'; document.getElementById('toggle-vacation').checked=S.user.mode==='vacation'||S.user.mode==='both'; var list=document.getElementById('categories-list-settings'); list.innerHTML=S.categories.map(function(cat){ return'
'+ '
'+ ''+cat.icon+''+ '
'+cat.name+'
'+ '
'+(cat.type==='income'?'Ingreso':'Gasto')+'
'+ '
'+ ''+ ''+ '
'; }).join(''); list.querySelectorAll('[data-edit-cat]').forEach(function(btn){ btn.addEventListener('click',function(){openEditCat(btn.getAttribute('data-edit-cat'));}); }); } // Edit category function openEditCat(catId){ var cat=S.categories.find(function(c){return c.id===catId;});if(!cat)return; S.editingCatId=catId; document.getElementById('edit-cat-name').value=cat.name; document.getElementById('edit-cat-icon').value=cat.icon; showModal('modal-edit-cat'); } document.getElementById('btn-save-edit-cat').addEventListener('click',function(){ var cat=S.categories.find(function(c){return c.id===S.editingCatId;});if(!cat)return; var name=document.getElementById('edit-cat-name').value.trim(); var icon=document.getElementById('edit-cat-icon').value.trim(); if(!name){toast('IngresΓ‘ un nombre','error');return;} cat.name=name;if(icon)cat.icon=icon; closeModal('modal-edit-cat');updateSettingsUI();renderCatPicker(); toast('CategorΓ­a actualizada βœ“','success'); api('updateCategory',{category:cat}).catch(function(){}); }); document.getElementById('btn-new-cat').addEventListener('click',function(){ S.newCatType='expense'; document.getElementById('cat-type-expense').classList.add('active');document.getElementById('cat-type-income').classList.remove('active'); showModal('modal-new-cat'); }); document.getElementById('cat-type-expense').addEventListener('click',function(){S.newCatType='expense';document.getElementById('cat-type-expense').classList.add('active');document.getElementById('cat-type-income').classList.remove('active');}); document.getElementById('cat-type-income').addEventListener('click',function(){S.newCatType='income';document.getElementById('cat-type-income').classList.add('active');document.getElementById('cat-type-expense').classList.remove('active');}); document.getElementById('btn-save-cat').addEventListener('click',function(){ var name=document.getElementById('cat-name').value.trim(),icon=document.getElementById('cat-icon').value.trim()||'πŸ“¦',color=document.getElementById('cat-color').value; if(!name){toast('IngresΓ‘ un nombre','error');return;} var cat={id:genId(),uid:S.user.uid,name:name,icon:icon,color:color,type:S.newCatType}; S.categories.push(cat);closeModal('modal-new-cat');updateSettingsUI(); toast('CategorΓ­a creada βœ“','success');api('addCategory',{category:cat}).catch(function(){}); }); // ── TC ──────────────────────────────────────────── document.getElementById('btn-open-tc').addEventListener('click',function(){ document.getElementById('tc-oficial').value=S.tc.oficial;document.getElementById('tc-blue').value=S.tc.blue;showModal('modal-tc'); }); document.getElementById('btn-save-tc').addEventListener('click',function(){ S.tc.oficial=parseFloat(document.getElementById('tc-oficial').value)||S.tc.oficial; S.tc.blue=parseFloat(document.getElementById('tc-blue').value)||S.tc.blue; localStorage.setItem('tc',JSON.stringify(S.tc));toast('Tipo de cambio guardado βœ“','success');closeModal('modal-tc'); if(S.activeTab==='savings')renderSavings(); }); // ── MODALS CLOSE ───────────────────────────────── document.querySelectorAll('[data-close]').forEach(function(btn){btn.addEventListener('click',function(){closeModal(btn.getAttribute('data-close'));});}); document.querySelectorAll('.modal-overlay').forEach(function(o){o.addEventListener('click',function(e){if(e.target===o)o.classList.remove('open');});}); // ── ACCOUNTS ───────────────────────────────────── function renderAccounts(){ var c=document.getElementById('accounts-content'); var nonSav=S.accounts.filter(function(a){return!isSavings(a);}); var total=nonSav.reduce(function(s,a){return s+(a.currency==='USD'?a.balance*S.tc.blue:a.balance);},0); var btnHtml='
'+ ''+ ''+ '
'; if(!S.accounts.length){ c.innerHTML='
🏦

Sin cuentas

CreΓ‘ tu primera cuenta para empezar a registrar movimientos.

'+btnHtml; }else{ var totalHtml='

Total disponible (sin ahorros)

$'+fmt(total)+'
En ARS equivalente al TC blue
'; var cards=nonSav.map(function(a){return accCardHTML(a,true);}).join(''); c.innerHTML=btnHtml+totalHtml+cards; } document.getElementById('btn-open-new-account').addEventListener('click',function(){openNewAccount();}); document.getElementById('btn-open-transfer').addEventListener('click',function(){openTransfer();}); c.querySelectorAll('[data-edit-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openEditAccount(btn.getAttribute('data-edit-acc'));});}); c.querySelectorAll('[data-del-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteAccount(btn.getAttribute('data-del-acc'));});}); c.querySelectorAll('[data-acc-history]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();showAccHistory(btn.getAttribute('data-acc-history'));});}); } function accCardHTML(acc,showActions){ var cfg=ACC_TYPES[acc.type]||ACC_TYPES.bank; var icon=acc.icon||cfg.icon; var balStr=acc.currency==='USD'?'U$'+fmt(acc.balance):'$'+fmt(acc.balance); var subStr=acc.currency==='USD'?'β‰ˆ $'+fmt(acc.balance*S.tc.blue)+' ARS':''; var actions=showActions?'
'+ ''+ ''+ ''+ '
':''; return''; } function openNewAccount(){ S.editingAccId=null; document.getElementById('acc-modal-title').textContent='Nueva cuenta'; document.getElementById('acc-name').value='';document.getElementById('acc-balance').value=''; document.getElementById('acc-icon').value='';document.getElementById('acc-color').value='#6c63ff'; document.getElementById('acc-type').value='bank';document.getElementById('acc-currency').value='ARS'; document.getElementById('btn-save-account').textContent='Crear cuenta'; showModal('modal-new-account'); } function openEditAccount(accId){ var acc=S.accounts.find(function(a){return a.id===accId;});if(!acc)return; S.editingAccId=accId; document.getElementById('acc-modal-title').textContent='Editar cuenta'; document.getElementById('acc-name').value=acc.name; document.getElementById('acc-balance').value=acc.balance; document.getElementById('acc-icon').value=acc.icon||''; document.getElementById('acc-color').value=acc.color||'#6c63ff'; document.getElementById('acc-type').value=acc.type; document.getElementById('acc-currency').value=acc.currency; document.getElementById('btn-save-account').textContent='Guardar cambios'; showModal('modal-new-account'); } document.getElementById('btn-save-account').addEventListener('click',function(){ var name=document.getElementById('acc-name').value.trim(); var type=document.getElementById('acc-type').value; var balance=parseFloat(document.getElementById('acc-balance').value)||0; var currency=document.getElementById('acc-currency').value; var icon=document.getElementById('acc-icon').value.trim()||(ACC_TYPES[type]||ACC_TYPES.bank).icon; var color=document.getElementById('acc-color').value; if(!name){toast('PonΓ© un nombre a la cuenta','error');return;} if(S.editingAccId){ var acc=S.accounts.find(function(a){return a.id===S.editingAccId;}); if(acc){acc.name=name;acc.type=type;acc.balance=balance;acc.currency=currency;acc.icon=icon;acc.color=color;} }else{ var newAcc={id:genId(),uid:S.user.uid,name:name,type:type,balance:balance,currency:currency,icon:icon,color:color,created_at:new Date().toISOString()}; S.accounts.push(newAcc); api('addAccount',{account:newAcc}).catch(function(){}); } saveAccountsLocal();closeModal('modal-new-account'); if(S.activeTab==='accounts')renderAccounts(); if(S.activeTab==='savings')renderSavings(); populateAccountSelect(); toast((S.editingAccId?'Cuenta actualizada':'Cuenta creada')+' βœ“','success'); if(S.editingAccId)api('updateAccount',{account:S.accounts.find(function(a){return a.id===S.editingAccId;})}).catch(function(){}); }); function deleteAccount(accId){ if(!confirm('ΒΏEliminar esta cuenta? Las transacciones asociadas no se borrarΓ‘n.'))return; S.accounts=S.accounts.filter(function(a){return a.id!==accId;}); saveAccountsLocal(); if(S.activeTab==='accounts')renderAccounts(); if(S.activeTab==='savings')renderSavings(); populateAccountSelect(); toast('Cuenta eliminada','success'); api('deleteAccount',{id:accId}).catch(function(){}); } function showAccHistory(accId){ var acc=S.accounts.find(function(a){return a.id===accId;});if(!acc)return; var txs=S.transactions.filter(function(t){return t.account_id===accId;}); var overlay=document.createElement('div');overlay.className='modal-overlay open';overlay.style.zIndex='600'; var txHtml=txs.length?'
'+txs.map(txHTML).join('')+'
':'
πŸ“­

Sin movimientos

'; overlay.innerHTML=''; overlay.addEventListener('click',function(e){if(e.target===overlay)overlay.remove();}); document.body.appendChild(overlay); document.getElementById('close-acc-history').addEventListener('click',function(){overlay.remove();}); } // Transfer function openTransfer(){ populateTransferSelects(); document.getElementById('transfer-amount').value=''; document.getElementById('transfer-date').value=todayStr(); showModal('modal-transfer'); } function populateTransferSelects(){ var opts=S.accounts.map(function(a){return'';}).join(''); document.getElementById('transfer-from').innerHTML=opts; document.getElementById('transfer-to').innerHTML=opts; } document.getElementById('btn-save-transfer').addEventListener('click',function(){ var fromId=document.getElementById('transfer-from').value; var toId=document.getElementById('transfer-to').value; var amount=parseFloat(document.getElementById('transfer-amount').value); var currency=document.getElementById('transfer-currency').value; var date=document.getElementById('transfer-date').value||todayStr(); if(fromId===toId){toast('ElegΓ­ cuentas distintas','error');return;} if(!amount||amount<=0){toast('IngresΓ‘ un monto vΓ‘lido','error');return;} var from=S.accounts.find(function(a){return a.id===fromId;}); var to=S.accounts.find(function(a){return a.id===toId;}); // Adjust balances var debit=currency===from.currency?amount:(currency==='USD'?amount*S.tc.blue:amount/S.tc.blue); var credit=currency===to.currency?amount:(currency==='USD'?amount*S.tc.blue:amount/S.tc.blue); from.balance=(from.balance||0)-debit; to.balance=(to.balance||0)+credit; saveAccountsLocal(); // Record as two transactions var txOut={id:genId(),uid:S.user.uid,date:date,type:'expense',amount:amount,currency:currency,tc:S.tc.blue,category_id:'c9',account_id:fromId,tags:[],description:'Transferencia β†’ '+to.name,note:'',shared:false,payer:'me',split_me:100,created_at:new Date().toISOString()}; var txIn={id:genId(),uid:S.user.uid,date:date,type:'income',amount:amount,currency:currency,tc:S.tc.blue,category_id:'i4',account_id:toId,tags:[],description:'Transferencia ← '+from.name,note:'',shared:false,payer:'me',split_me:100,created_at:new Date().toISOString()}; S.transactions.unshift(txIn);S.transactions.unshift(txOut); closeModal('modal-transfer');renderAccounts();renderDashboard(); toast('Transferencia realizada βœ“','success'); api('addTransaction',{transaction:txOut}).catch(function(){}); api('addTransaction',{transaction:txIn}).catch(function(){}); }); // ── SAVINGS ────────────────────────────────────── function renderSavings(){ var c=document.getElementById('savings-content'); var savs=S.accounts.filter(function(a){return isSavings(a);}); var tUSD=savs.reduce(function(s,a){return s+(a.currency==='USD'?a.balance:0);},0); var tARS=savs.reduce(function(s,a){return s+(a.currency==='ARS'?a.balance:a.balance*S.tc.blue);},0); var btnHtml=''; if(!savs.length){ c.innerHTML='
🐷

Sin ahorros

CreΓ‘ una cuenta de ahorro para llevar el registro de lo que tenΓ©s guardado.

'+btnHtml; }else{ var summary='

Total ahorrado

$'+fmt(tARS)+'
U$'+fmt(tUSD)+' en dΓ³lares Β· TC blue $'+fmt(S.tc.blue)+'
'; var cards=savs.map(function(a){return accCardHTML(a,true);}).join(''); c.innerHTML=btnHtml+summary+cards; } document.getElementById('btn-open-new-savings').addEventListener('click',function(){ openNewAccount();document.getElementById('acc-type').value='savings'; }); c.querySelectorAll('[data-edit-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openEditAccount(btn.getAttribute('data-edit-acc'));});}); c.querySelectorAll('[data-del-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteAccount(btn.getAttribute('data-del-acc'));});}); c.querySelectorAll('[data-acc-history]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();showAccHistory(btn.getAttribute('data-acc-history'));});}); } // ── POPULATE ACCOUNT SELECT ────────────────────── function populateAccountSelect(){ var sel=document.getElementById('tx-account-id'); var nonSav=S.accounts.filter(function(a){return!isSavings(a);}); sel.innerHTML=nonSav.length ?nonSav.map(function(a){return'';}).join('') :''; } // ── NEW / EDIT TX ──────────────────────────────── document.getElementById('fab').addEventListener('click',function(){openTxModal(null);}); function openTxModal(tx){ S.editingTxId=tx?tx.id:null; document.getElementById('tx-modal-title').textContent=tx?'Editar transacciΓ³n':'Nueva transacciΓ³n'; document.getElementById('btn-save-tx').textContent=tx?'Guardar cambios':'Guardar'; document.getElementById('tx-desc').value=tx?tx.description:''; document.getElementById('tx-amount').value=tx?tx.amount:''; document.getElementById('tx-note').value=tx?tx.note:''; document.getElementById('tx-date').value=tx?tx.date:todayStr(); document.getElementById('tx-currency').value=tx?tx.currency:'ARS'; document.getElementById('tc-field').style.display=(tx&&tx.currency==='USD')||false?'':'none'; if(tx&&tx.currency==='USD')document.getElementById('tx-tc').value=tx.tc; document.getElementById('tx-shared').checked=tx?tx.shared:false; document.getElementById('split-section').style.display=(tx&&tx.shared)?'':'none'; document.getElementById('split-me').value=tx?tx.split_me:50; document.getElementById('split-partner').value=tx?(100-tx.split_me):50; S.txType=tx?tx.type:'expense'; S.selectedCat=tx?tx.category_id:null; S.currentTags=tx?(tx.tags||[]).slice():[]; document.querySelectorAll('.toggle-opt[data-type]').forEach(function(b){b.classList.toggle('active',b.getAttribute('data-type')===S.txType);}); populateAccountSelect(); if(tx&&tx.account_id){var sel=document.getElementById('tx-account-id');for(var i=0;iΓ—'; pill.querySelector('button').addEventListener('click',function(){S.currentTags=S.currentTags.filter(function(t){return t!==tag;});renderTagsWrap();}); wrap.insertBefore(pill,inp); }); } function renderCatPicker(){ var grid=document.getElementById('cat-picker');grid.innerHTML=''; var filtered=S.categories.filter(function(cat){return cat.type===S.txType;}); if(!filtered.length){grid.innerHTML='

Sin categorΓ­as para este tipo

';return;} filtered.forEach(function(cat){ var d=document.createElement('div');d.className='cat-opt'+(S.selectedCat===cat.id?' selected':''); d.innerHTML=''+cat.icon+''+cat.name+''; d.addEventListener('click',function(){S.selectedCat=cat.id;renderCatPicker();});grid.appendChild(d); }); } document.getElementById('btn-save-tx').addEventListener('click',function(){ var desc=document.getElementById('tx-desc').value.trim(); var amount=parseFloat(document.getElementById('tx-amount').value); var currency=document.getElementById('tx-currency').value; var date=document.getElementById('tx-date').value||todayStr(); var tc=currency==='USD'?(parseFloat(document.getElementById('tx-tc').value)||S.tc.blue):1; var note=document.getElementById('tx-note').value.trim(); var shared=document.getElementById('tx-shared').checked; var payer=document.getElementById('tx-payer').value; var splitMe=parseInt(document.getElementById('split-me').value)||50; var accountId=document.getElementById('tx-account-id').value; if(!desc){toast('AgregΓ‘ una descripciΓ³n','error');return;} if(!amount||amount<=0){toast('IngresΓ‘ un monto vΓ‘lido','error');return;} if(!accountId){toast('SeleccionΓ‘ una cuenta','error');return;} var defaultCatId=S.txType==='income'?'i4':'c9'; if(S.editingTxId){ // Editing: reverse old effect on account, apply new var oldTx=S.transactions.find(function(t){return t.id===S.editingTxId;}); if(oldTx){ var oldAcc=S.accounts.find(function(a){return a.id===oldTx.account_id;}); if(oldAcc){var rev=oldTx.type==='income'?-oldTx.amount:oldTx.amount;if(oldTx.currency==='USD'&&oldAcc.currency==='ARS')rev*=oldTx.tc;else if(oldTx.currency==='ARS'&&oldAcc.currency==='USD')rev/=oldTx.tc;oldAcc.balance=(oldAcc.balance||0)+rev;} oldTx.description=desc;oldTx.amount=amount;oldTx.currency=currency;oldTx.tc=tc; oldTx.date=date;oldTx.note=note;oldTx.shared=shared;oldTx.payer=shared?payer:'me'; oldTx.split_me=shared?splitMe:100;oldTx.category_id=S.selectedCat||defaultCatId; oldTx.account_id=accountId;oldTx.tags=S.currentTags.slice(); } var newAcc=S.accounts.find(function(a){return a.id===accountId;}); if(newAcc){var delta=S.txType==='income'?amount:-amount;if(currency==='USD'&&newAcc.currency==='ARS')delta*=tc;else if(currency==='ARS'&&newAcc.currency==='USD')delta/=tc;newAcc.balance=(newAcc.balance||0)+delta;} saveAccountsLocal();closeModal('modal-new-tx');renderDashboard(); if(S.activeTab==='transactions')renderTransactions(); if(S.activeTab==='accounts')renderAccounts(); toast('TransacciΓ³n actualizada βœ“','success'); api('updateTransaction',{transaction:oldTx}).catch(function(){}); }else{ var acc=S.accounts.find(function(a){return a.id===accountId;}); if(acc){var d2=S.txType==='income'?amount:-amount;if(currency==='USD'&&acc.currency==='ARS')d2*=tc;else if(currency==='ARS'&&acc.currency==='USD')d2/=tc;acc.balance=(acc.balance||0)+d2;saveAccountsLocal();api('updateAccountBalance',{id:acc.id,balance:acc.balance}).catch(function(){});} var newTx={id:genId(),uid:S.user.uid,date:date,type:S.txType,amount:amount,currency:currency,tc:tc,category_id:S.selectedCat||defaultCatId,account_id:accountId,tags:S.currentTags.slice(),description:desc,note:note,shared:shared,payer:shared?payer:'me',split_me:shared?splitMe:100,created_at:new Date().toISOString()}; S.transactions.unshift(newTx);closeModal('modal-new-tx');renderDashboard(); if(S.activeTab==='transactions')renderTransactions(); if(S.activeTab==='accounts')renderAccounts(); toast('TransacciΓ³n guardada βœ“','success');api('addTransaction',{transaction:newTx}).catch(function(){}); } }); // ── TRANSACTIONS ────────────────────────────────── document.querySelectorAll('.filter-chip').forEach(function(btn){ btn.addEventListener('click',function(){ S.txFilter=btn.getAttribute('data-filter'); document.querySelectorAll('.filter-chip').forEach(function(b){b.classList.remove('active');});btn.classList.add('active');renderTransactions(); }); }); function renderTransactions(){ var container=document.getElementById('tx-list-container'); var txs=S.transactions.slice(); if(S.txFilter==='income')txs=txs.filter(function(t){return t.type==='income';}); if(S.txFilter==='expense')txs=txs.filter(function(t){return t.type==='expense';}); if(S.txFilter==='shared')txs=txs.filter(function(t){return t.shared;}); if(S.txFilter==='ARS')txs=txs.filter(function(t){return t.currency==='ARS';}); if(S.txFilter==='USD')txs=txs.filter(function(t){return t.currency==='USD';}); if(!txs.length){container.innerHTML='
πŸ“­

Sin movimientos

TocΓ‘ el + para agregar tu primer transacciΓ³n

';return;} container.innerHTML='
'+txs.map(function(tx){return txHTML(tx,true);}).join('')+'
'; container.querySelectorAll('[data-edit-tx]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();var tx=S.transactions.find(function(t){return t.id===btn.getAttribute('data-edit-tx');});if(tx)openTxModal(tx);});}); container.querySelectorAll('[data-del-tx]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteTx(btn.getAttribute('data-del-tx'));});}); } function deleteTx(txId){ if(!confirm('ΒΏEliminar esta transacciΓ³n?'))return; var tx=S.transactions.find(function(t){return t.id===txId;}); if(tx){ var acc=S.accounts.find(function(a){return a.id===tx.account_id;}); if(acc){var rev=tx.type==='income'?-tx.amount:tx.amount;if(tx.currency==='USD'&&acc.currency==='ARS')rev*=tx.tc;else if(tx.currency==='ARS'&&acc.currency==='USD')rev/=tx.tc;acc.balance=(acc.balance||0)+rev;saveAccountsLocal();} } S.transactions=S.transactions.filter(function(t){return t.id!==txId;}); renderTransactions();renderDashboard(); toast('TransacciΓ³n eliminada','success'); api('deleteTransaction',{id:txId}).catch(function(){}); } function txHTML(tx,showActions){ var cat=S.categories.find(function(c){return c.id===tx.category_id;})||{icon:'πŸ“¦',color:'#6b7280'}; var acc=S.accounts.find(function(a){return a.id===tx.account_id;}); var sign=tx.type==='income'?'+':'-',cls=tx.type==='income'?'income':'expense'; var tags=(tx.tags||[]).map(function(t){return'#'+t+'';}).join(''); var badge=tx.shared?'πŸ’‘':''; var accBadge=acc?'':''; var amt=tx.currency==='USD'?'U$'+fmt(tx.amount):'$'+fmt(tx.amount); var equiv=tx.currency==='USD'?'β‰ˆ $'+fmt(tx.amount*tx.tc)+' ARS':''; var actions=showActions?'
':''; return'
'+ '
'+cat.icon+'
'+ '
'+tx.description+'
'+ '
'+fmtDate(tx.date)+' '+accBadge+' '+badge+' '+tags+'
'+ '
'+ '
'+sign+amt+'
'+(equiv?'
'+equiv+'
':'')+'
'+ actions+'
'; } // ── DASHBOARD ───────────────────────────────────── function renderDashboard(){ var now=new Date(),month=now.getMonth(),year=now.getFullYear(); var savIds=S.accounts.filter(isSavings).map(function(a){return a.id;}); var txs=S.transactions.filter(function(t){var d=new Date(t.date);return d.getMonth()===month&&d.getFullYear()===year&&savIds.indexOf(t.account_id)===-1;}); var iARS=0,eARS=0,iUSD=0,eUSD=0; txs.forEach(function(t){var isIn=t.type==='income';if(t.currency==='ARS'){isIn?(iARS+=t.amount):(eARS+=t.amount);}else{isIn?(iUSD+=t.amount):(eUSD+=t.amount);}}); document.getElementById('balance-month-label').textContent=monthName(month)+' '+year; document.getElementById('balance-main-val').textContent='$'+fmt(iARS-eARS)+' ARS'; document.getElementById('dash-income').textContent='+$'+fmt(iARS); document.getElementById('dash-expense').textContent='-$'+fmt(eARS); var tARS=iARS-eARS,tUSD=iUSD-eUSD; var arsEl=document.getElementById('dash-ars');arsEl.textContent=(tARS>=0?'+':'')+' $'+fmt(tARS);arsEl.className='si-val '+(tARS>=0?'green':'red'); var usdEl=document.getElementById('dash-usd');usdEl.textContent=(tUSD>=0?'+':'')+'U$'+fmt(tUSD);usdEl.className='si-val '+(tUSD>=0?'green':'red'); document.getElementById('dash-count').textContent=txs.length; var cats={};txs.forEach(function(t){cats[t.category_id]=true;});document.getElementById('dash-cats').textContent=Object.keys(cats).length; renderPie(txs.filter(function(t){return t.type==='expense';})); var recent=S.transactions.slice(0,5); document.getElementById('dash-recent-list').innerHTML=recent.length?'
'+recent.map(function(t){return txHTML(t,false);}).join('')+'
':'
πŸ’Έ

Sin movimientos

EmpezΓ‘ cargando tu primer gasto

'; } function renderPie(expenses){ var canvas=document.getElementById('pieChart'),legend=document.getElementById('pie-legend'),ctx=canvas.getContext('2d'); var W=canvas.offsetWidth||300,H=220;canvas.width=W;canvas.height=H;ctx.clearRect(0,0,W,H); var catMap={};expenses.forEach(function(t){var val=t.currency==='USD'?t.amount*t.tc:t.amount;catMap[t.category_id]=(catMap[t.category_id]||0)+val;}); var entries=Object.keys(catMap).map(function(k){return[k,catMap[k]];}).sort(function(a,b){return b[1]-a[1];}); var total=entries.reduce(function(s,e){return s+e[1];},0)||1;legend.innerHTML=''; if(!entries.length){ctx.fillStyle='#ffffff22';ctx.beginPath();ctx.arc(W/2,H/2,80,0,Math.PI*2);ctx.fill();ctx.fillStyle='#9ca3af';ctx.textAlign='center';ctx.font='14px Inter';ctx.fillText('Sin datos',W/2,H/2+5);return;} var cx=W/2,cy=H/2,r=Math.min(cx,cy)-20,startAngle=-Math.PI/2; entries.forEach(function(entry){ var cat=S.categories.find(function(c){return c.id===entry[0];})||{color:'#6b7280',name:'Otros',icon:'πŸ“¦'}; var slice=(entry[1]/total)*Math.PI*2; ctx.beginPath();ctx.moveTo(cx,cy);ctx.arc(cx,cy,r,startAngle,startAngle+slice);ctx.closePath();ctx.fillStyle=cat.color;ctx.fill();ctx.strokeStyle='#1a1d27';ctx.lineWidth=2;ctx.stroke(); legend.innerHTML+='
'+cat.icon+' '+cat.name+' '+Math.round(entry[1]/total*100)+'%
'; startAngle+=slice; }); ctx.beginPath();ctx.arc(cx,cy,r*0.5,0,Math.PI*2);ctx.fillStyle='#1a1d27';ctx.fill(); ctx.fillStyle='#fff';ctx.textAlign='center';ctx.font='bold 14px Inter';ctx.fillText('$'+fmt(total),cx,cy+5); } // ── COUPLE ──────────────────────────────────────── document.getElementById('btn-send-partner').addEventListener('click',function(){ var email=document.getElementById('partner-email').value.trim();if(!email){toast('IngresΓ‘ un email','error');return;} api('linkPartner',{uid:S.user.uid,partner_email:email}).then(function(d){ S.user.partner_uid=d.partner_uid;S.user.partner_name=d.partner_name;localStorage.setItem('session',JSON.stringify(S.user)); toast('Β‘Pareja vinculada! βœ“','success');closeModal('modal-link-partner');renderCouple(); }).catch(function(e){toast(e.message,'error');}); }); function renderCouple(){ var c=document.getElementById('couple-content'); if(!S.user.partner_uid){ c.innerHTML='
πŸ‘€

'+S.user.name+'

Sin pareja vinculada

'+ '
πŸ’‘

Modo pareja activado

VinculΓ‘ tu cuenta con la de tu pareja para compartir gastos.

'+ ''; document.getElementById('btn-open-link-partner').addEventListener('click',function(){showModal('modal-link-partner');});return; } var shared=S.transactions.filter(function(t){return t.shared;}); var iOwe=0,theyOwe=0; shared.forEach(function(t){var val=t.currency==='USD'?t.amount*t.tc:t.amount;var my=val*(t.split_me||50)/100;if(t.payer==='me')theyOwe+=(val-my);else iOwe+=my;}); var net=theyOwe-iOwe; c.innerHTML='
πŸ‘€
πŸ’ž
πŸ‘€
'+ '

'+S.user.name+' & '+(S.user.partner_name||'Pareja')+'

Vinculados

'+ '

Balance compartido (ARS)

'+ (net>=0?'+':'')+' $'+fmt(Math.abs(net))+'
'+(net>0?'Te deben':'Le debΓ©s a '+(S.user.partner_name||'tu pareja'))+'
'+ '
Gastos compartidos
'+ '
'+(shared.length?shared.map(function(t){return txHTML(t,false);}).join(''):'
🀝

Sin gastos compartidos

')+'
'; } // ── VACATIONS ───────────────────────────────────── document.getElementById('btn-save-vac').addEventListener('click',function(){ var name=document.getElementById('vac-name').value.trim();if(!name){toast('PonΓ© un nombre al viaje','error');return;} var vac={id:genId(),uid:S.user.uid,name:name,start_date:document.getElementById('vac-start').value,end_date:document.getElementById('vac-end').value,budget:parseFloat(document.getElementById('vac-budget').value)||0,currency:document.getElementById('vac-currency').value,created_at:new Date().toISOString()}; S.vacations.unshift(vac);closeModal('modal-new-vac');renderVacations();toast('Viaje creado βœ“','success');api('addVacation',{vacation:vac}).catch(function(){}); }); document.getElementById('btn-save-vexp').addEventListener('click',function(){ var desc=document.getElementById('vexp-desc').value.trim(),amount=parseFloat(document.getElementById('vexp-amount').value); if(!desc||!amount){toast('CompletΓ‘ los campos','error');return;} var item={id:genId(),vacation_id:S.activeVacId,type:'expense',description:desc,amount:amount,currency:document.getElementById('vexp-currency').value,tc:parseFloat(document.getElementById('vexp-tc').value)||S.tc.blue,shared:document.getElementById('vexp-shared').checked,payer:document.getElementById('vexp-payer').value,split_me:parseInt(document.getElementById('vexp-split-me').value)||50,created_at:new Date().toISOString()}; if(!S.vacItems[S.activeVacId])S.vacItems[S.activeVacId]=[];S.vacItems[S.activeVacId].unshift(item); closeModal('modal-vac-expense');renderVacations();toast('Gasto guardado βœ“','success');api('addVacationItem',{item:item}).catch(function(){}); }); function calcVacSpent(vacId){return(S.vacItems[vacId]||[]).filter(function(i){return i.type==='expense';}).reduce(function(s,i){return s+(i.currency==='USD'?i.amount*(i.tc||S.tc.blue):i.amount);},0);} function renderVacations(){ var c=document.getElementById('vacation-content'); var btnHtml=''; if(!S.vacations.length){c.innerHTML='
✈️

Sin viajes

CreΓ‘ tu primer viaje para empezar.

'+btnHtml;} else{ var cards=S.vacations.map(function(v){var spent=calcVacSpent(v.id),pct=Math.min(100,Math.round(spent/v.budget*100))||0,curr=v.currency==='USD'?'U$':'$'; return'
✈️ '+v.name+'
πŸ“… '+fmtDate(v.start_date)+' β†’ '+fmtDate(v.end_date)+'
'+ '
'+ '
'+curr+fmt(spent)+' gastado'+curr+fmt(v.budget-spent)+' restante
'+ '
'; }).join('');c.innerHTML=btnHtml+cards; } document.getElementById('btn-open-new-vac').addEventListener('click',function(){showModal('modal-new-vac');}); c.querySelectorAll('[data-vac-expense]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();S.activeVacId=btn.getAttribute('data-vac-expense');showModal('modal-vac-expense');});}); c.querySelectorAll('[data-vac-detail]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openVacationDetail(btn.getAttribute('data-vac-detail'));});}); } function openVacationDetail(vacId){ var vac=S.vacations.find(function(v){return v.id===vacId;});if(!vac)return; var items=S.vacItems[vacId]||[],expenses=items.filter(function(i){return i.type==='expense';}),checks=items.filter(function(i){return i.type==='checklist';}); var spent=calcVacSpent(vacId),pct=Math.min(100,Math.round(spent/vac.budget*100))||0,curr=vac.currency==='USD'?'U$':'$'; var overlay=document.createElement('div');overlay.className='modal-overlay open';overlay.style.zIndex='600'; var checkHtml=checks.length?checks.map(function(i){return'
βœ“
'+i.description+'
';}).join(''):'

Sin Γ­tems

'; var expHtml=expenses.length?expenses.map(function(i){return'
✈️
'+i.description+'
'+(i.shared?'πŸ’‘':'')+'
-'+(i.currency==='USD'?'U$':'$')+fmt(i.amount)+'
';}).join(''):'

Sin gastos aΓΊn

'; overlay.innerHTML=''; overlay.addEventListener('click',function(e){if(e.target===overlay)overlay.remove();});document.body.appendChild(overlay); document.getElementById('close-vac-detail').addEventListener('click',function(){overlay.remove();}); document.getElementById('checklist-container').addEventListener('click',function(e){ var item=e.target.closest('.checklist-item');if(!item)return; var box=item.querySelector('.check-box'),txt=item.querySelector('.check-text'),iid=item.getAttribute('data-item-id'); var vi=(S.vacItems[vacId]||[]).find(function(x){return x.id===iid;});if(vi)vi.done=!vi.done;box.classList.toggle('checked');txt.classList.toggle('done'); }); document.getElementById('btn-add-check').addEventListener('click',function(){ var inp=document.getElementById('new-check-txt'),desc=inp.value.trim();if(!desc)return; var ni={id:genId(),vacation_id:vacId,type:'checklist',description:desc,done:false}; if(!S.vacItems[vacId])S.vacItems[vacId]=[];S.vacItems[vacId].push(ni);inp.value=''; var container=document.getElementById('checklist-container');var p=container.querySelector('p');if(p)p.remove(); var div=document.createElement('div');div.className='checklist-item';div.setAttribute('data-item-id',ni.id);div.innerHTML='
βœ“
'+desc+'';container.appendChild(div); api('addVacationItem',{item:ni}).catch(function(){}); }); } window.addEventListener('resize',function(){if(S.activeTab==='dashboard')renderDashboard();}); +(d.venta||'β€”'); } }).catch(function(){ document.getElementById('tc-oficial-live').textContent='⚠️ No se pudo actualizar β€” ingresalo manual'; }); showModal('modal-tc'); }); document.getElementById('btn-save-tc').addEventListener('click',function(){ S.tc.oficial=parseFloat(document.getElementById('tc-oficial').value)||S.tc.oficial; S.tc.blue=parseFloat(document.getElementById('tc-blue').value)||S.tc.blue; localStorage.setItem('tc',JSON.stringify(S.tc));toast('Tipo de cambio guardado βœ“','success');closeModal('modal-tc'); if(S.activeTab==='savings')renderSavings(); }); // ── MODALS CLOSE ───────────────────────────────── document.querySelectorAll('[data-close]').forEach(function(btn){btn.addEventListener('click',function(){closeModal(btn.getAttribute('data-close'));});}); document.querySelectorAll('.modal-overlay').forEach(function(o){o.addEventListener('click',function(e){if(e.target===o)o.classList.remove('open');});}); // ── ACCOUNTS ───────────────────────────────────── function renderAccounts(){ var c=document.getElementById('accounts-content'); var nonSav=S.accounts.filter(function(a){return!isSavings(a);}); var total=nonSav.reduce(function(s,a){return s+(a.currency==='USD'?a.balance*S.tc.blue:a.balance);},0); var btnHtml='
'+ ''+ ''+ '
'; if(!S.accounts.length){ c.innerHTML='
🏦

Sin cuentas

CreΓ‘ tu primera cuenta para empezar a registrar movimientos.

'+btnHtml; }else{ var totalHtml='

Total disponible (sin ahorros)

$'+fmt(total)+'
En ARS equivalente al TC blue
'; var cards=nonSav.map(function(a){return accCardHTML(a,true);}).join(''); c.innerHTML=btnHtml+totalHtml+cards; } document.getElementById('btn-open-new-account').addEventListener('click',function(){openNewAccount();}); document.getElementById('btn-open-transfer').addEventListener('click',function(){openTransfer();}); c.querySelectorAll('[data-edit-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openEditAccount(btn.getAttribute('data-edit-acc'));});}); c.querySelectorAll('[data-del-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteAccount(btn.getAttribute('data-del-acc'));});}); c.querySelectorAll('[data-acc-history]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();showAccHistory(btn.getAttribute('data-acc-history'));});}); } function accCardHTML(acc,showActions){ var cfg=ACC_TYPES[acc.type]||ACC_TYPES.bank; var icon=acc.icon||cfg.icon; var balStr=acc.currency==='USD'?'U$'+fmt(acc.balance):'$'+fmt(acc.balance); var subStr=acc.currency==='USD'?'β‰ˆ $'+fmt(acc.balance*S.tc.blue)+' ARS':''; var actions=showActions?'
'+ ''+ ''+ ''+ '
':''; return''; } function openNewAccount(){ S.editingAccId=null; document.getElementById('acc-modal-title').textContent='Nueva cuenta'; document.getElementById('acc-name').value='';document.getElementById('acc-balance').value=''; document.getElementById('acc-icon').value='';document.getElementById('acc-color').value='#6c63ff'; document.getElementById('acc-type').value='bank';document.getElementById('acc-currency').value='ARS'; document.getElementById('btn-save-account').textContent='Crear cuenta'; showModal('modal-new-account'); } function openEditAccount(accId){ var acc=S.accounts.find(function(a){return a.id===accId;});if(!acc)return; S.editingAccId=accId; document.getElementById('acc-modal-title').textContent='Editar cuenta'; document.getElementById('acc-name').value=acc.name; document.getElementById('acc-balance').value=acc.balance; document.getElementById('acc-icon').value=acc.icon||''; document.getElementById('acc-color').value=acc.color||'#6c63ff'; document.getElementById('acc-type').value=acc.type; document.getElementById('acc-currency').value=acc.currency; document.getElementById('btn-save-account').textContent='Guardar cambios'; showModal('modal-new-account'); } document.getElementById('btn-save-account').addEventListener('click',function(){ var name=document.getElementById('acc-name').value.trim(); var type=document.getElementById('acc-type').value; var balance=parseFloat(document.getElementById('acc-balance').value)||0; var currency=document.getElementById('acc-currency').value; var icon=document.getElementById('acc-icon').value.trim()||(ACC_TYPES[type]||ACC_TYPES.bank).icon; var color=document.getElementById('acc-color').value; if(!name){toast('PonΓ© un nombre a la cuenta','error');return;} if(S.editingAccId){ var acc=S.accounts.find(function(a){return a.id===S.editingAccId;}); if(acc){acc.name=name;acc.type=type;acc.balance=balance;acc.currency=currency;acc.icon=icon;acc.color=color;} }else{ var newAcc={id:genId(),uid:S.user.uid,name:name,type:type,balance:balance,currency:currency,icon:icon,color:color,created_at:new Date().toISOString()}; S.accounts.push(newAcc); api('addAccount',{account:newAcc}).catch(function(){}); } saveAccountsLocal();closeModal('modal-new-account'); if(S.activeTab==='accounts')renderAccounts(); if(S.activeTab==='savings')renderSavings(); populateAccountSelect(); toast((S.editingAccId?'Cuenta actualizada':'Cuenta creada')+' βœ“','success'); if(S.editingAccId)api('updateAccount',{account:S.accounts.find(function(a){return a.id===S.editingAccId;})}).catch(function(){}); }); function deleteAccount(accId){ if(!confirm('ΒΏEliminar esta cuenta? Las transacciones asociadas no se borrarΓ‘n.'))return; S.accounts=S.accounts.filter(function(a){return a.id!==accId;}); saveAccountsLocal(); if(S.activeTab==='accounts')renderAccounts(); if(S.activeTab==='savings')renderSavings(); populateAccountSelect(); toast('Cuenta eliminada','success'); api('deleteAccount',{id:accId}).catch(function(){}); } function showAccHistory(accId){ var acc=S.accounts.find(function(a){return a.id===accId;});if(!acc)return; var txs=S.transactions.filter(function(t){return t.account_id===accId;}); var overlay=document.createElement('div');overlay.className='modal-overlay open';overlay.style.zIndex='600'; var txHtml=txs.length?'
'+txs.map(txHTML).join('')+'
':'
πŸ“­

Sin movimientos

'; overlay.innerHTML=''; overlay.addEventListener('click',function(e){if(e.target===overlay)overlay.remove();}); document.body.appendChild(overlay); document.getElementById('close-acc-history').addEventListener('click',function(){overlay.remove();}); } // Transfer function openTransfer(){ populateTransferSelects(); document.getElementById('transfer-amount').value=''; document.getElementById('transfer-date').value=todayStr(); showModal('modal-transfer'); } function populateTransferSelects(){ var opts=S.accounts.map(function(a){return'';}).join(''); document.getElementById('transfer-from').innerHTML=opts; document.getElementById('transfer-to').innerHTML=opts; } document.getElementById('btn-save-transfer').addEventListener('click',function(){ var fromId=document.getElementById('transfer-from').value; var toId=document.getElementById('transfer-to').value; var amount=parseFloat(document.getElementById('transfer-amount').value); var currency=document.getElementById('transfer-currency').value; var date=document.getElementById('transfer-date').value||todayStr(); if(fromId===toId){toast('ElegΓ­ cuentas distintas','error');return;} if(!amount||amount<=0){toast('IngresΓ‘ un monto vΓ‘lido','error');return;} var from=S.accounts.find(function(a){return a.id===fromId;}); var to=S.accounts.find(function(a){return a.id===toId;}); // Adjust balances var debit=currency===from.currency?amount:(currency==='USD'?amount*S.tc.blue:amount/S.tc.blue); var credit=currency===to.currency?amount:(currency==='USD'?amount*S.tc.blue:amount/S.tc.blue); from.balance=(from.balance||0)-debit; to.balance=(to.balance||0)+credit; saveAccountsLocal(); // Record as two transactions var txOut={id:genId(),uid:S.user.uid,date:date,type:'expense',amount:amount,currency:currency,tc:S.tc.blue,category_id:'c9',account_id:fromId,tags:[],description:'Transferencia β†’ '+to.name,note:'',shared:false,payer:'me',split_me:100,created_at:new Date().toISOString()}; var txIn={id:genId(),uid:S.user.uid,date:date,type:'income',amount:amount,currency:currency,tc:S.tc.blue,category_id:'i4',account_id:toId,tags:[],description:'Transferencia ← '+from.name,note:'',shared:false,payer:'me',split_me:100,created_at:new Date().toISOString()}; S.transactions.unshift(txIn);S.transactions.unshift(txOut); closeModal('modal-transfer');renderAccounts();renderDashboard(); toast('Transferencia realizada βœ“','success'); api('addTransaction',{transaction:txOut}).catch(function(){}); api('addTransaction',{transaction:txIn}).catch(function(){}); }); // ── SAVINGS ────────────────────────────────────── function renderSavings(){ var c=document.getElementById('savings-content'); var savs=S.accounts.filter(function(a){return isSavings(a);}); var tUSD=savs.reduce(function(s,a){return s+(a.currency==='USD'?a.balance:0);},0); var tARS=savs.reduce(function(s,a){return s+(a.currency==='ARS'?a.balance:a.balance*S.tc.blue);},0); var btnHtml=''; if(!savs.length){ c.innerHTML='
🐷

Sin ahorros

CreΓ‘ una cuenta de ahorro para llevar el registro de lo que tenΓ©s guardado.

'+btnHtml; }else{ var summary='

Total ahorrado

$'+fmt(tARS)+'
U$'+fmt(tUSD)+' en dΓ³lares Β· TC blue $'+fmt(S.tc.blue)+'
'; var cards=savs.map(function(a){return accCardHTML(a,true);}).join(''); c.innerHTML=btnHtml+summary+cards; } document.getElementById('btn-open-new-savings').addEventListener('click',function(){ openNewAccount();document.getElementById('acc-type').value='savings'; }); c.querySelectorAll('[data-edit-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openEditAccount(btn.getAttribute('data-edit-acc'));});}); c.querySelectorAll('[data-del-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteAccount(btn.getAttribute('data-del-acc'));});}); c.querySelectorAll('[data-acc-history]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();showAccHistory(btn.getAttribute('data-acc-history'));});}); } // ── POPULATE ACCOUNT SELECT ────────────────────── function populateAccountSelect(){ var sel=document.getElementById('tx-account-id'); var nonSav=S.accounts.filter(function(a){return!isSavings(a);}); sel.innerHTML=nonSav.length ?nonSav.map(function(a){return'';}).join('') :''; } // ── NEW / EDIT TX ──────────────────────────────── document.getElementById('fab').addEventListener('click',function(){openTxModal(null);}); function openTxModal(tx){ S.editingTxId=tx?tx.id:null; document.getElementById('tx-modal-title').textContent=tx?'Editar transacciΓ³n':'Nueva transacciΓ³n'; document.getElementById('btn-save-tx').textContent=tx?'Guardar cambios':'Guardar'; document.getElementById('tx-desc').value=tx?tx.description:''; document.getElementById('tx-amount').value=tx?tx.amount:''; document.getElementById('tx-note').value=tx?tx.note:''; document.getElementById('tx-date').value=tx?tx.date:todayStr(); document.getElementById('tx-currency').value=tx?tx.currency:'ARS'; document.getElementById('tc-field').style.display=(tx&&tx.currency==='USD')||false?'':'none'; if(tx&&tx.currency==='USD')document.getElementById('tx-tc').value=tx.tc; document.getElementById('tx-shared').checked=tx?tx.shared:false; document.getElementById('split-section').style.display=(tx&&tx.shared)?'':'none'; document.getElementById('split-me').value=tx?tx.split_me:50; document.getElementById('split-partner').value=tx?(100-tx.split_me):50; S.txType=tx?tx.type:'expense'; S.selectedCat=tx?tx.category_id:null; S.currentTags=tx?(tx.tags||[]).slice():[]; document.querySelectorAll('.toggle-opt[data-type]').forEach(function(b){b.classList.toggle('active',b.getAttribute('data-type')===S.txType);}); populateAccountSelect(); if(tx&&tx.account_id){var sel=document.getElementById('tx-account-id');for(var i=0;iΓ—'; pill.querySelector('button').addEventListener('click',function(){S.currentTags=S.currentTags.filter(function(t){return t!==tag;});renderTagsWrap();}); wrap.insertBefore(pill,inp); }); } function renderCatPicker(){ var grid=document.getElementById('cat-picker');grid.innerHTML=''; var filtered=S.categories.filter(function(cat){return cat.type===S.txType;}); if(!filtered.length){grid.innerHTML='

Sin categorΓ­as para este tipo

';return;} filtered.forEach(function(cat){ var d=document.createElement('div');d.className='cat-opt'+(S.selectedCat===cat.id?' selected':''); d.innerHTML=''+cat.icon+''+cat.name+''; d.addEventListener('click',function(){S.selectedCat=cat.id;renderCatPicker();});grid.appendChild(d); }); } document.getElementById('btn-save-tx').addEventListener('click',function(){ var desc=document.getElementById('tx-desc').value.trim(); var amount=parseFloat(document.getElementById('tx-amount').value); var currency=document.getElementById('tx-currency').value; var date=document.getElementById('tx-date').value||todayStr(); var tc=currency==='USD'?(parseFloat(document.getElementById('tx-tc').value)||S.tc.blue):1; var note=document.getElementById('tx-note').value.trim(); var shared=document.getElementById('tx-shared').checked; var payer=document.getElementById('tx-payer').value; var splitMe=parseInt(document.getElementById('split-me').value)||50; var accountId=document.getElementById('tx-account-id').value; if(!desc){toast('AgregΓ‘ una descripciΓ³n','error');return;} if(!amount||amount<=0){toast('IngresΓ‘ un monto vΓ‘lido','error');return;} if(!accountId){toast('SeleccionΓ‘ una cuenta','error');return;} var defaultCatId=S.txType==='income'?'i4':'c9'; if(S.editingTxId){ // Editing: reverse old effect on account, apply new var oldTx=S.transactions.find(function(t){return t.id===S.editingTxId;}); if(oldTx){ var oldAcc=S.accounts.find(function(a){return a.id===oldTx.account_id;}); if(oldAcc){var rev=oldTx.type==='income'?-oldTx.amount:oldTx.amount;if(oldTx.currency==='USD'&&oldAcc.currency==='ARS')rev*=oldTx.tc;else if(oldTx.currency==='ARS'&&oldAcc.currency==='USD')rev/=oldTx.tc;oldAcc.balance=(oldAcc.balance||0)+rev;} oldTx.description=desc;oldTx.amount=amount;oldTx.currency=currency;oldTx.tc=tc; oldTx.date=date;oldTx.note=note;oldTx.shared=shared;oldTx.payer=shared?payer:'me'; oldTx.split_me=shared?splitMe:100;oldTx.category_id=S.selectedCat||defaultCatId; oldTx.account_id=accountId;oldTx.tags=S.currentTags.slice(); } var newAcc=S.accounts.find(function(a){return a.id===accountId;}); if(newAcc){var delta=S.txType==='income'?amount:-amount;if(currency==='USD'&&newAcc.currency==='ARS')delta*=tc;else if(currency==='ARS'&&newAcc.currency==='USD')delta/=tc;newAcc.balance=(newAcc.balance||0)+delta;} saveAccountsLocal();closeModal('modal-new-tx');renderDashboard(); if(S.activeTab==='transactions')renderTransactions(); if(S.activeTab==='accounts')renderAccounts(); toast('TransacciΓ³n actualizada βœ“','success'); api('updateTransaction',{transaction:oldTx}).catch(function(){}); }else{ var acc=S.accounts.find(function(a){return a.id===accountId;}); if(acc){var d2=S.txType==='income'?amount:-amount;if(currency==='USD'&&acc.currency==='ARS')d2*=tc;else if(currency==='ARS'&&acc.currency==='USD')d2/=tc;acc.balance=(acc.balance||0)+d2;saveAccountsLocal();api('updateAccountBalance',{id:acc.id,balance:acc.balance}).catch(function(){});} var newTx={id:genId(),uid:S.user.uid,date:date,type:S.txType,amount:amount,currency:currency,tc:tc,category_id:S.selectedCat||defaultCatId,account_id:accountId,tags:S.currentTags.slice(),description:desc,note:note,shared:shared,payer:shared?payer:'me',split_me:shared?splitMe:100,created_at:new Date().toISOString()}; S.transactions.unshift(newTx);closeModal('modal-new-tx');renderDashboard(); if(S.activeTab==='transactions')renderTransactions(); if(S.activeTab==='accounts')renderAccounts(); toast('TransacciΓ³n guardada βœ“','success');api('addTransaction',{transaction:newTx}).catch(function(){}); } }); // ── TRANSACTIONS ────────────────────────────────── document.querySelectorAll('.filter-chip').forEach(function(btn){ btn.addEventListener('click',function(){ S.txFilter=btn.getAttribute('data-filter'); document.querySelectorAll('.filter-chip').forEach(function(b){b.classList.remove('active');});btn.classList.add('active');renderTransactions(); }); }); function renderTransactions(){ var container=document.getElementById('tx-list-container'); var txs=S.transactions.slice(); if(S.txFilter==='income')txs=txs.filter(function(t){return t.type==='income';}); if(S.txFilter==='expense')txs=txs.filter(function(t){return t.type==='expense';}); if(S.txFilter==='shared')txs=txs.filter(function(t){return t.shared;}); if(S.txFilter==='ARS')txs=txs.filter(function(t){return t.currency==='ARS';}); if(S.txFilter==='USD')txs=txs.filter(function(t){return t.currency==='USD';}); if(!txs.length){container.innerHTML='
πŸ“­

Sin movimientos

TocΓ‘ el + para agregar tu primer transacciΓ³n

';return;} container.innerHTML='
'+txs.map(function(tx){return txHTML(tx,true);}).join('')+'
'; container.querySelectorAll('[data-edit-tx]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();var tx=S.transactions.find(function(t){return t.id===btn.getAttribute('data-edit-tx');});if(tx)openTxModal(tx);});}); container.querySelectorAll('[data-del-tx]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteTx(btn.getAttribute('data-del-tx'));});}); } function deleteTx(txId){ if(!confirm('ΒΏEliminar esta transacciΓ³n?'))return; var tx=S.transactions.find(function(t){return t.id===txId;}); if(tx){ var acc=S.accounts.find(function(a){return a.id===tx.account_id;}); if(acc){var rev=tx.type==='income'?-tx.amount:tx.amount;if(tx.currency==='USD'&&acc.currency==='ARS')rev*=tx.tc;else if(tx.currency==='ARS'&&acc.currency==='USD')rev/=tx.tc;acc.balance=(acc.balance||0)+rev;saveAccountsLocal();} } S.transactions=S.transactions.filter(function(t){return t.id!==txId;}); renderTransactions();renderDashboard(); toast('TransacciΓ³n eliminada','success'); api('deleteTransaction',{id:txId}).catch(function(){}); } function txHTML(tx,showActions){ var cat=S.categories.find(function(c){return c.id===tx.category_id;})||{icon:'πŸ“¦',color:'#6b7280'}; var acc=S.accounts.find(function(a){return a.id===tx.account_id;}); var sign=tx.type==='income'?'+':'-',cls=tx.type==='income'?'income':'expense'; var tags=(tx.tags||[]).map(function(t){return'#'+t+'';}).join(''); var badge=tx.shared?'πŸ’‘':''; var accBadge=acc?'':''; var amt=tx.currency==='USD'?'U$'+fmt(tx.amount):'$'+fmt(tx.amount); var equiv=tx.currency==='USD'?'β‰ˆ $'+fmt(tx.amount*tx.tc)+' ARS':''; var actions=showActions?'
':''; return'
'+ '
'+cat.icon+'
'+ '
'+tx.description+'
'+ '
'+fmtDate(tx.date)+' '+accBadge+' '+badge+' '+tags+'
'+ '
'+ '
'+sign+amt+'
'+(equiv?'
'+equiv+'
':'')+'
'+ actions+'
'; } // ── DASHBOARD ───────────────────────────────────── function renderDashboard(){ var now=new Date(),month=now.getMonth(),year=now.getFullYear(); // Excluir solo cuentas de ahorro que existen β€” si account_id estΓ‘ vacΓ­o, incluir igual var savIds=S.accounts.filter(isSavings).map(function(a){return a.id;}); var txs=S.transactions.filter(function(t){ var d=new Date(t.date); var inMonth=d.getMonth()===month&&d.getFullYear()===year; var notSavings=!t.account_id||savIds.indexOf(t.account_id)===-1; return inMonth&¬Savings; }); var iARS=0,eARS=0,iUSD=0,eUSD=0; txs.forEach(function(t){var isIn=t.type==='income';if(t.currency==='ARS'){isIn?(iARS+=t.amount):(eARS+=t.amount);}else{isIn?(iUSD+=t.amount):(eUSD+=t.amount);}}); document.getElementById('balance-month-label').textContent=monthName(month)+' '+year; document.getElementById('balance-main-val').textContent='$'+fmt(iARS-eARS)+' ARS'; document.getElementById('dash-income').textContent='+$'+fmt(iARS); document.getElementById('dash-expense').textContent='-$'+fmt(eARS); var tARS=iARS-eARS,tUSD=iUSD-eUSD; var arsEl=document.getElementById('dash-ars');arsEl.textContent=(tARS>=0?'+':'')+' $'+fmt(tARS);arsEl.className='si-val '+(tARS>=0?'green':'red'); var usdEl=document.getElementById('dash-usd');usdEl.textContent=(tUSD>=0?'+':'')+'U$'+fmt(tUSD);usdEl.className='si-val '+(tUSD>=0?'green':'red'); document.getElementById('dash-count').textContent=txs.length; var cats={};txs.forEach(function(t){cats[t.category_id]=true;});document.getElementById('dash-cats').textContent=Object.keys(cats).length; renderPie(txs.filter(function(t){return t.type==='expense';})); var recent=S.transactions.slice(0,5); document.getElementById('dash-recent-list').innerHTML=recent.length?'
'+recent.map(function(t){return txHTML(t,false);}).join('')+'
':'
πŸ’Έ

Sin movimientos

EmpezΓ‘ cargando tu primer gasto

'; } function renderPie(expenses){ var canvas=document.getElementById('pieChart'),legend=document.getElementById('pie-legend'),ctx=canvas.getContext('2d'); var W=canvas.offsetWidth||300,H=220;canvas.width=W;canvas.height=H;ctx.clearRect(0,0,W,H); var catMap={};expenses.forEach(function(t){var val=t.currency==='USD'?t.amount*t.tc:t.amount;catMap[t.category_id]=(catMap[t.category_id]||0)+val;}); var entries=Object.keys(catMap).map(function(k){return[k,catMap[k]];}).sort(function(a,b){return b[1]-a[1];}); var total=entries.reduce(function(s,e){return s+e[1];},0)||1;legend.innerHTML=''; if(!entries.length){ctx.fillStyle='#ffffff22';ctx.beginPath();ctx.arc(W/2,H/2,80,0,Math.PI*2);ctx.fill();ctx.fillStyle='#9ca3af';ctx.textAlign='center';ctx.font='14px Inter';ctx.fillText('Sin datos',W/2,H/2+5);return;} var cx=W/2,cy=H/2,r=Math.min(cx,cy)-20,startAngle=-Math.PI/2; entries.forEach(function(entry){ var cat=S.categories.find(function(c){return c.id===entry[0];})||{color:'#6b7280',name:'Otros',icon:'πŸ“¦'}; var slice=(entry[1]/total)*Math.PI*2; ctx.beginPath();ctx.moveTo(cx,cy);ctx.arc(cx,cy,r,startAngle,startAngle+slice);ctx.closePath();ctx.fillStyle=cat.color;ctx.fill();ctx.strokeStyle='#1a1d27';ctx.lineWidth=2;ctx.stroke(); legend.innerHTML+='
'+cat.icon+' '+cat.name+' '+Math.round(entry[1]/total*100)+'%
'; startAngle+=slice; }); ctx.beginPath();ctx.arc(cx,cy,r*0.5,0,Math.PI*2);ctx.fillStyle='#1a1d27';ctx.fill(); ctx.fillStyle='#fff';ctx.textAlign='center';ctx.font='bold 14px Inter';ctx.fillText('$'+fmt(total),cx,cy+5); } // ── COUPLE ──────────────────────────────────────── document.getElementById('btn-send-partner').addEventListener('click',function(){ var email=document.getElementById('partner-email').value.trim();if(!email){toast('IngresΓ‘ un email','error');return;} api('linkPartner',{uid:S.user.uid,partner_email:email}).then(function(d){ S.user.partner_uid=d.partner_uid;S.user.partner_name=d.partner_name;localStorage.setItem('session',JSON.stringify(S.user)); toast('Β‘Pareja vinculada! βœ“','success');closeModal('modal-link-partner');renderCouple(); }).catch(function(e){toast(e.message,'error');}); }); function renderCouple(){ var c=document.getElementById('couple-content'); if(!S.user.partner_uid){ c.innerHTML='
πŸ‘€

'+S.user.name+'

Sin pareja vinculada

'+ '
πŸ’‘

Modo pareja activado

VinculΓ‘ tu cuenta con la de tu pareja para compartir gastos.

'+ ''; document.getElementById('btn-open-link-partner').addEventListener('click',function(){showModal('modal-link-partner');});return; } var shared=S.transactions.filter(function(t){return t.shared;}); var iOwe=0,theyOwe=0; shared.forEach(function(t){var val=t.currency==='USD'?t.amount*t.tc:t.amount;var my=val*(t.split_me||50)/100;if(t.payer==='me')theyOwe+=(val-my);else iOwe+=my;}); var net=theyOwe-iOwe; c.innerHTML='
πŸ‘€
πŸ’ž
πŸ‘€
'+ '

'+S.user.name+' & '+(S.user.partner_name||'Pareja')+'

Vinculados

'+ '

Balance compartido (ARS)

'+ (net>=0?'+':'')+' $'+fmt(Math.abs(net))+'
'+(net>0?'Te deben':'Le debΓ©s a '+(S.user.partner_name||'tu pareja'))+'
'+ '
Gastos compartidos
'+ '
'+(shared.length?shared.map(function(t){return txHTML(t,false);}).join(''):'
🀝

Sin gastos compartidos

')+'
'; } // ── VACATIONS ───────────────────────────────────── document.getElementById('btn-save-vac').addEventListener('click',function(){ var name=document.getElementById('vac-name').value.trim();if(!name){toast('PonΓ© un nombre al viaje','error');return;} var vac={id:genId(),uid:S.user.uid,name:name,start_date:document.getElementById('vac-start').value,end_date:document.getElementById('vac-end').value,budget:parseFloat(document.getElementById('vac-budget').value)||0,currency:document.getElementById('vac-currency').value,created_at:new Date().toISOString()}; S.vacations.unshift(vac);closeModal('modal-new-vac');renderVacations();toast('Viaje creado βœ“','success');api('addVacation',{vacation:vac}).catch(function(){}); }); document.getElementById('btn-save-vexp').addEventListener('click',function(){ var desc=document.getElementById('vexp-desc').value.trim(),amount=parseFloat(document.getElementById('vexp-amount').value); if(!desc||!amount){toast('CompletΓ‘ los campos','error');return;} var item={id:genId(),vacation_id:S.activeVacId,type:'expense',description:desc,amount:amount,currency:document.getElementById('vexp-currency').value,tc:parseFloat(document.getElementById('vexp-tc').value)||S.tc.blue,shared:document.getElementById('vexp-shared').checked,payer:document.getElementById('vexp-payer').value,split_me:parseInt(document.getElementById('vexp-split-me').value)||50,created_at:new Date().toISOString()}; if(!S.vacItems[S.activeVacId])S.vacItems[S.activeVacId]=[];S.vacItems[S.activeVacId].unshift(item); closeModal('modal-vac-expense');renderVacations();toast('Gasto guardado βœ“','success');api('addVacationItem',{item:item}).catch(function(){}); }); function calcVacSpent(vacId){return(S.vacItems[vacId]||[]).filter(function(i){return i.type==='expense';}).reduce(function(s,i){return s+(i.currency==='USD'?i.amount*(i.tc||S.tc.blue):i.amount);},0);} function renderVacations(){ var c=document.getElementById('vacation-content'); var btnHtml=''; if(!S.vacations.length){c.innerHTML='
✈️

Sin viajes

CreΓ‘ tu primer viaje para empezar.

'+btnHtml;} else{ var cards=S.vacations.map(function(v){var spent=calcVacSpent(v.id),pct=Math.min(100,Math.round(spent/v.budget*100))||0,curr=v.currency==='USD'?'U$':'$'; return'
✈️ '+v.name+'
πŸ“… '+fmtDate(v.start_date)+' β†’ '+fmtDate(v.end_date)+'
'+ '
'+ '
'+curr+fmt(spent)+' gastado'+curr+fmt(v.budget-spent)+' restante
'+ '
'; }).join('');c.innerHTML=btnHtml+cards; } document.getElementById('btn-open-new-vac').addEventListener('click',function(){showModal('modal-new-vac');}); c.querySelectorAll('[data-vac-expense]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();S.activeVacId=btn.getAttribute('data-vac-expense');showModal('modal-vac-expense');});}); c.querySelectorAll('[data-vac-detail]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openVacationDetail(btn.getAttribute('data-vac-detail'));});}); } function openVacationDetail(vacId){ var vac=S.vacations.find(function(v){return v.id===vacId;});if(!vac)return; var items=S.vacItems[vacId]||[],expenses=items.filter(function(i){return i.type==='expense';}),checks=items.filter(function(i){return i.type==='checklist';}); var spent=calcVacSpent(vacId),pct=Math.min(100,Math.round(spent/vac.budget*100))||0,curr=vac.currency==='USD'?'U$':'$'; var overlay=document.createElement('div');overlay.className='modal-overlay open';overlay.style.zIndex='600'; var checkHtml=checks.length?checks.map(function(i){return'
βœ“
'+i.description+'
';}).join(''):'

Sin Γ­tems

'; var expHtml=expenses.length?expenses.map(function(i){return'
✈️
'+i.description+'
'+(i.shared?'πŸ’‘':'')+'
-'+(i.currency==='USD'?'U$':'$')+fmt(i.amount)+'
';}).join(''):'

Sin gastos aΓΊn

'; overlay.innerHTML=''; overlay.addEventListener('click',function(e){if(e.target===overlay)overlay.remove();});document.body.appendChild(overlay); document.getElementById('close-vac-detail').addEventListener('click',function(){overlay.remove();}); document.getElementById('checklist-container').addEventListener('click',function(e){ var item=e.target.closest('.checklist-item');if(!item)return; var box=item.querySelector('.check-box'),txt=item.querySelector('.check-text'),iid=item.getAttribute('data-item-id'); var vi=(S.vacItems[vacId]||[]).find(function(x){return x.id===iid;});if(vi)vi.done=!vi.done;box.classList.toggle('checked');txt.classList.toggle('done'); }); document.getElementById('btn-add-check').addEventListener('click',function(){ var inp=document.getElementById('new-check-txt'),desc=inp.value.trim();if(!desc)return; var ni={id:genId(),vacation_id:vacId,type:'checklist',description:desc,done:false}; if(!S.vacItems[vacId])S.vacItems[vacId]=[];S.vacItems[vacId].push(ni);inp.value=''; var container=document.getElementById('checklist-container');var p=container.querySelector('p');if(p)p.remove(); var div=document.createElement('div');div.className='checklist-item';div.setAttribute('data-item-id',ni.id);div.innerHTML='
βœ“
'+desc+'';container.appendChild(div); api('addVacationItem',{item:ni}).catch(function(){}); }); } window.addEventListener('resize',function(){if(S.activeTab==='dashboard')renderDashboard();}); +d.compra+' venta function loadAll(){ return Promise.all([ api('getTransactions',{uid:S.user.uid}), api('getCategories',{uid:S.user.uid}), api('getVacations',{uid:S.user.uid}), api('getAccounts',{uid:S.user.uid}) ]).then(function(results){ S.transactions=results[0].transactions||[]; var bc=results[1].categories||[]; S.categories=defaultCategories().concat(bc.filter(function(c){return c.type==='expense'||c.type==='income';})); S.vacations=results[2].vacations||[]; var ba=results[3].accounts||[]; ba.forEach(function(a){if(!S.accounts.find(function(x){return x.id===a.id;}))S.accounts.push(a);}); saveAccountsLocal(); }).catch(function(){if(!S.categories.length)S.categories=defaultCategories();}); } // ── TABS ───────────────────────────────────────── document.querySelectorAll('.nav-item').forEach(function(btn){btn.addEventListener('click',function(){switchTab(btn.getAttribute('data-tab'),btn);});}); function switchTab(tab,btn){ ['dashboard','transactions','accounts','savings','couple','vacation','settings'].forEach(function(t){var el=document.getElementById('tab-'+t);if(el)el.style.display='none';}); var active=document.getElementById('tab-'+tab);if(active)active.style.display=''; document.querySelectorAll('.nav-item').forEach(function(b){b.classList.remove('active');}); if(btn)btn.classList.add('active');S.activeTab=tab; var titles={dashboard:'Dashboard',transactions:'Movimientos',accounts:'Cuentas',savings:'Ahorros',couple:'Pareja',vacation:'Viajes',settings:'ConfiguraciΓ³n'}; document.getElementById('topbar-title').textContent=titles[tab]||tab; if(tab==='transactions')renderTransactions(); if(tab==='accounts')renderAccounts(); if(tab==='savings')renderSavings(); if(tab==='couple')renderCouple(); if(tab==='vacation')renderVacations(); if(tab==='settings')updateSettingsUI(); } document.getElementById('btn-go-settings').addEventListener('click',function(){switchTab('settings',document.querySelector('[data-tab="settings"]'));}); // ── MODE ────────────────────────────────────────── function updateModeUI(){ if(!S.user)return; var c=S.user.mode==='couple'||S.user.mode==='both',v=S.user.mode==='vacation'||S.user.mode==='both'; document.getElementById('nav-couple').style.display=c?'':'none'; document.getElementById('nav-vacation').style.display=v?'':'none'; var badges='';if(c)badges+='πŸ’‘ Pareja ';if(v)badges+='πŸ–οΈ Vacaciones'; document.getElementById('mode-indicator').innerHTML=badges; document.getElementById('shared-section').style.display=c?'':'none'; document.getElementById('vexp-shared-wrap').style.display=c?'':'none'; } document.getElementById('toggle-couple').addEventListener('change',function(){toggleMode('couple',this.checked);}); document.getElementById('toggle-vacation').addEventListener('change',function(){toggleMode('vacation',this.checked);}); function toggleMode(mode,checked){ if(!S.user)return; var c=S.user.mode==='couple'||S.user.mode==='both',v=S.user.mode==='vacation'||S.user.mode==='both'; if(mode==='couple')c=checked;if(mode==='vacation')v=checked; S.user.mode=c&&v?'both':c?'couple':v?'vacation':'normal'; localStorage.setItem('session',JSON.stringify(S.user));updateModeUI(); api('updateUserMode',{uid:S.user.uid,mode:S.user.mode}).catch(function(){}); } // ── SETTINGS / CATEGORIES ──────────────────────── function updateSettingsUI(){ if(!S.user)return; document.getElementById('settings-name').textContent=S.user.name||'β€”'; document.getElementById('settings-email').textContent=S.user.email||'β€”'; document.getElementById('settings-avatar').textContent=(S.user.name||'?')[0].toUpperCase(); document.getElementById('toggle-couple').checked=S.user.mode==='couple'||S.user.mode==='both'; document.getElementById('toggle-vacation').checked=S.user.mode==='vacation'||S.user.mode==='both'; var list=document.getElementById('categories-list-settings'); list.innerHTML=S.categories.map(function(cat){ return'
'+ '
'+ ''+cat.icon+''+ '
'+cat.name+'
'+ '
'+(cat.type==='income'?'Ingreso':'Gasto')+'
'+ '
'+ ''+ ''+ '
'; }).join(''); list.querySelectorAll('[data-edit-cat]').forEach(function(btn){ btn.addEventListener('click',function(){openEditCat(btn.getAttribute('data-edit-cat'));}); }); } // Edit category function openEditCat(catId){ var cat=S.categories.find(function(c){return c.id===catId;});if(!cat)return; S.editingCatId=catId; document.getElementById('edit-cat-name').value=cat.name; document.getElementById('edit-cat-icon').value=cat.icon; showModal('modal-edit-cat'); } document.getElementById('btn-save-edit-cat').addEventListener('click',function(){ var cat=S.categories.find(function(c){return c.id===S.editingCatId;});if(!cat)return; var name=document.getElementById('edit-cat-name').value.trim(); var icon=document.getElementById('edit-cat-icon').value.trim(); if(!name){toast('IngresΓ‘ un nombre','error');return;} cat.name=name;if(icon)cat.icon=icon; closeModal('modal-edit-cat');updateSettingsUI();renderCatPicker(); toast('CategorΓ­a actualizada βœ“','success'); api('updateCategory',{category:cat}).catch(function(){}); }); document.getElementById('btn-new-cat').addEventListener('click',function(){ S.newCatType='expense'; document.getElementById('cat-type-expense').classList.add('active');document.getElementById('cat-type-income').classList.remove('active'); showModal('modal-new-cat'); }); document.getElementById('cat-type-expense').addEventListener('click',function(){S.newCatType='expense';document.getElementById('cat-type-expense').classList.add('active');document.getElementById('cat-type-income').classList.remove('active');}); document.getElementById('cat-type-income').addEventListener('click',function(){S.newCatType='income';document.getElementById('cat-type-income').classList.add('active');document.getElementById('cat-type-expense').classList.remove('active');}); document.getElementById('btn-save-cat').addEventListener('click',function(){ var name=document.getElementById('cat-name').value.trim(),icon=document.getElementById('cat-icon').value.trim()||'πŸ“¦',color=document.getElementById('cat-color').value; if(!name){toast('IngresΓ‘ un nombre','error');return;} var cat={id:genId(),uid:S.user.uid,name:name,icon:icon,color:color,type:S.newCatType}; S.categories.push(cat);closeModal('modal-new-cat');updateSettingsUI(); toast('CategorΓ­a creada βœ“','success');api('addCategory',{category:cat}).catch(function(){}); }); // ── TC ──────────────────────────────────────────── document.getElementById('btn-open-tc').addEventListener('click',function(){ document.getElementById('tc-oficial').value=S.tc.oficial;document.getElementById('tc-blue').value=S.tc.blue;showModal('modal-tc'); }); document.getElementById('btn-save-tc').addEventListener('click',function(){ S.tc.oficial=parseFloat(document.getElementById('tc-oficial').value)||S.tc.oficial; S.tc.blue=parseFloat(document.getElementById('tc-blue').value)||S.tc.blue; localStorage.setItem('tc',JSON.stringify(S.tc));toast('Tipo de cambio guardado βœ“','success');closeModal('modal-tc'); if(S.activeTab==='savings')renderSavings(); }); // ── MODALS CLOSE ───────────────────────────────── document.querySelectorAll('[data-close]').forEach(function(btn){btn.addEventListener('click',function(){closeModal(btn.getAttribute('data-close'));});}); document.querySelectorAll('.modal-overlay').forEach(function(o){o.addEventListener('click',function(e){if(e.target===o)o.classList.remove('open');});}); // ── ACCOUNTS ───────────────────────────────────── function renderAccounts(){ var c=document.getElementById('accounts-content'); var nonSav=S.accounts.filter(function(a){return!isSavings(a);}); var total=nonSav.reduce(function(s,a){return s+(a.currency==='USD'?a.balance*S.tc.blue:a.balance);},0); var btnHtml='
'+ ''+ ''+ '
'; if(!S.accounts.length){ c.innerHTML='
🏦

Sin cuentas

CreΓ‘ tu primera cuenta para empezar a registrar movimientos.

'+btnHtml; }else{ var totalHtml='

Total disponible (sin ahorros)

$'+fmt(total)+'
En ARS equivalente al TC blue
'; var cards=nonSav.map(function(a){return accCardHTML(a,true);}).join(''); c.innerHTML=btnHtml+totalHtml+cards; } document.getElementById('btn-open-new-account').addEventListener('click',function(){openNewAccount();}); document.getElementById('btn-open-transfer').addEventListener('click',function(){openTransfer();}); c.querySelectorAll('[data-edit-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openEditAccount(btn.getAttribute('data-edit-acc'));});}); c.querySelectorAll('[data-del-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteAccount(btn.getAttribute('data-del-acc'));});}); c.querySelectorAll('[data-acc-history]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();showAccHistory(btn.getAttribute('data-acc-history'));});}); } function accCardHTML(acc,showActions){ var cfg=ACC_TYPES[acc.type]||ACC_TYPES.bank; var icon=acc.icon||cfg.icon; var balStr=acc.currency==='USD'?'U$'+fmt(acc.balance):'$'+fmt(acc.balance); var subStr=acc.currency==='USD'?'β‰ˆ $'+fmt(acc.balance*S.tc.blue)+' ARS':''; var actions=showActions?'
'+ ''+ ''+ ''+ '
':''; return''; } function openNewAccount(){ S.editingAccId=null; document.getElementById('acc-modal-title').textContent='Nueva cuenta'; document.getElementById('acc-name').value='';document.getElementById('acc-balance').value=''; document.getElementById('acc-icon').value='';document.getElementById('acc-color').value='#6c63ff'; document.getElementById('acc-type').value='bank';document.getElementById('acc-currency').value='ARS'; document.getElementById('btn-save-account').textContent='Crear cuenta'; showModal('modal-new-account'); } function openEditAccount(accId){ var acc=S.accounts.find(function(a){return a.id===accId;});if(!acc)return; S.editingAccId=accId; document.getElementById('acc-modal-title').textContent='Editar cuenta'; document.getElementById('acc-name').value=acc.name; document.getElementById('acc-balance').value=acc.balance; document.getElementById('acc-icon').value=acc.icon||''; document.getElementById('acc-color').value=acc.color||'#6c63ff'; document.getElementById('acc-type').value=acc.type; document.getElementById('acc-currency').value=acc.currency; document.getElementById('btn-save-account').textContent='Guardar cambios'; showModal('modal-new-account'); } document.getElementById('btn-save-account').addEventListener('click',function(){ var name=document.getElementById('acc-name').value.trim(); var type=document.getElementById('acc-type').value; var balance=parseFloat(document.getElementById('acc-balance').value)||0; var currency=document.getElementById('acc-currency').value; var icon=document.getElementById('acc-icon').value.trim()||(ACC_TYPES[type]||ACC_TYPES.bank).icon; var color=document.getElementById('acc-color').value; if(!name){toast('PonΓ© un nombre a la cuenta','error');return;} if(S.editingAccId){ var acc=S.accounts.find(function(a){return a.id===S.editingAccId;}); if(acc){acc.name=name;acc.type=type;acc.balance=balance;acc.currency=currency;acc.icon=icon;acc.color=color;} }else{ var newAcc={id:genId(),uid:S.user.uid,name:name,type:type,balance:balance,currency:currency,icon:icon,color:color,created_at:new Date().toISOString()}; S.accounts.push(newAcc); api('addAccount',{account:newAcc}).catch(function(){}); } saveAccountsLocal();closeModal('modal-new-account'); if(S.activeTab==='accounts')renderAccounts(); if(S.activeTab==='savings')renderSavings(); populateAccountSelect(); toast((S.editingAccId?'Cuenta actualizada':'Cuenta creada')+' βœ“','success'); if(S.editingAccId)api('updateAccount',{account:S.accounts.find(function(a){return a.id===S.editingAccId;})}).catch(function(){}); }); function deleteAccount(accId){ if(!confirm('ΒΏEliminar esta cuenta? Las transacciones asociadas no se borrarΓ‘n.'))return; S.accounts=S.accounts.filter(function(a){return a.id!==accId;}); saveAccountsLocal(); if(S.activeTab==='accounts')renderAccounts(); if(S.activeTab==='savings')renderSavings(); populateAccountSelect(); toast('Cuenta eliminada','success'); api('deleteAccount',{id:accId}).catch(function(){}); } function showAccHistory(accId){ var acc=S.accounts.find(function(a){return a.id===accId;});if(!acc)return; var txs=S.transactions.filter(function(t){return t.account_id===accId;}); var overlay=document.createElement('div');overlay.className='modal-overlay open';overlay.style.zIndex='600'; var txHtml=txs.length?'
'+txs.map(txHTML).join('')+'
':'
πŸ“­

Sin movimientos

'; overlay.innerHTML=''; overlay.addEventListener('click',function(e){if(e.target===overlay)overlay.remove();}); document.body.appendChild(overlay); document.getElementById('close-acc-history').addEventListener('click',function(){overlay.remove();}); } // Transfer function openTransfer(){ populateTransferSelects(); document.getElementById('transfer-amount').value=''; document.getElementById('transfer-date').value=todayStr(); showModal('modal-transfer'); } function populateTransferSelects(){ var opts=S.accounts.map(function(a){return'';}).join(''); document.getElementById('transfer-from').innerHTML=opts; document.getElementById('transfer-to').innerHTML=opts; } document.getElementById('btn-save-transfer').addEventListener('click',function(){ var fromId=document.getElementById('transfer-from').value; var toId=document.getElementById('transfer-to').value; var amount=parseFloat(document.getElementById('transfer-amount').value); var currency=document.getElementById('transfer-currency').value; var date=document.getElementById('transfer-date').value||todayStr(); if(fromId===toId){toast('ElegΓ­ cuentas distintas','error');return;} if(!amount||amount<=0){toast('IngresΓ‘ un monto vΓ‘lido','error');return;} var from=S.accounts.find(function(a){return a.id===fromId;}); var to=S.accounts.find(function(a){return a.id===toId;}); // Adjust balances var debit=currency===from.currency?amount:(currency==='USD'?amount*S.tc.blue:amount/S.tc.blue); var credit=currency===to.currency?amount:(currency==='USD'?amount*S.tc.blue:amount/S.tc.blue); from.balance=(from.balance||0)-debit; to.balance=(to.balance||0)+credit; saveAccountsLocal(); // Record as two transactions var txOut={id:genId(),uid:S.user.uid,date:date,type:'expense',amount:amount,currency:currency,tc:S.tc.blue,category_id:'c9',account_id:fromId,tags:[],description:'Transferencia β†’ '+to.name,note:'',shared:false,payer:'me',split_me:100,created_at:new Date().toISOString()}; var txIn={id:genId(),uid:S.user.uid,date:date,type:'income',amount:amount,currency:currency,tc:S.tc.blue,category_id:'i4',account_id:toId,tags:[],description:'Transferencia ← '+from.name,note:'',shared:false,payer:'me',split_me:100,created_at:new Date().toISOString()}; S.transactions.unshift(txIn);S.transactions.unshift(txOut); closeModal('modal-transfer');renderAccounts();renderDashboard(); toast('Transferencia realizada βœ“','success'); api('addTransaction',{transaction:txOut}).catch(function(){}); api('addTransaction',{transaction:txIn}).catch(function(){}); }); // ── SAVINGS ────────────────────────────────────── function renderSavings(){ var c=document.getElementById('savings-content'); var savs=S.accounts.filter(function(a){return isSavings(a);}); var tUSD=savs.reduce(function(s,a){return s+(a.currency==='USD'?a.balance:0);},0); var tARS=savs.reduce(function(s,a){return s+(a.currency==='ARS'?a.balance:a.balance*S.tc.blue);},0); var btnHtml=''; if(!savs.length){ c.innerHTML='
🐷

Sin ahorros

CreΓ‘ una cuenta de ahorro para llevar el registro de lo que tenΓ©s guardado.

'+btnHtml; }else{ var summary='

Total ahorrado

$'+fmt(tARS)+'
U$'+fmt(tUSD)+' en dΓ³lares Β· TC blue $'+fmt(S.tc.blue)+'
'; var cards=savs.map(function(a){return accCardHTML(a,true);}).join(''); c.innerHTML=btnHtml+summary+cards; } document.getElementById('btn-open-new-savings').addEventListener('click',function(){ openNewAccount();document.getElementById('acc-type').value='savings'; }); c.querySelectorAll('[data-edit-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openEditAccount(btn.getAttribute('data-edit-acc'));});}); c.querySelectorAll('[data-del-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteAccount(btn.getAttribute('data-del-acc'));});}); c.querySelectorAll('[data-acc-history]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();showAccHistory(btn.getAttribute('data-acc-history'));});}); } // ── POPULATE ACCOUNT SELECT ────────────────────── function populateAccountSelect(){ var sel=document.getElementById('tx-account-id'); var nonSav=S.accounts.filter(function(a){return!isSavings(a);}); sel.innerHTML=nonSav.length ?nonSav.map(function(a){return'';}).join('') :''; } // ── NEW / EDIT TX ──────────────────────────────── document.getElementById('fab').addEventListener('click',function(){openTxModal(null);}); function openTxModal(tx){ S.editingTxId=tx?tx.id:null; document.getElementById('tx-modal-title').textContent=tx?'Editar transacciΓ³n':'Nueva transacciΓ³n'; document.getElementById('btn-save-tx').textContent=tx?'Guardar cambios':'Guardar'; document.getElementById('tx-desc').value=tx?tx.description:''; document.getElementById('tx-amount').value=tx?tx.amount:''; document.getElementById('tx-note').value=tx?tx.note:''; document.getElementById('tx-date').value=tx?tx.date:todayStr(); document.getElementById('tx-currency').value=tx?tx.currency:'ARS'; document.getElementById('tc-field').style.display=(tx&&tx.currency==='USD')||false?'':'none'; if(tx&&tx.currency==='USD')document.getElementById('tx-tc').value=tx.tc; document.getElementById('tx-shared').checked=tx?tx.shared:false; document.getElementById('split-section').style.display=(tx&&tx.shared)?'':'none'; document.getElementById('split-me').value=tx?tx.split_me:50; document.getElementById('split-partner').value=tx?(100-tx.split_me):50; S.txType=tx?tx.type:'expense'; S.selectedCat=tx?tx.category_id:null; S.currentTags=tx?(tx.tags||[]).slice():[]; document.querySelectorAll('.toggle-opt[data-type]').forEach(function(b){b.classList.toggle('active',b.getAttribute('data-type')===S.txType);}); populateAccountSelect(); if(tx&&tx.account_id){var sel=document.getElementById('tx-account-id');for(var i=0;iΓ—'; pill.querySelector('button').addEventListener('click',function(){S.currentTags=S.currentTags.filter(function(t){return t!==tag;});renderTagsWrap();}); wrap.insertBefore(pill,inp); }); } function renderCatPicker(){ var grid=document.getElementById('cat-picker');grid.innerHTML=''; var filtered=S.categories.filter(function(cat){return cat.type===S.txType;}); if(!filtered.length){grid.innerHTML='

Sin categorΓ­as para este tipo

';return;} filtered.forEach(function(cat){ var d=document.createElement('div');d.className='cat-opt'+(S.selectedCat===cat.id?' selected':''); d.innerHTML=''+cat.icon+''+cat.name+''; d.addEventListener('click',function(){S.selectedCat=cat.id;renderCatPicker();});grid.appendChild(d); }); } document.getElementById('btn-save-tx').addEventListener('click',function(){ var desc=document.getElementById('tx-desc').value.trim(); var amount=parseFloat(document.getElementById('tx-amount').value); var currency=document.getElementById('tx-currency').value; var date=document.getElementById('tx-date').value||todayStr(); var tc=currency==='USD'?(parseFloat(document.getElementById('tx-tc').value)||S.tc.blue):1; var note=document.getElementById('tx-note').value.trim(); var shared=document.getElementById('tx-shared').checked; var payer=document.getElementById('tx-payer').value; var splitMe=parseInt(document.getElementById('split-me').value)||50; var accountId=document.getElementById('tx-account-id').value; if(!desc){toast('AgregΓ‘ una descripciΓ³n','error');return;} if(!amount||amount<=0){toast('IngresΓ‘ un monto vΓ‘lido','error');return;} if(!accountId){toast('SeleccionΓ‘ una cuenta','error');return;} var defaultCatId=S.txType==='income'?'i4':'c9'; if(S.editingTxId){ // Editing: reverse old effect on account, apply new var oldTx=S.transactions.find(function(t){return t.id===S.editingTxId;}); if(oldTx){ var oldAcc=S.accounts.find(function(a){return a.id===oldTx.account_id;}); if(oldAcc){var rev=oldTx.type==='income'?-oldTx.amount:oldTx.amount;if(oldTx.currency==='USD'&&oldAcc.currency==='ARS')rev*=oldTx.tc;else if(oldTx.currency==='ARS'&&oldAcc.currency==='USD')rev/=oldTx.tc;oldAcc.balance=(oldAcc.balance||0)+rev;} oldTx.description=desc;oldTx.amount=amount;oldTx.currency=currency;oldTx.tc=tc; oldTx.date=date;oldTx.note=note;oldTx.shared=shared;oldTx.payer=shared?payer:'me'; oldTx.split_me=shared?splitMe:100;oldTx.category_id=S.selectedCat||defaultCatId; oldTx.account_id=accountId;oldTx.tags=S.currentTags.slice(); } var newAcc=S.accounts.find(function(a){return a.id===accountId;}); if(newAcc){var delta=S.txType==='income'?amount:-amount;if(currency==='USD'&&newAcc.currency==='ARS')delta*=tc;else if(currency==='ARS'&&newAcc.currency==='USD')delta/=tc;newAcc.balance=(newAcc.balance||0)+delta;} saveAccountsLocal();closeModal('modal-new-tx');renderDashboard(); if(S.activeTab==='transactions')renderTransactions(); if(S.activeTab==='accounts')renderAccounts(); toast('TransacciΓ³n actualizada βœ“','success'); api('updateTransaction',{transaction:oldTx}).catch(function(){}); }else{ var acc=S.accounts.find(function(a){return a.id===accountId;}); if(acc){var d2=S.txType==='income'?amount:-amount;if(currency==='USD'&&acc.currency==='ARS')d2*=tc;else if(currency==='ARS'&&acc.currency==='USD')d2/=tc;acc.balance=(acc.balance||0)+d2;saveAccountsLocal();api('updateAccountBalance',{id:acc.id,balance:acc.balance}).catch(function(){});} var newTx={id:genId(),uid:S.user.uid,date:date,type:S.txType,amount:amount,currency:currency,tc:tc,category_id:S.selectedCat||defaultCatId,account_id:accountId,tags:S.currentTags.slice(),description:desc,note:note,shared:shared,payer:shared?payer:'me',split_me:shared?splitMe:100,created_at:new Date().toISOString()}; S.transactions.unshift(newTx);closeModal('modal-new-tx');renderDashboard(); if(S.activeTab==='transactions')renderTransactions(); if(S.activeTab==='accounts')renderAccounts(); toast('TransacciΓ³n guardada βœ“','success');api('addTransaction',{transaction:newTx}).catch(function(){}); } }); // ── TRANSACTIONS ────────────────────────────────── document.querySelectorAll('.filter-chip').forEach(function(btn){ btn.addEventListener('click',function(){ S.txFilter=btn.getAttribute('data-filter'); document.querySelectorAll('.filter-chip').forEach(function(b){b.classList.remove('active');});btn.classList.add('active');renderTransactions(); }); }); function renderTransactions(){ var container=document.getElementById('tx-list-container'); var txs=S.transactions.slice(); if(S.txFilter==='income')txs=txs.filter(function(t){return t.type==='income';}); if(S.txFilter==='expense')txs=txs.filter(function(t){return t.type==='expense';}); if(S.txFilter==='shared')txs=txs.filter(function(t){return t.shared;}); if(S.txFilter==='ARS')txs=txs.filter(function(t){return t.currency==='ARS';}); if(S.txFilter==='USD')txs=txs.filter(function(t){return t.currency==='USD';}); if(!txs.length){container.innerHTML='
πŸ“­

Sin movimientos

TocΓ‘ el + para agregar tu primer transacciΓ³n

';return;} container.innerHTML='
'+txs.map(function(tx){return txHTML(tx,true);}).join('')+'
'; container.querySelectorAll('[data-edit-tx]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();var tx=S.transactions.find(function(t){return t.id===btn.getAttribute('data-edit-tx');});if(tx)openTxModal(tx);});}); container.querySelectorAll('[data-del-tx]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteTx(btn.getAttribute('data-del-tx'));});}); } function deleteTx(txId){ if(!confirm('ΒΏEliminar esta transacciΓ³n?'))return; var tx=S.transactions.find(function(t){return t.id===txId;}); if(tx){ var acc=S.accounts.find(function(a){return a.id===tx.account_id;}); if(acc){var rev=tx.type==='income'?-tx.amount:tx.amount;if(tx.currency==='USD'&&acc.currency==='ARS')rev*=tx.tc;else if(tx.currency==='ARS'&&acc.currency==='USD')rev/=tx.tc;acc.balance=(acc.balance||0)+rev;saveAccountsLocal();} } S.transactions=S.transactions.filter(function(t){return t.id!==txId;}); renderTransactions();renderDashboard(); toast('TransacciΓ³n eliminada','success'); api('deleteTransaction',{id:txId}).catch(function(){}); } function txHTML(tx,showActions){ var cat=S.categories.find(function(c){return c.id===tx.category_id;})||{icon:'πŸ“¦',color:'#6b7280'}; var acc=S.accounts.find(function(a){return a.id===tx.account_id;}); var sign=tx.type==='income'?'+':'-',cls=tx.type==='income'?'income':'expense'; var tags=(tx.tags||[]).map(function(t){return'#'+t+'';}).join(''); var badge=tx.shared?'πŸ’‘':''; var accBadge=acc?'':''; var amt=tx.currency==='USD'?'U$'+fmt(tx.amount):'$'+fmt(tx.amount); var equiv=tx.currency==='USD'?'β‰ˆ $'+fmt(tx.amount*tx.tc)+' ARS':''; var actions=showActions?'
':''; return'
'+ '
'+cat.icon+'
'+ '
'+tx.description+'
'+ '
'+fmtDate(tx.date)+' '+accBadge+' '+badge+' '+tags+'
'+ '
'+ '
'+sign+amt+'
'+(equiv?'
'+equiv+'
':'')+'
'+ actions+'
'; } // ── DASHBOARD ───────────────────────────────────── function renderDashboard(){ var now=new Date(),month=now.getMonth(),year=now.getFullYear(); var savIds=S.accounts.filter(isSavings).map(function(a){return a.id;}); var txs=S.transactions.filter(function(t){var d=new Date(t.date);return d.getMonth()===month&&d.getFullYear()===year&&savIds.indexOf(t.account_id)===-1;}); var iARS=0,eARS=0,iUSD=0,eUSD=0; txs.forEach(function(t){var isIn=t.type==='income';if(t.currency==='ARS'){isIn?(iARS+=t.amount):(eARS+=t.amount);}else{isIn?(iUSD+=t.amount):(eUSD+=t.amount);}}); document.getElementById('balance-month-label').textContent=monthName(month)+' '+year; document.getElementById('balance-main-val').textContent='$'+fmt(iARS-eARS)+' ARS'; document.getElementById('dash-income').textContent='+$'+fmt(iARS); document.getElementById('dash-expense').textContent='-$'+fmt(eARS); var tARS=iARS-eARS,tUSD=iUSD-eUSD; var arsEl=document.getElementById('dash-ars');arsEl.textContent=(tARS>=0?'+':'')+' $'+fmt(tARS);arsEl.className='si-val '+(tARS>=0?'green':'red'); var usdEl=document.getElementById('dash-usd');usdEl.textContent=(tUSD>=0?'+':'')+'U$'+fmt(tUSD);usdEl.className='si-val '+(tUSD>=0?'green':'red'); document.getElementById('dash-count').textContent=txs.length; var cats={};txs.forEach(function(t){cats[t.category_id]=true;});document.getElementById('dash-cats').textContent=Object.keys(cats).length; renderPie(txs.filter(function(t){return t.type==='expense';})); var recent=S.transactions.slice(0,5); document.getElementById('dash-recent-list').innerHTML=recent.length?'
'+recent.map(function(t){return txHTML(t,false);}).join('')+'
':'
πŸ’Έ

Sin movimientos

EmpezΓ‘ cargando tu primer gasto

'; } function renderPie(expenses){ var canvas=document.getElementById('pieChart'),legend=document.getElementById('pie-legend'),ctx=canvas.getContext('2d'); var W=canvas.offsetWidth||300,H=220;canvas.width=W;canvas.height=H;ctx.clearRect(0,0,W,H); var catMap={};expenses.forEach(function(t){var val=t.currency==='USD'?t.amount*t.tc:t.amount;catMap[t.category_id]=(catMap[t.category_id]||0)+val;}); var entries=Object.keys(catMap).map(function(k){return[k,catMap[k]];}).sort(function(a,b){return b[1]-a[1];}); var total=entries.reduce(function(s,e){return s+e[1];},0)||1;legend.innerHTML=''; if(!entries.length){ctx.fillStyle='#ffffff22';ctx.beginPath();ctx.arc(W/2,H/2,80,0,Math.PI*2);ctx.fill();ctx.fillStyle='#9ca3af';ctx.textAlign='center';ctx.font='14px Inter';ctx.fillText('Sin datos',W/2,H/2+5);return;} var cx=W/2,cy=H/2,r=Math.min(cx,cy)-20,startAngle=-Math.PI/2; entries.forEach(function(entry){ var cat=S.categories.find(function(c){return c.id===entry[0];})||{color:'#6b7280',name:'Otros',icon:'πŸ“¦'}; var slice=(entry[1]/total)*Math.PI*2; ctx.beginPath();ctx.moveTo(cx,cy);ctx.arc(cx,cy,r,startAngle,startAngle+slice);ctx.closePath();ctx.fillStyle=cat.color;ctx.fill();ctx.strokeStyle='#1a1d27';ctx.lineWidth=2;ctx.stroke(); legend.innerHTML+='
'+cat.icon+' '+cat.name+' '+Math.round(entry[1]/total*100)+'%
'; startAngle+=slice; }); ctx.beginPath();ctx.arc(cx,cy,r*0.5,0,Math.PI*2);ctx.fillStyle='#1a1d27';ctx.fill(); ctx.fillStyle='#fff';ctx.textAlign='center';ctx.font='bold 14px Inter';ctx.fillText('$'+fmt(total),cx,cy+5); } // ── COUPLE ──────────────────────────────────────── document.getElementById('btn-send-partner').addEventListener('click',function(){ var email=document.getElementById('partner-email').value.trim();if(!email){toast('IngresΓ‘ un email','error');return;} api('linkPartner',{uid:S.user.uid,partner_email:email}).then(function(d){ S.user.partner_uid=d.partner_uid;S.user.partner_name=d.partner_name;localStorage.setItem('session',JSON.stringify(S.user)); toast('Β‘Pareja vinculada! βœ“','success');closeModal('modal-link-partner');renderCouple(); }).catch(function(e){toast(e.message,'error');}); }); function renderCouple(){ var c=document.getElementById('couple-content'); if(!S.user.partner_uid){ c.innerHTML='
πŸ‘€

'+S.user.name+'

Sin pareja vinculada

'+ '
πŸ’‘

Modo pareja activado

VinculΓ‘ tu cuenta con la de tu pareja para compartir gastos.

'+ ''; document.getElementById('btn-open-link-partner').addEventListener('click',function(){showModal('modal-link-partner');});return; } var shared=S.transactions.filter(function(t){return t.shared;}); var iOwe=0,theyOwe=0; shared.forEach(function(t){var val=t.currency==='USD'?t.amount*t.tc:t.amount;var my=val*(t.split_me||50)/100;if(t.payer==='me')theyOwe+=(val-my);else iOwe+=my;}); var net=theyOwe-iOwe; c.innerHTML='
πŸ‘€
πŸ’ž
πŸ‘€
'+ '

'+S.user.name+' & '+(S.user.partner_name||'Pareja')+'

Vinculados

'+ '

Balance compartido (ARS)

'+ (net>=0?'+':'')+' $'+fmt(Math.abs(net))+'
'+(net>0?'Te deben':'Le debΓ©s a '+(S.user.partner_name||'tu pareja'))+'
'+ '
Gastos compartidos
'+ '
'+(shared.length?shared.map(function(t){return txHTML(t,false);}).join(''):'
🀝

Sin gastos compartidos

')+'
'; } // ── VACATIONS ───────────────────────────────────── document.getElementById('btn-save-vac').addEventListener('click',function(){ var name=document.getElementById('vac-name').value.trim();if(!name){toast('PonΓ© un nombre al viaje','error');return;} var vac={id:genId(),uid:S.user.uid,name:name,start_date:document.getElementById('vac-start').value,end_date:document.getElementById('vac-end').value,budget:parseFloat(document.getElementById('vac-budget').value)||0,currency:document.getElementById('vac-currency').value,created_at:new Date().toISOString()}; S.vacations.unshift(vac);closeModal('modal-new-vac');renderVacations();toast('Viaje creado βœ“','success');api('addVacation',{vacation:vac}).catch(function(){}); }); document.getElementById('btn-save-vexp').addEventListener('click',function(){ var desc=document.getElementById('vexp-desc').value.trim(),amount=parseFloat(document.getElementById('vexp-amount').value); if(!desc||!amount){toast('CompletΓ‘ los campos','error');return;} var item={id:genId(),vacation_id:S.activeVacId,type:'expense',description:desc,amount:amount,currency:document.getElementById('vexp-currency').value,tc:parseFloat(document.getElementById('vexp-tc').value)||S.tc.blue,shared:document.getElementById('vexp-shared').checked,payer:document.getElementById('vexp-payer').value,split_me:parseInt(document.getElementById('vexp-split-me').value)||50,created_at:new Date().toISOString()}; if(!S.vacItems[S.activeVacId])S.vacItems[S.activeVacId]=[];S.vacItems[S.activeVacId].unshift(item); closeModal('modal-vac-expense');renderVacations();toast('Gasto guardado βœ“','success');api('addVacationItem',{item:item}).catch(function(){}); }); function calcVacSpent(vacId){return(S.vacItems[vacId]||[]).filter(function(i){return i.type==='expense';}).reduce(function(s,i){return s+(i.currency==='USD'?i.amount*(i.tc||S.tc.blue):i.amount);},0);} function renderVacations(){ var c=document.getElementById('vacation-content'); var btnHtml=''; if(!S.vacations.length){c.innerHTML='
✈️

Sin viajes

CreΓ‘ tu primer viaje para empezar.

'+btnHtml;} else{ var cards=S.vacations.map(function(v){var spent=calcVacSpent(v.id),pct=Math.min(100,Math.round(spent/v.budget*100))||0,curr=v.currency==='USD'?'U$':'$'; return'
✈️ '+v.name+'
πŸ“… '+fmtDate(v.start_date)+' β†’ '+fmtDate(v.end_date)+'
'+ '
'+ '
'+curr+fmt(spent)+' gastado'+curr+fmt(v.budget-spent)+' restante
'+ '
'; }).join('');c.innerHTML=btnHtml+cards; } document.getElementById('btn-open-new-vac').addEventListener('click',function(){showModal('modal-new-vac');}); c.querySelectorAll('[data-vac-expense]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();S.activeVacId=btn.getAttribute('data-vac-expense');showModal('modal-vac-expense');});}); c.querySelectorAll('[data-vac-detail]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openVacationDetail(btn.getAttribute('data-vac-detail'));});}); } function openVacationDetail(vacId){ var vac=S.vacations.find(function(v){return v.id===vacId;});if(!vac)return; var items=S.vacItems[vacId]||[],expenses=items.filter(function(i){return i.type==='expense';}),checks=items.filter(function(i){return i.type==='checklist';}); var spent=calcVacSpent(vacId),pct=Math.min(100,Math.round(spent/vac.budget*100))||0,curr=vac.currency==='USD'?'U$':'$'; var overlay=document.createElement('div');overlay.className='modal-overlay open';overlay.style.zIndex='600'; var checkHtml=checks.length?checks.map(function(i){return'
βœ“
'+i.description+'
';}).join(''):'

Sin Γ­tems

'; var expHtml=expenses.length?expenses.map(function(i){return'
✈️
'+i.description+'
'+(i.shared?'πŸ’‘':'')+'
-'+(i.currency==='USD'?'U$':'$')+fmt(i.amount)+'
';}).join(''):'

Sin gastos aΓΊn

'; overlay.innerHTML=''; overlay.addEventListener('click',function(e){if(e.target===overlay)overlay.remove();});document.body.appendChild(overlay); document.getElementById('close-vac-detail').addEventListener('click',function(){overlay.remove();}); document.getElementById('checklist-container').addEventListener('click',function(e){ var item=e.target.closest('.checklist-item');if(!item)return; var box=item.querySelector('.check-box'),txt=item.querySelector('.check-text'),iid=item.getAttribute('data-item-id'); var vi=(S.vacItems[vacId]||[]).find(function(x){return x.id===iid;});if(vi)vi.done=!vi.done;box.classList.toggle('checked');txt.classList.toggle('done'); }); document.getElementById('btn-add-check').addEventListener('click',function(){ var inp=document.getElementById('new-check-txt'),desc=inp.value.trim();if(!desc)return; var ni={id:genId(),vacation_id:vacId,type:'checklist',description:desc,done:false}; if(!S.vacItems[vacId])S.vacItems[vacId]=[];S.vacItems[vacId].push(ni);inp.value=''; var container=document.getElementById('checklist-container');var p=container.querySelector('p');if(p)p.remove(); var div=document.createElement('div');div.className='checklist-item';div.setAttribute('data-item-id',ni.id);div.innerHTML='
βœ“
'+desc+'';container.appendChild(div); api('addVacationItem',{item:ni}).catch(function(){}); }); } window.addEventListener('resize',function(){if(S.activeTab==='dashboard')renderDashboard();}); +d.venta); } }).catch(function(){console.warn('No se pudo obtener el dΓ³lar oficial');}); } function loadAll(){ return Promise.all([ api('getTransactions',{uid:S.user.uid}), api('getCategories',{uid:S.user.uid}), api('getVacations',{uid:S.user.uid}), api('getAccounts',{uid:S.user.uid}) ]).then(function(results){ S.transactions=results[0].transactions||[]; var bc=results[1].categories||[]; S.categories=defaultCategories().concat(bc.filter(function(c){return c.type==='expense'||c.type==='income';})); S.vacations=results[2].vacations||[]; var ba=results[3].accounts||[]; ba.forEach(function(a){if(!S.accounts.find(function(x){return x.id===a.id;}))S.accounts.push(a);}); saveAccountsLocal(); }).catch(function(){if(!S.categories.length)S.categories=defaultCategories();}); } // ── TABS ───────────────────────────────────────── document.querySelectorAll('.nav-item').forEach(function(btn){btn.addEventListener('click',function(){switchTab(btn.getAttribute('data-tab'),btn);});}); function switchTab(tab,btn){ ['dashboard','transactions','accounts','savings','couple','vacation','settings'].forEach(function(t){var el=document.getElementById('tab-'+t);if(el)el.style.display='none';}); var active=document.getElementById('tab-'+tab);if(active)active.style.display=''; document.querySelectorAll('.nav-item').forEach(function(b){b.classList.remove('active');}); if(btn)btn.classList.add('active');S.activeTab=tab; var titles={dashboard:'Dashboard',transactions:'Movimientos',accounts:'Cuentas',savings:'Ahorros',couple:'Pareja',vacation:'Viajes',settings:'ConfiguraciΓ³n'}; document.getElementById('topbar-title').textContent=titles[tab]||tab; if(tab==='transactions')renderTransactions(); if(tab==='accounts')renderAccounts(); if(tab==='savings')renderSavings(); if(tab==='couple')renderCouple(); if(tab==='vacation')renderVacations(); if(tab==='settings')updateSettingsUI(); } document.getElementById('btn-go-settings').addEventListener('click',function(){switchTab('settings',document.querySelector('[data-tab="settings"]'));}); // ── MODE ────────────────────────────────────────── function updateModeUI(){ if(!S.user)return; var c=S.user.mode==='couple'||S.user.mode==='both',v=S.user.mode==='vacation'||S.user.mode==='both'; document.getElementById('nav-couple').style.display=c?'':'none'; document.getElementById('nav-vacation').style.display=v?'':'none'; var badges='';if(c)badges+='πŸ’‘ Pareja ';if(v)badges+='πŸ–οΈ Vacaciones'; document.getElementById('mode-indicator').innerHTML=badges; document.getElementById('shared-section').style.display=c?'':'none'; document.getElementById('vexp-shared-wrap').style.display=c?'':'none'; } document.getElementById('toggle-couple').addEventListener('change',function(){toggleMode('couple',this.checked);}); document.getElementById('toggle-vacation').addEventListener('change',function(){toggleMode('vacation',this.checked);}); function toggleMode(mode,checked){ if(!S.user)return; var c=S.user.mode==='couple'||S.user.mode==='both',v=S.user.mode==='vacation'||S.user.mode==='both'; if(mode==='couple')c=checked;if(mode==='vacation')v=checked; S.user.mode=c&&v?'both':c?'couple':v?'vacation':'normal'; localStorage.setItem('session',JSON.stringify(S.user));updateModeUI(); api('updateUserMode',{uid:S.user.uid,mode:S.user.mode}).catch(function(){}); } // ── SETTINGS / CATEGORIES ──────────────────────── function updateSettingsUI(){ if(!S.user)return; document.getElementById('settings-name').textContent=S.user.name||'β€”'; document.getElementById('settings-email').textContent=S.user.email||'β€”'; document.getElementById('settings-avatar').textContent=(S.user.name||'?')[0].toUpperCase(); document.getElementById('toggle-couple').checked=S.user.mode==='couple'||S.user.mode==='both'; document.getElementById('toggle-vacation').checked=S.user.mode==='vacation'||S.user.mode==='both'; var list=document.getElementById('categories-list-settings'); list.innerHTML=S.categories.map(function(cat){ return'
'+ '
'+ ''+cat.icon+''+ '
'+cat.name+'
'+ '
'+(cat.type==='income'?'Ingreso':'Gasto')+'
'+ '
'+ ''+ ''+ '
'; }).join(''); list.querySelectorAll('[data-edit-cat]').forEach(function(btn){ btn.addEventListener('click',function(){openEditCat(btn.getAttribute('data-edit-cat'));}); }); } // Edit category function openEditCat(catId){ var cat=S.categories.find(function(c){return c.id===catId;});if(!cat)return; S.editingCatId=catId; document.getElementById('edit-cat-name').value=cat.name; document.getElementById('edit-cat-icon').value=cat.icon; showModal('modal-edit-cat'); } document.getElementById('btn-save-edit-cat').addEventListener('click',function(){ var cat=S.categories.find(function(c){return c.id===S.editingCatId;});if(!cat)return; var name=document.getElementById('edit-cat-name').value.trim(); var icon=document.getElementById('edit-cat-icon').value.trim(); if(!name){toast('IngresΓ‘ un nombre','error');return;} cat.name=name;if(icon)cat.icon=icon; closeModal('modal-edit-cat');updateSettingsUI();renderCatPicker(); toast('CategorΓ­a actualizada βœ“','success'); api('updateCategory',{category:cat}).catch(function(){}); }); document.getElementById('btn-new-cat').addEventListener('click',function(){ S.newCatType='expense'; document.getElementById('cat-type-expense').classList.add('active');document.getElementById('cat-type-income').classList.remove('active'); showModal('modal-new-cat'); }); document.getElementById('cat-type-expense').addEventListener('click',function(){S.newCatType='expense';document.getElementById('cat-type-expense').classList.add('active');document.getElementById('cat-type-income').classList.remove('active');}); document.getElementById('cat-type-income').addEventListener('click',function(){S.newCatType='income';document.getElementById('cat-type-income').classList.add('active');document.getElementById('cat-type-expense').classList.remove('active');}); document.getElementById('btn-save-cat').addEventListener('click',function(){ var name=document.getElementById('cat-name').value.trim(),icon=document.getElementById('cat-icon').value.trim()||'πŸ“¦',color=document.getElementById('cat-color').value; if(!name){toast('IngresΓ‘ un nombre','error');return;} var cat={id:genId(),uid:S.user.uid,name:name,icon:icon,color:color,type:S.newCatType}; S.categories.push(cat);closeModal('modal-new-cat');updateSettingsUI(); toast('CategorΓ­a creada βœ“','success');api('addCategory',{category:cat}).catch(function(){}); }); // ── TC ──────────────────────────────────────────── document.getElementById('btn-open-tc').addEventListener('click',function(){ document.getElementById('tc-oficial').value=S.tc.oficial;document.getElementById('tc-blue').value=S.tc.blue;showModal('modal-tc'); }); document.getElementById('btn-save-tc').addEventListener('click',function(){ S.tc.oficial=parseFloat(document.getElementById('tc-oficial').value)||S.tc.oficial; S.tc.blue=parseFloat(document.getElementById('tc-blue').value)||S.tc.blue; localStorage.setItem('tc',JSON.stringify(S.tc));toast('Tipo de cambio guardado βœ“','success');closeModal('modal-tc'); if(S.activeTab==='savings')renderSavings(); }); // ── MODALS CLOSE ───────────────────────────────── document.querySelectorAll('[data-close]').forEach(function(btn){btn.addEventListener('click',function(){closeModal(btn.getAttribute('data-close'));});}); document.querySelectorAll('.modal-overlay').forEach(function(o){o.addEventListener('click',function(e){if(e.target===o)o.classList.remove('open');});}); // ── ACCOUNTS ───────────────────────────────────── function renderAccounts(){ var c=document.getElementById('accounts-content'); var nonSav=S.accounts.filter(function(a){return!isSavings(a);}); var total=nonSav.reduce(function(s,a){return s+(a.currency==='USD'?a.balance*S.tc.blue:a.balance);},0); var btnHtml='
'+ ''+ ''+ '
'; if(!S.accounts.length){ c.innerHTML='
🏦

Sin cuentas

CreΓ‘ tu primera cuenta para empezar a registrar movimientos.

'+btnHtml; }else{ var totalHtml='

Total disponible (sin ahorros)

$'+fmt(total)+'
En ARS equivalente al TC blue
'; var cards=nonSav.map(function(a){return accCardHTML(a,true);}).join(''); c.innerHTML=btnHtml+totalHtml+cards; } document.getElementById('btn-open-new-account').addEventListener('click',function(){openNewAccount();}); document.getElementById('btn-open-transfer').addEventListener('click',function(){openTransfer();}); c.querySelectorAll('[data-edit-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openEditAccount(btn.getAttribute('data-edit-acc'));});}); c.querySelectorAll('[data-del-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteAccount(btn.getAttribute('data-del-acc'));});}); c.querySelectorAll('[data-acc-history]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();showAccHistory(btn.getAttribute('data-acc-history'));});}); } function accCardHTML(acc,showActions){ var cfg=ACC_TYPES[acc.type]||ACC_TYPES.bank; var icon=acc.icon||cfg.icon; var balStr=acc.currency==='USD'?'U$'+fmt(acc.balance):'$'+fmt(acc.balance); var subStr=acc.currency==='USD'?'β‰ˆ $'+fmt(acc.balance*S.tc.blue)+' ARS':''; var actions=showActions?'
'+ ''+ ''+ ''+ '
':''; return''; } function openNewAccount(){ S.editingAccId=null; document.getElementById('acc-modal-title').textContent='Nueva cuenta'; document.getElementById('acc-name').value='';document.getElementById('acc-balance').value=''; document.getElementById('acc-icon').value='';document.getElementById('acc-color').value='#6c63ff'; document.getElementById('acc-type').value='bank';document.getElementById('acc-currency').value='ARS'; document.getElementById('btn-save-account').textContent='Crear cuenta'; showModal('modal-new-account'); } function openEditAccount(accId){ var acc=S.accounts.find(function(a){return a.id===accId;});if(!acc)return; S.editingAccId=accId; document.getElementById('acc-modal-title').textContent='Editar cuenta'; document.getElementById('acc-name').value=acc.name; document.getElementById('acc-balance').value=acc.balance; document.getElementById('acc-icon').value=acc.icon||''; document.getElementById('acc-color').value=acc.color||'#6c63ff'; document.getElementById('acc-type').value=acc.type; document.getElementById('acc-currency').value=acc.currency; document.getElementById('btn-save-account').textContent='Guardar cambios'; showModal('modal-new-account'); } document.getElementById('btn-save-account').addEventListener('click',function(){ var name=document.getElementById('acc-name').value.trim(); var type=document.getElementById('acc-type').value; var balance=parseFloat(document.getElementById('acc-balance').value)||0; var currency=document.getElementById('acc-currency').value; var icon=document.getElementById('acc-icon').value.trim()||(ACC_TYPES[type]||ACC_TYPES.bank).icon; var color=document.getElementById('acc-color').value; if(!name){toast('PonΓ© un nombre a la cuenta','error');return;} if(S.editingAccId){ var acc=S.accounts.find(function(a){return a.id===S.editingAccId;}); if(acc){acc.name=name;acc.type=type;acc.balance=balance;acc.currency=currency;acc.icon=icon;acc.color=color;} }else{ var newAcc={id:genId(),uid:S.user.uid,name:name,type:type,balance:balance,currency:currency,icon:icon,color:color,created_at:new Date().toISOString()}; S.accounts.push(newAcc); api('addAccount',{account:newAcc}).catch(function(){}); } saveAccountsLocal();closeModal('modal-new-account'); if(S.activeTab==='accounts')renderAccounts(); if(S.activeTab==='savings')renderSavings(); populateAccountSelect(); toast((S.editingAccId?'Cuenta actualizada':'Cuenta creada')+' βœ“','success'); if(S.editingAccId)api('updateAccount',{account:S.accounts.find(function(a){return a.id===S.editingAccId;})}).catch(function(){}); }); function deleteAccount(accId){ if(!confirm('ΒΏEliminar esta cuenta? Las transacciones asociadas no se borrarΓ‘n.'))return; S.accounts=S.accounts.filter(function(a){return a.id!==accId;}); saveAccountsLocal(); if(S.activeTab==='accounts')renderAccounts(); if(S.activeTab==='savings')renderSavings(); populateAccountSelect(); toast('Cuenta eliminada','success'); api('deleteAccount',{id:accId}).catch(function(){}); } function showAccHistory(accId){ var acc=S.accounts.find(function(a){return a.id===accId;});if(!acc)return; var txs=S.transactions.filter(function(t){return t.account_id===accId;}); var overlay=document.createElement('div');overlay.className='modal-overlay open';overlay.style.zIndex='600'; var txHtml=txs.length?'
'+txs.map(txHTML).join('')+'
':'
πŸ“­

Sin movimientos

'; overlay.innerHTML=''; overlay.addEventListener('click',function(e){if(e.target===overlay)overlay.remove();}); document.body.appendChild(overlay); document.getElementById('close-acc-history').addEventListener('click',function(){overlay.remove();}); } // Transfer function openTransfer(){ populateTransferSelects(); document.getElementById('transfer-amount').value=''; document.getElementById('transfer-date').value=todayStr(); showModal('modal-transfer'); } function populateTransferSelects(){ var opts=S.accounts.map(function(a){return'';}).join(''); document.getElementById('transfer-from').innerHTML=opts; document.getElementById('transfer-to').innerHTML=opts; } document.getElementById('btn-save-transfer').addEventListener('click',function(){ var fromId=document.getElementById('transfer-from').value; var toId=document.getElementById('transfer-to').value; var amount=parseFloat(document.getElementById('transfer-amount').value); var currency=document.getElementById('transfer-currency').value; var date=document.getElementById('transfer-date').value||todayStr(); if(fromId===toId){toast('ElegΓ­ cuentas distintas','error');return;} if(!amount||amount<=0){toast('IngresΓ‘ un monto vΓ‘lido','error');return;} var from=S.accounts.find(function(a){return a.id===fromId;}); var to=S.accounts.find(function(a){return a.id===toId;}); // Adjust balances var debit=currency===from.currency?amount:(currency==='USD'?amount*S.tc.blue:amount/S.tc.blue); var credit=currency===to.currency?amount:(currency==='USD'?amount*S.tc.blue:amount/S.tc.blue); from.balance=(from.balance||0)-debit; to.balance=(to.balance||0)+credit; saveAccountsLocal(); // Record as two transactions var txOut={id:genId(),uid:S.user.uid,date:date,type:'expense',amount:amount,currency:currency,tc:S.tc.blue,category_id:'c9',account_id:fromId,tags:[],description:'Transferencia β†’ '+to.name,note:'',shared:false,payer:'me',split_me:100,created_at:new Date().toISOString()}; var txIn={id:genId(),uid:S.user.uid,date:date,type:'income',amount:amount,currency:currency,tc:S.tc.blue,category_id:'i4',account_id:toId,tags:[],description:'Transferencia ← '+from.name,note:'',shared:false,payer:'me',split_me:100,created_at:new Date().toISOString()}; S.transactions.unshift(txIn);S.transactions.unshift(txOut); closeModal('modal-transfer');renderAccounts();renderDashboard(); toast('Transferencia realizada βœ“','success'); api('addTransaction',{transaction:txOut}).catch(function(){}); api('addTransaction',{transaction:txIn}).catch(function(){}); }); // ── SAVINGS ────────────────────────────────────── function renderSavings(){ var c=document.getElementById('savings-content'); var savs=S.accounts.filter(function(a){return isSavings(a);}); var tUSD=savs.reduce(function(s,a){return s+(a.currency==='USD'?a.balance:0);},0); var tARS=savs.reduce(function(s,a){return s+(a.currency==='ARS'?a.balance:a.balance*S.tc.blue);},0); var btnHtml=''; if(!savs.length){ c.innerHTML='
🐷

Sin ahorros

CreΓ‘ una cuenta de ahorro para llevar el registro de lo que tenΓ©s guardado.

'+btnHtml; }else{ var summary='

Total ahorrado

$'+fmt(tARS)+'
U$'+fmt(tUSD)+' en dΓ³lares Β· TC blue $'+fmt(S.tc.blue)+'
'; var cards=savs.map(function(a){return accCardHTML(a,true);}).join(''); c.innerHTML=btnHtml+summary+cards; } document.getElementById('btn-open-new-savings').addEventListener('click',function(){ openNewAccount();document.getElementById('acc-type').value='savings'; }); c.querySelectorAll('[data-edit-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openEditAccount(btn.getAttribute('data-edit-acc'));});}); c.querySelectorAll('[data-del-acc]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteAccount(btn.getAttribute('data-del-acc'));});}); c.querySelectorAll('[data-acc-history]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();showAccHistory(btn.getAttribute('data-acc-history'));});}); } // ── POPULATE ACCOUNT SELECT ────────────────────── function populateAccountSelect(){ var sel=document.getElementById('tx-account-id'); var nonSav=S.accounts.filter(function(a){return!isSavings(a);}); sel.innerHTML=nonSav.length ?nonSav.map(function(a){return'';}).join('') :''; } // ── NEW / EDIT TX ──────────────────────────────── document.getElementById('fab').addEventListener('click',function(){openTxModal(null);}); function openTxModal(tx){ S.editingTxId=tx?tx.id:null; document.getElementById('tx-modal-title').textContent=tx?'Editar transacciΓ³n':'Nueva transacciΓ³n'; document.getElementById('btn-save-tx').textContent=tx?'Guardar cambios':'Guardar'; document.getElementById('tx-desc').value=tx?tx.description:''; document.getElementById('tx-amount').value=tx?tx.amount:''; document.getElementById('tx-note').value=tx?tx.note:''; document.getElementById('tx-date').value=tx?tx.date:todayStr(); document.getElementById('tx-currency').value=tx?tx.currency:'ARS'; document.getElementById('tc-field').style.display=(tx&&tx.currency==='USD')||false?'':'none'; if(tx&&tx.currency==='USD')document.getElementById('tx-tc').value=tx.tc; document.getElementById('tx-shared').checked=tx?tx.shared:false; document.getElementById('split-section').style.display=(tx&&tx.shared)?'':'none'; document.getElementById('split-me').value=tx?tx.split_me:50; document.getElementById('split-partner').value=tx?(100-tx.split_me):50; S.txType=tx?tx.type:'expense'; S.selectedCat=tx?tx.category_id:null; S.currentTags=tx?(tx.tags||[]).slice():[]; document.querySelectorAll('.toggle-opt[data-type]').forEach(function(b){b.classList.toggle('active',b.getAttribute('data-type')===S.txType);}); populateAccountSelect(); if(tx&&tx.account_id){var sel=document.getElementById('tx-account-id');for(var i=0;iΓ—'; pill.querySelector('button').addEventListener('click',function(){S.currentTags=S.currentTags.filter(function(t){return t!==tag;});renderTagsWrap();}); wrap.insertBefore(pill,inp); }); } function renderCatPicker(){ var grid=document.getElementById('cat-picker');grid.innerHTML=''; var filtered=S.categories.filter(function(cat){return cat.type===S.txType;}); if(!filtered.length){grid.innerHTML='

Sin categorΓ­as para este tipo

';return;} filtered.forEach(function(cat){ var d=document.createElement('div');d.className='cat-opt'+(S.selectedCat===cat.id?' selected':''); d.innerHTML=''+cat.icon+''+cat.name+''; d.addEventListener('click',function(){S.selectedCat=cat.id;renderCatPicker();});grid.appendChild(d); }); } document.getElementById('btn-save-tx').addEventListener('click',function(){ var desc=document.getElementById('tx-desc').value.trim(); var amount=parseFloat(document.getElementById('tx-amount').value); var currency=document.getElementById('tx-currency').value; var date=document.getElementById('tx-date').value||todayStr(); var tc=currency==='USD'?(parseFloat(document.getElementById('tx-tc').value)||S.tc.blue):1; var note=document.getElementById('tx-note').value.trim(); var shared=document.getElementById('tx-shared').checked; var payer=document.getElementById('tx-payer').value; var splitMe=parseInt(document.getElementById('split-me').value)||50; var accountId=document.getElementById('tx-account-id').value; if(!desc){toast('AgregΓ‘ una descripciΓ³n','error');return;} if(!amount||amount<=0){toast('IngresΓ‘ un monto vΓ‘lido','error');return;} if(!accountId){toast('SeleccionΓ‘ una cuenta','error');return;} var defaultCatId=S.txType==='income'?'i4':'c9'; if(S.editingTxId){ // Editing: reverse old effect on account, apply new var oldTx=S.transactions.find(function(t){return t.id===S.editingTxId;}); if(oldTx){ var oldAcc=S.accounts.find(function(a){return a.id===oldTx.account_id;}); if(oldAcc){var rev=oldTx.type==='income'?-oldTx.amount:oldTx.amount;if(oldTx.currency==='USD'&&oldAcc.currency==='ARS')rev*=oldTx.tc;else if(oldTx.currency==='ARS'&&oldAcc.currency==='USD')rev/=oldTx.tc;oldAcc.balance=(oldAcc.balance||0)+rev;} oldTx.description=desc;oldTx.amount=amount;oldTx.currency=currency;oldTx.tc=tc; oldTx.date=date;oldTx.note=note;oldTx.shared=shared;oldTx.payer=shared?payer:'me'; oldTx.split_me=shared?splitMe:100;oldTx.category_id=S.selectedCat||defaultCatId; oldTx.account_id=accountId;oldTx.tags=S.currentTags.slice(); } var newAcc=S.accounts.find(function(a){return a.id===accountId;}); if(newAcc){var delta=S.txType==='income'?amount:-amount;if(currency==='USD'&&newAcc.currency==='ARS')delta*=tc;else if(currency==='ARS'&&newAcc.currency==='USD')delta/=tc;newAcc.balance=(newAcc.balance||0)+delta;} saveAccountsLocal();closeModal('modal-new-tx');renderDashboard(); if(S.activeTab==='transactions')renderTransactions(); if(S.activeTab==='accounts')renderAccounts(); toast('TransacciΓ³n actualizada βœ“','success'); api('updateTransaction',{transaction:oldTx}).catch(function(){}); }else{ var acc=S.accounts.find(function(a){return a.id===accountId;}); if(acc){var d2=S.txType==='income'?amount:-amount;if(currency==='USD'&&acc.currency==='ARS')d2*=tc;else if(currency==='ARS'&&acc.currency==='USD')d2/=tc;acc.balance=(acc.balance||0)+d2;saveAccountsLocal();api('updateAccountBalance',{id:acc.id,balance:acc.balance}).catch(function(){});} var newTx={id:genId(),uid:S.user.uid,date:date,type:S.txType,amount:amount,currency:currency,tc:tc,category_id:S.selectedCat||defaultCatId,account_id:accountId,tags:S.currentTags.slice(),description:desc,note:note,shared:shared,payer:shared?payer:'me',split_me:shared?splitMe:100,created_at:new Date().toISOString()}; S.transactions.unshift(newTx);closeModal('modal-new-tx');renderDashboard(); if(S.activeTab==='transactions')renderTransactions(); if(S.activeTab==='accounts')renderAccounts(); toast('TransacciΓ³n guardada βœ“','success');api('addTransaction',{transaction:newTx}).catch(function(){}); } }); // ── TRANSACTIONS ────────────────────────────────── document.querySelectorAll('.filter-chip').forEach(function(btn){ btn.addEventListener('click',function(){ S.txFilter=btn.getAttribute('data-filter'); document.querySelectorAll('.filter-chip').forEach(function(b){b.classList.remove('active');});btn.classList.add('active');renderTransactions(); }); }); function renderTransactions(){ var container=document.getElementById('tx-list-container'); var txs=S.transactions.slice(); if(S.txFilter==='income')txs=txs.filter(function(t){return t.type==='income';}); if(S.txFilter==='expense')txs=txs.filter(function(t){return t.type==='expense';}); if(S.txFilter==='shared')txs=txs.filter(function(t){return t.shared;}); if(S.txFilter==='ARS')txs=txs.filter(function(t){return t.currency==='ARS';}); if(S.txFilter==='USD')txs=txs.filter(function(t){return t.currency==='USD';}); if(!txs.length){container.innerHTML='
πŸ“­

Sin movimientos

TocΓ‘ el + para agregar tu primer transacciΓ³n

';return;} container.innerHTML='
'+txs.map(function(tx){return txHTML(tx,true);}).join('')+'
'; container.querySelectorAll('[data-edit-tx]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();var tx=S.transactions.find(function(t){return t.id===btn.getAttribute('data-edit-tx');});if(tx)openTxModal(tx);});}); container.querySelectorAll('[data-del-tx]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();deleteTx(btn.getAttribute('data-del-tx'));});}); } function deleteTx(txId){ if(!confirm('ΒΏEliminar esta transacciΓ³n?'))return; var tx=S.transactions.find(function(t){return t.id===txId;}); if(tx){ var acc=S.accounts.find(function(a){return a.id===tx.account_id;}); if(acc){var rev=tx.type==='income'?-tx.amount:tx.amount;if(tx.currency==='USD'&&acc.currency==='ARS')rev*=tx.tc;else if(tx.currency==='ARS'&&acc.currency==='USD')rev/=tx.tc;acc.balance=(acc.balance||0)+rev;saveAccountsLocal();} } S.transactions=S.transactions.filter(function(t){return t.id!==txId;}); renderTransactions();renderDashboard(); toast('TransacciΓ³n eliminada','success'); api('deleteTransaction',{id:txId}).catch(function(){}); } function txHTML(tx,showActions){ var cat=S.categories.find(function(c){return c.id===tx.category_id;})||{icon:'πŸ“¦',color:'#6b7280'}; var acc=S.accounts.find(function(a){return a.id===tx.account_id;}); var sign=tx.type==='income'?'+':'-',cls=tx.type==='income'?'income':'expense'; var tags=(tx.tags||[]).map(function(t){return'#'+t+'';}).join(''); var badge=tx.shared?'πŸ’‘':''; var accBadge=acc?'':''; var amt=tx.currency==='USD'?'U$'+fmt(tx.amount):'$'+fmt(tx.amount); var equiv=tx.currency==='USD'?'β‰ˆ $'+fmt(tx.amount*tx.tc)+' ARS':''; var actions=showActions?'
':''; return'
'+ '
'+cat.icon+'
'+ '
'+tx.description+'
'+ '
'+fmtDate(tx.date)+' '+accBadge+' '+badge+' '+tags+'
'+ '
'+ '
'+sign+amt+'
'+(equiv?'
'+equiv+'
':'')+'
'+ actions+'
'; } // ── DASHBOARD ───────────────────────────────────── function renderDashboard(){ var now=new Date(),month=now.getMonth(),year=now.getFullYear(); var savIds=S.accounts.filter(isSavings).map(function(a){return a.id;}); var txs=S.transactions.filter(function(t){var d=new Date(t.date);return d.getMonth()===month&&d.getFullYear()===year&&savIds.indexOf(t.account_id)===-1;}); var iARS=0,eARS=0,iUSD=0,eUSD=0; txs.forEach(function(t){var isIn=t.type==='income';if(t.currency==='ARS'){isIn?(iARS+=t.amount):(eARS+=t.amount);}else{isIn?(iUSD+=t.amount):(eUSD+=t.amount);}}); document.getElementById('balance-month-label').textContent=monthName(month)+' '+year; document.getElementById('balance-main-val').textContent='$'+fmt(iARS-eARS)+' ARS'; document.getElementById('dash-income').textContent='+$'+fmt(iARS); document.getElementById('dash-expense').textContent='-$'+fmt(eARS); var tARS=iARS-eARS,tUSD=iUSD-eUSD; var arsEl=document.getElementById('dash-ars');arsEl.textContent=(tARS>=0?'+':'')+' $'+fmt(tARS);arsEl.className='si-val '+(tARS>=0?'green':'red'); var usdEl=document.getElementById('dash-usd');usdEl.textContent=(tUSD>=0?'+':'')+'U$'+fmt(tUSD);usdEl.className='si-val '+(tUSD>=0?'green':'red'); document.getElementById('dash-count').textContent=txs.length; var cats={};txs.forEach(function(t){cats[t.category_id]=true;});document.getElementById('dash-cats').textContent=Object.keys(cats).length; renderPie(txs.filter(function(t){return t.type==='expense';})); var recent=S.transactions.slice(0,5); document.getElementById('dash-recent-list').innerHTML=recent.length?'
'+recent.map(function(t){return txHTML(t,false);}).join('')+'
':'
πŸ’Έ

Sin movimientos

EmpezΓ‘ cargando tu primer gasto

'; } function renderPie(expenses){ var canvas=document.getElementById('pieChart'),legend=document.getElementById('pie-legend'),ctx=canvas.getContext('2d'); var W=canvas.offsetWidth||300,H=220;canvas.width=W;canvas.height=H;ctx.clearRect(0,0,W,H); var catMap={};expenses.forEach(function(t){var val=t.currency==='USD'?t.amount*t.tc:t.amount;catMap[t.category_id]=(catMap[t.category_id]||0)+val;}); var entries=Object.keys(catMap).map(function(k){return[k,catMap[k]];}).sort(function(a,b){return b[1]-a[1];}); var total=entries.reduce(function(s,e){return s+e[1];},0)||1;legend.innerHTML=''; if(!entries.length){ctx.fillStyle='#ffffff22';ctx.beginPath();ctx.arc(W/2,H/2,80,0,Math.PI*2);ctx.fill();ctx.fillStyle='#9ca3af';ctx.textAlign='center';ctx.font='14px Inter';ctx.fillText('Sin datos',W/2,H/2+5);return;} var cx=W/2,cy=H/2,r=Math.min(cx,cy)-20,startAngle=-Math.PI/2; entries.forEach(function(entry){ var cat=S.categories.find(function(c){return c.id===entry[0];})||{color:'#6b7280',name:'Otros',icon:'πŸ“¦'}; var slice=(entry[1]/total)*Math.PI*2; ctx.beginPath();ctx.moveTo(cx,cy);ctx.arc(cx,cy,r,startAngle,startAngle+slice);ctx.closePath();ctx.fillStyle=cat.color;ctx.fill();ctx.strokeStyle='#1a1d27';ctx.lineWidth=2;ctx.stroke(); legend.innerHTML+='
'+cat.icon+' '+cat.name+' '+Math.round(entry[1]/total*100)+'%
'; startAngle+=slice; }); ctx.beginPath();ctx.arc(cx,cy,r*0.5,0,Math.PI*2);ctx.fillStyle='#1a1d27';ctx.fill(); ctx.fillStyle='#fff';ctx.textAlign='center';ctx.font='bold 14px Inter';ctx.fillText('$'+fmt(total),cx,cy+5); } // ── COUPLE ──────────────────────────────────────── document.getElementById('btn-send-partner').addEventListener('click',function(){ var email=document.getElementById('partner-email').value.trim();if(!email){toast('IngresΓ‘ un email','error');return;} api('linkPartner',{uid:S.user.uid,partner_email:email}).then(function(d){ S.user.partner_uid=d.partner_uid;S.user.partner_name=d.partner_name;localStorage.setItem('session',JSON.stringify(S.user)); toast('Β‘Pareja vinculada! βœ“','success');closeModal('modal-link-partner');renderCouple(); }).catch(function(e){toast(e.message,'error');}); }); function renderCouple(){ var c=document.getElementById('couple-content'); if(!S.user.partner_uid){ c.innerHTML='
πŸ‘€

'+S.user.name+'

Sin pareja vinculada

'+ '
πŸ’‘

Modo pareja activado

VinculΓ‘ tu cuenta con la de tu pareja para compartir gastos.

'+ ''; document.getElementById('btn-open-link-partner').addEventListener('click',function(){showModal('modal-link-partner');});return; } var shared=S.transactions.filter(function(t){return t.shared;}); var iOwe=0,theyOwe=0; shared.forEach(function(t){var val=t.currency==='USD'?t.amount*t.tc:t.amount;var my=val*(t.split_me||50)/100;if(t.payer==='me')theyOwe+=(val-my);else iOwe+=my;}); var net=theyOwe-iOwe; c.innerHTML='
πŸ‘€
πŸ’ž
πŸ‘€
'+ '

'+S.user.name+' & '+(S.user.partner_name||'Pareja')+'

Vinculados

'+ '

Balance compartido (ARS)

'+ (net>=0?'+':'')+' $'+fmt(Math.abs(net))+'
'+(net>0?'Te deben':'Le debΓ©s a '+(S.user.partner_name||'tu pareja'))+'
'+ '
Gastos compartidos
'+ '
'+(shared.length?shared.map(function(t){return txHTML(t,false);}).join(''):'
🀝

Sin gastos compartidos

')+'
'; } // ── VACATIONS ───────────────────────────────────── document.getElementById('btn-save-vac').addEventListener('click',function(){ var name=document.getElementById('vac-name').value.trim();if(!name){toast('PonΓ© un nombre al viaje','error');return;} var vac={id:genId(),uid:S.user.uid,name:name,start_date:document.getElementById('vac-start').value,end_date:document.getElementById('vac-end').value,budget:parseFloat(document.getElementById('vac-budget').value)||0,currency:document.getElementById('vac-currency').value,created_at:new Date().toISOString()}; S.vacations.unshift(vac);closeModal('modal-new-vac');renderVacations();toast('Viaje creado βœ“','success');api('addVacation',{vacation:vac}).catch(function(){}); }); document.getElementById('btn-save-vexp').addEventListener('click',function(){ var desc=document.getElementById('vexp-desc').value.trim(),amount=parseFloat(document.getElementById('vexp-amount').value); if(!desc||!amount){toast('CompletΓ‘ los campos','error');return;} var item={id:genId(),vacation_id:S.activeVacId,type:'expense',description:desc,amount:amount,currency:document.getElementById('vexp-currency').value,tc:parseFloat(document.getElementById('vexp-tc').value)||S.tc.blue,shared:document.getElementById('vexp-shared').checked,payer:document.getElementById('vexp-payer').value,split_me:parseInt(document.getElementById('vexp-split-me').value)||50,created_at:new Date().toISOString()}; if(!S.vacItems[S.activeVacId])S.vacItems[S.activeVacId]=[];S.vacItems[S.activeVacId].unshift(item); closeModal('modal-vac-expense');renderVacations();toast('Gasto guardado βœ“','success');api('addVacationItem',{item:item}).catch(function(){}); }); function calcVacSpent(vacId){return(S.vacItems[vacId]||[]).filter(function(i){return i.type==='expense';}).reduce(function(s,i){return s+(i.currency==='USD'?i.amount*(i.tc||S.tc.blue):i.amount);},0);} function renderVacations(){ var c=document.getElementById('vacation-content'); var btnHtml=''; if(!S.vacations.length){c.innerHTML='
✈️

Sin viajes

CreΓ‘ tu primer viaje para empezar.

'+btnHtml;} else{ var cards=S.vacations.map(function(v){var spent=calcVacSpent(v.id),pct=Math.min(100,Math.round(spent/v.budget*100))||0,curr=v.currency==='USD'?'U$':'$'; return'
✈️ '+v.name+'
πŸ“… '+fmtDate(v.start_date)+' β†’ '+fmtDate(v.end_date)+'
'+ '
'+ '
'+curr+fmt(spent)+' gastado'+curr+fmt(v.budget-spent)+' restante
'+ '
'; }).join('');c.innerHTML=btnHtml+cards; } document.getElementById('btn-open-new-vac').addEventListener('click',function(){showModal('modal-new-vac');}); c.querySelectorAll('[data-vac-expense]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();S.activeVacId=btn.getAttribute('data-vac-expense');showModal('modal-vac-expense');});}); c.querySelectorAll('[data-vac-detail]').forEach(function(btn){btn.addEventListener('click',function(e){e.stopPropagation();openVacationDetail(btn.getAttribute('data-vac-detail'));});}); } function openVacationDetail(vacId){ var vac=S.vacations.find(function(v){return v.id===vacId;});if(!vac)return; var items=S.vacItems[vacId]||[],expenses=items.filter(function(i){return i.type==='expense';}),checks=items.filter(function(i){return i.type==='checklist';}); var spent=calcVacSpent(vacId),pct=Math.min(100,Math.round(spent/vac.budget*100))||0,curr=vac.currency==='USD'?'U$':'$'; var overlay=document.createElement('div');overlay.className='modal-overlay open';overlay.style.zIndex='600'; var checkHtml=checks.length?checks.map(function(i){return'
βœ“
'+i.description+'
';}).join(''):'

Sin Γ­tems

'; var expHtml=expenses.length?expenses.map(function(i){return'
✈️
'+i.description+'
'+(i.shared?'πŸ’‘':'')+'
-'+(i.currency==='USD'?'U$':'$')+fmt(i.amount)+'
';}).join(''):'

Sin gastos aΓΊn

'; overlay.innerHTML=''; overlay.addEventListener('click',function(e){if(e.target===overlay)overlay.remove();});document.body.appendChild(overlay); document.getElementById('close-vac-detail').addEventListener('click',function(){overlay.remove();}); document.getElementById('checklist-container').addEventListener('click',function(e){ var item=e.target.closest('.checklist-item');if(!item)return; var box=item.querySelector('.check-box'),txt=item.querySelector('.check-text'),iid=item.getAttribute('data-item-id'); var vi=(S.vacItems[vacId]||[]).find(function(x){return x.id===iid;});if(vi)vi.done=!vi.done;box.classList.toggle('checked');txt.classList.toggle('done'); }); document.getElementById('btn-add-check').addEventListener('click',function(){ var inp=document.getElementById('new-check-txt'),desc=inp.value.trim();if(!desc)return; var ni={id:genId(),vacation_id:vacId,type:'checklist',description:desc,done:false}; if(!S.vacItems[vacId])S.vacItems[vacId]=[];S.vacItems[vacId].push(ni);inp.value=''; var container=document.getElementById('checklist-container');var p=container.querySelector('p');if(p)p.remove(); var div=document.createElement('div');div.className='checklist-item';div.setAttribute('data-item-id',ni.id);div.innerHTML='
βœ“
'+desc+'';container.appendChild(div); api('addVacationItem',{item:ni}).catch(function(){}); }); } window.addEventListener('resize',function(){if(S.activeTab==='dashboard')renderDashboard();});