mirror of
https://github.com/Dvorinka/Trackeep.git
synced 2026-06-04 20:42:59 +00:00
first test
This commit is contained in:
@@ -0,0 +1,568 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/trackeep/backend/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// GoalsHabitsHandler handles goals and habits operations
|
||||
type GoalsHabitsHandler struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewGoalsHabitsHandler creates a new goals and habits handler
|
||||
func NewGoalsHabitsHandler(db *gorm.DB) *GoalsHabitsHandler {
|
||||
return &GoalsHabitsHandler{db: db}
|
||||
}
|
||||
|
||||
// Goal Handlers
|
||||
|
||||
// CreateGoal creates a new goal
|
||||
func (h *GoalsHabitsHandler) CreateGoal(c *gin.Context) {
|
||||
userID := c.GetUint("user_id")
|
||||
|
||||
var req struct {
|
||||
Title string `json:"title" binding:"required"`
|
||||
Description string `json:"description"`
|
||||
Category string `json:"category"`
|
||||
TargetValue float64 `json:"target_value"`
|
||||
Unit string `json:"unit"`
|
||||
Deadline time.Time `json:"deadline"`
|
||||
Priority string `json:"priority"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
goal := models.Goal{
|
||||
UserID: userID,
|
||||
Title: req.Title,
|
||||
Description: req.Description,
|
||||
Category: req.Category,
|
||||
TargetValue: req.TargetValue,
|
||||
CurrentValue: 0,
|
||||
Unit: req.Unit,
|
||||
Deadline: req.Deadline,
|
||||
Status: "active",
|
||||
Priority: req.Priority,
|
||||
Progress: 0,
|
||||
IsCompleted: false,
|
||||
}
|
||||
|
||||
if err := h.db.Create(&goal).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create goal"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, goal)
|
||||
}
|
||||
|
||||
// GetGoals retrieves user's goals
|
||||
func (h *GoalsHabitsHandler) GetGoals(c *gin.Context) {
|
||||
userID := c.GetUint("user_id")
|
||||
|
||||
category := c.Query("category")
|
||||
status := c.Query("status")
|
||||
priority := c.Query("priority")
|
||||
limit := 20
|
||||
if l := c.Query("limit"); l != "" {
|
||||
if parsed, err := strconv.Atoi(l); err == nil && parsed > 0 && parsed <= 100 {
|
||||
limit = parsed
|
||||
}
|
||||
}
|
||||
|
||||
query := h.db.Where("user_id = ?", userID)
|
||||
if category != "" {
|
||||
query = query.Where("category = ?", category)
|
||||
}
|
||||
if status != "" {
|
||||
query = query.Where("status = ?", status)
|
||||
}
|
||||
if priority != "" {
|
||||
query = query.Where("priority = ?", priority)
|
||||
}
|
||||
|
||||
var goals []models.Goal
|
||||
if err := query.Preload("Milestones").Order("created_at DESC").Limit(limit).Find(&goals).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch goals"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"goals": goals,
|
||||
"limit": limit,
|
||||
})
|
||||
}
|
||||
|
||||
// GetGoal retrieves a specific goal
|
||||
func (h *GoalsHabitsHandler) GetGoal(c *gin.Context) {
|
||||
userID := c.GetUint("user_id")
|
||||
goalID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid goal ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var goal models.Goal
|
||||
if err := h.db.Where("id = ? AND user_id = ?", goalID, userID).
|
||||
Preload("Milestones").
|
||||
First(&goal).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Goal not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, goal)
|
||||
}
|
||||
|
||||
// UpdateGoal updates a goal
|
||||
func (h *GoalsHabitsHandler) UpdateGoal(c *gin.Context) {
|
||||
userID := c.GetUint("user_id")
|
||||
goalID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid goal ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var goal models.Goal
|
||||
if err := h.db.Where("id = ? AND user_id = ?", goalID, userID).First(&goal).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Goal not found"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Category string `json:"category"`
|
||||
TargetValue float64 `json:"target_value"`
|
||||
CurrentValue float64 `json:"current_value"`
|
||||
Unit string `json:"unit"`
|
||||
Deadline time.Time `json:"deadline"`
|
||||
Status string `json:"status"`
|
||||
Priority string `json:"priority"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Update fields
|
||||
if req.Title != "" {
|
||||
goal.Title = req.Title
|
||||
}
|
||||
if req.Description != "" {
|
||||
goal.Description = req.Description
|
||||
}
|
||||
if req.Category != "" {
|
||||
goal.Category = req.Category
|
||||
}
|
||||
if req.TargetValue > 0 {
|
||||
goal.TargetValue = req.TargetValue
|
||||
}
|
||||
if req.CurrentValue >= 0 {
|
||||
goal.CurrentValue = req.CurrentValue
|
||||
}
|
||||
if req.Unit != "" {
|
||||
goal.Unit = req.Unit
|
||||
}
|
||||
if !req.Deadline.IsZero() {
|
||||
goal.Deadline = req.Deadline
|
||||
}
|
||||
if req.Status != "" {
|
||||
goal.Status = req.Status
|
||||
}
|
||||
if req.Priority != "" {
|
||||
goal.Priority = req.Priority
|
||||
}
|
||||
|
||||
if err := h.db.Save(&goal).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update goal"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, goal)
|
||||
}
|
||||
|
||||
// DeleteGoal deletes a goal
|
||||
func (h *GoalsHabitsHandler) DeleteGoal(c *gin.Context) {
|
||||
userID := c.GetUint("user_id")
|
||||
goalID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid goal ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var goal models.Goal
|
||||
if err := h.db.Where("id = ? AND user_id = ?", goalID, userID).First(&goal).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Goal not found"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.db.Delete(&goal).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete goal"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Goal deleted successfully"})
|
||||
}
|
||||
|
||||
// Habit Handlers
|
||||
|
||||
// CreateHabit creates a new habit
|
||||
func (h *GoalsHabitsHandler) CreateHabit(c *gin.Context) {
|
||||
userID := c.GetUint("user_id")
|
||||
|
||||
var req struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Description string `json:"description"`
|
||||
Category string `json:"category"`
|
||||
TargetFrequency int `json:"target_frequency"`
|
||||
FrequencyUnit string `json:"frequency_unit"`
|
||||
TargetValue float64 `json:"target_value"`
|
||||
Unit string `json:"unit"`
|
||||
TimeOfDay string `json:"time_of_day"`
|
||||
DaysOfWeek []string `json:"days_of_week"`
|
||||
IsActive bool `json:"is_active"`
|
||||
IsPublic bool `json:"is_public"`
|
||||
GoalID *uint `json:"goal_id"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
habit := models.Habit{
|
||||
UserID: userID,
|
||||
Name: req.Name,
|
||||
Description: req.Description,
|
||||
Category: req.Category,
|
||||
TargetFrequency: req.TargetFrequency,
|
||||
FrequencyUnit: req.FrequencyUnit,
|
||||
TargetValue: req.TargetValue,
|
||||
Unit: req.Unit,
|
||||
TimeOfDay: req.TimeOfDay,
|
||||
DaysOfWeek: req.DaysOfWeek,
|
||||
IsActive: req.IsActive,
|
||||
IsPublic: req.IsPublic,
|
||||
GoalID: req.GoalID,
|
||||
Streak: 0,
|
||||
LongestStreak: 0,
|
||||
CompletionRate: 0,
|
||||
}
|
||||
|
||||
if err := h.db.Create(&habit).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create habit"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, habit)
|
||||
}
|
||||
|
||||
// GetHabits retrieves user's habits
|
||||
func (h *GoalsHabitsHandler) GetHabits(c *gin.Context) {
|
||||
userID := c.GetUint("user_id")
|
||||
|
||||
category := c.Query("category")
|
||||
isActive := c.Query("is_active")
|
||||
limit := 20
|
||||
if l := c.Query("limit"); l != "" {
|
||||
if parsed, err := strconv.Atoi(l); err == nil && parsed > 0 && parsed <= 100 {
|
||||
limit = parsed
|
||||
}
|
||||
}
|
||||
|
||||
query := h.db.Where("user_id = ?", userID)
|
||||
if category != "" {
|
||||
query = query.Where("category = ?", category)
|
||||
}
|
||||
if isActive != "" {
|
||||
active := isActive == "true"
|
||||
query = query.Where("is_active = ?", active)
|
||||
}
|
||||
|
||||
var habits []models.Habit
|
||||
if err := query.Preload("Goal").Order("created_at DESC").Limit(limit).Find(&habits).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch habits"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"habits": habits,
|
||||
"limit": limit,
|
||||
})
|
||||
}
|
||||
|
||||
// GetHabit retrieves a specific habit
|
||||
func (h *GoalsHabitsHandler) GetHabit(c *gin.Context) {
|
||||
userID := c.GetUint("user_id")
|
||||
habitID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid habit ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var habit models.Habit
|
||||
if err := h.db.Where("id = ? AND user_id = ?", habitID, userID).
|
||||
Preload("Goal").
|
||||
Preload("HabitEntries", "entry_date >= ?", time.Now().AddDate(0, 0, -30)).
|
||||
First(&habit).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Habit not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, habit)
|
||||
}
|
||||
|
||||
// UpdateHabit updates a habit
|
||||
func (h *GoalsHabitsHandler) UpdateHabit(c *gin.Context) {
|
||||
userID := c.GetUint("user_id")
|
||||
habitID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid habit ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var habit models.Habit
|
||||
if err := h.db.Where("id = ? AND user_id = ?", habitID, userID).First(&habit).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Habit not found"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Category string `json:"category"`
|
||||
TargetFrequency int `json:"target_frequency"`
|
||||
FrequencyUnit string `json:"frequency_unit"`
|
||||
TargetValue float64 `json:"target_value"`
|
||||
Unit string `json:"unit"`
|
||||
TimeOfDay string `json:"time_of_day"`
|
||||
DaysOfWeek []string `json:"days_of_week"`
|
||||
IsActive bool `json:"is_active"`
|
||||
IsPublic bool `json:"is_public"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Update fields
|
||||
if req.Name != "" {
|
||||
habit.Name = req.Name
|
||||
}
|
||||
if req.Description != "" {
|
||||
habit.Description = req.Description
|
||||
}
|
||||
if req.Category != "" {
|
||||
habit.Category = req.Category
|
||||
}
|
||||
if req.TargetFrequency > 0 {
|
||||
habit.TargetFrequency = req.TargetFrequency
|
||||
}
|
||||
if req.FrequencyUnit != "" {
|
||||
habit.FrequencyUnit = req.FrequencyUnit
|
||||
}
|
||||
if req.TargetValue > 0 {
|
||||
habit.TargetValue = req.TargetValue
|
||||
}
|
||||
if req.Unit != "" {
|
||||
habit.Unit = req.Unit
|
||||
}
|
||||
if req.TimeOfDay != "" {
|
||||
habit.TimeOfDay = req.TimeOfDay
|
||||
}
|
||||
if req.DaysOfWeek != nil {
|
||||
habit.DaysOfWeek = req.DaysOfWeek
|
||||
}
|
||||
habit.IsActive = req.IsActive
|
||||
habit.IsPublic = req.IsPublic
|
||||
|
||||
if err := h.db.Save(&habit).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update habit"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, habit)
|
||||
}
|
||||
|
||||
// DeleteHabit deletes a habit
|
||||
func (h *GoalsHabitsHandler) DeleteHabit(c *gin.Context) {
|
||||
userID := c.GetUint("user_id")
|
||||
habitID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid habit ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var habit models.Habit
|
||||
if err := h.db.Where("id = ? AND user_id = ?", habitID, userID).First(&habit).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Habit not found"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.db.Delete(&habit).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete habit"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Habit deleted successfully"})
|
||||
}
|
||||
|
||||
// HabitEntry Handlers
|
||||
|
||||
// CreateHabitEntry creates a new habit entry
|
||||
func (h *GoalsHabitsHandler) CreateHabitEntry(c *gin.Context) {
|
||||
userID := c.GetUint("user_id")
|
||||
|
||||
var req struct {
|
||||
HabitID uint `json:"habit_id" binding:"required"`
|
||||
EntryDate time.Time `json:"entry_date" binding:"required"`
|
||||
Value float64 `json:"value"`
|
||||
TargetValue float64 `json:"target_value"`
|
||||
Unit string `json:"unit"`
|
||||
Notes string `json:"notes"`
|
||||
Quality int `json:"quality"`
|
||||
TimeSpent int `json:"time_spent"`
|
||||
Location string `json:"location"`
|
||||
Mood string `json:"mood"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Verify habit ownership
|
||||
var habit models.Habit
|
||||
if err := h.db.Where("id = ? AND user_id = ?", req.HabitID, userID).First(&habit).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Habit not found"})
|
||||
return
|
||||
}
|
||||
|
||||
entry := models.HabitEntry{
|
||||
HabitID: req.HabitID,
|
||||
EntryDate: req.EntryDate,
|
||||
Value: req.Value,
|
||||
TargetValue: req.TargetValue,
|
||||
Unit: req.Unit,
|
||||
Notes: req.Notes,
|
||||
Quality: req.Quality,
|
||||
TimeSpent: req.TimeSpent,
|
||||
Location: req.Location,
|
||||
Mood: req.Mood,
|
||||
}
|
||||
|
||||
if err := h.db.Create(&entry).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create habit entry"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, entry)
|
||||
}
|
||||
|
||||
// GetHabitEntries retrieves habit entries
|
||||
func (h *GoalsHabitsHandler) GetHabitEntries(c *gin.Context) {
|
||||
userID := c.GetUint("user_id")
|
||||
habitID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid habit ID"})
|
||||
return
|
||||
}
|
||||
|
||||
// Verify habit ownership
|
||||
var habit models.Habit
|
||||
if err := h.db.Where("id = ? AND user_id = ?", habitID, userID).First(&habit).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Habit not found"})
|
||||
return
|
||||
}
|
||||
|
||||
startDate := c.Query("start_date")
|
||||
endDate := c.Query("end_date")
|
||||
limit := 50
|
||||
if l := c.Query("limit"); l != "" {
|
||||
if parsed, err := strconv.Atoi(l); err == nil && parsed > 0 && parsed <= 100 {
|
||||
limit = parsed
|
||||
}
|
||||
}
|
||||
|
||||
query := h.db.Where("habit_id = ?", habitID)
|
||||
if startDate != "" {
|
||||
if parsed, err := time.Parse("2006-01-02", startDate); err == nil {
|
||||
query = query.Where("entry_date >= ?", parsed)
|
||||
}
|
||||
}
|
||||
if endDate != "" {
|
||||
if parsed, err := time.Parse("2006-01-02", endDate); err == nil {
|
||||
query = query.Where("entry_date <= ?", parsed)
|
||||
}
|
||||
}
|
||||
|
||||
var entries []models.HabitEntry
|
||||
if err := query.Order("entry_date DESC").Limit(limit).Find(&entries).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch habit entries"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"entries": entries,
|
||||
"limit": limit,
|
||||
})
|
||||
}
|
||||
|
||||
// GetDashboardStats retrieves dashboard statistics for goals and habits
|
||||
func (h *GoalsHabitsHandler) GetDashboardStats(c *gin.Context) {
|
||||
userID := c.GetUint("user_id")
|
||||
|
||||
// Goal stats
|
||||
var totalGoals, activeGoals, completedGoals int64
|
||||
h.db.Model(&models.Goal{}).Where("user_id = ?", userID).Count(&totalGoals)
|
||||
h.db.Model(&models.Goal{}).Where("user_id = ? AND status = ?", userID, "active").Count(&activeGoals)
|
||||
h.db.Model(&models.Goal{}).Where("user_id = ? AND status = ?", userID, "completed").Count(&completedGoals)
|
||||
|
||||
// Habit stats
|
||||
var totalHabits, activeHabits int64
|
||||
h.db.Model(&models.Habit{}).Where("user_id = ?", userID).Count(&totalHabits)
|
||||
h.db.Model(&models.Habit{}).Where("user_id = ? AND is_active = ?", userID, true).Count(&activeHabits)
|
||||
|
||||
// Today's habit entries
|
||||
today := time.Now().Truncate(24 * time.Hour)
|
||||
tomorrow := today.Add(24 * time.Hour)
|
||||
var todayEntries int64
|
||||
h.db.Model(&models.HabitEntry{}).
|
||||
Joins("JOIN habits ON habit_entries.habit_id = habits.id").
|
||||
Where("habits.user_id = ? AND habit_entries.entry_date >= ? AND habit_entries.entry_date < ?", userID, today, tomorrow).
|
||||
Count(&todayEntries)
|
||||
|
||||
// Current week streak
|
||||
weekAgo := time.Now().AddDate(0, 0, -7)
|
||||
var weekEntries int64
|
||||
h.db.Model(&models.HabitEntry{}).
|
||||
Joins("JOIN habits ON habit_entries.habit_id = habits.id").
|
||||
Where("habits.user_id = ? AND habit_entries.entry_date >= ? AND habit_entries.is_completed = ?", userID, weekAgo, true).
|
||||
Count(&weekEntries)
|
||||
|
||||
stats := gin.H{
|
||||
"goals": gin.H{
|
||||
"total": totalGoals,
|
||||
"active": activeGoals,
|
||||
"completed": completedGoals,
|
||||
},
|
||||
"habits": gin.H{
|
||||
"total": totalHabits,
|
||||
"active": activeHabits,
|
||||
"today_entries": todayEntries,
|
||||
"week_streak": weekEntries,
|
||||
},
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, stats)
|
||||
}
|
||||
Reference in New Issue
Block a user