diff --git a/assets/css/custom.css b/assets/css/custom.css new file mode 100644 index 0000000..1706e51 --- /dev/null +++ b/assets/css/custom.css @@ -0,0 +1,35 @@ +body { + background-color: #f8f9fa; +} + +.card { + border: none; + border-radius: 0.5rem; +} + +.btn-primary { + background-color: #0d6efd; + border-color: #0d6efd; +} + +.btn-danger { + background-color: #dc3545; + border-color: #dc3545; +} + +.table { + font-size: 0.9rem; +} + +.table th { + font-weight: 600; +} + +.toast-header { + background-color: #0d6efd; + color: white; +} + +.toast-body { + background-color: white; +} diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100644 index 0000000..118da48 --- /dev/null +++ b/assets/js/main.js @@ -0,0 +1,149 @@ +document.addEventListener('DOMContentLoaded', () => { + const initialBalanceEl = document.getElementById('initialBalance'); + const currentBalanceEl = document.getElementById('currentBalance'); + const transactionHistoryEl = document.getElementById('transactionHistory'); + const incomeForm = document.getElementById('incomeForm'); + const expenseForm = document.getElementById('expenseForm'); + const incomeModal = new bootstrap.Modal(document.getElementById('incomeModal')); + const expenseModal = new bootstrap.Modal(document.getElementById('expenseModal')); + const toastEl = document.getElementById('liveToast'); + const toast = new bootstrap.Toast(toastEl); + + // Search elements + const startDateEl = document.getElementById('startDate'); + const endDateEl = document.getElementById('endDate'); + const searchBtn = document.getElementById('searchBtn'); + const clearBtn = document.getElementById('clearBtn'); + + let transactions = JSON.parse(localStorage.getItem('transactions')) || []; + let initialBalance = parseFloat(localStorage.getItem('initialBalance')) || 0; + + initialBalanceEl.value = initialBalance; + + const formatCurrency = (amount) => { + return new Intl.NumberFormat('en-IN', { style: 'currency', currency: 'INR' }).format(amount); + }; + + const showToast = (message, type) => { + const toastBody = toastEl.querySelector('.toast-body'); + const toastHeader = toastEl.querySelector('.toast-header'); + toastBody.textContent = message; + toastHeader.classList.remove('bg-success', 'bg-danger'); + toastHeader.classList.add(type === 'success' ? 'bg-success' : 'bg-danger'); + toast.show(); + }; + + const renderTransactions = (transactionsToRender) => { + transactionHistoryEl.innerHTML = ''; + let balance = initialBalance; + transactions.forEach(t => balance += t.amount); + + if (transactionsToRender.length === 0) { + transactionHistoryEl.innerHTML = 'No transactions found for this period.'; + } else { + transactionsToRender.slice().reverse().forEach(transaction => { + const row = document.createElement('tr'); + const typeClass = transaction.amount > 0 ? 'text-success' : 'text-danger'; + row.innerHTML = ` + ${new Date(transaction.date).toLocaleDateString()} + ${transaction.amount > 0 ? 'Income' : 'Expense'} + ${transaction.description} + ${formatCurrency(transaction.amount)} + + `; + transactionHistoryEl.appendChild(row); + }); + } + + currentBalanceEl.textContent = formatCurrency(balance); + }; + + const addTransaction = (amount, description, date) => { + const transaction = { + id: Date.now(), + date: date || new Date().toISOString().split('T')[0], + amount: amount, + description: description + }; + transactions.push(transaction); + localStorage.setItem('transactions', JSON.stringify(transactions)); + renderTransactions(transactions); + }; + + const deleteTransaction = (id) => { + transactions = transactions.filter(t => t.id !== id); + localStorage.setItem('transactions', JSON.stringify(transactions)); + renderTransactions(transactions); + showToast('Transaction deleted successfully!', 'success'); + }; + + transactionHistoryEl.addEventListener('click', (e) => { + if (e.target.closest('.delete-btn')) { + const id = parseInt(e.target.closest('.delete-btn').dataset.id); + deleteTransaction(id); + } + }); + + initialBalanceEl.addEventListener('input', (e) => { + initialBalance = parseFloat(e.target.value) || 0; + localStorage.setItem('initialBalance', initialBalance); + renderTransactions(transactions); + }); + + incomeForm.addEventListener('submit', (e) => { + e.preventDefault(); + const amount = parseFloat(document.getElementById('incomeAmount').value); + const description = document.getElementById('incomeDescription').value; + const date = document.getElementById('incomeDate').value; + if (amount > 0 && description && date) { + addTransaction(amount, description, date); + showToast('Income added successfully!', 'success'); + incomeForm.reset(); + document.getElementById('incomeDate').valueAsDate = new Date(); + incomeModal.hide(); + } else { + showToast('Please enter a valid amount, description, and date.', 'danger'); + } + }); + + expenseForm.addEventListener('submit', (e) => { + e.preventDefault(); + const amount = parseFloat(document.getElementById('expenseAmount').value); + const description = document.getElementById('expenseDescription').value; + const date = document.getElementById('expenseDate').value; + if (amount > 0 && description && date) { + addTransaction(-amount, description, date); + showToast('Expense added successfully!', 'success'); + expenseForm.reset(); + document.getElementById('expenseDate').valueAsDate = new Date(); + expenseModal.hide(); + } else { + showToast('Please enter a valid amount, description, and date.', 'danger'); + } + }); + + searchBtn.addEventListener('click', () => { + const startDate = startDateEl.value; + const endDate = endDateEl.value; + + if (!startDate || !endDate) { + showToast('Please select both a start and end date.', 'danger'); + return; + } + + const filteredTransactions = transactions.filter(t => { + const transactionDate = t.date; + return transactionDate >= startDate && transactionDate <= endDate; + }); + + renderTransactions(filteredTransactions); + }); + + clearBtn.addEventListener('click', () => { + startDateEl.value = ''; + endDateEl.value = ''; + renderTransactions(transactions); + }); + + renderTransactions(transactions); +}); diff --git a/db/migrations/001_create_tables.sql b/db/migrations/001_create_tables.sql new file mode 100644 index 0000000..9b49745 --- /dev/null +++ b/db/migrations/001_create_tables.sql @@ -0,0 +1,16 @@ +CREATE TABLE IF NOT EXISTS `users` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `username` VARCHAR(50) NOT NULL UNIQUE, + `password` VARCHAR(255) NOT NULL, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `transactions` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `user_id` INT NOT NULL, + `description` VARCHAR(255) NOT NULL, + `amount` DECIMAL(10, 2) NOT NULL, + `type` ENUM('income', 'expense') NOT NULL, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/fpdf.zip b/fpdf.zip new file mode 100644 index 0000000..e69de29 diff --git a/index.php b/index.php index 7205f3d..21c892a 100644 --- a/index.php +++ b/index.php @@ -1,150 +1,160 @@ - - + - - - New Style - - - - - - - - - - - - - - - - - - - + + + Rahul sharma + + + + + + + + + + -
-
-

Analyzing your requirements and generating your website…

-
- Loading… -
-

AI is collecting your requirements and applying the first changes.

-

This page will update automatically as the plan is implemented.

-

Runtime: PHP — UTC

+ +
+
+
+
+

Personal Finance Calculator

+

A simple way to track your income and expenses.

+
+ +
+
+
+ + +
+
Current Balance
+

₹0.00

+
+ + +
+
+
+ +
+
+
Transaction History
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + + + + + + + + + + + + +
DateTypeDescriptionAmountActions
+
+
+
+
+
+
+ + + -
- + + + + +
+ +
+ + + + - + \ No newline at end of file diff --git a/migrate.php b/migrate.php new file mode 100644 index 0000000..8367ff2 --- /dev/null +++ b/migrate.php @@ -0,0 +1,28 @@ +exec($sql); + echo "Success.\n"; + } + + echo "\nAll migrations completed successfully.\n"; + +} catch (PDOException $e) { + die("Database migration failed: " . $e->getMessage() . "\n"); +} +