track / index.html
Khatvathiren's picture
Design a comprehensive application for personal finance management tailored for the user profile "Ben Mburu" with the slogan "GOD DID IT".The application should initiate tracking from June 24, 2025, in the currency of shillings.Requirements: 1.**User Profile**: - Name: Ben Mburu - Slogan: GOD DID IT 2.**Income and Expense Tracking**: - Categories for expenses: clothes, food, transport, etc.- Categories for income: salary, commission, tips, etc which should be Ksh. 0 for the start. Income Distribution Plan showing 50%, 30%, 20% allocation which the amount is Ksh. 0 for the start.- Functionality to input and categorize transactions easily.3.**Savings Plan**: - Options to set savings goals for: - Household products with a target of - Business capital - Emergencies, Savings Goals for Household (80k), Business (100k), and Emergency (20k) funds. 4.**Comparative Analysis**: - A dedicated section for a paragraph comparison of income versus expenses.- Ensure that savings and expenses do not exceed income.5.**User Interface Design**: - Incorporate various colors, specifically FBFEF9, 191923, 0E79B2, BF1363, F39237, and additional complementary colors for aesthetics.6.**To-Do List Feature**: - A section to create and manage a to-do list.- Each task should have a deadline.7.**Calendar Integration**: - A calendar to highlight events and important dates.8.**Monthly Reporting**: - Generate monthly reports summarizing income, expenses, and savings.overview, transactions, savings,analysis, todo list and calender should be in a seperate page or tab above arraged in arow. Each transaction made either income or expenses should update the whole app or system in every necessary category and keep the records. - Each report should include a paragraph illustrating the financial standing of the user.Ensure the application is user-friendly, visually appealing, and provides insightful financial analytics to help Ben Mburu manage his finances effectively. - Initial Deployment
57f3e27 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ben Mburu's Finance Tracker | GOD DID IT</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#FBFEF9',
secondary: '#191923',
accent1: '#0E79B2',
accent2: '#BF1363',
accent3: '#F39237',
}
}
}
}
</script>
<style>
.tab-content {
display: none;
}
.tab-content.active {
display: block;
animation: fadeIn 0.5s;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.progress-bar {
height: 10px;
border-radius: 5px;
transition: width 0.5s ease-in-out;
}
.calendar-day {
cursor: pointer;
transition: all 0.2s;
}
.calendar-day:hover {
transform: scale(1.05);
}
.calendar-day.has-event {
background-color: rgba(14, 121, 178, 0.2);
}
.calendar-day.today {
border: 2px solid #F39237;
}
.transaction-item {
transition: all 0.3s;
}
.transaction-item:hover {
transform: translateX(5px);
}
</style>
</head>
<body class="bg-primary text-secondary min-h-screen">
<div class="container mx-auto px-4 py-8">
<!-- Header -->
<header class="bg-accent1 text-primary rounded-lg p-6 mb-8 shadow-lg">
<div class="flex flex-col md:flex-row justify-between items-center">
<div>
<h1 class="text-3xl font-bold">Ben Mburu's Finance Tracker</h1>
<p class="text-accent3 italic text-xl">GOD DID IT</p>
<p class="mt-2">Tracking since: June 24, 2025</p>
</div>
<div class="mt-4 md:mt-0 bg-accent2 p-3 rounded-full">
<p class="text-xl font-bold">Balance: <span id="totalBalance">0</span> Ksh</p>
</div>
</div>
</header>
<!-- Navigation Tabs -->
<div class="flex overflow-x-auto mb-8 border-b-2 border-accent2">
<button class="tab-btn px-6 py-3 font-medium bg-accent1 text-primary rounded-t-lg mr-2" data-tab="overview">Overview</button>
<button class="tab-btn px-6 py-3 font-medium hover:bg-accent1 hover:text-primary rounded-t-lg mr-2" data-tab="transactions">Transactions</button>
<button class="tab-btn px-6 py-3 font-medium hover:bg-accent1 hover:text-primary rounded-t-lg mr-2" data-tab="savings">Savings</button>
<button class="tab-btn px-6 py-3 font-medium hover:bg-accent1 hover:text-primary rounded-t-lg mr-2" data-tab="analysis">Analysis</button>
<button class="tab-btn px-6 py-3 font-medium hover:bg-accent1 hover:text-primary rounded-t-lg mr-2" data-tab="todo">To-Do List</button>
<button class="tab-btn px-6 py-3 font-medium hover:bg-accent1 hover:text-primary rounded-t-lg" data-tab="calendar">Calendar</button>
</div>
<!-- Overview Tab -->
<div id="overview" class="tab-content active p-6 bg-white rounded-lg shadow-md">
<h2 class="text-2xl font-bold mb-6 text-accent2">Financial Overview</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
<!-- Income Card -->
<div class="bg-green-50 p-4 rounded-lg border-l-4 border-green-500">
<h3 class="font-semibold text-lg mb-2">Total Income</h3>
<p class="text-2xl font-bold" id="totalIncome">0 Ksh</p>
<div class="mt-4">
<h4 class="font-medium mb-1">Income Distribution</h4>
<div class="bg-gray-200 h-2 rounded-full mb-1">
<div class="bg-green-500 h-2 rounded-full progress-bar" style="width: 50%" id="needsBar"></div>
</div>
<p>Needs (50%): <span id="needsAmount">0</span> Ksh</p>
<div class="bg-gray-200 h-2 rounded-full mb-1 mt-2">
<div class="bg-blue-500 h-2 rounded-full progress-bar" style="width: 30%" id="wantsBar"></div>
</div>
<p>Wants (30%): <span id="wantsAmount">0</span> Ksh</p>
<div class="bg-gray-200 h-2 rounded-full mb-1 mt-2">
<div class="bg-purple-500 h-2 rounded-full progress-bar" style="width: 20%" id="savingsBar"></div>
</div>
<p>Savings (20%): <span id="savingsAmount">0</span> Ksh</p>
</div>
</div>
<!-- Expenses Card -->
<div class="bg-red-50 p-4 rounded-lg border-l-4 border-red-500">
<h3 class="font-semibold text-lg mb-2">Total Expenses</h3>
<p class="text-2xl font-bold" id="totalExpenses">0 Ksh</p>
<div class="mt-4">
<h4 class="font-medium mb-2">Expense Categories</h4>
<div id="expenseCategoriesOverview">
<p class="text-gray-600">No expenses recorded yet</p>
</div>
</div>
</div>
<!-- Savings Progress Card -->
<div class="bg-blue-50 p-4 rounded-lg border-l-4 border-blue-500">
<h3 class="font-semibold text-lg mb-2">Savings Progress</h3>
<div class="space-y-4">
<div>
<h4 class="font-medium">Household (80k)</h4>
<div class="bg-gray-200 h-2 rounded-full mb-1">
<div class="bg-accent2 h-2 rounded-full progress-bar" style="width: 0%" id="householdBar"></div>
</div>
<p><span id="householdSaved">0</span> Ksh saved (<span id="householdPercent">0</span>%)</p>
</div>
<div>
<h4 class="font-medium">Business (100k)</h4>
<div class="bg-gray-200 h-2 rounded-full mb-1">
<div class="bg-accent3 h-2 rounded-full progress-bar" style="width: 0%" id="businessBar"></div>
</div>
<p><span id="businessSaved">0</span> Ksh saved (<span id="businessPercent">0</span>%)</p>
</div>
<div>
<h4 class="font-medium">Emergency (20k)</h4>
<div class="bg-gray-200 h-2 rounded-full mb-1">
<div class="bg-accent2 h-2 rounded-full progress-bar" style="width: 0%" id="emergencyBar"></div>
</div>
<p><span id="emergencySaved">0</span> Ksh saved (<span id="emergencyPercent">0</span>%)</p>
</div>
</div>
</div>
</div>
<div class="bg-accent3 bg-opacity-10 p-4 rounded-lg">
<h3 class="font-semibold text-lg mb-2">Financial Status</h3>
<p id="financialStatusText">You haven't recorded any transactions yet. Start by adding your income and expenses to get a clear picture of your financial health.</p>
</div>
</div>
<!-- Transactions Tab -->
<div id="transactions" class="tab-content p-6 bg-white rounded-lg shadow-md">
<h2 class="text-2xl font-bold mb-6 text-accent2">Record Transactions</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<!-- Add Transaction Form -->
<div>
<div class="bg-gray-50 p-4 rounded-lg mb-6">
<h3 class="font-semibold text-lg mb-4">Add New Transaction</h3>
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Transaction Type</label>
<select id="transactionType" class="w-full p-2 border rounded">
<option value="income">Income</option>
<option value="expense">Expense</option>
</select>
</div>
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Category</label>
<select id="transactionCategory" class="w-full p-2 border rounded">
<!-- Income categories -->
<option value="salary" class="income-option">Salary</option>
<option value="commission" class="income-option">Commission</option>
<option value="tips" class="income-option">Tips</option>
<!-- Expense categories -->
<option value="clothes" class="expense-option" style="display:none">Clothes</option>
<option value="food" class="expense-option" style="display:none">Food</option>
<option value="transport" class="expense-option" style="display:none">Transport</option>
<option value="housing" class="expense-option" style="display:none">Housing</option>
<option value="utilities" class="expense-option" style="display:none">Utilities</option>
<option value="entertainment" class="expense-option" style="display:none">Entertainment</option>
</select>
</div>
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Amount (Ksh)</label>
<input type="number" id="transactionAmount" class="w-full p-2 border rounded" placeholder="0">
</div>
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Date</label>
<input type="date" id="transactionDate" class="w-full p-2 border rounded">
</div>
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Description (Optional)</label>
<textarea id="transactionDescription" class="w-full p-2 border rounded" rows="2"></textarea>
</div>
<button id="addTransactionBtn" class="bg-accent1 text-white py-2 px-4 rounded hover:bg-blue-700 transition">
Add Transaction
</button>
</div>
</div>
<!-- Recent Transactions -->
<div>
<h3 class="font-semibold text-lg mb-4">Recent Transactions</h3>
<div class="mb-4">
<input type="text" id="transactionSearch" class="w-full p-2 border rounded mb-2" placeholder="Search transactions...">
<div class="flex space-x-2">
<select id="transactionFilterType" class="p-2 border rounded">
<option value="all">All Types</option>
<option value="income">Income</option>
<option value="expense">Expense</option>
</select>
<select id="transactionFilterCategory" class="p-2 border rounded">
<option value="all">All Categories</option>
<!-- Will be populated by JavaScript -->
</select>
</div>
</div>
<div class="max-h-96 overflow-y-auto">
<table class="w-full">
<thead class="bg-gray-100">
<tr>
<th class="p-2 text-left">Date</th>
<th class="p-2 text-left">Description</th>
<th class="p-2 text-left">Category</th>
<th class="p-2 text-right">Amount</th>
<th class="p-2 text-right">Action</th>
</tr>
</thead>
<tbody id="transactionsList">
<tr>
<td colspan="5" class="p-4 text-center text-gray-500">No transactions recorded yet</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Savings Tab -->
<div id="savings" class="tab-content p-6 bg-white rounded-lg shadow-md">
<h2 class="text-2xl font-bold mb-6 text-accent2">Savings Goals</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
<!-- Household Savings -->
<div class="bg-purple-50 p-4 rounded-lg border-l-4 border-purple-500">
<h3 class="font-semibold text-lg mb-2">Household Products</h3>
<p class="text-sm text-gray-600 mb-2">Target: 80,000 Ksh</p>
<div class="bg-gray-200 h-4 rounded-full mb-2">
<div class="bg-purple-500 h-4 rounded-full progress-bar" id="householdProgressBar" style="width: 0%"></div>
</div>
<p class="text-lg font-bold"><span id="householdSavedAmount">0</span> Ksh saved (<span id="householdPercentage">0</span>%)</p>
<div class="mt-4">
<label class="block text-sm font-medium mb-1">Add to Savings</label>
<div class="flex">
<input type="number" id="householdAddAmount" class="flex-1 p-2 border rounded-l" placeholder="Amount">
<button class="bg-purple-500 text-white px-3 rounded-r" onclick="addToSavings('household')">Add</button>
</div>
</div>
</div>
<!-- Business Savings -->
<div class="bg-green-50 p-4 rounded-lg border-l-4 border-green-500">
<h3 class="font-semibold text-lg mb-2">Business Capital</h3>
<p class="text-sm text-gray-600 mb-2">Target: 100,000 Ksh</p>
<div class="bg-gray-200 h-4 rounded-full mb-2">
<div class="bg-green-500 h-4 rounded-full progress-bar" id="businessProgressBar" style="width: 0%"></div>
</div>
<p class="text-lg font-bold"><span id="businessSavedAmount">0</span> Ksh saved (<span id="businessPercentage">0</span>%)</p>
<div class="mt-4">
<label class="block text-sm font-medium mb-1">Add to Savings</label>
<div class="flex">
<input type="number" id="businessAddAmount" class="flex-1 p-2 border rounded-l" placeholder="Amount">
<button class="bg-green-500 text-white px-3 rounded-r" onclick="addToSavings('business')">Add</button>
</div>
</div>
</div>
<!-- Emergency Savings -->
<div class="bg-red-50 p-4 rounded-lg border-l-4 border-red-500">
<h3 class="font-semibold text-lg mb-2">Emergency Fund</h3>
<p class="text-sm text-gray-600 mb-2">Target: 20,000 Ksh</p>
<div class="bg-gray-200 h-4 rounded-full mb-2">
<div class="bg-red-500 h-4 rounded-full progress-bar" id="emergencyProgressBar" style="width: 0%"></div>
</div>
<p class="text-lg font-bold"><span id="emergencySavedAmount">0</span> Ksh saved (<span id="emergencyPercentage">0</span>%)</p>
<div class="mt-4">
<label class="block text-sm font-medium mb-1">Add to Savings</label>
<div class="flex">
<input type="number" id="emergencyAddAmount" class="flex-1 p-2 border rounded-l" placeholder="Amount">
<button class="bg-red-500 text-white px-3 rounded-r" onclick="addToSavings('emergency')">Add</button>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<h3 class="font-semibold text-lg mb-4">Savings History</h3>
<div class="overflow-x-auto">
<table class="w-full">
<thead class="bg-gray-100">
<tr>
<th class="p-2 text-left">Date</th>
<th class="p-2 text-left">Goal</th>
<th class="p-2 text-right">Amount</th>
<th class="p-2 text-right">Balance</th>
</tr>
</thead>
<tbody id="savingsHistory">
<tr>
<td colspan="4" class="p-4 text-center text-gray-500">No savings recorded yet</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Analysis Tab -->
<div id="analysis" class="tab-content p-6 bg-white rounded-lg shadow-md">
<h2 class="text-2xl font-bold mb-6 text-accent2">Financial Analysis</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 mb-8">
<!-- Income vs Expenses Chart -->
<div class="bg-gray-50 p-4 rounded-lg">
<h3 class="font-semibold text-lg mb-4">Income vs Expenses</h3>
<div class="h-64 flex items-end space-x-2" id="incomeExpenseChart">
<div class="flex-1 flex flex-col items-center">
<div class="bg-green-500 w-full rounded-t" id="incomeBar" style="height: 0%"></div>
<p class="mt-2 text-sm">Income</p>
</div>
<div class="flex-1 flex flex-col items-center">
<div class="bg-red-500 w-full rounded-t" id="expenseBar" style="height: 0%"></div>
<p class="mt-2 text-sm">Expenses</p>
</div>
</div>
</div>
<!-- Expense Breakdown -->
<div class="bg-gray-50 p-4 rounded-lg">
<h3 class="font-semibold text-lg mb-4">Expense Breakdown</h3>
<div class="h-64 flex items-center justify-center" id="expensePieChart">
<p class="text-gray-500">No expenses to display</p>
</div>
</div>
</div>
<!-- Financial Analysis Text -->
<div class="bg-accent1 bg-opacity-10 p-4 rounded-lg">
<h3 class="font-semibold text-lg mb-2">Financial Analysis Report</h3>
<p id="financialAnalysisText">You haven't recorded enough transactions to generate a financial analysis. Start by adding your income and expenses to get valuable insights into your financial habits.</p>
</div>
<!-- Monthly Report -->
<div class="mt-8 bg-white border rounded-lg p-4 shadow">
<h3 class="font-semibold text-lg mb-4">Generate Monthly Report</h3>
<div class="flex items-center space-x-4 mb-4">
<select id="reportMonth" class="p-2 border rounded">
<option value="1">January</option>
<option value="2">February</option>
<option value="3">March</option>
<option value="4">April</option>
<option value="5">May</option>
<option value="6">June</option>
<option value="7">July</option>
<option value="8">August</option>
<option value="9">September</option>
<option value="10">October</option>
<option value="11">November</option>
<option value="12">December</option>
</select>
<select id="reportYear" class="p-2 border rounded">
<option value="2025">2025</option>
<option value="2026">2026</option>
</select>
<button id="generateReportBtn" class="bg-accent2 text-white py-2 px-4 rounded hover:bg-gray-800 transition">
Generate Report
</button>
</div>
<div id="monthlyReport" class="hidden bg-gray-50 p-4 rounded-lg">
<h4 class="font-semibold mb-2">Monthly Report - <span id="reportTitle">June 2025</span></h4>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
<div class="bg-green-50 p-2 rounded">
<p class="font-medium">Total Income:</p>
<p class="text-xl" id="reportIncome">0 Ksh</p>
</div>
<div class="bg-red-50 p-2 rounded">
<p class="font-medium">Total Expenses:</p>
<p class="text-xl" id="reportExpenses">0 Ksh</p>
</div>
<div class="bg-blue-50 p-2 rounded">
<p class="font-medium">Net Savings:</p>
<p class="text-xl" id="reportSavings">0 Ksh</p>
</div>
</div>
<div class="mb-4">
<h5 class="font-medium mb-2">Top Expense Categories:</h5>
<div id="reportTopExpenses">
<p class="text-gray-600">No expense data available</p>
</div>
</div>
<div>
<h5 class="font-medium mb-2">Financial Summary:</h5>
<p id="reportSummary">No financial data available for this month.</p>
</div>
</div>
</div>
</div>
<!-- To-Do List Tab -->
<div id="todo" class="tab-content p-6 bg-white rounded-lg shadow-md">
<h2 class="text-2xl font-bold mb-6 text-accent2">To-Do List</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<!-- Add Task Form -->
<div class="bg-gray-50 p-4 rounded-lg">
<h3 class="font-semibold text-lg mb-4">Add New Task</h3>
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Task Title</label>
<input type="text" id="taskTitle" class="w-full p-2 border rounded" placeholder="What needs to be done?">
</div>
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Priority</label>
<select id="taskPriority" class="w-full p-2 border rounded">
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
</select>
</div>
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Deadline</label>
<input type="date" id="taskDeadline" class="w-full p-2 border rounded">
</div>
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Description (Optional)</label>
<textarea id="taskDescription" class="w-full p-2 border rounded" rows="3"></textarea>
</div>
<button id="addTaskBtn" class="bg-accent2 text-white py-2 px-4 rounded hover:bg-gray-800 transition">
Add Task
</button>
</div>
<!-- Task List -->
<div>
<h3 class="font-semibold text-lg mb-4">Your Tasks</h3>
<div class="mb-4 flex space-x-2">
<select id="taskFilterStatus" class="p-2 border rounded">
<option value="all">All Tasks</option>
<option value="pending">Pending</option>
<option value="completed">Completed</option>
</select>
<select id="taskFilterPriority" class="p-2 border rounded">
<option value="all">All Priorities</option>
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
</select>
</div>
<div class="max-h-96 overflow-y-auto">
<ul id="taskList">
<li class="p-4 text-center text-gray-500">No tasks added yet</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Calendar Tab -->
<div id="calendar" class="tab-content p-6 bg-white rounded-lg shadow-md">
<h2 class="text-2xl font-bold mb-6 text-accent2">Calendar</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<!-- Calendar -->
<div class="md:col-span-2">
<div class="flex justify-between items-center mb-4">
<h3 class="font-semibold text-lg" id="calendarMonthYear">June 2025</h3>
<div class="flex space-x-2">
<button id="prevMonthBtn" class="p-2 rounded-full hover:bg-gray-100">
<i class="fas fa-chevron-left"></i>
</button>
<button id="todayBtn" class="p-2 rounded-full hover:bg-gray-100">Today</button>
<button id="nextMonthBtn" class="p-2 rounded-full hover:bg-gray-100">
<i class="fas fa-chevron-right"></i>
</button>
</div>
</div>
<div class="bg-white rounded-lg shadow overflow-hidden">
<div class="grid grid-cols-7 bg-gray-100">
<div class="p-2 text-center font-medium text-sm">Sun</div>
<div class="p-2 text-center font-medium text-sm">Mon</div>
<div class="p-2 text-center font-medium text-sm">Tue</div>
<div class="p-2 text-center font-medium text-sm">Wed</div>
<div class="p-2 text-center font-medium text-sm">Thu</div>
<div class="p-2 text-center font-medium text-sm">Fri</div>
<div class="p-2 text-center font-medium text-sm">Sat</div>
</div>
<div class="grid grid-cols-7" id="calendarDays">
<!-- Calendar days will be populated by JavaScript -->
</div>
</div>
</div>
<!-- Events for Selected Day -->
<div>
<h3 class="font-semibold text-lg mb-4" id="selectedDayTitle">No day selected</h3>
<div class="mb-4 bg-gray-50 p-4 rounded-lg">
<h4 class="font-medium mb-2">Add Event</h4>
<div class="mb-2">
<label class="block text-sm font-medium mb-1">Event Title</label>
<input type="text" id="eventTitle" class="w-full p-2 border rounded">
</div>
<div class="mb-2">
<label class="block text-sm font-medium mb-1">Time</label>
<input type="time" id="eventTime" class="w-full p-2 border rounded">
</div>
<div class="mb-2">
<label class="block text-sm font-medium mb-1">Description (Optional)</label>
<textarea id="eventDescription" class="w-full p-2 border rounded" rows="2"></textarea>
</div>
<button id="addEventBtn" class="bg-accent2 text-white py-1 px-3 rounded text-sm hover:bg-gray-800 transition">
Add Event
</button>
</div>
<div>
<h4 class="font-medium mb-2">Events</h4>
<ul id="dayEventsList">
<li class="text-gray-500">No events scheduled</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<script>
// Initialize data
let financeData = {
transactions: [],
savings: {
household: { saved: 0, target: 80000, history: [] },
business: { saved: 0, target: 100000, history: [] },
emergency: { saved: 0, target: 20000, history: [] }
},
todoList: [],
calendarEvents: {},
currentDate: new Date(2025, 5, 24) // June 24, 2025
};
// DOM Elements
const tabButtons = document.querySelectorAll('.tab-btn');
const tabContents = document.querySelectorAll('.tab-content');
const transactionTypeSelect = document.getElementById('transactionType');
const transactionCategorySelect = document.getElementById('transactionCategory');
const transactionAmountInput = document.getElementById('transactionAmount');
const transactionDateInput = document.getElementById('transactionDate');
const transactionDescriptionInput = document.getElementById('transactionDescription');
const addTransactionBtn = document.getElementById('addTransactionBtn');
const transactionsList = document.getElementById('transactionsList');
const transactionSearchInput = document.getElementById('transactionSearch');
const transactionFilterType = document.getElementById('transactionFilterType');
const transactionFilterCategory = document.getElementById('transactionFilterCategory');
const totalBalanceElement = document.getElementById('totalBalance');
const totalIncomeElement = document.getElementById('totalIncome');
const totalExpensesElement = document.getElementById('totalExpenses');
const financialStatusText = document.getElementById('financialStatusText');
const needsAmountElement = document.getElementById('needsAmount');
const wantsAmountElement = document.getElementById('wantsAmount');
const savingsAmountElement = document.getElementById('savingsAmount');
const needsBar = document.getElementById('needsBar');
const wantsBar = document.getElementById('wantsBar');
const savingsBar = document.getElementById('savingsBar');
const expenseCategoriesOverview = document.getElementById('expenseCategoriesOverview');
const householdSavedElement = document.getElementById('householdSaved');
const householdPercentElement = document.getElementById('householdPercent');
const businessSavedElement = document.getElementById('businessSaved');
const businessPercentElement = document.getElementById('businessPercent');
const emergencySavedElement = document.getElementById('emergencySaved');
const emergencyPercentElement = document.getElementById('emergencyPercent');
const householdBarElement = document.getElementById('householdBar');
const businessBarElement = document.getElementById('businessBar');
const emergencyBarElement = document.getElementById('emergencyBar');
const incomeBar = document.getElementById('incomeBar');
const expenseBar = document.getElementById('expenseBar');
const expensePieChart = document.getElementById('expensePieChart');
const financialAnalysisText = document.getElementById('financialAnalysisText');
const reportMonthSelect = document.getElementById('reportMonth');
const reportYearSelect = document.getElementById('reportYear');
const generateReportBtn = document.getElementById('generateReportBtn');
const monthlyReport = document.getElementById('monthlyReport');
const reportTitle = document.getElementById('reportTitle');
const reportIncome = document.getElementById('reportIncome');
const reportExpenses = document.getElementById('reportExpenses');
const reportSavings = document.getElementById('reportSavings');
const reportTopExpenses = document.getElementById('reportTopExpenses');
const reportSummary = document.getElementById('reportSummary');
const taskTitleInput = document.getElementById('taskTitle');
const taskPrioritySelect = document.getElementById('taskPriority');
const taskDeadlineInput = document.getElementById('taskDeadline');
const taskDescriptionInput = document.getElementById('taskDescription');
const addTaskBtn = document.getElementById('addTaskBtn');
const taskList = document.getElementById('taskList');
const taskFilterStatus = document.getElementById('taskFilterStatus');
const taskFilterPriority = document.getElementById('taskFilterPriority');
const calendarMonthYear = document.getElementById('calendarMonthYear');
const prevMonthBtn = document.getElementById('prevMonthBtn');
const todayBtn = document.getElementById('todayBtn');
const nextMonthBtn = document.getElementById('nextMonthBtn');
const calendarDays = document.getElementById('calendarDays');
const selectedDayTitle = document.getElementById('selectedDayTitle');
const eventTitleInput = document.getElementById('eventTitle');
const eventTimeInput = document.getElementById('eventTime');
const eventDescriptionInput = document.getElementById('eventDescription');
const addEventBtn = document.getElementById('addEventBtn');
const dayEventsList = document.getElementById('dayEventsList');
// Initialize date for transaction
transactionDateInput.valueAsDate = new Date();
// Tab switching
tabButtons.forEach(button => {
button.addEventListener('click', () => {
const tabId = button.getAttribute('data-tab');
// Update active tab button
tabButtons.forEach(btn => {
btn.classList.remove('bg-accent1', 'text-primary');
btn.classList.add('hover:bg-accent1', 'hover:text-primary');
});
button.classList.add('bg-accent1', 'text-primary');
button.classList.remove('hover:bg-accent1', 'hover:text-primary');
// Update active tab content
tabContents.forEach(content => {
content.classList.remove('active');
});
document.getElementById(tabId).classList.add('active');
// Special cases
if (tabId === 'calendar') {
renderCalendar();
}
});
});
// Transaction type change
transactionTypeSelect.addEventListener('change', () => {
const type = transactionTypeSelect.value;
const incomeOptions = document.querySelectorAll('.income-option');
const expenseOptions = document.querySelectorAll('.expense-option');
if (type === 'income') {
incomeOptions.forEach(option => {
option.style.display = 'block';
});
expenseOptions.forEach(option => {
option.style.display = 'none';
});
} else {
incomeOptions.forEach(option => {
option.style.display = 'none';
});
expenseOptions.forEach(option => {
option.style.display = 'block';
});
}
// Reset category to first available
transactionCategorySelect.selectedIndex = 0;
});
// Add transaction
addTransactionBtn.addEventListener('click', () => {
const type = transactionTypeSelect.value;
const category = transactionCategorySelect.value;
const amount = parseFloat(transactionAmountInput.value);
const date = transactionDateInput.value;
const description = transactionDescriptionInput.value.trim();
if (!amount || amount <= 0) {
alert('Please enter a valid amount');
return;
}
if (!date) {
alert('Please select a date');
return;
}
const transaction = {
id: Date.now(),
type,
category,
amount,
date,
description,
timestamp: new Date().toISOString()
};
financeData.transactions.push(transaction);
saveData();
renderTransactions();
updateFinancialOverview();
// Reset form
transactionAmountInput.value = '';
transactionDescriptionInput.value = '';
transactionDateInput.valueAsDate = new Date();
});
// Filter transactions
transactionSearchInput.addEventListener('input', renderTransactions);
transactionFilterType.addEventListener('change', renderTransactions);
transactionFilterCategory.addEventListener('change', renderTransactions);
// Render transactions
function renderTransactions() {
const searchTerm = transactionSearchInput.value.toLowerCase();
const filterType = transactionFilterType.value;
const filterCategory = transactionFilterCategory.value;
// Update category filter options
updateCategoryFilterOptions();
const filteredTransactions = financeData.transactions.filter(transaction => {
const matchesSearch = transaction.description.toLowerCase().includes(searchTerm) ||
transaction.category.toLowerCase().includes(searchTerm) ||
transaction.amount.toString().includes(searchTerm);
const matchesType = filterType === 'all' || transaction.type === filterType;
const matchesCategory = filterCategory === 'all' || transaction.category === filterCategory;
return matchesSearch && matchesType && matchesCategory;
});
if (filteredTransactions.length === 0) {
transactionsList.innerHTML = `
<tr>
<td colspan="5" class="p-4 text-center text-gray-500">No transactions found</td>
</tr>
`;
return;
}
transactionsList.innerHTML = '';
filteredTransactions.sort((a, b) => new Date(b.date) - new Date(a.date)).forEach(transaction => {
const row = document.createElement('tr');
row.className = 'transaction-item border-b hover:bg-gray-50';
row.innerHTML = `
<td class="p-2">${formatDate(transaction.date)}</td>
<td class="p-2">${transaction.description || 'No description'}</td>
<td class="p-2 capitalize">${transaction.category}</td>
<td class="p-2 text-right font-medium ${transaction.type === 'income' ? 'text-green-600' : 'text-red-600'}">
${transaction.type === 'income' ? '+' : '-'}${transaction.amount.toLocaleString()} Ksh
</td>
<td class="p-2 text-right">
<button class="text-red-500 hover:text-red-700" onclick="deleteTransaction(${transaction.id})">
<i class="fas fa-trash"></i>
</button>
</td>
`;
transactionsList.appendChild(row);
});
}
// Update category filter options
function updateCategoryFilterOptions() {
const currentCategories = new Set();
financeData.transactions.forEach(t => currentCategories.add(t.category));
const currentValue = transactionFilterCategory.value;
transactionFilterCategory.innerHTML = `
<option value="all">All Categories</option>
${Array.from(currentCategories).map(cat =>
`<option value="${cat}" ${cat === currentValue ? 'selected' : ''}>${capitalizeFirstLetter(cat)}</option>`
).join('')}
`;
}
// Delete transaction
function deleteTransaction(id) {
if (confirm('Are you sure you want to delete this transaction?')) {
financeData.transactions = financeData.transactions.filter(t => t.id !== id);
saveData();
renderTransactions();
updateFinancialOverview();
}
}
// Update financial overview
function updateFinancialOverview() {
// Calculate totals
const totalIncome = financeData.transactions
.filter(t => t.type === 'income')
.reduce((sum, t) => sum + t.amount, 0);
const totalExpenses = financeData.transactions
.filter(t => t.type === 'expense')
.reduce((sum, t) => sum + t.amount, 0);
const totalSavings = financeData.savings.household.saved +
financeData.savings.business.saved +
financeData.savings.emergency.saved;
const balance = totalIncome - totalExpenses - totalSavings;
// Update UI
totalBalanceElement.textContent = balance.toLocaleString();
totalIncomeElement.textContent = totalIncome.toLocaleString() + ' Ksh';
totalExpensesElement.textContent = totalExpenses.toLocaleString() + ' Ksh';
// Update income distribution
const needsAmount = totalIncome * 0.5;
const wantsAmount = totalIncome * 0.3;
const savingsAmount = totalIncome * 0.2;
needsAmountElement.textContent = needsAmount.toLocaleString();
wantsAmountElement.textContent = wantsAmount.toLocaleString();
savingsAmountElement.textContent = savingsAmount.toLocaleString();
needsBar.style.width = '50%';
wantsBar.style.width = '30%';
savingsBar.style.width = '20%';
// Update expense categories overview
const expenseCategories = {};
financeData.transactions
.filter(t => t.type === 'expense')
.forEach(t => {
expenseCategories[t.category] = (expenseCategories[t.category] || 0) + t.amount;
});
if (Object.keys(expenseCategories).length === 0) {
expenseCategoriesOverview.innerHTML = '<p class="text-gray-600">No expenses recorded yet</p>';
} else {
expenseCategoriesOverview.innerHTML = Object.entries(expenseCategories)
.sort((a, b) => b[1] - a[1])
.map(([category, amount]) => `
<div class="flex justify-between mb-1">
<span class="capitalize">${category}</span>
<span class="font-medium">${amount.toLocaleString()} Ksh</span>
</div>
`).join('');
}
// Update savings progress
updateSavingsProgress();
// Update financial status text
updateFinancialStatusText(totalIncome, totalExpenses, balance);
// Update charts
updateCharts(totalIncome, totalExpenses, expenseCategories);
}
// Update savings progress
function updateSavingsProgress() {
// Household
const householdPercent = (financeData.savings.household.saved / financeData.savings.household.target * 100).toFixed(1);
householdSavedElement.textContent = financeData.savings.household.saved.toLocaleString();
householdPercentElement.textContent = householdPercent;
householdBarElement.style.width = `${Math.min(100, householdPercent)}%`;
// Business
const businessPercent = (financeData.savings.business.saved / financeData.savings.business.target * 100).toFixed(1);
businessSavedElement.textContent = financeData.savings.business.saved.toLocaleString();
businessPercentElement.textContent = businessPercent;
businessBarElement.style.width = `${Math.min(100, businessPercent)}%`;
// Emergency
const emergencyPercent = (financeData.savings.emergency.saved / financeData.savings.emergency.target * 100).toFixed(1);
emergencySavedElement.textContent = financeData.savings.emergency.saved.toLocaleString();
emergencyPercentElement.textContent = emergencyPercent;
emergencyBarElement.style.width = `${Math.min(100, emergencyPercent)}%`;
}
// Update financial status text
function updateFinancialStatusText(totalIncome, totalExpenses, balance) {
if (totalIncome === 0 && totalExpenses === 0) {
financialStatusText.textContent = 'You haven\'t recorded any transactions yet. Start by adding your income and expenses to get a clear picture of your financial health.';
return;
}
let statusText = '';
if (totalIncome > totalExpenses) {
const savingsRate = ((totalIncome - totalExpenses) / totalIncome * 100).toFixed(1);
statusText = `You're doing well! Your income (${totalIncome.toLocaleString()} Ksh) exceeds your expenses (${totalExpenses.toLocaleString()} Ksh), giving you a savings rate of ${savingsRate}%. `;
if (savingsRate > 20) {
statusText += 'This is excellent! Consider investing some of your surplus to grow your wealth.';
} else if (savingsRate > 10) {
statusText += 'This is good, but there might be room for improvement in your spending habits.';
} else {
statusText += 'Try to increase your savings rate by reducing unnecessary expenses.';
}
} else if (totalIncome < totalExpenses) {
const deficit = totalExpenses - totalIncome;
statusText = `Warning! Your expenses (${totalExpenses.toLocaleString()} Ksh) exceed your income (${totalIncome.toLocaleString()} Ksh), creating a deficit of ${deficit.toLocaleString()} Ksh. `;
statusText += 'This is not sustainable. Please review your expenses and look for areas to cut back.';
} else {
statusText = 'Your income and expenses are balanced. While you\'re not going into debt, you\'re also not saving anything. Consider finding ways to increase your income or reduce expenses to build savings.';
}
financialStatusText.textContent = statusText;
}
// Update charts
function updateCharts(totalIncome, totalExpenses, expenseCategories) {
// Income vs Expenses bar chart
const maxValue = Math.max(totalIncome, totalExpenses) || 1;
incomeBar.style.height = `${(totalIncome / maxValue * 100)}%`;
expenseBar.style.height = `${(totalExpenses / maxValue * 100)}%`;
// Expense pie chart
if (Object.keys(expenseCategories).length === 0) {
expensePieChart.innerHTML = '<p class="text-gray-500">No expenses to display</p>';
return;
}
const sortedCategories = Object.entries(expenseCategories)
.sort((a, b) => b[1] - a[1]);
const colors = ['#BF1363', '#F39237', '#0E79B2', '#191923', '#FBFEF9'];
expensePieChart.innerHTML = '';
// Create a simple pie chart using CSS
const pieChart = document.createElement('div');
pieChart.className = 'relative w-48 h-48 rounded-full mx-auto';
let cumulativePercent = 0;
sortedCategories.forEach(([category, amount], index) => {
const percent = (amount / totalExpenses * 100);
const slice = document.createElement('div');
slice.className = 'absolute top-0 left-0 w-full h-full';
slice.style.clipPath = `polygon(50% 50%, ${50 + 50 * Math.cos(cumulativePercent / 100 * 2 * Math.PI)}% ${50 + 50 * Math.sin(cumulativePercent / 100 * 2 * Math.PI)}%, ${50 + 50 * Math.cos((cumulativePercent + percent) / 100 * 2 * Math.PI)}% ${50 + 50 * Math.sin((cumulativePercent + percent) / 100 * 2 * Math.PI)}%)`;
slice.style.backgroundColor = colors[index % colors.length];
pieChart.appendChild(slice);
cumulativePercent += percent;
});
expensePieChart.appendChild(pieChart);
// Add legend
const legend = document.createElement('div');
legend.className = 'flex flex-wrap justify-center mt-4 gap-2';
sortedCategories.forEach(([category, amount], index) => {
const legendItem = document.createElement('div');
legendItem.className = 'flex items-center text-sm';
const colorBox = document.createElement('div');
colorBox.className = 'w-3 h-3 mr-1';
colorBox.style.backgroundColor = colors[index % colors.length];
const label = document.createElement('span');
label.textContent = `${capitalizeFirstLetter(category)} (${(amount / totalExpenses * 100).toFixed(1)}%)`;
legendItem.appendChild(colorBox);
legendItem.appendChild(label);
legend.appendChild(legendItem);
});
expensePieChart.appendChild(legend);
// Update financial analysis text
updateFinancialAnalysisText(totalIncome, totalExpenses, sortedCategories);
}
// Update financial analysis text
function updateFinancialAnalysisText(totalIncome, totalExpenses, sortedCategories) {
if (totalIncome === 0 && totalExpenses === 0) {
financialAnalysisText.textContent = 'You haven\'t recorded enough transactions to generate a financial analysis. Start by adding your income and expenses to get valuable insights into your financial habits.';
return;
}
let analysisText = '';
// Net income analysis
const netIncome = totalIncome - totalExpenses;
if (netIncome > 0) {
analysisText = `Your net income is positive at ${netIncome.toLocaleString()} Ksh, which means you're saving money each month. `;
} else if (netIncome < 0) {
analysisText = `Your net income is negative at ${Math.abs(netIncome).toLocaleString()} Ksh, which means you're spending more than you earn. `;
} else {
analysisText = 'Your income and expenses are exactly balanced this month. ';
}
// Top expense analysis
if (sortedCategories.length > 0) {
analysisText += 'Your largest expense category is ' + capitalizeFirstLetter(sortedCategories[0][0]) +
`, accounting for ${(sortedCategories[0][1] / totalExpenses * 100).toFixed(1)}% of your total expenses. `;
if (sortedCategories.length > 1) {
analysisText += `Other significant expenses include ${capitalizeFirstLetter(sortedCategories[1][0])} `;
if (sortedCategories.length > 2) {
analysisText += `and ${capitalizeFirstLetter(sortedCategories[2][0])}. `;
} else {
analysisText += '. ';
}
}
}
// Savings rate analysis
if (totalIncome > 0) {
const savingsRate = ((totalIncome - totalExpenses) / totalIncome * 100).toFixed(1);
analysisText += `Your savings rate is ${savingsRate}%. `;
if (savingsRate >= 20) {
analysisText += 'This is an excellent savings rate! Keep up the good work.';
} else if (savingsRate >= 10) {
analysisText += 'This is a decent savings rate, but there might be room for improvement.';
} else {
analysisText += 'Consider finding ways to increase your savings rate by either increasing income or reducing expenses.';
}
}
financialAnalysisText.textContent = analysisText;
}
// Add to savings
function addToSavings(goal) {
const inputElement = document.getElementById(`${goal}AddAmount`);
const amount = parseFloat(inputElement.value);
if (!amount || amount <= 0) {
alert('Please enter a valid amount');
return;
}
// Check if balance allows this savings
const totalIncome = financeData.transactions
.filter(t => t.type === 'income')
.reduce((sum, t) => sum + t.amount, 0);
const totalExpenses = financeData.transactions
.filter(t => t.type === 'expense')
.reduce((sum, t) => sum + t.amount, 0);
const totalSavings = financeData.savings.household.saved +
financeData.savings.business.saved +
financeData.savings.emergency.saved;
const balance = totalIncome - totalExpenses - totalSavings;
if (amount > balance) {
alert(`You only have ${balance.toLocaleString()} Ksh available for savings.`);
return;
}
// Add to savings
financeData.savings[goal].saved += amount;
// Record in history
financeData.savings[goal].history.push({
date: new Date().toISOString(),
amount,
balance: financeData.savings[goal].saved
});
saveData();
updateFinancialOverview();
renderSavingsHistory();
// Reset input
inputElement.value = '';
}
// Render savings history
function renderSavingsHistory() {
const history = [];
// Combine all savings history
Object.entries(financeData.savings).forEach(([goal, data]) => {
data.history.forEach(entry => {
history.push({
...entry,
goal: capitalizeFirstLetter(goal)
});
});
});
// Sort by date
history.sort((a, b) => new Date(b.date) - new Date(a.date));
if (history.length === 0) {
savingsHistory.innerHTML = `
<tr>
<td colspan="4" class="p-4 text-center text-gray-500">No savings recorded yet</td>
</tr>
`;
return;
}
savingsHistory.innerHTML = history.map(entry => `
<tr class="border-b">
<td class="p-2">${formatDate(entry.date)}</td>
<td class="p-2">${entry.goal}</td>
<td class="p-2 text-right">+${entry.amount.toLocaleString()} Ksh</td>
<td class="p-2 text-right">${entry.balance.toLocaleString()} Ksh</td>
</tr>
`).join('');
}
// Generate monthly report
generateReportBtn.addEventListener('click', () => {
const month = parseInt(reportMonthSelect.value);
const year = parseInt(reportYearSelect.value);
// Filter transactions for the selected month/year
const monthTransactions = financeData.transactions.filter(t => {
const date = new Date(t.date);
return date.getMonth() + 1 === month && date.getFullYear() === year;
});
const monthIncome = monthTransactions
.filter(t => t.type === 'income')
.reduce((sum, t) => sum + t.amount, 0);
const monthExpenses = monthTransactions
.filter(t => t.type === 'expense')
.reduce((sum, t) => sum + t.amount, 0);
const monthSavings = monthIncome - monthExpenses;
// Get top 3 expense categories
const expenseCategories = {};
monthTransactions
.filter(t => t.type === 'expense')
.forEach(t => {
expenseCategories[t.category] = (expenseCategories[t.category] || 0) + t.amount;
});
const topExpenses = Object.entries(expenseCategories)
.sort((a, b) => b[1] - a[1])
.slice(0, 3);
// Update report UI
reportTitle.textContent = `${new Date(year, month - 1).toLocaleString('default', { month: 'long' })} ${year}`;
reportIncome.textContent = `${monthIncome.toLocaleString()} Ksh`;
reportExpenses.textContent = `${monthExpenses.toLocaleString()} Ksh`;
reportSavings.textContent = `${monthSavings.toLocaleString()} Ksh`;
// Update top expenses
if (topExpenses.length === 0) {
reportTopExpenses.innerHTML = '<p class="text-gray-600">No expense data available</p>';
} else {
reportTopExpenses.innerHTML = topExpenses.map(([category, amount]) => `
<div class="flex justify-between mb-1">
<span class="capitalize">${category}</span>
<span class="font-medium">${amount.toLocaleString()} Ksh (${(amount / monthExpenses * 100).toFixed(1)}%)</span>
</div>
`).join('');
}
// Generate summary text
let summary = `In ${new Date(year, month - 1).toLocaleString('default', { month: 'long' })}, you earned ${monthIncome.toLocaleString()} Ksh and spent ${monthExpenses.toLocaleString()} Ksh. `;
if (monthIncome > monthExpenses) {
summary += `This resulted in a net savings of ${monthSavings.toLocaleString()} Ksh. `;
if (topExpenses.length > 0) {
summary += `Your largest expense was ${topExpenses[0][0]}, accounting for ${(topExpenses[0][1] / monthExpenses * 100).toFixed(1)}% of your total expenses.`;
}
} else if (monthIncome < monthExpenses) {
summary += `This resulted in a deficit of ${Math.abs(monthSavings).toLocaleString()} Ksh. `;
if (topExpenses.length > 0) {
summary += `Consider reviewing your spending on ${topExpenses[0][0]}, which accounted for ${(topExpenses[0][1] / monthExpenses * 100).toFixed(1)}% of your expenses.`;
}
} else {
summary += 'Your income and expenses were exactly balanced this month.';
}
reportSummary.textContent = summary;
// Show report
monthlyReport.classList.remove('hidden');
});
// To-Do List functionality
addTaskBtn.addEventListener('click', () => {
const title = taskTitleInput.value.trim();
const priority = taskPrioritySelect.value;
const deadline = taskDeadlineInput.value;
const description = taskDescriptionInput.value.trim();
if (!title) {
alert('Please enter a task title');
return;
}
if (!deadline) {
alert('Please select a deadline');
return;
}
const task = {
id: Date.now(),
title,
priority,
deadline,
description,
completed: false,
createdAt: new Date().toISOString()
};
financeData.todoList.push(task);
saveData();
renderTasks();
// Reset form
taskTitleInput.value = '';
taskPrioritySelect.value = 'medium';
taskDeadlineInput.value = '';
taskDescriptionInput.value = '';
});
// Filter tasks
taskFilterStatus.addEventListener('change', renderTasks);
taskFilterPriority.addEventListener('change', renderTasks);
// Render tasks
function renderTasks() {
const filterStatus = taskFilterStatus.value;
const filterPriority = taskFilterPriority.value;
const filteredTasks = financeData.todoList.filter(task => {
const matchesStatus = filterStatus === 'all' ||
(filterStatus === 'pending' && !task.completed) ||
(filterStatus === 'completed' && task.completed);
const matchesPriority = filterPriority === 'all' || task.priority === filterPriority;
return matchesStatus && matchesPriority;
});
if (filteredTasks.length === 0) {
taskList.innerHTML = '<li class="p-4 text-center text-gray-500">No tasks found</li>';
return;
}
// Sort by deadline (soonest first), then priority (high first), then creation date (newest first)
filteredTasks.sort((a, b) => {
// Completed tasks go to bottom
if (a.completed && !b.completed) return 1;
if (!a.completed && b.completed) return -1;
// Sort by deadline
const deadlineA = new Date(a.deadline);
const deadlineB = new Date(b.deadline);
if (deadlineA < deadlineB) return -1;
if (deadlineA > deadlineB) return 1;
// Sort by priority
const priorityOrder = { high: 3, medium: 2, low: 1 };
if (priorityOrder[a.priority] > priorityOrder[b.priority]) return -1;
if (priorityOrder[a.priority] < priorityOrder[b.priority]) return 1;
// Sort by creation date
return new Date(b.createdAt) - new Date(a.createdAt);
});
taskList.innerHTML = filteredTasks.map(task => `
<li class="border-b p-4 ${task.completed ? 'bg-gray-50' : ''}">
<div class="flex items-start justify-between">
<div class="flex items-start">
<input type="checkbox" ${task.completed ? 'checked' : ''}
class="mt-1 mr-2"
onchange="toggleTaskCompletion(${task.id}, this.checked)">
<div>
<h4 class="font-medium ${task.completed ? 'line-through text-gray-500' : ''}">${task.title}</h4>
${task.description ? `<p class="text-sm text-gray-600 mt-1">${task.description}</p>` : ''}
<div class="flex items-center mt-2 text-sm">
<span class="inline-block w-3 h-3 rounded-full mr-1
${task.priority === 'high' ? 'bg-red-500' :
task.priority === 'medium' ? 'bg-yellow-500' : 'bg-green-500'}"></span>
<span class="mr-4 capitalize">${task.priority} priority</span>
<span class="text-gray-500"><i class="far fa-calendar-alt mr-1"></i> Due: ${formatDate(task.deadline)}</span>
</div>
</div>
</div>
<button class="text-red-500 hover:text-red-700" onclick="deleteTask(${task.id})">
<i class="fas fa-trash"></i>
</button>
</div>
</li>
`).join('');
}
// Toggle task completion
function toggleTaskCompletion(id, completed) {
const task = financeData.todoList.find(t => t.id === id);
if (task) {
task.completed = completed;
saveData();
renderTasks();
}
}
// Delete task
function deleteTask(id) {
if (confirm('Are you sure you want to delete this task?')) {
financeData.todoList = financeData.todoList.filter(t => t.id !== id);
saveData();
renderTasks();
}
}
// Calendar functionality
let currentCalendarDate = new Date();
// Initialize calendar buttons
prevMonthBtn.addEventListener('click', () => {
currentCalendarDate.setMonth(currentCalendarDate.getMonth() - 1);
renderCalendar();
});
nextMonthBtn.addEventListener('click', () => {
currentCalendarDate.setMonth(currentCalendarDate.getMonth() + 1);
renderCalendar();
});
todayBtn.addEventListener('click', () => {
currentCalendarDate = new Date();
renderCalendar();
});
// Render calendar
function renderCalendar() {
const year = currentCalendarDate.getFullYear();
const month = currentCalendarDate.getMonth();
// Set month/year title
calendarMonthYear.textContent = new Date(year, month).toLocaleString('default', { month: 'long', year: 'numeric' });
// Get first day of month and last day of month
const firstDay = new Date(year, month, 1);
const lastDay = new Date(year, month + 1, 0);
// Get days in month
const daysInMonth = lastDay.getDate();
// Get starting day of week (0 = Sunday, 6 = Saturday)
const startingDay = firstDay.getDay();
// Clear previous calendar
calendarDays.innerHTML = '';
// Add empty cells for days before the first day of the month
for (let i = 0; i < startingDay; i++) {
const emptyCell = document.createElement('div');
emptyCell.className = 'p-2 border';
calendarDays.appendChild(emptyCell);
}
// Add cells for each day of the month
for (let day = 1; day <= daysInMonth; day++) {
const date = new Date(year, month, day);
const dateString = date.toISOString().split('T')[0];
const dayCell = document.createElement('div');
dayCell.className = 'calendar-day p-2 border h-24 overflow-y-auto';
// Highlight today
const today = new Date();
if (date.getDate() === today.getDate() &&
date.getMonth() === today.getMonth() &&
date.getFullYear() === today.getFullYear()) {
dayCell.classList.add('today');
}
// Check if day has events
const hasEvents = financeData.calendarEvents[dateString] && financeData.calendarEvents[dateString].length > 0;
if (hasEvents) {
dayCell.classList.add('has-event');
}
dayCell.innerHTML = `
<div class="font-medium">${day}</div>
${hasEvents ? `<div class="text-xs mt-1 text-white bg-accent2 rounded-full px-1 inline-block">${financeData.calendarEvents[dateString].length} event(s)</div>` : ''}
`;
dayCell.addEventListener('click', () => showDayEvents(date));
calendarDays.appendChild(dayCell);
}
// Set selected day to today if not already set
const today = new Date();
if (!document.querySelector('.calendar-day.selected')) {
showDayEvents(today);
}
}
// Show events for a specific day
function showDayEvents(date) {
const dateString = date.toISOString().split('T')[0];
selectedDayTitle.textContent = date.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
// Clear previous selection
document.querySelectorAll('.calendar-day').forEach(day => {
day.classList.remove('selected', 'bg-accent1', 'text-primary');
});
// Highlight selected day
const dayElements = document.querySelectorAll('.calendar-day');
const dayIndex = Array.from(dayElements).findIndex(day => {
return day.textContent.includes(date.getDate().toString()) &&
!day.textContent.includes((date.getDate() + 1).toString());
});
if (dayIndex !== -1) {
dayElements[dayIndex].classList.add('selected', 'bg-accent1', 'text-primary');
}
// Show events for this day
const events = financeData.calendarEvents[dateString] || [];
if (events.length === 0) {
dayEventsList.innerHTML = '<li class="text-gray-500">No events scheduled</li>';
} else {
dayEventsList.innerHTML = events.map(event => `
<li class="border-b py-2">
<div class="flex justify-between items-start">
<div>
<h4 class="font-medium">${event.title}</h4>
<p class="text-sm text-gray-600">${event.time}</p>
${event.description ? `<p class="text-sm mt-1">${event.description}</p>` : ''}
</div>
<button class="text-red-500 hover:text-red-700" onclick="deleteEvent('${dateString}', ${event.id})">
<i class="fas fa-trash"></i>
</button>
</div>
</li>
`).join('');
}
// Set current date for adding new events
eventTitleInput.dataset.date = dateString;
}
// Add event
addEventBtn.addEventListener('click', () => {
const title = eventTitleInput.value.trim();
const time = eventTimeInput.value;
const description = eventDescriptionInput.value.trim();
const dateString = eventTitleInput.dataset.date;
if (!title) {
alert('Please enter an event title');
return;
}
if (!time) {
alert('Please select a time');
return;
}
const event = {
id: Date.now(),
title,
time,
description
};
// Initialize events array for this date if it doesn't exist
if (!financeData.calendarEvents[dateString]) {
financeData.calendarEvents[dateString] = [];
}
financeData.calendarEvents[dateString].push(event);
saveData();
// Refresh calendar and events list
renderCalendar();
showDayEvents(new Date(dateString));
// Reset form
eventTitleInput.value = '';
eventTimeInput.value = '';
eventDescriptionInput.value = '';
});
// Delete event
function deleteEvent(dateString, id) {
if (confirm('Are you sure you want to delete this event?')) {
financeData.calendarEvents[dateString] = financeData.calendarEvents[dateString].filter(e => e.id !== id);
// Remove date key if no events left
if (financeData.calendarEvents[dateString].length === 0) {
delete financeData.calendarEvents[dateString];
}
saveData();
// Refresh calendar and events list
renderCalendar();
showDayEvents(new Date(dateString));
}
}
// Helper functions
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
}
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
// Save data to localStorage
function saveData() {
localStorage.setItem('benMburuFinanceData', JSON.stringify(financeData));
}
// Load data from localStorage
function loadData() {
const savedData = localStorage.getItem('benMburuFinanceData');
if (savedData) {
financeData = JSON.parse(savedData);
// Convert date strings to Date objects for transactions
financeData.transactions.forEach(t => {
t.date = new Date(t.date).toISOString().split('T')[0];
});
// Convert date strings to Date objects for tasks
financeData.todoList.forEach(t => {
t.deadline = new Date(t.deadline).toISOString().split('T')[0];
});
}
}
// Initialize app
function init() {
loadData();
// Set current date for transaction
transactionDateInput.valueAsDate = new Date();
// Set current date for task deadline
taskDeadlineInput.valueAsDate = new Date();
// Set current time for event
const
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Khatvathiren/track" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>