Skip to content

feat: Goal-based Savings Tracking & Milestones (#133)#465

Open
qiridigital wants to merge 1 commit intorohitdash08:mainfrom
qiridigital:feat/savings-goals
Open

feat: Goal-based Savings Tracking & Milestones (#133)#465
qiridigital wants to merge 1 commit intorohitdash08:mainfrom
qiridigital:feat/savings-goals

Conversation

@qiridigital
Copy link

Summary

Implements Goal-based Savings Tracking & Milestones as described in #133.

Acceptance Criteria

  • Production ready implementation
  • Includes tests
  • Documentation updated (schema.sql)

Database

  • savings_goals table: user_id, name, target_amount, current_amount, currency, deadline, achieved
  • savings_contributions table: goal_id, amount, notes
  • Indexes on user_id and goal_id for query performance

Backend (routes/goals.py)

Endpoint Method Description
/goals GET List all goals for authenticated user
/goals POST Create a new savings goal
/goals/:id GET Get goal detail with contribution history
/goals/:id PATCH Update goal (name, target, deadline)
/goals/:id DELETE Delete goal and all contributions
/goals/:id/contribute POST Add a contribution; auto-detects milestone achievement
  • All endpoints require JWT authentication
  • Validation on name (required), target_amount (positive), contribution amount (positive)
  • Progress percentage auto-calculated
  • Milestone detection: when current_amount >= target_amount, goal is marked as achieved and milestone_reached: true is returned

Frontend

  • api/goals.ts - Typed API client for all 6 endpoints
  • pages/Goals.tsx - Full savings goals dashboard:
    • Create Goal form (name, target amount, deadline)
    • Active Goals grid with progress bars
    • Inline contribute form per goal
    • Milestone celebration toast when goal is achieved
    • Achieved Goals section with success styling
    • Empty state message
  • Route at /goals (protected), nav link in Navbar

Tests (test_goals.py) - 13 tests

  • CRUD: create, list, get detail, update, delete
  • Validation: empty name, negative amount, negative contribution
  • Goal not found (404)
  • Contributions: basic contribute, milestone achieved
  • Multiple goals listing
  • Auth required (401)

/claim #133

- 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
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 / SavingsContribution models + DB schema, and add /goals CRUD + /goals/{id}/contribute endpoints.
  • 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"),
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants