Skip to content

Feature/budget management upstream#251

Merged
Ashish-Kumar-Dash merged 4 commits into
mainfrom
feature/budget-management-upstream
May 20, 2026
Merged

Feature/budget management upstream#251
Ashish-Kumar-Dash merged 4 commits into
mainfrom
feature/budget-management-upstream

Conversation

@KotapatiSaiMounika
Copy link
Copy Markdown
Collaborator

@KotapatiSaiMounika KotapatiSaiMounika commented May 20, 2026

Summary

This PR contains the original budget management implementation from PR #240 along with stabilization fixes, workflow validation, and UI/backend improvements.

Fixes and Improvements

  • fixed authorization issue in budget routes
  • fixed missing uuidv4 import causing transaction failures
  • removed duplicate error handling
  • added proper budget route registration
  • improved event selection UX using dropdown-based filtering
  • added unit-based event filtering
  • improved transaction validation and error handling

Tested Locally

  • budget allocation flow
  • expense recording flow
  • refund workflow
  • transaction history updates
  • role-based permissions
  • insufficient budget validation
  • event filtering by organizational unit

Additional Notes

This PR acts as a stabilization/follow-up PR for #240 and includes both the original contributor changes and maintainer fixes/review updates.

Summary by CodeRabbit

  • New Features
    • Added Budget Management system accessible from new navigation menu item
    • Allocate budgets to organizational units
    • Record expenses and refunds against unit budgets
    • View budget overview showing allocated, spent, and remaining amounts per unit
    • Browse paginated transaction history with filtering capabilities
    • Track budget changes over time with detailed transaction records

Review Change Stack

@vercel
Copy link
Copy Markdown

vercel Bot commented May 20, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
student-database-cosa Ready Ready Preview, Comment May 20, 2026 1:43pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

Warning

Rate limit exceeded

@KotapatiSaiMounika has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 10 minutes and 7 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 62494323-4286-469c-93dc-616b87f16327

📥 Commits

Reviewing files that changed from the base of the PR and between fe279d2 and e5bcca6.

📒 Files selected for processing (5)
  • backend/index.js
  • backend/models/schema.js
  • backend/seed.js
  • frontend/src/config/dashboardComponents.js
  • frontend/src/config/navbarConfig.js

Walkthrough

This PR adds a complete budget management system for organizational units. It introduces a BudgetTransaction data model with compound indexing, backend business logic for allocating, spending, and refunding budgets with role-based access control, REST API endpoints for transaction submission and history retrieval, and a React frontend featuring unit selection, transaction forms, and paginated history display.

Changes

Budget Management Feature

Layer / File(s) Summary
Backend data model and persistence
backend/models/schema.js, backend/seed.js
BudgetTransaction schema with UUID transaction IDs, unit/event relationships, and compound indexes on (unit_id, created_at) and (event_id, created_at). Seed data updated to clear transactions alongside other collections.
Budget transaction business logic and access control
backend/controllers/budgetController.js
Transaction types (ALLOCATION, EXPENSE, REFUND) with role-to-category mapping. Utility helpers for error creation, amount validation, and budget summary formatting. Role-based unit visibility filtering (PRESIDENT unrestricted; GENSEC and CLUB_COORDINATOR by category/contact). Event budget state transitions for each transaction type with spent-budget validation for refunds.
Backend API endpoints and routing
backend/controllers/budgetController.js, backend/routes/budget.js, backend/index.js
getBudgetOverview returns single or multi-unit budgets with role filtering. getBudgetTransactions supports pagination and optional filters (unitId, eventId, type). Three wrapper handlers (allocateBudget, recordExpense, recordRefund) delegate to core transaction logic. Routes mounted at /api/budget with authentication and admin role guards.
Frontend budget state and data fetching hook
frontend/src/hooks/useBudgetManagement.js
useBudgetManagement hook manages unit loading, selection, budget overview, and paginated transaction fetching from /api/budget endpoints. Provides actions to submit transactions, change pages, and refresh. Handles loading and error states for both units and transaction data.
Frontend BudgetManagement UI component
frontend/src/Components/Budget/BudgetManagement.jsx
Renders unit selector, budget summary cards (allocated/spent/remaining), transaction form with role-based button visibility (allocate for authorized roles, spend/refund for all), and transaction history table with pagination. Includes event filtering by unit and formatted currency/datetime display.
Frontend page routing and navigation integration
frontend/src/pages/budgetPage.jsx, frontend/src/config/dashboardComponents.js, frontend/src/config/navbarConfig.js
BudgetPage wrapper component composes Layout with header. Registered in DashboardComponents. Wallet icon added to navigation; "Budget" menu item added to GENSEC_COMMON_NAV and role-specific arrays (PRESIDENT, CLUB_COORDINATOR).

Sequence Diagram

sequenceDiagram
  participant User as User (Role)
  participant UI as BudgetManagement
  participant Hook as useBudgetManagement
  participant API as Backend API
  participant DB as Database
  
  User->>UI: Select unit, enter amount
  UI->>Hook: submitTransaction(endpoint, {unitId, amount, eventId, description})
  Hook->>API: POST /api/budget/allocate|spend|refund
  API->>DB: Validate unit, event, update budget_info
  API->>DB: Create BudgetTransaction record
  DB-->>API: Return transaction with balance_after
  API-->>Hook: Transaction + updated budget
  Hook->>Hook: Refresh page 1 transactions
  Hook-->>UI: Updated budget, transactions, status
  UI-->>User: Display success/error, update summary
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • harshitap1305

Poem

🐰 A budget feature hops along the trail,
With units selected and transactions that sail,
From allocate, spend, to refund so fair,
The ledger now tracks every coin with care.
Role-based access guards the way,
As budgets grow wiser, day by day!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Feature/budget management upstream' is partially related to the changeset, referring to a real aspect (budget management feature) but is vague and uses non-standard phrasing ('upstream') that doesn't clearly convey the main change. Refine the title to be more specific and descriptive, such as 'Add budget management feature with allocation, expense tracking, and reporting' to better communicate the core functionality introduced.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The PR description provides a good summary of changes and tested scenarios but does not follow the repository's required template structure, missing key sections like Related Issue, explicit Changes Introduced breakdown, and proper Testing checkboxes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/budget-management-upstream

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: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/controllers/budgetController.js`:
- Around line 146-151: The EXPENSE branch updates event.budget.spent without
checking that spent + amount <= allocated, allowing event-level overspend;
before assigning event.budget in the TRANSACTION_TYPES.EXPENSE branch (where
event.budget = Object.assign(... existingBudget ...)), add a validation that
computes newSpent = spent + amount and if newSpent > allocated return/throw the
same insufficient-budget error used at unit-level (or call the same helper),
otherwise set event.budget with spent: newSpent; reference
TRANSACTION_TYPES.EXPENSE, event.budget, existingBudget, allocated, spent, and
amount to locate the exact spot to add this check.
- Around line 340-341: The pagination parsing currently converts invalid strings
to NaN causing runtime errors; update the handler that defines page and limit
(the code reading req.query.page and req.query.limit and assigning to
page/limit) to robustly validate inputs: parse as integers (e.g., parseInt),
check Number.isFinite/Number.isInteger and that page >= 1 and limit between 1
and 100; if validation fails, return a 400 Bad Request (res.status(400).json({
error: 'Invalid pagination parameters' })) instead of using Math.max/Math.min on
NaN, otherwise use the validated parsed values as page and limit for
.skip()/.limit().

In `@frontend/src/Components/Budget/BudgetManagement.jsx`:
- Around line 61-84: The fetchEvents useEffect can have out-of-order responses
overwrite state when selectedUnitId changes; modify the effect to ignore stale
responses by capturing the current selectedUnitId (e.g., const currentId =
selectedUnitId) or using an AbortController before calling api.get inside
fetchEvents, and only call setEvents(filteredEvents) if currentId ===
selectedUnitId (or if the fetch was not aborted). Also ensure any created
AbortController is cleaned up in the effect's return handler to cancel in-flight
requests when selectedUnitId changes. Refer to the fetchEvents function and the
useEffect that depends on selectedUnitId to implement this guard.

In `@frontend/src/hooks/useBudgetManagement.js`:
- Around line 125-126: The normalization for nextPage in useBudgetManagement.js
can produce NaN because Number(nextPage || 1) will be NaN for non-numeric
strings; change the logic that computes normalized (the normalized variable) to
explicitly parse and validate nextPage (e.g., use Number.parseInt or
Number(nextPage) then check Number.isFinite/Number.isInteger or isNaN) and
default to 1 when parsing fails, then call fetchBudgetData(selectedUnitId,
normalized); ensure the code references nextPage, normalized, and
fetchBudgetData so invalid inputs never propagate to the API.
- Around line 74-76: In useBudgetManagement's fetch error path (the catch block
that currently calls setError), also clear any previous budget/unit state so
stale data doesn't stay visible after a failed fetch — e.g., call the
budget-related setters (replace with your actual names, e.g., setBudget(null),
setUnitData(null), setBudgetItems([]) or similar) before calling
setError(err?.response?.data?.message || "Failed to load budget data"); keep the
finally block as-is.
🪄 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: 29207a15-6c9e-4d49-8da0-db16c1a617c9

📥 Commits

Reviewing files that changed from the base of the PR and between ad4069a and fe279d2.

📒 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

Comment on lines +146 to +151
if (type === TRANSACTION_TYPES.EXPENSE) {
event.budget = Object.assign({}, existingBudget, {
allocated,
spent: spent + amount,
});
return;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add event-level insufficient-budget validation for expenses.

EXPENSE updates event.budget.spent without enforcing spent + amount <= allocated, so event budgets can be overspent even when unit-level budget passes.

Suggested fix
   if (type === TRANSACTION_TYPES.EXPENSE) {
+    if (spent + amount > allocated) {
+      throw createHttpError(400, "Insufficient remaining event budget");
+    }
     event.budget = Object.assign({}, existingBudget, {
       allocated,
       spent: spent + amount,
     });
     return;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (type === TRANSACTION_TYPES.EXPENSE) {
event.budget = Object.assign({}, existingBudget, {
allocated,
spent: spent + amount,
});
return;
if (type === TRANSACTION_TYPES.EXPENSE) {
if (spent + amount > allocated) {
throw createHttpError(400, "Insufficient remaining event budget");
}
event.budget = Object.assign({}, existingBudget, {
allocated,
spent: spent + amount,
});
return;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/controllers/budgetController.js` around lines 146 - 151, The EXPENSE
branch updates event.budget.spent without checking that spent + amount <=
allocated, allowing event-level overspend; before assigning event.budget in the
TRANSACTION_TYPES.EXPENSE branch (where event.budget = Object.assign(...
existingBudget ...)), add a validation that computes newSpent = spent + amount
and if newSpent > allocated return/throw the same insufficient-budget error used
at unit-level (or call the same helper), otherwise set event.budget with spent:
newSpent; reference TRANSACTION_TYPES.EXPENSE, event.budget, existingBudget,
allocated, spent, and amount to locate the exact spot to add this check.

