Fix today's habits timezone calculation and include entry data

- Calculate today based on user's timezone instead of always using UTC
- Include habit entry in response if it exists for the current day
- Update GetTodaysHabitsHandler to fetch and return entry information
- Add entry field to TodaysHabitDTO and TodaysHabitResponse
- Update tests to reflect new behavior of including completed habits
- Add test for habits with value entries
This commit is contained in:
2025-11-29 02:09:30 +01:00
parent 9a0637aa4c
commit 90d5628a42
6 changed files with 130 additions and 32 deletions
+14 -7
View File
@@ -48,14 +48,21 @@ type MarkHabitRequest struct {
Value *float64 `json:"value,omitempty"`
}
type TodaysHabitEntryResponse struct {
ID string `json:"id"`
Value *float64 `json:"value,omitempty"`
CompletedAt time.Time `json:"completed_at"`
}
type TodaysHabitResponse struct {
ID string `json:"id"`
Name string `json:"name"`
Type value_objects.HabitType `json:"type"`
TargetValue *float64 `json:"target_value,omitempty"`
IsNegative bool `json:"is_negative"`
ScheduledDate time.Time `json:"scheduled_date"`
IsCarriedOver bool `json:"is_carried_over"`
ID string `json:"id"`
Name string `json:"name"`
Type value_objects.HabitType `json:"type"`
TargetValue *float64 `json:"target_value,omitempty"`
IsNegative bool `json:"is_negative"`
ScheduledDate time.Time `json:"scheduled_date"`
IsCarriedOver bool `json:"is_carried_over"`
Entry *TodaysHabitEntryResponse `json:"entry,omitempty"`
}
type UserHabitResponse struct {
+30 -4
View File
@@ -9,6 +9,7 @@ import (
"apocapoc-api/internal/application/commands"
"apocapoc-api/internal/application/queries"
"apocapoc-api/internal/domain/repositories"
"apocapoc-api/internal/shared/errors"
"github.com/go-chi/chi/v5"
@@ -24,6 +25,7 @@ type HabitHandlers struct {
archiveHandler *commands.ArchiveHabitHandler
markHandler *commands.MarkHabitHandler
unmarkHandler *commands.UnmarkHabitHandler
userRepo repositories.UserRepository
}
func NewHabitHandlers(
@@ -36,6 +38,7 @@ func NewHabitHandlers(
archiveHandler *commands.ArchiveHabitHandler,
markHandler *commands.MarkHabitHandler,
unmarkHandler *commands.UnmarkHabitHandler,
userRepo repositories.UserRepository,
) *HabitHandlers {
return &HabitHandlers{
createHandler: createHandler,
@@ -47,6 +50,7 @@ func NewHabitHandlers(
archiveHandler: archiveHandler,
markHandler: markHandler,
unmarkHandler: unmarkHandler,
userRepo: userRepo,
}
}
@@ -432,7 +436,7 @@ func (h *HabitHandlers) GetHabitEntries(w http.ResponseWriter, r *http.Request)
// GetTodaysHabits godoc
// @Summary Get today's habits
// @Description Get all habits scheduled for today for the authenticated user
// @Description Get all habits scheduled for today for the authenticated user. Includes the entry for today if it exists.
// @Tags habits
// @Produce json
// @Security BearerAuth
@@ -447,12 +451,24 @@ func (h *HabitHandlers) GetTodaysHabits(w http.ResponseWriter, r *http.Request)
return
}
timezone := "UTC"
user, err := h.userRepo.FindByID(r.Context(), userID)
if err != nil {
respondError(w, http.StatusInternalServerError, "Failed to get user")
return
}
loc, err := time.LoadLocation(user.Timezone)
if err != nil {
loc = time.UTC
}
today := time.Now().In(loc)
todayDate := time.Date(today.Year(), today.Month(), today.Day(), 0, 0, 0, 0, time.UTC)
query := queries.GetTodaysHabitsQuery{
UserID: userID,
Timezone: timezone,
Date: time.Now().UTC(),
Timezone: user.Timezone,
Date: todayDate,
}
habits, err := h.getTodaysHandler.Handle(r.Context(), query)
@@ -463,6 +479,15 @@ func (h *HabitHandlers) GetTodaysHabits(w http.ResponseWriter, r *http.Request)
response := make([]TodaysHabitResponse, len(habits))
for i, habit := range habits {
var entryResponse *TodaysHabitEntryResponse
if habit.Entry != nil {
entryResponse = &TodaysHabitEntryResponse{
ID: habit.Entry.ID,
Value: habit.Entry.Value,
CompletedAt: habit.Entry.CompletedAt,
}
}
response[i] = TodaysHabitResponse{
ID: habit.ID,
Name: habit.Name,
@@ -471,6 +496,7 @@ func (h *HabitHandlers) GetTodaysHabits(w http.ResponseWriter, r *http.Request)
IsNegative: habit.IsNegative,
ScheduledDate: habit.ScheduledDate,
IsCarriedOver: habit.IsCarriedOver,
Entry: entryResponse,
}
}
@@ -67,7 +67,7 @@ func setupTestServer(t *testing.T) *TestServer {
deleteUserHandler := commands.NewDeleteUserHandler(userRepo)
authHandlers := NewAuthHandlers(registerHandler, loginHandler, refreshTokenHandler, revokeTokenHandler, revokeAllTokensHandler, verifyEmailHandler, resendVerificationEmailHandler, requestPasswordResetHandler, resetPasswordHandler, jwtService, refreshTokenRepo, refreshTokenExpiry)
habitHandlers := NewHabitHandlers(createHandler, getTodaysHandler, getUserHabitsHandler, getHabitByIDHandler, getHabitEntriesHandler, updateHandler, archiveHandler, markHandler, unmarkHandler)
habitHandlers := NewHabitHandlers(createHandler, getTodaysHandler, getUserHabitsHandler, getHabitByIDHandler, getHabitEntriesHandler, updateHandler, archiveHandler, markHandler, unmarkHandler, userRepo)
statsHandlers := NewStatsHandlers(getHabitStatsHandler)
healthHandlers := NewHealthHandlers(db)
userHandlers := NewUserHandlers(deleteUserHandler)