Skip to main content

Overview

SpendWisely George implements a daily budget system that tracks spending for each calendar day (IST timezone) and displays real-time budget usage with visual indicators. The budget resets automatically at midnight.

How It Works

1

Set daily budget

User configures budget amount (e.g., ₹350) in Settings
2

Track today's spending

System filters transactions by today’s date (IST) and type=DEBIT
3

Calculate usage

Budget usage % = (today’s spend / daily budget) × 100
4

Update UI

Progress bar and percentage update in real-time

Setting Budget

UI Component

index.html
<div class="pt-4 border-t border-slate-100">
    <label class="block text-xs font-bold text-slate-400 uppercase mb-2">
        Daily Budget
    </label>
    <input type="number" id="budgetInput"
        class="w-full p-4 bg-slate-100 rounded-xl font-bold text-slate-900 outline-none focus:ring-2 ring-blue-500"
        onchange="pushSettings()">
</div>

Saving to Backend

index.html
async function pushSettings(updateKey = false) {
    const budget = parseFloat(document.getElementById('budgetInput').value);
    const privacy = document.getElementById('privacyToggle').checked;
    const key = document.getElementById('geminiKeyInput').value;
    
    const payload = { 
        action: "save_settings", 
        budget: budget, 
        privacy: privacy 
    };
    if (updateKey && key) payload.geminiKey = key;
    
    // Update local state immediately
    appData.budget = budget; 
    appData.privacy = privacy; 
    renderUI();
    
    // Persist to Google Apps Script
    await fetch(SCRIPT_URL, { 
        method: 'POST', 
        body: JSON.stringify(payload) 
    });
    
    if (updateKey) { 
        alert("Key Saved!"); 
        document.getElementById('geminiKeyInput').value = ""; 
    }
}

Google Apps Script Storage

google_script.js
const SCRIPT_PROP = PropertiesService.getScriptProperties();

if (body.action === "save_settings") {
    SCRIPT_PROP.setProperty("DAILY_BUDGET", body.budget.toString());
    SCRIPT_PROP.setProperty("PRIVACY_MODE", body.privacy.toString());
    if (body.geminiKey) SCRIPT_PROP.setProperty("GEMINI_KEY", body.geminiKey);
    
    return ContentService.createTextOutput(
        JSON.stringify({ status: "Saved" })
    ).setMimeType(ContentService.MimeType.JSON);
}

Loading Budget

google_script.js
function doGet() {
    const sheet = getTargetSheet();
    const data = sheet.getDataRange().getValues();

    const balance = calculateBalance(sheet);
    const budget = SCRIPT_PROP.getProperty("DAILY_BUDGET") || 350;  // Default ₹350
    
    const privacyRaw = SCRIPT_PROP.getProperty("PRIVACY_MODE");
    const privacy = privacyRaw === null ? true : (privacyRaw === "true");

    const expenses = [];
    // ... fetch expenses ...

    return ContentService.createTextOutput(JSON.stringify({
        balance: balance,
        expenses: expenses,
        budget: budget,
        privacy: privacy
    })).setMimeType(ContentService.MimeType.JSON);
}

Budget Tracking

Calculating Today’s Spending

index.html
function renderUI() {
    // Get today's date in IST timezone
    const todayIST = new Date().toLocaleDateString('en-US', { 
        timeZone: 'Asia/Kolkata' 
    });
    
    // Filter transactions for today, DEBIT only
    const todaySpend = appData.expenses.filter(t => 
        new Date(t.date).toLocaleDateString('en-US', { 
            timeZone: 'Asia/Kolkata' 
        }) === todayIST && t.type === 'DEBIT'
    ).reduce((sum, t) => sum + parseFloat(t.amount), 0);
    
    // Calculate percentage (cap at 100%)
    const budgetPct = Math.min((todaySpend / appData.budget) * 100, 100);
    
    // Update progress bar
    document.getElementById('budgetBar').style.width = `${budgetPct}%`;
    document.getElementById('budgetPercent').innerText = `${Math.round(budgetPct)}%`;
}

UI Components