Comment on lines +340 to +341
const page = Math.max(1, Number(req.query.page || 1));
const limit = Math.min(100, Math.max(1, Number(req.query.limit || 20)));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify current pagination parsing pattern and absence/presence of integer guards.
rg -n -C2 'const page = Math.max\(1, Number\(req\.query\.page \|\| 1\)\);|const limit = Math.min\(100, Math.max\(1, Number\(req\.query\.limit \|\| 20\)\)\);|Number\.parseInt\(req\.query\.(page|limit)|Number\.isInteger\(.*(page|limit)' backend/controllers/budgetController.js

Repository: OpenLake/Student_Database_COSA

Length of output: 308


🏁 Script executed:

#!/bin/bash
# Search for other pagination/query parameter parsing patterns in the codebase
rg -n 'req\.query\.(page|limit|skip|offset)' backend/controllers/ --type js -A1

Repository: OpenLake/Student_Database_COSA

Length of output: 343


🏁 Script executed:

#!/bin/bash
# Check if there's any error handling or validation middleware
rg -n 'try|catch|validation|validator|middleware' backend/controllers/budgetController.js | head -20

Repository: OpenLake/Student_Database_COSA

Length of output: 182


🏁 Script executed:

#!/bin/bash
# Look for how .skip() and .limit() are used with these variables
rg -n '\.skip\(|\.limit\(' backend/controllers/budgetController.js -B2 -A2

Repository: OpenLake/Student_Database_COSA

Length of output: 319


🏁 Script executed:

#!/bin/bash
# Check what happens in the catch block for error handling
sed -n '337,410p' backend/controllers/budgetController.js

Repository: OpenLake/Student_Database_COSA

Length of output: 2412


🏁 Script executed:

#!/bin/bash
# Test Math.max behavior with NaN
node <<'EOF'
console.log("Math.max(1, NaN):", Math.max(1, NaN));
console.log("Math.min(100, Math.max(1, NaN)):", Math.min(100, Math.max(1, NaN)));
console.log("Number('invalid'):", Number('invalid'));
console.log("(NaN - 1) * 20:", (NaN - 1) * 20);
console.log("Calculation when Number('abc') is used:");
const page = Math.max(1, Number('abc' || 1));
const limit = Math.min(100, Math.max(1, Number('def' || 20)));
console.log("page:", page, "limit:", limit);
console.log("(page - 1) * limit:", (page - 1) * limit);
EOF

