Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
964bd56
Added starter budget planner page
Dec 3, 2025
2788307
Add page layout
gokuleducator Dec 3, 2025
7f6495e
Modify main.html
Acardenas96 Dec 4, 2025
45cb1bc
Modify Footer
Acardenas96 Dec 4, 2025
d4a3da2
index.ejs: Added file
Dec 7, 2025
850c162
budget.ejs: Renamed
Dec 7, 2025
4b3e998
app.js: Minor edit
Dec 7, 2025
12ee57e
index.js: Minor edit
Dec 7, 2025
e4368df
budget.css: Basic css for now
Dec 7, 2025
39d5065
budget.js: Starter js functionality
Dec 7, 2025
27d903a
budget.ejs: Added categories
Dec 10, 2025
9f9e86c
Merge pull request #1 from nochinxx/jerome
nochinxx Dec 10, 2025
c32d3c5
Modify header and inxed ejs files
Acardenas96 Dec 10, 2025
4afb750
index.ejs: Re-created
Dec 10, 2025
6eba450
Merge pull request #3 from nochinxx/index
nochinxx Dec 10, 2025
d8b338c
Merge pull request #4 from nochinxx/abraham
nochinxx Dec 10, 2025
a9869d3
index.ejs: Created "start budgeting" btn
Dec 10, 2025
258e436
index.ejs: Minor tag change
Dec 10, 2025
8724301
style.css: Added start btn styling
Dec 10, 2025
da19924
budget.ejs: Added "back to home" btn
Dec 10, 2025
5e7fd7f
budget.css: Minor style changes
Dec 10, 2025
ac57e9e
budget.css: More minor style changes
Dec 10, 2025
4076994
budget.ejs: Minor tweak to tags
Dec 10, 2025
42481c6
mario: Added new routes to app
nochinxx Dec 10, 2025
e73fad6
mario: created categories and transactions models and tables
nochinxx Dec 10, 2025
b3673fd
mario: created transaction and budget, modified existing routes, bud…
nochinxx Dec 10, 2025
758ae0e
mario: made budget.ejs dynamic
nochinxx Dec 10, 2025
fde6f2d
Merge pull request #5 from nochinxx/mario
nochinxx Dec 10, 2025
049a1c2
Merge branch 'main' into jerome
jm-fernando Dec 11, 2025
80b0c85
Add transaction
gokuleducator Dec 11, 2025
f82dbeb
Merge pull request #6 from nochinxx/jerome
gokuleducator Dec 11, 2025
6ef973f
Merge pull request #7 from nochinxx/views
nochinxx Dec 12, 2025
62ff697
Align text inside button to center
gokuleducator Dec 12, 2025
ca2a2db
update color in budget page
gokuleducator Dec 12, 2025
a8a7db9
update home page layout
gokuleducator Dec 12, 2025
77b5e23
Merge pull request #8 from nochinxx/gokul
nochinxx Dec 12, 2025
56657eb
Revise about section to describe budget planner features
gokuleducator Dec 12, 2025
29ea8db
Merge pull request #9 from nochinxx/gokulkrishnaradhakrishnannair-pat…
nochinxx Dec 12, 2025
1b327a4
removed one of the form that didn't work for expenses
nochinxx Dec 12, 2025
a1664fd
Merge pull request #10 from nochinxx/mario
nochinxx Dec 12, 2025
f713287
Get edit and delete buttons, for edit we send the user to a form page…
nochinxx Dec 12, 2025
ecb9585
made the new routees for edit and delete
nochinxx Dec 12, 2025
27ee3bb
edit page
nochinxx Dec 12, 2025
061eda2
new queries for edit and delete
nochinxx Dec 12, 2025
34f6ce2
make button for budget when the user is logged in
nochinxx Dec 12, 2025
795563e
Merge pull request #11 from nochinxx/mario
nochinxx Dec 12, 2025
b78ebe4
Add route handler to delete transaction
gokuleducator Dec 12, 2025
98950c0
Update transaction route and package file
gokuleducator Dec 12, 2025
f702707
Remove unordered list in budget page
gokuleducator Dec 12, 2025
bf1a1fd
Remove unordered list in budget page
gokuleducator Dec 12, 2025
785301b
Add delete button
gokuleducator Dec 12, 2025
2553708
added to trust proxy so that the cookie actually loads
nochinxx Dec 13, 2025
744a8bd
Merge pull request #13 from nochinxx/mario
nochinxx Dec 13, 2025
829936f
budget.css: Removed bullet points
Dec 13, 2025
80c79c6
Merge branch 'main' into jerome
jm-fernando Dec 13, 2025
49ad4cf
Merge pull request #14 from nochinxx/jerome
jm-fernando Dec 13, 2025
0e1414d
Merge pull request #15 from nochinxx/main
gokuleducator Dec 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
- `controllers/` - Route handlers
- `routes/` - Express route definitions
- `middlewares/` - Custom middleware functions
- `views/` - EJS templates
- `views/` - EJS templatesa

When working in this Express.js application, follow the existing patterns in controllers, models, and routes folders. Handle all form validation with express-validator and use the error handling middleware for consistent error responses.
13 changes: 12 additions & 1 deletion app.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,18 @@ const { pool } = require('./config/database');
const indexRoutes = require('./routes/index');
const authRoutes = require('./routes/auth');
const userRoutes = require('./routes/user');
// New routes
const budgetRouter = require('./routes/budget');
const transactionRouter = require('./routes/transaction');

// Import custom middleware
const { setLocals } = require('./middlewares/locals');
const { handleErrors } = require('./middlewares/error-handler');

// Initialize Express app
const app = express();
// Tell Express it's behind a proxy (Render/Cloudflare), so secure cookies work
app.set('trust proxy', 1);

// Test database connection on startup
if (process.env.DATABASE_URL) {
Expand All @@ -43,8 +48,11 @@ if (process.env.DATABASE_URL) {

// Configure Express
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// data parsed as simple key-value pairs
app.use(express.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.static('public'));


// Set up EJS view engine
app.set('view engine', 'ejs');
Expand Down Expand Up @@ -110,6 +118,9 @@ app.use(setLocals);
app.use('/', indexRoutes);
app.use('/auth', authRoutes);
app.use('/user', userRoutes);
// new routes
app.use('/', budgetRouter);
app.use('/', transactionRouter);

// Error handling middleware
app.use(handleErrors);
Expand Down
51 changes: 51 additions & 0 deletions models/Category.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
const { pool } = require('../config/database');

// Get all categories for a user
async function getCategoriesForUser(userId) {
const result = await pool.query(
`
SELECT *
FROM categories
WHERE user_id = $1
ORDER BY type, name
`,
[userId]
);
return result.rows;
}

// Create a new category
async function createCategory(userId, { name, type }) {
const result = await pool.query(
`
INSERT INTO categories (user_id, name, type)
VALUES ($1, $2, $3)
RETURNING *
`,
[userId, name, type]
);
return result.rows[0];
}

// seeder for first time users
async function seedDefaultCategoriesForUser(userId) {
const defaults = [
{ name: 'Salary', type: 'income' },
{ name: 'Freelance', type: 'income' },
{ name: 'Rent', type: 'expense' },
{ name: 'Food', type: 'expense' },
{ name: 'Utilities', type: 'expense' },
{ name: 'Savings', type: 'expense' },
{ name: 'Entertainment', type: 'expense' },
];

for (const c of defaults) {
await createCategory(userId, c);
}
}

module.exports = {
getCategoriesForUser,
createCategory,
seedDefaultCategoriesForUser,
};
98 changes: 98 additions & 0 deletions models/Transaction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
const { pool } = require('../config/database');

async function getTransactionsForUser(userId) {
// Fetch transactions along with category names

const result = await pool.query(
`
SELECT t.*, c.name AS category_name
FROM transactions t
LEFT JOIN categories c ON t.category_id = c.id
WHERE t.user_id = $1
ORDER BY t.date DESC, t.created_at DESC
`,
[userId]
);
return result.rows;
}
// Create a new transaction
async function createTransaction(userId, { categoryId, type, amount, description, date }) {
const result = await pool.query(
`
INSERT INTO transactions (user_id, category_id, type, amount, description, date)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING *
`,
[userId, categoryId || null, type, amount, description || null, date]
);
return result.rows[0];
}
// Get dashboard summary (total income, total expense, balance)
async function getDashboardSummary(userId) {
const result = await pool.query(
`
SELECT
COALESCE(SUM(CASE WHEN type = 'income' THEN amount END), 0) AS total_income,
COALESCE(SUM(CASE WHEN type = 'expense' THEN amount END), 0) AS total_expense
FROM transactions
WHERE user_id = $1
`,
[userId]
);

const row = result.rows[0];
return {
totalIncome: Number(row.total_income),
totalExpense: Number(row.total_expense),
balance: Number(row.total_income) - Number(row.total_expense)
};
}
// Get a single transaction by ID
async function getTransactionById(userId, transactionId) {
const result = await pool.query(
`
SELECT t.*, c.name AS category_name
FROM transactions t
LEFT JOIN categories c ON t.category_id = c.id
WHERE t.user_id = $1 AND t.id = $2
`,
[userId, transactionId]
);
return result.rows[0];
}
// Update a transaction
async function updateTransaction(userId, transactionId, { amount, type, categoryId, description, date }) {
const result = await pool.query(
`
UPDATE transactions
SET amount = $1,
type = $2,
category_id = $3,
description = $4,
date = $5
WHERE id = $6 AND user_id = $7
RETURNING *
`,
[amount, type, categoryId, description, date, transactionId, userId]
);
return result.rows[0];
}
// Delete a transaction
async function deleteTransaction(userId, transactionId) {
await pool.query(
`
DELETE FROM transactions
WHERE id = $1 AND user_id = $2
`,
[transactionId, userId]
);
}

module.exports = {
getTransactionsForUser,
createTransaction,
getDashboardSummary,
getTransactionById,
updateTransaction,
deleteTransaction,
};
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@
"dependencies": {
"bcrypt": "^5.1.1",
"connect-pg-simple": "^10.0.0",
"csurf": "^1.11.0",
"dotenv": "^16.3.1",
"csurf": "^1.2.2",
"dotenv": "^16.6.1",
"ejs": "^3.1.9",
"express": "^4.18.2",
"express": "^4.22.1",
"express-session": "^1.18.1",
"express-validator": "^7.0.1",
"multer": "^1.4.5-lts.2",
"pg": "^8.13.1"
},
"devDependencies": {
"nodemon": "^3.0.2"
"nodemon": "^3.1.11"
}
}
147 changes: 147 additions & 0 deletions public/css/budget.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
/* Updated: Soft yellow background */
background-color: #FFF5E1;
color: #333;
}

header {
/* Updated: Vibrant orange header background */
background-color: #FF9900;
color: #FFFFFF;
text-align: center;
padding: 15px;
}

.budget-container {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
}

section {
/* Updated: Lighter section background */
background: #FFF5E1;
border: 1px solid #FFCC00;
padding: 10px;
margin: 10px 0;
width: 100%;
max-width: 800px;
box-sizing: border-box;
border-radius: 8px;
}

/* Updated: Added text-align center to income-section */
.income-section {
text-align: center;
/* Updated: Lighter section background */
background: #FFF5E1;
border: 1px solid #FFCC00;
padding: 10px;
margin: 10px 0;
width: 100%;
max-width: 800px;
box-sizing: border-box;
border-radius: 8px;
}

button {
/* Updated: Orange button background */
background-color: #FF9900;
color: #FFFFFF;
border: none;
padding: 8px 15px;
cursor: pointer;
border-radius: 4px;
/* Added margin for spacing around buttons */
margin: 5px;
}

button:hover {
opacity: 0.8;
}

.navbar {
display: flex;
justify-content: center;
align-items: center;
position: relative;
padding: 10px 20px;
border-bottom: 2px solid #ddd;
/* Updated: Darker orange/brown base */
background-color: #A0522D;
}

.navbar nav {
display: flex;
align-items: center;
gap: 20px;
}

.nav-home {
position: absolute;
left: 20px;
text-decoration: none;
font-weight: 600;
font-size: 1.1rem;
/* Updated: Bright yellow link color */
color: #FFCC00;
}

.nav-home:hover {
text-decoration: underline;
}

.navbar h1 {
margin: 0;
}

ul {
list-style-type: none;
padding: 0;
/* Updated: Added text-align center to expense-inputs */
.expense-inputs {
text-align: center;
}

/* Added margin to inputs and select for better spacing when centered */
input, select {
margin: 5px;
}

/* Removes bullet points */
main ul {
list-style-type: none;
padding-left: 0; /* Ensures the list items align with other content */
}

/* make the list items look cleaner */
main ul li {
display: flex;
justify-content: space-between; /* Pushes the edit/delete buttons to the right */
padding: 8px 0;
border-bottom: 1px solid #FFCC00; /* Use your existing border color */
align-items: center;
}

/* Style for the edit/delete*/
main ul li a {
margin-right: 10px;
color: #A0522D; /* Use your existing navbar color for links */
text-decoration: none;
font-size: 0.9em;
}

main ul li button {
/* Inherit your existing button styles*/
background-color: #DB4437; /* A distinct red for delete actions */
color: white;
border: none;
padding: 5px 10px;
cursor: pointer;
border-radius: 4px;
}

Loading