Skip to content

Implement budget management backend and dashboard UI#240

Open
gmarav05 wants to merge 2 commits intoOpenLake:mainfrom
gmarav05:feature/budget-management-upstream
Open

Implement budget management backend and dashboard UI#240
gmarav05 wants to merge 2 commits intoOpenLake:mainfrom
gmarav05:feature/budget-management-upstream

Conversation

@gmarav05
Copy link
Copy Markdown
Contributor

@gmarav05 gmarav05 commented Mar 23, 2026


name: Pull Request
about: Propose and submit changes to the project for review
title: PR: Implement Budget Management backend APIs and dashboard UI
labels: ""
assignees: harshitap1305, sakshi1755

Related Issue

  • Closes #

Changes Introduced

  • Added Budget Management module with transaction APIs like allocate, spend, refund budget overview, and transaction history with pagination.
  • Fixed Seed cleanup now also clears budget transaction records to keep seeded data consistent.
  • Updated the Dashboard navigation and component mapping to include a new Budget section for admin roles.

Why This Change?

  • Problem is there was no centralized flow to manage club/council budgets with proper tracking.
  • The Solution is to Introduce backend transaction handling with role based access and frontend dashboard screens for managing and viewing budget activity.

Screenshots / Video (if applicable)

Before After
Budget section not available Budget section available with overview + transaction actions

Testing

  • Ran unit tests and all passed (npm test in the relevant directory).
  • Manually tested the following scenarios
    • Test Case 1: Allocate budget for a selected unit and verify allocated amount increases in overview.
    • Test Case 2: Record expense and refund, verify ledger entry is created and remaining budget updates correctly.
  • Tested on different browsers (Chrome, Firefox) for UI changes.
  • Verified there are no new console warnings or errors.

Documentation Updates

  • Updated the README.md with new instructions.
  • Added clear code comments where logic is complex.
  • N/A

Checklist

  • I have created a new branch for this PR (git checkout -b feature/my-amazing-feature).
  • I have starred the repository.
  • My code follows the project's coding style and conventions.
  • My commit messages are clear and follow the project's guidelines.
  • I have performed a self-review of my own code.
  • I have added tests that prove my fix is effective or that my feature works.
  • All new and existing tests passed locally with my changes.
  • This PR introduces no breaking changes (or they are clearly documented).

Deployment Notes

  • Requires a database migration/schema update.
  • Requires new environment variables to be set.
  • N/A

💬 Additional Notes

  • New model added for budget transactions and integrated into seed cleanup.
  • Seeded president account uses Google auth strategy, while test/local accounts use password-based login for manual testing.

Summary by CodeRabbit

Release Notes

New Features

  • Added comprehensive Budget Management system enabling budget allocation, expense recording, and refund processing
  • New dedicated Budget page displaying budget overview and transaction history with pagination
  • Budget navigation menu item added for applicable user roles
  • Role-based access controls for budget operations ensure appropriate permission levels

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 23, 2026

@gmarav05 is attempting to deploy a commit to the openlake's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 23, 2026

Walkthrough

A comprehensive budget management system is introduced, including a backend controller with transaction handling (allocateBudget, recordExpense, recordRefund), a BudgetTransaction model for persistence, API routes with role-based authorization, and a frontend React hook and components for budget overview, transaction history, and form-based operations.

Changes

Cohort / File(s) Summary
Backend: Budget Model & Schema
backend/models/schema.js
Introduces BudgetTransaction schema with transaction type, unit/event references, amount, and audit fields; includes compound indexes on unit and event for query optimization.
Backend: Budget Controller
backend/controllers/budgetController.js
Core business logic for allocateBudget, recordExpense, recordRefund operations; implements shared runBudgetTransaction function with validation, access control, and Mongoose session transactions; provides read endpoints for budget overview and transaction history with pagination.
Backend: Budget Routes & Integration
backend/routes/budget.js, backend/index.js, backend/seed.js
Defines budget API endpoints with middleware-based role authorization; mounts routes at /api/budget; updates seed script to clear BudgetTransaction documents during data reset.
Frontend: Budget Hook & Page Setup
frontend/src/hooks/useBudgetManagement.js, frontend/src/pages/budgetPage.jsx, frontend/src/config/dashboardComponents.js
Custom hook for state management (units, budget data, transactions, pagination, error handling); page wrapper component; registers budget route in dashboard configuration.
Frontend: Budget UI Component & Navigation
frontend/src/Components/Budget/BudgetManagement.jsx, frontend/src/config/navbarConfig.js
React component for budget UI with unit selection, transaction form, history table, and pagination; adds "Budget" navigation item (Wallet icon) to applicable navbar configs.

Sequence Diagram(s)