Repository: OpenLake/Student_Database_COSA

Length of output: 261


🏁 Script executed:

#!/bin/bash
# Check the complete error handling in the catch block
sed -n '405,420p' backend/controllers/budgetController.js

Repository: OpenLake/Student_Database_COSA

Length of output: 517


🏁 Script executed:

#!/bin/bash
# Search for other instances of pagination/query parameter parsing to see if this is widespread
rg -n 'Number\(req\.query' backend/controllers/ --type js

Repository: OpenLake/Student_Database_COSA

Length of output: 298


🏁 Script executed:

#!/bin/bash
# Look for any input validation utilities or middleware
find backend -type f -name "*validation*" -o -name "*validator*" -o -name "*middleware*" | head -10

Repository: OpenLake/Student_Database_COSA

Length of output: 94


🏁 Script executed:

#!/bin/bash
# Check what's in the middlewares directory
ls -la backend/middlewares/

Repository: OpenLake/Student_Database_COSA

Length of output: 446


🏁 Script executed:

#!/bin/bash
# Search for any pagination or query validation middleware
rg -n 'pagination|page|limit|query.*validat' backend/middlewares/ --type js

Repository: OpenLake/Student_Database_COSA

Length of output: 160


Harden pagination parsing against non-numeric query inputs.

Invalid page/limit query values become NaN due to Math.max(1, Number('invalid')) returning NaN rather than a fallback—this breaks .skip() and .limit(), surfacing as a 500 error instead of a proper 400 Bad Request.

Suggested fix
-    const page = Math.max(1, Number(req.query.page || 1));
-    const limit = Math.min(100, Math.max(1, Number(req.query.limit || 20)));
+    const pageInput = Number.parseInt(req.query.page, 10);
+    const limitInput = Number.parseInt(req.query.limit, 10);
+    const page = Number.isInteger(pageInput) && pageInput > 0 ? pageInput : 1;
+    const limitBase =
+      Number.isInteger(limitInput) && limitInput > 0 ? limitInput : 20;
+    const limit = Math.min(100, limitBase);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/controllers/budgetController.js` around lines 340 - 341, The
pagination parsing currently converts invalid strings to NaN causing runtime
errors; update the handler that defines page and limit (the code reading
req.query.page and req.query.limit and assigning to page/limit) to robustly
validate inputs: parse as integers (e.g., parseInt), check
Number.isFinite/Number.isInteger and that page >= 1 and limit between 1 and 100;
if validation fails, return a 400 Bad Request (res.status(400).json({ error:
'Invalid pagination parameters' })) instead of using Math.max/Math.min on NaN,
otherwise use the validated parsed values as page and limit for
.skip()/.limit().

Comment on lines +61 to +84
useEffect(() => {
const fetchEvents = async () => {
if (!selectedUnitId) {
setEvents([]);
return;
}

try {
const response = await api.get("/api/events/events");
const filteredEvents = (response.data || []).filter(
(event) =>
event.organizing_unit_id &&
event.organizing_unit_id._id === selectedUnitId,
);

setEvents(filteredEvents);
} catch (error) {
console.error("Failed to fetch events", error);
setEvents([]);
}
};

fetchEvents();
}, [selectedUnitId]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prevent out-of-order event fetches from overwriting current unit events.

Lines 62–76 can race when users switch units quickly; an older response may overwrite events for the currently selected unit.

Proposed fix
   useEffect(() => {
+  let isActive = true;
   const fetchEvents = async () => {
     if (!selectedUnitId) {
-      setEvents([]);
+      if (isActive) setEvents([]);
       return;
     }

     try {
       const response = await api.get("/api/events/events");
       const filteredEvents = (response.data || []).filter(
         (event) =>
          event.organizing_unit_id &&
          event.organizing_unit_id._id === selectedUnitId,
 );
-
-        setEvents(filteredEvents);
+      if (isActive) setEvents(filteredEvents);
     } catch (error) {
       console.error("Failed to fetch events", error);
-      setEvents([]);
+      if (isActive) setEvents([]);
     }
   };

   fetchEvents();
+  return () => {
+    isActive = false;
+  };
 }, [selectedUnitId]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
const fetchEvents = async () => {
if (!selectedUnitId) {
setEvents([]);
return;
}
try {
const response = await api.get("/api/events/events");
const filteredEvents = (response.data || []).filter(
(event) =>
event.organizing_unit_id &&
event.organizing_unit_id._id === selectedUnitId,
);
setEvents(filteredEvents);
} catch (error) {
console.error("Failed to fetch events", error);
setEvents([]);
}
};
fetchEvents();
}, [selectedUnitId]);
useEffect(() => {
let isActive = true;
const fetchEvents = async () => {
if (!selectedUnitId) {
if (isActive) setEvents([]);
return;
}
try {
const response = await api.get("/api/events/events");
const filteredEvents = (response.data || []).filter(
(event) =>
event.organizing_unit_id &&
event.organizing_unit_id._id === selectedUnitId,
);
if (isActive) setEvents(filteredEvents);
} catch (error) {
console.error("Failed to fetch events", error);
if (isActive) setEvents([]);
}
};
fetchEvents();
return () => {
isActive = false;
};
}, [selectedUnitId]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/Components/Budget/BudgetManagement.jsx` around lines 61 - 84,
The fetchEvents useEffect can have out-of-order responses overwrite state when
selectedUnitId changes; modify the effect to ignore stale responses by capturing
the current selectedUnitId (e.g., const currentId = selectedUnitId) or using an
AbortController before calling api.get inside fetchEvents, and only call
setEvents(filteredEvents) if currentId === selectedUnitId (or if the fetch was
not aborted). Also ensure any created AbortController is cleaned up in the
effect's return handler to cancel in-flight requests when selectedUnitId
changes. Refer to the fetchEvents function and the useEffect that depends on
selectedUnitId to implement this guard.

Comment on lines +74 to +76
} catch (err) {
setError(err?.response?.data?.message || "Failed to load budget data");
} finally {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clear stale budget state on fetch failure.

On Line 74, the error path only sets error, so previous unit data can remain visible after a failed fetch for the newly selected unit.

Proposed fix
     } catch (err) {
       setError(err?.response?.data?.message || "Failed to load budget data");
+      setBudget(null);
+      setTransactions([]);
+      setPagination({ total: 0, page: 1, limit: DEFAULT_PAGE_SIZE });
     } finally {
       setIsLoadingData(false);
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (err) {
setError(err?.response?.data?.message || "Failed to load budget data");
} finally {
} catch (err) {
setError(err?.response?.data?.message || "Failed to load budget data");
setBudget(null);
setTransactions([]);
setPagination({ total: 0, page: 1, limit: DEFAULT_PAGE_SIZE });
} finally {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/hooks/useBudgetManagement.js` around lines 74 - 76, In
useBudgetManagement's fetch error path (the catch block that currently calls
setError), also clear any previous budget/unit state so stale data doesn't stay
visible after a failed fetch — e.g., call the budget-related setters (replace
with your actual names, e.g., setBudget(null), setUnitData(null),
setBudgetItems([]) or similar) before calling
setError(err?.response?.data?.message || "Failed to load budget data"); keep the
finally block as-is.

Comment on lines +125 to +126
const normalized = Math.max(1, Number(nextPage || 1));
await fetchBudgetData(selectedUnitId, normalized);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Harden page normalization against NaN.

Line 125 can produce NaN when nextPage is non-numeric, which then propagates to the API query.

Proposed fix
-      const normalized = Math.max(1, Number(nextPage || 1));
+      const parsed = Number(nextPage);
+      const normalized = Number.isFinite(parsed)
+        ? Math.max(1, Math.trunc(parsed))
+        : 1;
       await fetchBudgetData(selectedUnitId, normalized);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/hooks/useBudgetManagement.js` around lines 125 - 126, The
normalization for nextPage in useBudgetManagement.js can produce NaN because
Number(nextPage || 1) will be NaN for non-numeric strings; change the logic that
computes normalized (the normalized variable) to explicitly parse and validate
nextPage (e.g., use Number.parseInt or Number(nextPage) then check
Number.isFinite/Number.isInteger or isNaN) and default to 1 when parsing fails,
then call fetchBudgetData(selectedUnitId, normalized); ensure the code
references nextPage, normalized, and fetchBudgetData so invalid inputs never
propagate to the API.

@Ashish-Kumar-Dash
Copy link
Copy Markdown
Contributor

LGTM

@Ashish-Kumar-Dash Ashish-Kumar-Dash merged commit 6aec79e into main May 20, 2026
6 of 7 checks passed
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.

4 participants