feat: Goal-based Savings Tracking & Milestones (#133)#465
Open
qiridigital wants to merge 1 commit intorohitdash08:mainfrom
Open
feat: Goal-based Savings Tracking & Milestones (#133)#465qiridigital wants to merge 1 commit intorohitdash08:mainfrom
qiridigital wants to merge 1 commit intorohitdash08:mainfrom
Conversation
- Models: SavingsGoal and SavingsContribution with progress tracking - Backend: routes/goals.py with full CRUD + contribute endpoint - GET /goals - list all user goals - POST /goals - create goal with name, target_amount, deadline - GET /goals/:id - goal detail with contributions history - PATCH /goals/:id - update goal properties - DELETE /goals/:id - remove goal and contributions - POST /goals/:id/contribute - add contribution, auto-detect milestone - Schema: savings_goals and savings_contributions tables with indexes - Frontend: api/goals.ts typed client + pages/Goals.tsx with: - Create goal form - Active goals grid with progress bars - Inline contribute form - Milestone celebration toast on goal achievement - Achieved goals section - Nav: added Goals link - Tests: test_goals.py with 13 tests covering CRUD, validation, contributions, milestone detection, listing, and auth Closes rohitdash08#133
There was a problem hiding this comment.
Pull request overview
Adds a new “Savings Goals” feature across backend and frontend, including CRUD endpoints, contribution tracking, and a UI page to create/manage goals.
Changes:
- Backend: introduce
SavingsGoal/SavingsContributionmodels + DB schema, and add/goalsCRUD +/goals/{id}/contributeendpoints. - Frontend: add Goals page, API client helpers, route, and navbar entry.
- Tests: add an integration-style test suite covering core goals flows.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 16 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/backend/tests/test_goals.py | Adds tests for listing/creating/updating/deleting goals and contributing. |
| packages/backend/app/routes/goals.py | Implements goals CRUD and contribution endpoints. |
| packages/backend/app/routes/init.py | Registers the goals blueprint under /goals. |
| packages/backend/app/models.py | Adds SQLAlchemy models for goals and contributions. |
| packages/backend/app/db/schema.sql | Adds tables and indexes for goals + contributions. |
| app/src/pages/Goals.tsx | New UI page to create goals, contribute, and view progress. |
| app/src/components/layout/Navbar.tsx | Adds “Goals” navigation link. |
| app/src/App.tsx | Adds protected /goals route. |
| app/src/api/goals.ts | Adds frontend API wrapper/types for goals endpoints. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def create_goal(): | ||
| """Create a new savings goal.""" | ||
| uid = int(get_jwt_identity()) | ||
| data = request.get_json(force=True) |
Comment on lines
+54
to
+56
| target = data.get("target_amount") | ||
| if target is None or float(target) <= 0: | ||
| return jsonify(error="target_amount must be positive"), 400 |
Comment on lines
+62
to
+64
| currency=data.get("currency", "INR"), | ||
| deadline=data.get("deadline"), | ||
| ) |
| if not goal: | ||
| return jsonify(error="goal not found"), 404 | ||
|
|
||
| data = request.get_json(force=True) |
|
|
||
| data = request.get_json(force=True) | ||
| if "name" in data: | ||
| goal.name = data["name"] |
Comment on lines
+194
to
+196
| <span>{formatMoney(g.current_amount)}</span> | ||
| <span className="text-muted-foreground"> | ||
| of {formatMoney(g.target_amount)} |
Comment on lines
+58
to
+62
| setCreating(true); | ||
| try { | ||
| await createGoal({ | ||
| name: name.trim(), | ||
| target_amount: parseFloat(target), |
Comment on lines
+91
to
+93
| try { | ||
| const result = await contribute(goalId, { | ||
| amount: parseFloat(contributeAmount), |
Comment on lines
+138
to
+150
| @bp.post("/<int:goal_id>/contribute") | ||
| @jwt_required() | ||
| def contribute(goal_id: int): | ||
| """Add a contribution to a savings goal.""" | ||
| uid = int(get_jwt_identity()) | ||
| goal = SavingsGoal.query.filter_by(id=goal_id, user_id=uid).first() | ||
| if not goal: | ||
| return jsonify(error="goal not found"), 404 | ||
|
|
||
| data = request.get_json(force=True) | ||
| amount = data.get("amount") | ||
| if amount is None or float(amount) <= 0: | ||
| return jsonify(error="amount must be positive"), 400 |
Comment on lines
+61
to
+63
| target_amount=float(target), | ||
| currency=data.get("currency", "INR"), | ||
| deadline=data.get("deadline"), |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements Goal-based Savings Tracking & Milestones as described in #133.
Acceptance Criteria
Database
savings_goalstable: user_id, name, target_amount, current_amount, currency, deadline, achievedsavings_contributionstable: goal_id, amount, notesBackend (routes/goals.py)
/goals/goals/goals/:id/goals/:id/goals/:id/goals/:id/contributemilestone_reached: trueis returnedFrontend
/goals(protected), nav link in NavbarTests (test_goals.py) - 13 tests
/claim #133