๐Ÿ’Ž
Loading...
๐Ÿ’Ž
Smart Sheet Studio
๐Ÿ”„
Your net worth
$0
Loading...
Overview
๐Ÿ’ฐ
Total Assets
$0
๐Ÿ’ณ
Total Debt
$0
๐Ÿ“ˆ
Avg Growth
โ€”
๐ŸŽฏ
Freedom Score
0%
12-month growth
Net worth chart0 months
JanFebMarAprMayJunJulAugSepOctNovDec
Debt payoff
๐Ÿ† Debt Payoff Progress0%
Enter original debt in Settings to track progress
Recent monthsView all โ†’
MonthNet WorthChange
Log your first month โ†’
โš™๏ธ
Assets & Liabilities
Update balances. Saves to your Google Sheet.
Assets
๐ŸฆBank / CashSavings & checking
๐Ÿ Home / Real EstateEstimated value
๐Ÿ“ˆInvestmentsStocks, ETFs, 401k
โ‚ฟCrypto
๐Ÿš—VehiclesMarket value
๐Ÿ’ŽOther Assets
Total Assets$0
Liabilities
Current = today ยท Original = starting amount (once)
๐Ÿ  Mortgage
๐Ÿš— Car Loan
๐Ÿ’ณ Credit Cards
๐ŸŽ“ Student Loans
Total Debt$0

Saves directly to your private Google Sheet

๐Ÿ“…
Monthly Log
๏ผ‹
Tap + to log this month. Update once a month.
MonthNet WorthChange
Tap + to log your first month
โœ…
Monthly Checklist
0 of 5 tasks done
This month
Year tracker
๐Ÿ’ก Set a reminder on the 1st โ€” "Wealth Check-in"
๐Ÿ’ŽDashboard
โš™๏ธSettings
๐Ÿ“…Log
โœ…Checklist
async function formatSheet(sid, S){ if(!S){ const meta=await gapi('GET',`https://sheets.googleapis.com/v4/spreadsheets/${sid}`); S={};meta.sheets.forEach(s=>{S[s.properties.title]=s.properties.sheetId;}); } const SH=S['Start Here'],SET=S['Settings'],LOG=S['Monthly Log'],CHK=S['Monthly Checklist'],DBD=S['Dashboard']; // Color palette matching Excel exactly const W ={red:1,green:1,blue:1}; const G1 ={red:0.953,green:0.957,blue:0.965}; // #F3F4F6 margin const G2 ={red:0.976,green:0.980,blue:0.984}; // #F9FAFB alt rows const G3 ={red:0.973,green:0.980,blue:0.988}; // #F8FAFC subtle const NVY={red:0.118,green:0.161,blue:0.231}; // #1E293B navy const GRB={red:0.925,green:0.992,blue:0.961}; // #ECFDF5 green bg const RDB={red:1.0,green:0.945,blue:0.949}; // #FFF1F2 red bg const GRT={red:0.020,green:0.588,blue:0.412}; // #059669 green text const RDT={red:0.863,green:0.149,blue:0.149}; // #DC2626 red text const BLK={red:0.067,green:0.094,blue:0.153}; // #111827 black text const MUT={red:0.612,green:0.639,blue:0.686}; // #9CA3AF muted const GRY={red:0.420,green:0.447,blue:0.502}; // #6B7280 gray text const BLU={red:0,green:0,blue:1}; // #0000FF blue inputs const PRP={red:0.486,green:0.231,blue:0.929}; // #7C3AED purple const C1 ={red:0.820,green:0.980,blue:0.898}; // #D1FAE5 const C2 ={red:0.859,green:0.918,blue:0.996}; // #DBEAFE const C3 ={red:0.996,green:0.953,blue:0.780}; // #FEF3C7 const C4 ={red:0.929,green:0.914,blue:0.996}; // #EDE9FE const C5 ={red:0.800,green:0.984,blue:0.945}; // #CCFBF1 const T1 ={red:0.153,green:0.314,blue:0.039}; // #27500A const T2 ={red:0.047,green:0.267,blue:0.486}; // #0C447C const T3 ={red:0.388,green:0.220,blue:0.024}; // #633806 const T4 ={red:0.235,green:0.204,blue:0.537}; // #3C3489 const T5 ={red:0.031,green:0.314,blue:0.255}; // #085041 const GNB={red:0.063,green:0.725,blue:0.506}; // #10B981 green bar const AMB={red:0.573,green:0.251,blue:0.055}; // #92400E amber const YLW={red:1.0,green:0.984,blue:0.925}; // #FFFBEB yellow bg const GRN={red:0.941,green:0.992,blue:0.969}; // #F0FDF4 light green function rc(shId,r0,r1,c0,c1x,bg,fc,bold,fs,italic){ const fmt={backgroundColor:bg,textFormat:{}}; if(fc)fmt.textFormat.foregroundColor=fc; if(bold!==undefined)fmt.textFormat.bold=bold; if(fs)fmt.textFormat.fontSize=fs; if(italic)fmt.textFormat.italic=true; return{repeatCell:{range:{sheetId:shId,startRowIndex:r0,endRowIndex:r1,startColumnIndex:c0,endColumnIndex:c1x},cell:{userEnteredFormat:fmt},fields:'userEnteredFormat'}}; } function cw(shId,c0,c1x,px){return{updateDimensionProperties:{range:{sheetId:shId,dimension:'COLUMNS',startIndex:c0,endIndex:c1x},properties:{pixelSize:px},fields:'pixelSize'}};} function rh(shId,r0,r1,px){return{updateDimensionProperties:{range:{sheetId:shId,dimension:'ROWS',startIndex:r0,endIndex:r1},properties:{pixelSize:px},fields:'pixelSize'}};} function frz(shId,rows){return{updateSheetProperties:{properties:{sheetId:shId,gridProperties:{frozenRowCount:rows}},fields:'gridProperties.frozenRowCount'}};} function mrg(shId,r0,r1,c0,c1x){return{mergeCells:{range:{sheetId:shId,startRowIndex:r0,endRowIndex:r1,startColumnIndex:c0,endColumnIndex:c1x},mergeType:'MERGE_ALL'}};} function brd(shId,r0,r1,c0,c1x,color){ const b={style:'SOLID',color:color||{red:0.878,green:0.890,blue:0.910}}; return{updateBorders:{range:{sheetId:shId,startRowIndex:r0,endRowIndex:r1,startColumnIndex:c0,endColumnIndex:c1x},bottom:b,right:b}}; } const requests=[ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• SETTINGS โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // Margin cols A and E+ rc(SET,0,25,0,1,G1,null,false,null),rc(SET,0,25,4,6,G1,null,false,null), // B2 ASSETS header rc(SET,1,2,1,4,W,BLK,true,14), // B3 column headers rc(SET,2,3,1,2,G2,GRY,true,10),rc(SET,2,3,2,3,G2,GRY,true,10), // Asset rows 4-9 white/gray alternating, labels gray, inputs BLUE bold 12 rc(SET,3,4,1,2,W,GRY,false,11),rc(SET,3,4,2,3,W,BLU,true,12), rc(SET,4,5,1,2,W,GRY,false,11),rc(SET,4,5,2,3,W,BLU,true,12), rc(SET,5,6,1,2,W,GRY,false,11),rc(SET,5,6,2,3,W,BLU,true,12), rc(SET,6,7,1,2,W,GRY,false,11),rc(SET,6,7,2,3,W,BLU,true,12), rc(SET,7,8,1,2,W,GRY,false,11),rc(SET,7,8,2,3,W,BLU,true,12), rc(SET,8,9,1,2,W,GRY,false,11),rc(SET,8,9,2,3,W,BLU,true,12), // Row 10 TOTAL ASSETS green rc(SET,9,10,1,2,GRB,GRT,true,12),rc(SET,9,10,2,3,GRB,GRT,true,14),rc(SET,9,10,3,4,GRB,null,false,null), // Row 11 empty rc(SET,10,11,1,4,G1,null,false,null), // Row 12 LIABILITIES header rc(SET,11,12,1,4,W,BLK,true,14), // Row 13 column headers rc(SET,12,13,1,2,G2,GRY,true,10),rc(SET,12,13,2,3,G2,GRY,true,10),rc(SET,12,13,3,4,G2,GRY,true,10), // Liability rows 14-18 white/gray alternating, inputs BLUE rc(SET,13,14,1,2,W,GRY,false,11),rc(SET,13,14,2,3,W,BLU,true,12),rc(SET,13,14,3,4,W,BLU,true,12), rc(SET,14,15,1,2,W,GRY,false,11),rc(SET,14,15,2,3,W,BLU,true,12),rc(SET,14,15,3,4,W,BLU,true,12), rc(SET,15,16,1,2,W,GRY,false,11),rc(SET,15,16,2,3,W,BLU,true,12),rc(SET,15,16,3,4,W,BLU,true,12), rc(SET,16,17,1,2,W,GRY,false,11),rc(SET,16,17,2,3,W,BLU,true,12),rc(SET,16,17,3,4,W,BLU,true,12), rc(SET,17,18,1,2,W,GRY,false,11),rc(SET,17,18,2,3,W,BLU,true,12),rc(SET,17,18,3,4,W,BLU,true,12), // Row 19 TOTAL LIABILITIES red rc(SET,18,19,1,2,RDB,RDT,true,12),rc(SET,18,19,2,3,RDB,RDT,true,14),rc(SET,18,19,3,4,RDB,RDT,true,14), // Row 20 empty rc(SET,19,20,1,4,G1,null,false,null), // Row 21 NET WORTH navy big rc(SET,20,21,1,4,NVY,W,true,22), // Row 22 hint rc(SET,21,22,1,4,YLW,GRY,false,9,true), cw(SET,0,1,20),cw(SET,1,2,220),cw(SET,2,3,160),cw(SET,3,4,160),cw(SET,4,5,20), rh(SET,20,21,52), // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• MONTHLY LOG โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• rc(LOG,0,18,0,1,G1,null,false,null),rc(LOG,0,18,6,7,G1,null,false,null), // Row 2 header white bold 14 rc(LOG,1,2,1,6,W,BLK,true,14), // Row 3 column headers gray rc(LOG,2,3,1,6,G2,GRY,true,10), // Month rows 4-15 alternating, month name bold black, inputs BLUE ...[3,5,7,9,11,13].map(i=>[rc(LOG,i,i+1,1,2,W,BLK,true,11),rc(LOG,i,i+1,2,4,W,BLU,true,null),rc(LOG,i,i+1,4,6,W,BLK,true,12)]).flat(), ...[4,6,8,10,12,14].map(i=>[rc(LOG,i,i+1,1,2,G2,BLK,true,11),rc(LOG,i,i+1,2,4,G2,BLU,true,null),rc(LOG,i,i+1,4,6,G2,BLK,true,12)]).flat(), // Row 16 Year Summary navy rc(LOG,15,16,1,2,NVY,W,true,11),rc(LOG,15,16,2,6,GRN,GRT,true,11), // Row 17 labels rc(LOG,16,17,2,6,GRN,MUT,false,9), frz(LOG,3), cw(LOG,0,1,20),cw(LOG,1,2,130),cw(LOG,2,3,160),cw(LOG,3,4,160),cw(LOG,4,5,150),cw(LOG,5,6,150), // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• MONTHLY CHECKLIST โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• rc(CHK,0,28,0,1,G1,null,false,null),rc(CHK,0,28,6,7,G1,null,false,null), // Rows 2-4 navy block rc(CHK,1,5,1,6,NVY,null,false,null), rc(CHK,2,3,1,6,NVY,W,true,16), // Row 5 green bar rc(CHK,4,5,1,6,GNB,null,false,null),rh(CHK,4,5,6), // Row 6 subtitle rc(CHK,5,6,1,6,G3,GRY,false,10,true), // Row 7 empty rc(CHK,6,7,1,6,G1,null,false,null), // Row 8 column headers rc(CHK,7,8,1,2,G2,null,false,null),rc(CHK,7,8,2,3,G2,MUT,true,10),rc(CHK,7,8,3,4,G2,MUT,true,10),rc(CHK,7,8,4,5,G2,MUT,true,10), // Tasks 9-13 step numbers colored, content alternating rc(CHK,8,9,1,2,C1,T1,true,11),rc(CHK,8,9,2,4,W,BLK,false,11),rc(CHK,8,9,4,5,W,MUT,false,16), rc(CHK,9,10,1,2,C2,T2,true,11),rc(CHK,9,10,2,4,G2,BLK,false,11),rc(CHK,9,10,4,5,G2,MUT,false,16), rc(CHK,10,11,1,2,C3,T3,true,11),rc(CHK,10,11,2,4,W,BLK,false,11),rc(CHK,10,11,4,5,W,MUT,false,16), rc(CHK,11,12,1,2,C4,T4,true,11),rc(CHK,11,12,2,4,G2,BLK,false,11),rc(CHK,11,12,4,5,G2,MUT,false,16), rc(CHK,12,13,1,2,C5,T5,true,11),rc(CHK,12,13,2,4,W,BLK,false,11),rc(CHK,12,13,4,5,W,MUT,false,16), // Row 15 tracker label rc(CHK,14,15,1,6,G1,MUT,true,9), // Month cells rows 17,20,23 rc(CHK,16,17,1,6,G1,null,false,null), rc(CHK,17,18,1,2,W,{red:0.216,green:0.255,blue:0.318},false,10),rc(CHK,17,18,2,3,G2,{red:0.216,green:0.255,blue:0.318},false,10), rc(CHK,17,18,3,4,W,{red:0.216,green:0.255,blue:0.318},false,10),rc(CHK,17,18,4,5,G2,{red:0.216,green:0.255,blue:0.318},false,10), rc(CHK,19,20,1,6,G1,null,false,null), rc(CHK,20,21,1,2,W,{red:0.216,green:0.255,blue:0.318},false,10),rc(CHK,20,21,2,3,G2,{red:0.216,green:0.255,blue:0.318},false,10), rc(CHK,20,21,3,4,W,{red:0.216,green:0.255,blue:0.318},false,10),rc(CHK,20,21,4,5,G2,{red:0.216,green:0.255,blue:0.318},false,10), rc(CHK,22,23,1,6,G1,null,false,null), rc(CHK,23,24,1,2,W,{red:0.216,green:0.255,blue:0.318},false,10),rc(CHK,23,24,2,3,G2,{red:0.216,green:0.255,blue:0.318},false,10), rc(CHK,23,24,3,4,W,{red:0.216,green:0.255,blue:0.318},false,10),rc(CHK,23,24,4,5,G2,{red:0.216,green:0.255,blue:0.318},false,10), rc(CHK,25,26,1,6,YLW,AMB,false,10), rc(CHK,27,28,1,6,G2,GRY,false,9,true), rh(CHK,16,17,8),rh(CHK,18,19,8),rh(CHK,21,22,8), cw(CHK,0,1,20),cw(CHK,1,2,40),cw(CHK,2,3,200),cw(CHK,3,4,140),cw(CHK,4,5,70), // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• DASHBOARD โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• rc(DBD,0,28,0,1,G1,null,false,null), // Row 2 navy header full width rc(DBD,1,2,1,14,NVY,W,true,18), // Rows 3-4 empty rc(DBD,2,4,1,14,G1,null,false,null), // Row 5 metric labels rc(DBD,4,5,1,5,W,MUT,true,10),rc(DBD,4,5,5,9,W,MUT,true,10),rc(DBD,4,5,9,13,W,MUT,true,10), // Row 6 big metric values rc(DBD,5,6,1,5,W,GRT,true,24),rc(DBD,5,6,5,9,W,RDT,true,24),rc(DBD,5,6,9,13,W,PRP,true,24), // Rows 7 empty, Row 8 color bars rc(DBD,6,7,1,14,G1,null,false,null), rc(DBD,7,8,1,5,GNB,null,false,null),rc(DBD,7,8,5,9,{red:0.937,green:0.267,blue:0.267},null,false,null),rc(DBD,7,8,9,13,{red:0.486,green:0.231,blue:0.929},null,false,null), rh(DBD,7,8,6), // Rows 9-10 empty rc(DBD,8,10,1,14,G1,null,false,null), // Row 11 section headers rc(DBD,10,11,1,14,W,BLK,true,12), // Row 12 sparkline area (tall row for chart) rc(DBD,11,12,1,14,W,GRY,false,10,true),rh(DBD,11,12,80), // Row 13 month labels rc(DBD,12,13,1,14,W,MUT,false,9), // Row 14 empty rc(DBD,13,14,1,14,G1,null,false,null), // Row 15 debt payoff header rc(DBD,14,15,1,14,W,BLK,true,12), // Row 16 debt text rc(DBD,15,16,1,14,W,GRY,false,11,true), // Row 17 progress bar rc(DBD,16,17,1,14,W,GRY,false,10),rh(DBD,16,17,20), // Rows 18-19 empty rc(DBD,17,19,1,14,G1,null,false,null), // Row 20 stat labels rc(DBD,19,20,1,5,W,MUT,true,9),rc(DBD,19,20,5,9,W,MUT,true,9),rc(DBD,19,20,9,13,W,MUT,true,9), // Row 21 stat values rc(DBD,20,21,1,5,W,BLK,true,18),rc(DBD,20,21,5,9,W,GRT,true,18),rc(DBD,20,21,9,13,W,RDT,true,18), cw(DBD,0,1,20),cw(DBD,1,2,80),cw(DBD,2,3,80),cw(DBD,3,4,80),cw(DBD,4,5,80),cw(DBD,5,6,80),cw(DBD,6,7,80),cw(DBD,7,8,80),cw(DBD,8,9,80),cw(DBD,9,10,80),cw(DBD,10,11,80),cw(DBD,11,12,80),cw(DBD,12,13,80), ]; // Split into batches of 50 to avoid API limits for(let i=0;iparseFloat(arr[r]?.[c])||0; D={assets:{bank:g(a,0,0),home:g(a,1,0),investments:g(a,2,0),crypto:g(a,3,0),vehicles:g(a,4,0),other:g(a,5,0)},liab:{mortgage:{cur:g(l,0,0),orig:g(l,0,1)},car:{cur:g(l,1,0),orig:g(l,1,1)},cc:{cur:g(l,2,0),orig:g(l,2,1)},student:{cur:g(l,3,0),orig:g(l,3,1)},other:{cur:g(l,4,0),orig:g(l,4,1)}},log:MOS.map((m,i)=>({month:m,assets:g(log,i,0),liab:g(log,i,1)}))}; localStorage.setItem('wd_data_'+SHEET_ID,JSON.stringify(D)); bootApp(); }catch(e){TOKEN=null;document.getElementById('loading').style.display='none';document.getElementById('signin-screen').style.display='flex';toast('Session expired โ€” sign in again');} } async function syncData(){if(!TOKEN)await reAuth();toast('Syncing...');await loadData();renderAll();fillForm();toast('โœ“ Synced');} async function reAuth(){ return new Promise(res=>{ const c=google.accounts.oauth2.initTokenClient({client_id:CLIENT_ID,scope:SCOPES,prompt:'',callback:async r=>{if(!r.error){TOKEN=r.access_token;}res();}}); c.requestAccessToken(); }); } function getEmpty(){return{assets:{bank:0,home:0,investments:0,crypto:0,vehicles:0,other:0},liab:{mortgage:{cur:0,orig:0},car:{cur:0,orig:0},cc:{cur:0,orig:0},student:{cur:0,orig:0},other:{cur:0,orig:0}},log:MOS.map(m=>({month:m,assets:0,liab:0}))};} function bootApp(){document.getElementById('loading').style.display='none';document.getElementById('setup-screen').style.display='none';document.getElementById('signin-screen').style.display='none';document.getElementById('tbar').style.display='flex';renderAll();fillForm();go('dashboard');} function renderAll(){if(!D)return;renderDash();renderLog();renderChecklist();} function renderDash(){ const ta=Object.values(D.assets).reduce((a,b)=>a+b,0); const td=Object.values(D.liab).reduce((a,b)=>a+(b.cur||0),0); const to=Object.values(D.liab).reduce((a,b)=>a+(b.orig||0),0); const nw=ta-td,fr=to>0?Math.max(0,Math.min(1,(to-td)/to)):0; const fil=D.log.filter(e=>e.assets>0||e.liab>0); const nwArr=fil.map(e=>e.assets-e.liab); let avg=0;if(nwArr.length>1){const ch=nwArr.slice(1).map((v,i)=>v-nwArr[i]);avg=ch.reduce((a,b)=>a+b,0)/ch.length;} const el=document.getElementById('d-nw');el.textContent=fmt(nw);el.className='hdr-nw'+(nw<0?' neg':''); document.getElementById('d-badge').textContent=avg?fmtC(avg)+' avg/month':'Log months to see growth'; document.getElementById('d-assets').textContent=fmt(ta);document.getElementById('d-debt').textContent=fmt(td); document.getElementById('d-freedom').textContent=Math.round(fr*1000)/10+'%';document.getElementById('d-growth').textContent=avg?fmtC(Math.round(avg)):'โ€”'; const pct=Math.round(fr*1000)/10;document.getElementById('d-pct').textContent=pct+'%'; setTimeout(()=>{document.getElementById('d-pfill').style.width=pct+'%';},300); if(to>0)document.getElementById('d-psub').textContent=fmt(to-td)+' paid off ยท '+fmt(td)+' remaining of '+fmt(to); renderChart(D.log);document.getElementById('d-chart-badge').textContent=fil.length+' month'+(fil.length!==1?'s':''); const rec=fil.slice(-5).reverse();const logEl=document.getElementById('d-log'); if(!rec.length){logEl.innerHTML='
Log your first month โ†’
';return;} logEl.innerHTML=rec.map((e,i)=>{const nwv=e.assets-e.liab,chg=i'+e.month.slice(0,3)+''+fmt(nwv)+''+(chg!==null?fmtC(chg):'โ€”')+'โœ๏ธ';}).join(''); logEl.addEventListener('click',ev=>{const t=ev.target.closest('.log-edit');if(t&&t.dataset.month)editLog(t.dataset.month);}); } function renderChart(log){ const svg=document.getElementById('d-chart'),f=log.filter(e=>e.assets>0||e.liab>0); if(f.length<2){svg.innerHTML='Log 2+ months to see chart';return;} const v=f.map(e=>e.assets-e.liab),mn=Math.min(...v),mx=Math.max(...v),rng=mx-mn||1,W=340,H=100,P=6; const pts=v.map((val,i)=>[P+(i/(v.length-1))*(W-P*2),H-P-((val-mn)/rng)*(H-P*2)]); const path='M '+pts.map(p=>p[0]+','+p[1]).join(' L ');const last=pts[pts.length-1];const color=v[v.length-1]>=v[0]?'#10B981':'#EF4444'; svg.innerHTML=``; } function renderLog(){ if(!D)return;const f=D.log.filter(e=>e.assets>0||e.liab>0),el=document.getElementById('log-rows'); if(!f.length){el.innerHTML='
Tap + to log your first month
';return;} el.innerHTML=f.map((e,i)=>{const nwv=e.assets-e.liab,chg=i>0?nwv-(f[i-1].assets-f[i-1].liab):null;return'
'+e.month+''+fmt(nwv)+''+(chg!==null?fmtC(chg):'โ€”')+'โœ๏ธ
';}).join(''); el.addEventListener('click',ev=>{const t=ev.target.closest('.log-edit');if(t&&t.dataset.month)editLog(t.dataset.month);}); } function fillForm(){ if(!D)return;const a=D.assets,l=D.liab,s=(id,v)=>{const e=document.getElementById(id);if(e)e.value=v||'';}; s('f-bank',a.bank);s('f-home',a.home);s('f-invest',a.investments);s('f-crypto',a.crypto);s('f-vehicles',a.vehicles);s('f-other',a.other); s('f-mc',l.mortgage.cur);s('f-mo',l.mortgage.orig);s('f-cc2',l.car.cur);s('f-co',l.car.orig);s('f-ccc',l.cc.cur);s('f-cco',l.cc.orig);s('f-sc',l.student.cur);s('f-so',l.student.orig); calcTotals(); } function calcTotals(){ const ta=['f-bank','f-home','f-invest','f-crypto','f-vehicles','f-other'].reduce((s,id)=>s+num(id),0); const td=['f-mc','f-cc2','f-ccc','f-sc'].reduce((s,id)=>s+num(id),0); document.getElementById('f-ta').textContent=fmt(ta);document.getElementById('f-td').textContent=fmt(td); } async function saveSettings(){ if(!TOKEN)await reAuth(); const btn=document.getElementById('save-btn');btn.textContent='Saving...';btn.disabled=true; const a={bank:num('f-bank'),home:num('f-home'),investments:num('f-invest'),crypto:num('f-crypto'),vehicles:num('f-vehicles'),other:num('f-other')}; const l={mortgage:{cur:num('f-mc'),orig:num('f-mo')},car:{cur:num('f-cc2'),orig:num('f-co')},cc:{cur:num('f-ccc'),orig:num('f-cco')},student:{cur:num('f-sc'),orig:num('f-so')},other:{cur:0,orig:0}}; D.assets=a;D.liab=l;localStorage.setItem('wd_data_'+SHEET_ID,JSON.stringify(D)); try{ await sheetsWrite(SHEET_ID,'Settings!C4:C9',[[a.bank],[a.home],[a.investments],[a.crypto],[a.vehicles],[a.other]]); await sheetsWrite(SHEET_ID,'Settings!C14:D18',[[l.mortgage.cur,l.mortgage.orig],[l.car.cur,l.car.orig],[l.cc.cur,l.cc.orig],[l.student.cur,l.student.orig],[0,0]]); renderDash();btn.textContent='โœ“ Saved!';btn.style.background='var(--green2)'; setTimeout(()=>{btn.textContent='๐Ÿ’พ Save to My Google Sheet';btn.disabled=false;btn.style.background='';},2200); toast('โœ“ Saved to your Google Sheet'); }catch(e){btn.textContent='๐Ÿ’พ Save to My Google Sheet';btn.disabled=false;toast('Save failed');} } function openModal(){ const sel=document.getElementById('m-month');sel.innerHTML=MOS.map(m=>``).join(''); sel.selectedIndex=new Date().getMonth();document.getElementById('m-a').value='';document.getElementById('m-l').value=''; document.getElementById('m-nw').textContent='$0';setupPrefill();document.getElementById('modal').style.display='block'; } function editLog(month){ const e=D.log.find(x=>x.month===month);if(!e)return; const sel=document.getElementById('m-month');sel.innerHTML=MOS.map(m=>``).join(''); sel.value=month;document.getElementById('m-a').value=e.assets||'';document.getElementById('m-l').value=e.liab||'';calcM();setupPrefill();document.getElementById('modal').style.display='block'; } function setupPrefill(){ if(!D)return;const ta=Object.values(D.assets).reduce((a,b)=>a+b,0),td=Object.values(D.liab).reduce((a,b)=>a+(b.cur||0),0); const bar=document.getElementById('prefill-bar'),btn=document.getElementById('btn-use'); bar.style.display='block';btn.textContent=`Use current โ†’ Assets ${fmt(ta)} ยท Debt ${fmt(td)}`; btn.onclick=()=>{document.getElementById('m-a').value=ta;document.getElementById('m-l').value=td;calcM();hidePrefill();}; } function hidePrefill(){document.getElementById('prefill-bar').style.display='none';} function closeModal(){document.getElementById('modal').style.display='none';} function calcM(){const nw=(parseFloat(document.getElementById('m-a').value)||0)-(parseFloat(document.getElementById('m-l').value)||0);const el=document.getElementById('m-nw');el.textContent=fmt(nw);el.style.color=nw>=0?'var(--green)':'var(--red)';} async function saveLog(){ if(!TOKEN)await reAuth(); const month=document.getElementById('m-month').value; const assets=parseFloat(document.getElementById('m-a').value)||0; const liab=parseFloat(document.getElementById('m-l').value)||0; const idx=D.log.findIndex(e=>e.month===month); if(idx>=0){D.log[idx].assets=assets;D.log[idx].liab=liab;} localStorage.setItem('wd_data_'+SHEET_ID,JSON.stringify(D)); try{ const row=MOS.indexOf(month)+4; await sheetsWrite(SHEET_ID,`Monthly Log!C${row}:D${row}`,[[assets,liab]]); renderLog();renderDash();closeModal();toast('โœ“ '+month+' logged'); }catch(e){closeModal();toast('Save failed');} } function renderChecklist(){ const mo=MOS[new Date().getMonth()];document.getElementById('chk-mo-lbl').textContent=mo+"'s tasks"; document.getElementById('chk-prog').textContent=CHK.filter(Boolean).length+' of 5 tasks done'; const tasks=[['Check bank balance','Banking app'],['Check credit card','CC app'],['Update Settings','Settings'],['Log this month','Log tab'],['Review Dashboard','Dashboard']]; document.getElementById('chk-items').innerHTML=tasks.map((t,i)=>`
${t[0]}${t[1]}
`).join(''); document.getElementById('mon-grid').innerHTML=MOS.map((m,i)=>`
${m.slice(0,3)}
${MDONE.includes(i)?'โœ“':''}
`).join(''); } function toggleChk(i){CHK[i]=!CHK[i];localStorage.setItem('wd_chk_'+SHEET_ID,JSON.stringify(CHK));renderChecklist();} function toggleMonth(i){const idx=MDONE.indexOf(i);if(idx>=0)MDONE.splice(idx,1);else MDONE.push(i);localStorage.setItem('wd_md_'+SHEET_ID,JSON.stringify(MDONE));renderChecklist();} function go(tab){['dashboard','settings','log','checklist'].forEach(t=>{document.getElementById('screen-'+t).classList.remove('active');document.getElementById('tab-'+t).classList.remove('active');});document.getElementById('screen-'+tab).classList.add('active');document.getElementById('tab-'+tab).classList.add('active');if(tab==='settings')calcTotals();if(tab==='checklist')renderChecklist();} function toast(msg){const t=document.getElementById('toast');t.textContent=msg;t.classList.add('show');setTimeout(()=>t.classList.remove('show'),2800);} ['f-bank','f-home','f-invest','f-crypto','f-vehicles','f-other','f-mc','f-cc2','f-ccc','f-sc'].forEach(id=>{ window.addEventListener('DOMContentLoaded',()=>{const e=document.getElementById(id);if(e)e.addEventListener('input',calcTotals);}); }); // Make sign in button show loading state until GIS is ready function waitForGoogle(cb, tries=0){ if(typeof google!=='undefined'&&google.accounts){cb();} else if(tries<20){setTimeout(()=>waitForGoogle(cb,tries+1),300);} } window.addEventListener('DOMContentLoaded',()=>{ document.getElementById('tbar').style.display='none'; document.getElementById('loading').style.display='none'; if(SHEET_ID&&D){ bootApp(); }else{ document.getElementById('signin-screen').style.display='flex'; } });