aboutsummaryrefslogtreecommitdiffstats
path: root/backend/internal/api
diff options
context:
space:
mode:
authorLibravatarLibravatar Biswa Kalyan Bhuyan <biswa@surgot.in> 2025-04-27 23:02:42 +0530
committerLibravatarLibravatar Biswa Kalyan Bhuyan <biswa@surgot.in> 2025-04-27 23:02:42 +0530
commit538d933baef56d7ee76f78617b553d63713efa24 (patch)
tree3fcbc4208849dfa0e5dc8fe5761e103a3591c283 /backend/internal/api
parent3941d80ff120238b973451325b834ebd8377281e (diff)
downloadfinance-master.tar.gz
finance-master.tar.bz2
finance-master.zip
finance: feat: added the goal page with some improvements of uiHEADmaster
Diffstat (limited to 'backend/internal/api')
-rw-r--r--backend/internal/api/handlers/goal_handler.go128
-rw-r--r--backend/internal/api/v1/goals/goals.go26
2 files changed, 152 insertions, 2 deletions
diff --git a/backend/internal/api/handlers/goal_handler.go b/backend/internal/api/handlers/goal_handler.go
index 09bea74..efbe7f9 100644
--- a/backend/internal/api/handlers/goal_handler.go
+++ b/backend/internal/api/handlers/goal_handler.go
@@ -6,6 +6,7 @@ import (
"strconv"
"time"
+ "finance/backend/internal/core"
"finance/backend/internal/database"
"finance/backend/internal/models"
@@ -35,13 +36,21 @@ type UpdateGoalProgressInput struct {
CurrentAmount int64 `json:"currentAmount" binding:"required"`
}
+// LinkTransactionInput defines the structure for linking a transaction to a goal
+type LinkTransactionInput struct {
+ TransactionID uint `json:"transactionId" binding:"required"`
+}
+
// GoalHandler handles all goal-related operations in the API
type GoalHandler struct {
+ goalService *core.GoalService
}
// NewGoalHandler creates and returns a new GoalHandler instance
func NewGoalHandler() *GoalHandler {
- return &GoalHandler{}
+ return &GoalHandler{
+ goalService: core.NewGoalService(),
+ }
}
// GetGoals retrieves all goals for the authenticated user
@@ -270,3 +279,120 @@ func (h *GoalHandler) DeleteGoal(c *gin.Context) {
log.Printf("Goal ID %d deleted successfully for user %d", goalID, userID)
c.JSON(http.StatusOK, gin.H{"message": "Goal deleted successfully"})
}
+
+// GetGoalProgressDetails retrieves a goal with detailed progress information
+func (h *GoalHandler) GetGoalProgressDetails(c *gin.Context) {
+ userID := c.MustGet("userID").(uint)
+ goalID, err := strconv.ParseUint(c.Param("id"), 10, 32)
+ if err != nil {
+ log.Printf("Error parsing goal ID: %v", err)
+ c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid goal ID"})
+ return
+ }
+
+ progress, err := h.goalService.GetGoalProgress(userID, uint(goalID))
+ if err != nil {
+ log.Printf("Error fetching goal progress for ID %d, user %d: %v", goalID, userID, err)
+ c.JSON(http.StatusNotFound, gin.H{"error": "Goal not found"})
+ return
+ }
+
+ c.JSON(http.StatusOK, progress)
+}
+
+// GetAllGoalsProgressDetails retrieves all goals with enhanced progress details
+func (h *GoalHandler) GetAllGoalsProgressDetails(c *gin.Context) {
+ userID := c.MustGet("userID").(uint)
+
+ // Filter by status if provided
+ status := c.Query("status")
+
+ progress, err := h.goalService.GetAllGoalsProgress(userID, status)
+ if err != nil {
+ log.Printf("Error fetching all goals progress for user %d: %v", userID, err)
+ c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get goal progress"})
+ return
+ }
+
+ c.JSON(http.StatusOK, progress)
+}
+
+// LinkTransactionToGoal links a transaction to a specific goal
+func (h *GoalHandler) LinkTransactionToGoal(c *gin.Context) {
+ userID := c.MustGet("userID").(uint)
+ goalID, err := strconv.ParseUint(c.Param("id"), 10, 32)
+ if err != nil {
+ log.Printf("Error parsing goal ID: %v", err)
+ c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid goal ID"})
+ return
+ }
+
+ var input LinkTransactionInput
+ if err := c.ShouldBindJSON(&input); err != nil {
+ log.Printf("Error binding JSON for linking transaction: %v", err)
+ c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+ return
+ }
+
+ // Verify the goal belongs to the user
+ var goal models.Goal
+ if err := database.DB.Where("id = ? AND user_id = ?", goalID, userID).First(&goal).Error; err != nil {
+ log.Printf("Error fetching goal ID %d for user %d: %v", goalID, userID, err)
+ c.JSON(http.StatusNotFound, gin.H{"error": "Goal not found"})
+ return
+ }
+
+ // Verify the transaction belongs to the user
+ var transaction models.Transaction
+ if err := database.DB.Where("id = ? AND user_id = ?", input.TransactionID, userID).First(&transaction).Error; err != nil {
+ log.Printf("Error fetching transaction ID %d for user %d: %v", input.TransactionID, userID, err)
+ c.JSON(http.StatusNotFound, gin.H{"error": "Transaction not found"})
+ return
+ }
+
+ // Link the transaction to the goal
+ if err := h.goalService.LinkTransactionToGoal(input.TransactionID, uint(goalID)); err != nil {
+ log.Printf("Error linking transaction %d to goal %d: %v", input.TransactionID, goalID, err)
+ c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to link transaction to goal"})
+ return
+ }
+
+ log.Printf("Transaction ID %d successfully linked to goal ID %d for user %d", input.TransactionID, goalID, userID)
+ c.JSON(http.StatusOK, gin.H{"message": "Transaction linked to goal successfully"})
+}
+
+// RecalculateGoalProgress recalculates a goal's progress based on linked transactions
+func (h *GoalHandler) RecalculateGoalProgress(c *gin.Context) {
+ userID := c.MustGet("userID").(uint)
+ goalID, err := strconv.ParseUint(c.Param("id"), 10, 32)
+ if err != nil {
+ log.Printf("Error parsing goal ID: %v", err)
+ c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid goal ID"})
+ return
+ }
+
+ // Verify the goal belongs to the user
+ var goal models.Goal
+ if err := database.DB.Where("id = ? AND user_id = ?", goalID, userID).First(&goal).Error; err != nil {
+ log.Printf("Error fetching goal ID %d for user %d: %v", goalID, userID, err)
+ c.JSON(http.StatusNotFound, gin.H{"error": "Goal not found"})
+ return
+ }
+
+ // Recalculate the goal progress
+ if err := h.goalService.UpdateGoalFromTransactions(uint(goalID)); err != nil {
+ log.Printf("Error recalculating goal progress for ID %d: %v", goalID, err)
+ c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to recalculate goal progress"})
+ return
+ }
+
+ // Fetch the updated goal to return in response
+ if err := database.DB.First(&goal, goalID).Error; err != nil {
+ log.Printf("Error fetching updated goal after recalculation: %v", err)
+ c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch updated goal"})
+ return
+ }
+
+ log.Printf("Goal ID %d progress recalculated successfully for user %d", goalID, userID)
+ c.JSON(http.StatusOK, goal)
+}
diff --git a/backend/internal/api/v1/goals/goals.go b/backend/internal/api/v1/goals/goals.go
index 1d2cd6f..4f99468 100644
--- a/backend/internal/api/v1/goals/goals.go
+++ b/backend/internal/api/v1/goals/goals.go
@@ -1,7 +1,7 @@
package goals
import (
- "finance/backend/handlers"
+ "finance/backend/internal/api/handlers"
"github.com/gin-gonic/gin"
)
@@ -41,3 +41,27 @@ func UpdateGoalProgress() gin.HandlerFunc {
handler := handlers.NewGoalHandler()
return handler.UpdateGoalProgress
}
+
+// GetGoalProgressDetails returns goal with enhanced progress tracking details
+func GetGoalProgressDetails() gin.HandlerFunc {
+ handler := handlers.NewGoalHandler()
+ return handler.GetGoalProgressDetails
+}
+
+// GetAllGoalsProgressDetails returns all goals with enhanced progress tracking details
+func GetAllGoalsProgressDetails() gin.HandlerFunc {
+ handler := handlers.NewGoalHandler()
+ return handler.GetAllGoalsProgressDetails
+}
+
+// LinkTransactionToGoal links a transaction to a goal for progress tracking
+func LinkTransactionToGoal() gin.HandlerFunc {
+ handler := handlers.NewGoalHandler()
+ return handler.LinkTransactionToGoal
+}
+
+// RecalculateGoalProgress recalculates the progress of a goal based on its transactions
+func RecalculateGoalProgress() gin.HandlerFunc {
+ handler := handlers.NewGoalHandler()
+ return handler.RecalculateGoalProgress
+}