sequenceDiagram
    participant User as User (Browser)
    participant UI as BudgetManagement
    participant Hook as useBudgetManagement
    participant API as Backend API
    participant Auth as Auth Middleware
    participant Controller as budgetController
    participant DB as Database

    User->>UI: Select unit & enter amount
    UI->>Hook: submitTransaction(endpoint, amount, ...)
    Hook->>API: POST /api/budget/{allocate|spend|refund}<br/>(unitId, amount, eventId, description)
    API->>Auth: Check authentication & role
    Auth-->>API: ✓ Authorized
    API->>Controller: allocateBudget/recordExpense/recordRefund
    
    Controller->>DB: Start Mongoose session
    Controller->>DB: Load OrganizationalUnit
    Controller->>DB: Validate access (unit, event)
    Controller->>DB: Update unit.budget_info<br/>(validate remaining balance)
    
    alt eventId provided
        Controller->>DB: Load Event (validate belongs to unit)
        Controller->>DB: Update event.budget<br/>(allocated/spent adjustments)
    end
    
    Controller->>DB: Create BudgetTransaction record<br/>(capture unit, amount, type, creator)
    Controller->>DB: Commit transaction
    
    DB-->>Controller: Transaction created + budget overview
    Controller-->>API: HTTP 200 + response
    API-->>Hook: Success response
    Hook->>API: GET /api/budget/overview & transactions
    API-->>Hook: Updated budget data
    Hook->>UI: Update state (budget, transactions)
    UI-->>User: Display updated budget & history
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

The changes span heterogeneous components (schema, controller with transaction logic, routes with authorization, React hook with async state management, and UI components) across backend and frontend. Review requires verification of transaction validation logic, access control enforcement, Mongoose session handling, state management patterns, API integration, and data flow consistency.

Poem

🐰 A budget tale, from coin to cart,
With transaction hops and database art,
Allocate, spend, refund with care,
Role-based guardians watching there,
Frontend and backend, a harmonious pair! 💰

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main changes: implementing a budget management system with both backend APIs and frontend UI components.
Description check ✅ Passed The PR description covers all major template sections including related issues, changes introduced, problem/solution/impact, testing performed, documentation updates, and deployment notes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
backend/controllers/budgetController.js (2)

78-92: Consider caching coordinator unit lookup to avoid duplicate queries.

For CLUB_COORDINATOR role, both getAccessibleUnitFilter (line 80) and assertUnitAccess (line 114) perform the same database query to find the coordinator's unit by email. When both functions are called in sequence (e.g., in getBudgetTransactions without a specific unitId), this results in redundant database calls.

♻️ Possible optimization

Consider caching the coordinator's unit ID on the user object during authentication middleware, or passing the already-fetched unit through the call chain to avoid the duplicate lookup.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/controllers/budgetController.js` around lines 78 - 92, The
coordinator unit lookup in getAccessibleUnitFilter and assertUnitAccess (for
role === ROLES.CLUB_COORDINATOR using normalizeEmail and
OrganizationalUnit.findOne(...).select("_id")) is duplicated; modify the flow to
fetch the coordinator's unit once and reuse it by either attaching the resolved
unitId/_id to the user object in the authentication middleware (e.g.,
user.coordUnitId or user.organizationalUnit) or by changing the signatures of
getAccessibleUnitFilter and assertUnitAccess to accept an optional coordUnitId
parameter so callers (like getBudgetTransactions) can pass the already-fetched
unit instead of triggering a second OrganizationalUnit.findOne call.

390-399: Ensure appropriate indexes exist on BudgetTransaction collection.

The query sorts by created_at and filters by unit_id (and optionally event_id, type). For efficient pagination at scale, consider adding a compound index:

// In your migration or schema definition
BudgetTransactionSchema.index({ unit_id: 1, created_at: -1 });

This is especially relevant when the $in filter (line 371) contains multiple unit IDs.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/controllers/budgetController.js` around lines 390 - 399, The query
using BudgetTransaction.find(...) sorts by created_at and filters by unit_id
(and optionally event_id, type), so add a compound index on the
BudgetTransaction schema/migration to support the filter+sort pattern — e.g.,
create an index like BudgetTransactionSchema.index({ unit_id: 1, created_at: -1
}) (and extend to include event_id or type if those filters are commonly used)
to ensure efficient pagination for BudgetTransaction.find,
BudgetTransaction.countDocuments and the path that builds the $in filter for
unit_id.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/models/schema.js`:
- Around line 648-654: Add the missing import for the UUID generator used by the
schema: require or import the v4 function as uuidv4 and ensure it's defined
before budgetTransactionSchema so the default () => `BGT_${uuidv4()}` can call
it; e.g. add const { v4: uuidv4 } = require('uuid') (or equivalent ES import) at
the top of the file and remove any duplicate/conflicting definitions if present.

In `@backend/routes/budget.js`:
- Line 27: The route uses an undefined constant ROLE_GROUPS.REVIEW_ROLES in the
authorizeRole middleware which causes all requests to be denied; fix it by
replacing ROLE_GROUPS.REVIEW_ROLES with the actual existing key from the
ROLE_GROUPS enum/object (or add REVIEW_ROLES to ROLE_GROUPS if that was
intended), e.g., change the middleware argument in the /allocate route to the
correct symbol (authorizeRole(ROLE_GROUPS.<EXISTING_REVIEW_GROUP>)) or update
the ROLE_GROUPS definition to include REVIEW_ROLES so authorizeRole receives a
valid value.

---

Nitpick comments:
In `@backend/controllers/budgetController.js`:
- Around line 78-92: The coordinator unit lookup in getAccessibleUnitFilter and
assertUnitAccess (for role === ROLES.CLUB_COORDINATOR using normalizeEmail and
OrganizationalUnit.findOne(...).select("_id")) is duplicated; modify the flow to
fetch the coordinator's unit once and reuse it by either attaching the resolved
unitId/_id to the user object in the authentication middleware (e.g.,
user.coordUnitId or user.organizationalUnit) or by changing the signatures of
getAccessibleUnitFilter and assertUnitAccess to accept an optional coordUnitId
parameter so callers (like getBudgetTransactions) can pass the already-fetched
unit instead of triggering a second OrganizationalUnit.findOne call.
- Around line 390-399: The query using BudgetTransaction.find(...) sorts by
created_at and filters by unit_id (and optionally event_id, type), so add a
compound index on the BudgetTransaction schema/migration to support the
filter+sort pattern — e.g., create an index like BudgetTransactionSchema.index({
unit_id: 1, created_at: -1 }) (and extend to include event_id or type if those
filters are commonly used) to ensure efficient pagination for
BudgetTransaction.find, BudgetTransaction.countDocuments and the path that
builds the $in filter for unit_id.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1dcca6d2-d31f-432b-906d-ed759f5e00de

📥 Commits

Reviewing files that changed from the base of the PR and between 060ea48 and e463476.

📒 Files selected for processing (10)
  • backend/controllers/budgetController.js
  • backend/index.js
  • backend/models/schema.js
  • backend/routes/budget.js
  • backend/seed.js
  • frontend/src/Components/Budget/BudgetManagement.jsx
  • frontend/src/config/dashboardComponents.js
  • frontend/src/config/navbarConfig.js
  • frontend/src/hooks/useBudgetManagement.js
  • frontend/src/pages/budgetPage.jsx

@harshitap1305
Copy link
Copy Markdown
Member

harshitap1305 commented Mar 27, 2026

@gmarav05, Currently, eventId is set up as a free-text input (input type="text"). Because the backend strictly expects a valid MongoDB ObjectId, users typing an event name (like "TechFest 2024") or making a typo will immediately get a 400 Invalid event ID format error.
Suggested Fix:
Instead of a text input, let's fetch the list of events associated with the selectedUnitId and use a dropdown (exactly like how you handled the Organizational Units).

@harshitap1305
Copy link
Copy Markdown
Member

@gmarav05Also fix this: Currently, if a budget transaction fails, two banners appear at the same time (a red error banner from the hook, and a blue status banner from the component.
Suggested Fix:

  • Fix the double-banner: Remove the setError(message) line inside the hook's submitTransaction catch block. Let the component handle the feedback entirely.
  • Switch to Toasts: Instead of using static text banners at the top of the page, let's use a short Toast notification for success/error messages. It's much cleaner, saves screen space, and the user won't miss it if they have scrolled down to the bottom of the form.

@harshitap1305
Copy link
Copy Markdown
Member

@gmarav05 also pls fix/update the below things:

  1. Prevent API Race Conditions (Pagination): If a user clicks "Next Page" really fast, an older request might resolve after a newer one. You can easily fix this by adding a cleanup flag in your useEffect:
useEffect(() => {
  let ignore = false;
  const loadData = async () => { /* fetch data */ if (!ignore) setBudget(...); };
  loadData();
  return () => { ignore = true; }; // Cleanup ignores stale responses
}, [selectedUnitId, fetchBudgetData]);
  1. Button Loading States: You disabled the submit buttons during isSubmitting, but let's also change the text so users know what's happening (e.g., {isSubmitting ? "Processing..." : "Record Expense"}).
  2. Improve the overall UI/UX

@harshitap1305 harshitap1305 self-requested a review March 27, 2026 19:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants