From ee7a8e24da1dfc959d78236a63ee65acc4be45e4 Mon Sep 17 00:00:00 2001 From: Mo Tarbin Date: Sat, 20 Jul 2024 03:42:38 -0400 Subject: [PATCH] Update ChoreHistory model to include updatedAt field, Support history modification --- internal/chore/handler.go | 161 ++++++++++++++++++++++++++++-- internal/chore/model/model.go | 3 +- internal/chore/repo/repository.go | 18 +++- 3 files changed, 174 insertions(+), 8 deletions(-) diff --git a/internal/chore/handler.go b/internal/chore/handler.go index 0067940..333521a 100644 --- a/internal/chore/handler.go +++ b/internal/chore/handler.go @@ -655,12 +655,20 @@ func (h *Handler) updateAssignee(c *gin.Context) { func (h *Handler) skipChore(c *gin.Context) { rawID := c.Param("id") id, err := strconv.Atoi(rawID) + if err != nil { c.JSON(400, gin.H{ "error": "Invalid ID", }) return } + currentUser, ok := auth.CurrentUser(c) + if !ok { + c.JSON(500, gin.H{ + "error": "Error getting current user", + }) + return + } chore, err := h.choreRepo.GetChore(c, id) if err != nil { @@ -669,23 +677,31 @@ func (h *Handler) skipChore(c *gin.Context) { }) return } - newDueDate, err := scheduleNextDueDate(chore, chore.NextDueDate.UTC()) + nextDueDate, err := scheduleNextDueDate(chore, chore.NextDueDate.UTC()) if err != nil { c.JSON(500, gin.H{ "error": "Error scheduling next due date", }) return } - chore.NextDueDate = newDueDate - if err := h.choreRepo.UpsertChore(c, chore); err != nil { + + nextAssigedTo := chore.AssignedTo + if err := h.choreRepo.CompleteChore(c, chore, nil, currentUser.ID, nextDueDate, nil, nextAssigedTo); err != nil { c.JSON(500, gin.H{ - "error": "Error skipping chore", + "error": "Error completing chore", + }) + return + } + updatedChore, err := h.choreRepo.GetChore(c, id) + if err != nil { + c.JSON(500, gin.H{ + "error": "Error getting chore", }) return } c.JSON(200, gin.H{ - "res": chore, + "res": updatedChore, }) } @@ -842,7 +858,7 @@ func (h *Handler) completeChore(c *gin.Context) { return } - if err := h.choreRepo.CompleteChore(c, chore, additionalNotes, currentUser.ID, nextDueDate, completedDate, nextAssignedTo); err != nil { + if err := h.choreRepo.CompleteChore(c, chore, additionalNotes, currentUser.ID, nextDueDate, &completedDate, nextAssignedTo); err != nil { c.JSON(500, gin.H{ "error": "Error completing chore", }) @@ -918,6 +934,137 @@ func (h *Handler) GetChoreDetail(c *gin.Context) { }) } +func (h *Handler) ModifyHistory(c *gin.Context) { + + currentUser, ok := auth.CurrentUser(c) + if !ok { + c.JSON(500, gin.H{ + "error": "Error getting current user", + }) + return + } + + rawID := c.Param("id") + choreID, err := strconv.Atoi(rawID) + if err != nil { + c.JSON(400, gin.H{ + "error": "Invalid Chore ID", + }) + return + } + type ModifyHistoryReq struct { + CompletedAt *time.Time `json:"completedAt"` + DueDate *time.Time `json:"dueDate"` + Notes *string `json:"notes"` + } + + var req ModifyHistoryReq + if err := c.ShouldBindJSON(&req); err != nil { + log.Print(err) + c.JSON(400, gin.H{ + "error": "Invalid request", + }) + return + } + rawHistoryID := c.Param("history_id") + historyID, err := strconv.Atoi(rawHistoryID) + if err != nil { + c.JSON(400, gin.H{ + "error": "Invalid History ID", + }) + return + } + + history, err := h.choreRepo.GetChoreHistoryByID(c, choreID, historyID) + if err != nil { + c.JSON(500, gin.H{ + "error": "Error getting chore history", + }) + return + } + + if currentUser.ID != history.CompletedBy || currentUser.ID != history.AssignedTo { + c.JSON(403, gin.H{ + "error": "You are not allowed to modify this history", + }) + return + } + if req.CompletedAt != nil { + history.CompletedAt = req.CompletedAt + } + if req.DueDate != nil { + history.DueDate = req.DueDate + } + if req.Notes != nil { + history.Note = req.Notes + } + + if err := h.choreRepo.UpdateChoreHistory(c, history); err != nil { + c.JSON(500, gin.H{ + "error": "Error updating history", + }) + return + } + + c.JSON(200, gin.H{ + "res": history, + }) +} + +func (h *Handler) DeleteHistory(c *gin.Context) { + + currentUser, ok := auth.CurrentUser(c) + if !ok { + c.JSON(500, gin.H{ + "error": "Error getting current user", + }) + return + } + + rawID := c.Param("id") + choreID, err := strconv.Atoi(rawID) + if err != nil { + c.JSON(400, gin.H{ + "error": "Invalid Chore ID", + }) + return + } + + rawHistoryID := c.Param("history_id") + historyID, err := strconv.Atoi(rawHistoryID) + if err != nil { + c.JSON(400, gin.H{ + "error": "Invalid History ID", + }) + return + } + + history, err := h.choreRepo.GetChoreHistoryByID(c, choreID, historyID) + if err != nil { + c.JSON(500, gin.H{ + "error": "Error getting chore history", + }) + return + } + + if currentUser.ID != history.CompletedBy || currentUser.ID != history.AssignedTo { + c.JSON(403, gin.H{ + "error": "You are not allowed to delete this history", + }) + return + } + + if err := h.choreRepo.DeleteChoreHistory(c, historyID); err != nil { + c.JSON(500, gin.H{ + "error": "Error deleting history", + }) + return + } + + c.JSON(200, gin.H{ + "message": "History deleted successfully", + }) +} func checkNextAssignee(chore *chModel.Chore, choresHistory []*chModel.ChoreHistory, performerID int) (int, error) { // copy the history to avoid modifying the original: history := make([]*chModel.ChoreHistory, len(choresHistory)) @@ -1006,6 +1153,8 @@ func Routes(router *gin.Engine, h *Handler, auth *jwt.GinJWTMiddleware) { choresRoutes.GET("/:id", h.getChore) choresRoutes.GET("/:id/details", h.GetChoreDetail) choresRoutes.GET("/:id/history", h.GetChoreHistory) + choresRoutes.PUT("/:id/history/:history_id", h.ModifyHistory) + choresRoutes.DELETE("/:id/history/:history_id", h.DeleteHistory) choresRoutes.POST("/:id/do", h.completeChore) choresRoutes.POST("/:id/skip", h.skipChore) choresRoutes.PUT("/:id/assignee", h.updateAssignee) diff --git a/internal/chore/model/model.go b/internal/chore/model/model.go index c9cf28c..38d8354 100644 --- a/internal/chore/model/model.go +++ b/internal/chore/model/model.go @@ -36,11 +36,12 @@ type ChoreAssignees struct { type ChoreHistory struct { ID int `json:"id" gorm:"primary_key"` // Unique identifier ChoreID int `json:"choreId" gorm:"column:chore_id"` // The chore this history is for - CompletedAt time.Time `json:"completedAt" gorm:"column:completed_at"` // When the chore was completed + CompletedAt *time.Time `json:"completedAt" gorm:"column:completed_at"` // When the chore was completed CompletedBy int `json:"completedBy" gorm:"column:completed_by"` // Who completed the chore AssignedTo int `json:"assignedTo" gorm:"column:assigned_to"` // Who the chore was assigned to Note *string `json:"notes" gorm:"column:notes"` // Notes about the chore DueDate *time.Time `json:"dueDate" gorm:"column:due_date"` // When the chore was due + UpdatedAt *time.Time `json:"updatedAt" gorm:"column:updated_at"` // When the record was last updated } type FrequencyMetadata struct { diff --git a/internal/chore/repo/repository.go b/internal/chore/repo/repository.go index 7284202..e7035d5 100644 --- a/internal/chore/repo/repository.go +++ b/internal/chore/repo/repository.go @@ -73,7 +73,7 @@ func (r *ChoreRepository) IsChoreOwner(c context.Context, choreID int, userID in // return chores, nil // } -func (r *ChoreRepository) CompleteChore(c context.Context, chore *chModel.Chore, note *string, userID int, dueDate *time.Time, completedDate time.Time, nextAssignedTo int) error { +func (r *ChoreRepository) CompleteChore(c context.Context, chore *chModel.Chore, note *string, userID int, dueDate *time.Time, completedDate *time.Time, nextAssignedTo int) error { err := r.db.WithContext(c).Transaction(func(tx *gorm.DB) error { ch := &chModel.ChoreHistory{ ChoreID: chore.ID, @@ -119,6 +119,22 @@ func (r *ChoreRepository) GetChoreHistoryWithLimit(c context.Context, choreID in return histories, nil } +func (r *ChoreRepository) GetChoreHistoryByID(c context.Context, choreID int, historyID int) (*chModel.ChoreHistory, error) { + var history chModel.ChoreHistory + if err := r.db.WithContext(c).Where("id = ? and chore_id = ? ", historyID, choreID).First(&history).Error; err != nil { + return nil, err + } + return &history, nil +} + +func (r *ChoreRepository) UpdateChoreHistory(c context.Context, history *chModel.ChoreHistory) error { + return r.db.WithContext(c).Save(history).Error +} + +func (r *ChoreRepository) DeleteChoreHistory(c context.Context, historyID int) error { + return r.db.WithContext(c).Delete(&chModel.ChoreHistory{}, historyID).Error +} + func (r *ChoreRepository) UpdateChoreAssignees(c context.Context, assignees []*chModel.ChoreAssignees) error { return r.db.WithContext(c).Save(&assignees).Error }