Day 10: Overview
This is it! Today, you'll combine everything you've learned over the past 9 days to build a complete, functional CRUD (Create, Read, Update, Delete) application. This project demonstrates that you understand HTML structure, CSS styling, JavaScript logic, DOM manipulation, event handling, state management, and data persistence.
Project Options
Choose ONE of these projects to build:
- Budget Tracker - Track income and expenses
- Notes App - Create, edit, and organize notes
- Habit Tracker - Track daily habits and streaks
- Simple To-Do List - Advanced to-do with categories
Today's tutorial: We'll build a Budget Tracker
Learning Objectives
By the end of this tutorial, you will:
- Build a complete CRUD application from scratch
- Implement all four CRUD operations
- Manage application state effectively
- Persist data using LocalStorage
- Create a clean, professional UI
- Understand why React makes this easier
Part 1: Project Planning
Requirements
Must Have:
- Add items (Create)
- Display items (Read)
- Edit items (Update)
- Delete items (Delete)
- Store data in array/objects
- Re-render UI when state changes
- Clean, styled UI
- Organized, commented code
Bonus Features:
- LocalStorage persistence
- Filter/search functionality
- Statistics/summaries
- Form validation
- Responsive design
Data Structure
// Single transaction object
{
id: 1,
type: 'income' | 'expense',
description: 'Monthly Salary',
amount: 5000,
category: 'Salary',
date: '2025-01-15',
createdAt: 1234567890
}
// State: Array of transactions
let transactions = [
{ id: 1, type: 'income', description: 'Salary', amount: 5000, category: 'Salary', date: '2025-01-15' },
{ id: 2, type: 'expense', description: 'Groceries', amount: 150, category: 'Food', date: '2025-01-16' }
];
Part 2: Building the Budget Tracker
Step 1: HTML Structure
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Budget Tracker</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="container">
<header>
<h1>Budget Tracker</h1>
<p class="tagline">Take control of your finances</p>
</header>
<!-- Summary Cards -->
<div class="summary">
<div class="summary-card income-card">
<div class="card-title">Income</div>
<div class="card-amount" id="totalIncome">$0.00</div>
</div>
<div class="summary-card expense-card">
<div class="card-title">Expenses</div>
<div class="card-amount" id="totalExpense">$0.00</div>
</div>
<div class="summary-card balance-card">
<div class="card-title">Balance</div>
<div class="card-amount" id="balance">$0.00</div>
</div>
</div>
<!-- Add Transaction Form -->
<section class="add-transaction">
<h2 id="formTitle">Add New Transaction</h2>
<form id="transactionForm">
<div class="form-row">
<div class="form-group">
<label>Type</label>
<select id="type" required>
<option value="income">Income</option>
<option value="expense">Expense</option>
</select>
</div>
<div class="form-group">
<label>Category</label>
<select id="category" required>
<option value="Salary">Salary</option>
<option value="Freelance">Freelance</option>
<option value="Food">Food</option>
<option value="Transport">Transport</option>
<option value="Bills">Bills</option>
<option value="Entertainment">Entertainment</option>
<option value="Other">Other</option>
</select>
</div>
</div>
<div class="form-row">
<div class="form-group flex-2">
<label>Description</label>
<input type="text" id="description" placeholder="e.g., Monthly salary" required>
</div>
<div class="form-group">
<label>Amount</label>
<input type="number" id="amount" placeholder="0.00" step="0.01" min="0.01" required>
</div>
<div class="form-group">
<label>Date</label>
<input type="date" id="date" required>
</div>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary" id="submitBtn">
Add Transaction
</button>
<button type="button" class="btn btn-secondary" id="cancelBtn" style="display: none;">
Cancel
</button>
</div>
</form>
</section>
<!-- Transactions List -->
<section class="transactions">
<div class="transactions-header">
<h2>Transaction History</h2>
<div class="filter-group">
<label for="filterType">Filter:</label>
<select id="filterType">
<option value="all">All</option>
<option value="income">Income</option>
<option value="expense">Expenses</option>
</select>
</div>
</div>
<div id="transactionsList" class="transactions-list">
<!-- Transactions will be rendered here -->
</div>
</section>
</div>
<script src="script.js"></script>
</body>
</html>
Step 2: CSS Styling (styles.css)
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
/* Header */
header {
text-align: center;
color: white;
margin-bottom: 30px;
}
header h1 {
font-size: 48px;
margin-bottom: 10px;
}
.tagline {
font-size: 18px;
opacity: 0.9;
}
/* Summary Cards */
.summary {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.summary-card {
background: white;
padding: 25px;
border-radius: 15px;
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.card-title {
font-size: 14px;
color: #666;
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 10px;
}
.card-amount {
font-size: 32px;
font-weight: bold;
}
.income-card .card-amount {
color: #4CAF50;
}
.expense-card .card-amount {
color: #f44336;
}
.balance-card .card-amount {
color: #2196F3;
}
/* Form Section */
.add-transaction {
background: white;
padding: 30px;
border-radius: 15px;
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
margin-bottom: 30px;
}
.add-transaction h2 {
color: #333;
margin-bottom: 20px;
}
.form-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin-bottom: 15px;
}
.form-group {
display: flex;
flex-direction: column;
}
.form-group.flex-2 {
grid-column: span 2;
}
label {
font-size: 14px;
font-weight: 600;
color: #555;
margin-bottom: 8px;
}
input, select {
padding: 12px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 16px;
font-family: inherit;
transition: border-color 0.3s;
}
input:focus, select:focus {
outline: none;
border-color: #667eea;
}
.form-actions {
display: flex;
gap: 10px;
margin-top: 20px;
}
.btn {
padding: 12px 30px;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
}
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
.btn-secondary {
background: #9E9E9E;
color: white;
}
.btn-secondary:hover {
background: #757575;
}
/* Transactions Section */
.transactions {
background: white;
padding: 30px;
border-radius: 15px;
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.transactions-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
flex-wrap: wrap;
gap: 15px;
}
.transactions-header h2 {
color: #333;
}
.filter-group {
display: flex;
align-items: center;
gap: 10px;
}
.filter-group label {
margin: 0;
}
.filter-group select {
padding: 8px 12px;
}
/* Transaction List */
.transactions-list {
display: flex;
flex-direction: column;
gap: 15px;
}
.transaction-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px;
background: #f8f9fa;
border-radius: 10px;
border-left: 4px solid #ddd;
transition: all 0.3s;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.transaction-item:hover {
transform: translateX(5px);
box-shadow: 0 3px 10px rgba(0,0,0,0.1);
}
.transaction-item.income {
border-left-color: #4CAF50;
}
.transaction-item.expense {
border-left-color: #f44336;
}
.transaction-info {
flex: 1;
}
.transaction-description {
font-size: 18px;
font-weight: 600;
color: #333;
margin-bottom: 5px;
}
.transaction-details {
font-size: 14px;
color: #666;
}
.transaction-category {
background: #e0e0e0;
padding: 2px 8px;
border-radius: 12px;
font-size: 12px;
margin-left: 10px;
}
.transaction-amount {
font-size: 24px;
font-weight: bold;
margin: 0 20px;
}
.transaction-amount.income {
color: #4CAF50;
}
.transaction-amount.expense {
color: #f44336;
}
.transaction-actions {
display: flex;
gap: 10px;
}
.btn-small {
padding: 8px 16px;
font-size: 14px;
border: none;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s;
}
.btn-edit {
background: #2196F3;
color: white;
}
.btn-edit:hover {
background: #1976D2;
}
.btn-delete {
background: #f44336;
color: white;
}
.btn-delete:hover {
background: #d32f2f;
}
.empty-state {
text-align: center;
padding: 60px 20px;
color: #999;
}
.empty-state p {
font-size: 18px;
margin-bottom: 10px;
}
/* Responsive */
@media (max-width: 768px) {
header h1 {
font-size: 36px;
}
.form-row {
grid-template-columns: 1fr;
}
.form-group.flex-2 {
grid-column: span 1;
}
.transaction-item {
flex-direction: column;
align-items: flex-start;
}
.transaction-amount {
margin: 10px 0;
}
.transaction-actions {
width: 100%;
justify-content: flex-end;
}
}
Step 3: JavaScript Implementation (script.js)
// ==================== STATE ====================
let transactions = [];
let nextId = 1;
let editingId = null;
// ==================== DOM ELEMENTS ====================
const transactionForm = document.querySelector('#transactionForm');
const typeInput = document.querySelector('#type');
const categoryInput = document.querySelector('#category');
const descriptionInput = document.querySelector('#description');
const amountInput = document.querySelector('#amount');
const dateInput = document.querySelector('#date');
const submitBtn = document.querySelector('#submitBtn');
const cancelBtn = document.querySelector('#cancelBtn');
const formTitle = document.querySelector('#formTitle');
const transactionsList = document.querySelector('#transactionsList');
const filterType = document.querySelector('#filterType');
const totalIncome = document.querySelector('#totalIncome');
const totalExpense = document.querySelector('#totalExpense');
const balance = document.querySelector('#balance');
// ==================== INITIALIZATION ====================
// Set today's date as default
dateInput.valueAsDate = new Date();
// Load data from localStorage
loadFromLocalStorage();
// Initial render
render();
// ==================== CRUD FUNCTIONS ====================
// CREATE
function addTransaction(transaction) {
const newTransaction = {
id: nextId++,
...transaction,
createdAt: Date.now()
};
transactions.push(newTransaction);
saveToLocalStorage();
render();
}
// READ
function getFilteredTransactions() {
const filter = filterType.value;
if (filter === 'all') return transactions;
return transactions.filter(t => t.type === filter);
}
// UPDATE
function updateTransaction(id, updates) {
const index = transactions.findIndex(t => t.id === id);
if (index !== -1) {
transactions[index] = { ...transactions[index], ...updates };
saveToLocalStorage();
render();
}
}
// DELETE
function deleteTransaction(id) {
if (confirm('Are you sure you want to delete this transaction?')) {
transactions = transactions.filter(t => t.id !== id);
saveToLocalStorage();
render();
}
}
// ==================== LOCAL STORAGE ====================
function saveToLocalStorage() {
localStorage.setItem('transactions', JSON.stringify(transactions));
localStorage.setItem('nextId', nextId.toString());
}
function loadFromLocalStorage() {
const savedTransactions = localStorage.getItem('transactions');
const savedNextId = localStorage.getItem('nextId');
if (savedTransactions) {
transactions = JSON.parse(savedTransactions);
}
if (savedNextId) {
nextId = parseInt(savedNextId);
}
}
// ==================== RENDER FUNCTIONS ====================
function render() {
renderTransactions();
renderSummary();
}
function renderTransactions() {
const filtered = getFilteredTransactions();
if (filtered.length === 0) {
transactionsList.innerHTML = `
<div class="empty-state">
<p>📝 No transactions yet!</p>
<p>Add your first transaction above</p>
</div>
`;
return;
}
transactionsList.innerHTML = '';
// Sort by date (newest first)
const sorted = [...filtered].sort((a, b) => new Date(b.date) - new Date(a.date));
sorted.forEach(transaction => {
const item = createTransactionElement(transaction);
transactionsList.appendChild(item);
});
}
function createTransactionElement(transaction) {
const div = document.createElement('div');
div.className = `transaction-item ${transaction.type}`;
div.dataset.id = transaction.id;
const sign = transaction.type === 'income' ? '+' : '-';
div.innerHTML = `
<div class="transaction-info">
<div class="transaction-description">${transaction.description}</div>
<div class="transaction-details">
${transaction.date}
<span class="transaction-category">${transaction.category}</span>
</div>
</div>
<div class="transaction-amount ${transaction.type}">
${sign}$${parseFloat(transaction.amount).toFixed(2)}
</div>
<div class="transaction-actions">
<button class="btn-small btn-edit" onclick="startEdit(${transaction.id})">Edit</button>
<button class="btn-small btn-delete" onclick="deleteTransaction(${transaction.id})">Delete</button>
</div>
`;
return div;
}
function renderSummary() {
const income = transactions
.filter(t => t.type === 'income')
.reduce((sum, t) => sum + parseFloat(t.amount), 0);
const expense = transactions
.filter(t => t.type === 'expense')
.reduce((sum, t) => sum + parseFloat(t.amount), 0);
const balanceAmount = income - expense;
totalIncome.textContent = `$${income.toFixed(2)}`;
totalExpense.textContent = `$${expense.toFixed(2)}`;
balance.textContent = `$${balanceAmount.toFixed(2)}`;
// Color code the balance
if (balanceAmount > 0) {
balance.style.color = '#4CAF50';
} else if (balanceAmount < 0) {
balance.style.color = '#f44336';
} else {
balance.style.color = '#2196F3';
}
}
// ==================== EDIT MODE ====================
function startEdit(id) {
editingId = id;
const transaction = transactions.find(t => t.id === id);
if (transaction) {
// Populate form
typeInput.value = transaction.type;
categoryInput.value = transaction.category;
descriptionInput.value = transaction.description;
amountInput.value = transaction.amount;
dateInput.value = transaction.date;
// Update UI
formTitle.textContent = 'Edit Transaction';
submitBtn.textContent = 'Update Transaction';
cancelBtn.style.display = 'inline-block';
// Scroll to form
transactionForm.scrollIntoView({ behavior: 'smooth' });
}
}
function cancelEdit() {
editingId = null;
transactionForm.reset();
dateInput.valueAsDate = new Date();
formTitle.textContent = 'Add New Transaction';
submitBtn.textContent = 'Add Transaction';
cancelBtn.style.display = 'none';
}
// ==================== EVENT HANDLERS ====================
function handleSubmit(e) {
e.preventDefault();
const transaction = {
type: typeInput.value,
category: categoryInput.value,
description: descriptionInput.value.trim(),
amount: amountInput.value,
date: dateInput.value
};
if (editingId !== null) {
// Update existing transaction
updateTransaction(editingId, transaction);
cancelEdit();
} else {
// Add new transaction
addTransaction(transaction);
transactionForm.reset();
dateInput.valueAsDate = new Date();
}
descriptionInput.focus();
}
// ==================== EVENT LISTENERS ====================
transactionForm.addEventListener('submit', handleSubmit);
cancelBtn.addEventListener('click', cancelEdit);
filterType.addEventListener('change', render);
// ==================== UTILITY FUNCTIONS ====================
// Export data as JSON
function exportData() {
const dataStr = JSON.stringify(transactions, null, 2);
const dataBlob = new Blob([dataStr], { type: 'application/json' });
const url = URL.createObjectURL(dataBlob);
const link = document.createElement('a');
link.href = url;
link.download = `budget-tracker-${Date.now()}.json`;
link.click();
}
// Clear all data
function clearAllData() {
if (confirm('Are you sure you want to delete ALL transactions? This cannot be undone!')) {
transactions = [];
nextId = 1;
saveToLocalStorage();
render();
}
}
// Make functions globally available for onclick
window.startEdit = startEdit;
window.deleteTransaction = deleteTransaction;
window.exportData = exportData;
window.clearAllData = clearAllData;
Part 3: Understanding Why React Helps
Now that you've built this app, you've experienced:
Problems You Faced
- Manual DOM Manipulation - You had to create every element manually
- Re-rendering Everything - Every change rebuilds the entire list
- State Management Complexity - Keeping state and UI in sync is hard
- Code Organization - Everything in one file gets messy
- Reusability - Hard to reuse transaction items elsewhere
How React Solves These
// Your Vanilla JS (complex):
function createTransactionElement(transaction) {
const div = document.createElement('div');
div.className = `transaction-item ${transaction.type}`;
div.innerHTML = `...long HTML string...`;
return div;
}
// React (simple):
function TransactionItem({ transaction }) {
return (
<div className={`transaction-item ${transaction.type}`}>
<div className="transaction-info">
<div className="transaction-description">
{transaction.description}
</div>
</div>
</div>
);
}
React Benefits:
- Declarative UI (describe what you want, not how)
- Automatic re-rendering (only updates what changed)
- Component reusability
- Built-in state management
- Better code organization
Part 4: Alternative Projects
Notes App
Features:
- Create, edit, delete notes
- Markdown support
- Search/filter notes
- Color coding
- LocalStorage persistence
Habit Tracker
Features:
- Add daily habits
- Mark as complete
- Track streaks
- Statistics dashboard
- Calendar view
Part 5: Enhancements
Add These Features
- Search Functionality
function searchTransactions(query) {
return transactions.filter(t =>
t.description.toLowerCase().includes(query.toLowerCase()) ||
t.category.toLowerCase().includes(query.toLowerCase())
);
}
- Date Range Filter
function filterByDateRange(startDate, endDate) {
return transactions.filter(t => {
const date = new Date(t.date);
return date >= startDate && date <= endDate;
});
}
- Charts/Graphs
- Use Chart.js library
- Show income vs expense over time
- Pie chart for categories
- Export to CSV
function exportToCSV() {
const csv = transactions.map(t =>
`${t.date},${t.type},${t.description},${t.category},${t.amount}`
).join('\n');
const blob = new Blob([csv], { type: 'text/csv' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'transactions.csv';
link.click();
}
Key Takeaways
What You've Learned
HTML - Structure and semantics CSS - Layout, styling, responsiveness JavaScript - Logic, functions, arrays, objects DOM - Selecting and manipulating elements Events - Handling user interactions State - Managing application data Storage - Persisting data with LocalStorage Full App - Putting it all together
The Pattern You've Mastered
User Action → Update State → Save to Storage → Re-render UI
This is the foundation of ALL modern web applications!
Next Steps: Learning React
Now you're ready for React because you understand:
- Why React exists - Solves the problems you just experienced
- Components - You built reusable functions to create elements
- State - You managed data and kept UI in sync
- Props - You passed data to your render functions
- Rendering - You updated the UI when data changed
React makes all of this easier and more scalable!
Congratulations!
You've completed the 10-day Web Fundamentals course! You can now:
- Build fully functional web applications
- Understand how the web works
- Manipulate the DOM with confidence
- Handle user interactions and events
- Manage application state
- Make API calls
- Solve real problems with code
You're ready for React, Node.js, and full-stack development!
Keep building projects, keep learning, and remember: the best way to learn is by doing. Build something new every week, and you'll be amazed at how quickly you improve.
Happy coding!
GlenH - Dec 9, 2025gghayoge at gmail.com