The budget indicator is displayed in the main balance card:
index.html
<div class="card p-6 bg-slate-900 text-white border-none relative overflow-hidden shadow-2xl">
    <!-- Balance display -->
    <h1 id="uiBalance" class="text-5xl font-black tracking-tighter mb-4 relative z-10">...</h1>

    <!-- Budget Progress Bar -->
    <div class="relative pt-1 z-10">
        <div class="flex mb-2 items-center justify-between">
            <div>
                <span class="text-[10px] font-semibold inline-block py-1 px-2 uppercase rounded-full text-blue-200 bg-blue-900"
                    id="budgetLabel">
                    Budget Usage
                </span>
            </div>
            <div class="text-right">
                <span class="text-[10px] font-semibold inline-block text-blue-200"
                    id="budgetPercent">
                    0%
                </span>
            </div>
        </div>
        <div class="overflow-hidden h-2 mb-4 text-xs flex rounded bg-slate-700">
            <div id="budgetBar" style="width:0%" 
                class="bg-blue-500 transition-all duration-1000">
            </div>
        </div>
    </div>
</div>

Visual States

The progress bar changes appearance based on usage:
/* Blue bar indicates healthy spending */
.bg-blue-500 { background-color: #3b82f6; }
Budget usage: 45% Status: On track
The current implementation caps the visual progress at 100%, even if actual spending exceeds the budget. The percentage display will show the true value.

Budget Alerts

Current Implementation

The system provides visual-only alerts through the progress bar. There are no popup notifications or sound alerts in the current version.

Future Enhancement Example

// Add to renderUI() function
if (budgetPct >= 90 && budgetPct < 100) {
    // Show warning notification
    document.getElementById('budgetLabel').innerText = "⚠️ Budget Warning";
    document.getElementById('budgetLabel').classList.add('bg-orange-900', 'text-orange-200');
} else if (budgetPct >= 100) {
    // Show over-budget alert
    document.getElementById('budgetLabel').innerText = "🚨 Over Budget";
    document.getElementById('budgetLabel').classList.add('bg-red-900', 'text-red-200');
} else {
    // Normal state
    document.getElementById('budgetLabel').innerText = "Budget Usage";
    document.getElementById('budgetLabel').classList.add('bg-blue-900', 'text-blue-200');
}

Budget Reset Logic

Budget automatically resets at midnight IST because the calculation filters by todayIST:
index.html
const todayIST = new Date().toLocaleDateString('en-US', { 
    timeZone: 'Asia/Kolkata' 
});

const todaySpend = appData.expenses.filter(t => 
    new Date(t.date).toLocaleDateString('en-US', { 
        timeZone: 'Asia/Kolkata' 
    }) === todayIST && t.type === 'DEBIT'
).reduce((sum, t) => sum + parseFloat(t.amount), 0);
When the date changes:
  • todayIST becomes the new date (e.g., “3/6/2026”)
  • Filter matches only transactions from the new date
  • Yesterday’s transactions are excluded
  • todaySpend resets to 0
  • Progress bar returns to 0%

Budget vs Balance

Balance

Cumulative total of all CREDIT - DEBIT transactions across all timeDisplayed in: Top balance card

Budget

Daily spending limit that resets at midnight ISTDisplayed in: Progress bar under balance

Relationship

// Balance: All-time total
appData.balance = Σ(CREDIT) - Σ(DEBIT)  // All transactions

// Budget: Today's spending vs daily limit
todaySpend = Σ(today's DEBIT)  // Today only
budgetPct = (todaySpend / appData.budget) × 100

Integration with Expense Tracking

Budget updates in real-time when expenses are added:
index.html
async function sendToAI(mode) {
    // ... AI processing ...
    
    if (data.status === "Success") {
        icon.innerText = mode === 'add' ? "✅" : "🧠";
        tip.innerText = data.ai_response;
        
        if (mode === 'add') {
            appData.balance = parseFloat(data.balance);
            if (data.parsed) appData.expenses.push(data.parsed);
            
            // renderUI() recalculates budget progress
            renderUI();
        }
    }
}

Manual Fallback Updates Budget Too

index.html
async function handleManualFallback(text) {
    // ... parse expense ...
    
    appData.balance = (type === 'CREDIT') 
        ? (appData.balance + amount) 
        : (appData.balance - amount);
    appData.expenses.push(manualTx);
    
    // Budget bar updates here
    renderUI();
}

Budget Analytics

While not displayed in UI, the current implementation provides data for analytics:

Example: Budget Adherence Rate

// Calculate how often user stays under budget
function calculateAdherence(expenses, budget) {
    const dailySpends = {};
    
    expenses.forEach(tx => {
        if (tx.type === 'DEBIT') {
            const date = new Date(tx.date).toLocaleDateString('en-US', {
                timeZone: 'Asia/Kolkata'
            });
            dailySpends[date] = (dailySpends[date] || 0) + tx.amount;
        }
    });
    
    const days = Object.keys(dailySpends);
    const underBudgetDays = days.filter(d => dailySpends[d] <= budget).length;
    
    return (underBudgetDays / days.length) * 100;
}

// Usage
const adherence = calculateAdherence(appData.expenses, appData.budget);
console.log(`Budget adherence: ${adherence.toFixed(1)}%`);

Settings Integration

Loading Current Budget

index.html
function renderSettings() { 
    document.getElementById('budgetInput').value = appData.budget; 
    document.getElementById('privacyToggle').checked = appData.privacy; 
}

// Called during init()
renderSettings();

Initialization Flow

index.html
async function init() {
    setStatus("SYNCING...", "text-orange-500", "bg-orange-500");
    disableInput(true);

    try {
        const res = await fetch(SCRIPT_URL);
        const data = await res.json();

        appData.balance = parseFloat(data.balance) || 0;
        appData.expenses = data.expenses || [];
        appData.budget = parseFloat(data.budget) || 350;  // Load budget
        if (data.privacy !== undefined) appData.privacy = data.privacy;

        renderUI();       // Updates budget bar
        renderSettings(); // Updates budget input field
        setStatus("ONLINE", "text-green-500", "bg-green-500");
        disableInput(false);
    } catch (e) {
        console.error("INIT ERROR:", e);
        setStatus("OFFLINE", "text-red-500", "bg-red-500");
    }
}

Best Practices

Don’t set too low: ₹100/day might be unrealistic if you eat outCalculate from history: Review last month’s average daily spendExample calculation:
Last month spend: ₹12,000
Days in month: 30
Average daily: ₹400
Realistic budget: ₹350-450
Budget is for daily recurring expenses like:
  • Food and coffee
  • Transport
  • Small purchases
Don’t include:
  • Rent (monthly)
  • Large purchases (electronics)
  • One-time expenses
Check budget adherence every Sunday:
// How many days did you stay under budget this week?
const thisWeek = expenses.filter(t => 
    isThisWeek(t.date) && t.type === 'DEBIT'
);
Increase budget during:
  • Festival seasons (Diwali, Christmas)
  • Travel periods
  • Special events
Decrease during:
  • Work-from-home phases
  • Saving goals

Troubleshooting

Check:
  • Transactions have correct date format (ISO 8601)
  • Transactions are marked as type=DEBIT
  • Browser timezone is set correctly
Debug:
console.log('Today IST:', new Date().toLocaleDateString('en-US', { 
    timeZone: 'Asia/Kolkata' 
}));
console.log('Today\'s expenses:', appData.expenses.filter(t => 
    new Date(t.date).toLocaleDateString('en-US', { 
        timeZone: 'Asia/Kolkata' 
    }) === todayIST && t.type === 'DEBIT'
));
Possible causes:
  • Budget value is 0 or undefined
  • No DEBIT transactions today
  • Date filtering issue
Verify:
console.log('Budget:', appData.budget);
console.log('Today spend:', todaySpend);
console.log('Budget %:', budgetPct);
This happens if:
  • Browser timezone is not IST
  • System clock is incorrect
Fix: Ensure your system is set to IST (UTC+5:30) or the code uses hardcoded IST conversion.
Check:
  • Settings panel onchange event is firing
  • Google Apps Script is receiving the save_settings action
  • Script Properties are writable (check Apps Script permissions)
Test:
// In browser console
await pushSettings();
// Then reload and check if budget persists

Future Enhancements

Weekly/Monthly Budgets

Support for longer budget periods beyond daily

Category Budgets

Separate budgets for Food, Transport, etc.

Smart Budgets

AI suggests budget based on spending history

Push Notifications

Browser notifications when nearing budget limit

Example: Category-wise Budget

// Future implementation concept
const categoryBudgets = {
    'Food': 200,
    'Transport': 100,
    'Tech': 50
};

function calculateCategoryBudget(category) {
    const todayIST = new Date().toLocaleDateString('en-US', { 
        timeZone: 'Asia/Kolkata' 
    });
    
    const categorySpend = appData.expenses.filter(t => 
        new Date(t.date).toLocaleDateString('en-US', { 
            timeZone: 'Asia/Kolkata' 
        }) === todayIST && 
        t.type === 'DEBIT' &&
        t.category === category
    ).reduce((sum, t) => sum + parseFloat(t.amount), 0);
    
    return (categorySpend / categoryBudgets[category]) * 100;
}

Build docs developers (and LLMs) love