Fix timezone bug in task kanban categorization
Task creation/update responses were using UTC time for kanban column categorization, causing tasks to incorrectly appear as overdue when the server had passed midnight UTC but the user's local time was still the previous day. Changes: - Add timezone-aware response functions (NewTaskResponseWithTime, etc.) - Pass userNow from middleware to all task service methods - Update handlers to use timezone-aware time from X-Timezone header - Update tests to pass the now parameter 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -103,13 +103,15 @@ func (h *TaskHandler) GetTasksByResidence(c *gin.Context) {
|
||||
// CreateTask handles POST /api/tasks/
|
||||
func (h *TaskHandler) CreateTask(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
userNow := middleware.GetUserNow(c)
|
||||
|
||||
var req requests.CreateTaskRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
response, err := h.taskService.CreateTask(&req, user.ID)
|
||||
response, err := h.taskService.CreateTask(&req, user.ID, userNow)
|
||||
if err != nil {
|
||||
if errors.Is(err, services.ErrResidenceAccessDenied) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": i18n.LocalizedMessage(c, "error.residence_access_denied")})
|
||||
@@ -124,6 +126,8 @@ func (h *TaskHandler) CreateTask(c *gin.Context) {
|
||||
// UpdateTask handles PUT/PATCH /api/tasks/:id/
|
||||
func (h *TaskHandler) UpdateTask(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
userNow := middleware.GetUserNow(c)
|
||||
|
||||
taskID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_task_id")})
|
||||
@@ -136,7 +140,7 @@ func (h *TaskHandler) UpdateTask(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
response, err := h.taskService.UpdateTask(uint(taskID), user.ID, &req)
|
||||
response, err := h.taskService.UpdateTask(uint(taskID), user.ID, &req, userNow)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrTaskNotFound):
|
||||
@@ -178,13 +182,15 @@ func (h *TaskHandler) DeleteTask(c *gin.Context) {
|
||||
// MarkInProgress handles POST /api/tasks/:id/mark-in-progress/
|
||||
func (h *TaskHandler) MarkInProgress(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
userNow := middleware.GetUserNow(c)
|
||||
|
||||
taskID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_task_id")})
|
||||
return
|
||||
}
|
||||
|
||||
response, err := h.taskService.MarkInProgress(uint(taskID), user.ID)
|
||||
response, err := h.taskService.MarkInProgress(uint(taskID), user.ID, userNow)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrTaskNotFound):
|
||||
@@ -202,13 +208,15 @@ func (h *TaskHandler) MarkInProgress(c *gin.Context) {
|
||||
// CancelTask handles POST /api/tasks/:id/cancel/
|
||||
func (h *TaskHandler) CancelTask(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
userNow := middleware.GetUserNow(c)
|
||||
|
||||
taskID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_task_id")})
|
||||
return
|
||||
}
|
||||
|
||||
response, err := h.taskService.CancelTask(uint(taskID), user.ID)
|
||||
response, err := h.taskService.CancelTask(uint(taskID), user.ID, userNow)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrTaskNotFound):
|
||||
@@ -228,13 +236,15 @@ func (h *TaskHandler) CancelTask(c *gin.Context) {
|
||||
// UncancelTask handles POST /api/tasks/:id/uncancel/
|
||||
func (h *TaskHandler) UncancelTask(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
userNow := middleware.GetUserNow(c)
|
||||
|
||||
taskID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_task_id")})
|
||||
return
|
||||
}
|
||||
|
||||
response, err := h.taskService.UncancelTask(uint(taskID), user.ID)
|
||||
response, err := h.taskService.UncancelTask(uint(taskID), user.ID, userNow)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrTaskNotFound):
|
||||
@@ -252,13 +262,15 @@ func (h *TaskHandler) UncancelTask(c *gin.Context) {
|
||||
// ArchiveTask handles POST /api/tasks/:id/archive/
|
||||
func (h *TaskHandler) ArchiveTask(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
userNow := middleware.GetUserNow(c)
|
||||
|
||||
taskID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_task_id")})
|
||||
return
|
||||
}
|
||||
|
||||
response, err := h.taskService.ArchiveTask(uint(taskID), user.ID)
|
||||
response, err := h.taskService.ArchiveTask(uint(taskID), user.ID, userNow)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrTaskNotFound):
|
||||
@@ -278,13 +290,15 @@ func (h *TaskHandler) ArchiveTask(c *gin.Context) {
|
||||
// UnarchiveTask handles POST /api/tasks/:id/unarchive/
|
||||
func (h *TaskHandler) UnarchiveTask(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
userNow := middleware.GetUserNow(c)
|
||||
|
||||
taskID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.LocalizedMessage(c, "error.invalid_task_id")})
|
||||
return
|
||||
}
|
||||
|
||||
response, err := h.taskService.UnarchiveTask(uint(taskID), user.ID)
|
||||
response, err := h.taskService.UnarchiveTask(uint(taskID), user.ID, userNow)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrTaskNotFound):
|
||||
@@ -389,6 +403,8 @@ func (h *TaskHandler) GetCompletion(c *gin.Context) {
|
||||
// Supports both JSON and multipart form data (for image uploads)
|
||||
func (h *TaskHandler) CreateCompletion(c *gin.Context) {
|
||||
user := c.MustGet(middleware.AuthUserKey).(*models.User)
|
||||
userNow := middleware.GetUserNow(c)
|
||||
|
||||
var req requests.CreateTaskCompletionRequest
|
||||
|
||||
contentType := c.GetHeader("Content-Type")
|
||||
@@ -460,7 +476,7 @@ func (h *TaskHandler) CreateCompletion(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
response, err := h.taskService.CreateCompletion(&req, user.ID)
|
||||
response, err := h.taskService.CreateCompletion(&req, user.ID, userNow)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, services.ErrTaskNotFound):
|
||||
|
||||
Reference in New Issue
Block a user