package handlers import ( "net/http" "strconv" "github.com/calendarapi/internal/middleware" "github.com/calendarapi/internal/models" "github.com/calendarapi/internal/service" "github.com/calendarapi/internal/utils" "github.com/go-chi/chi/v5" ) type TaskHandler struct { taskSvc *service.TaskService } func NewTaskHandler(taskSvc *service.TaskService) *TaskHandler { return &TaskHandler{taskSvc: taskSvc} } func (h *TaskHandler) List(w http.ResponseWriter, r *http.Request) { userID, _ := middleware.GetUserID(r.Context()) q := r.URL.Query() params := service.ListTasksParams{ Limit: 50, } if s := q.Get("status"); s != "" { params.Status = &s } if p := q.Get("priority"); p != "" { params.Priority = &p } if pid := q.Get("project_id"); pid != "" { if id, err := utils.ValidateUUID(pid); err == nil { params.ProjectID = &id } } if df := q.Get("due_from"); df != "" { if t, err := utils.ParseTime(df); err == nil { params.DueFrom = &t } } if dt := q.Get("due_to"); dt != "" { if t, err := utils.ParseTime(dt); err == nil { params.DueTo = &t } } if l := q.Get("limit"); l != "" { if v, err := strconv.Atoi(l); err == nil { params.Limit = v } } params.Cursor = q.Get("cursor") tasks, cursor, err := h.taskSvc.List(r.Context(), userID, params) if err != nil { utils.WriteError(w, err) return } limit := int(utils.ClampLimit(params.Limit)) page := models.PageInfo{Limit: limit} if cursor != nil { page.NextCursor = cursor } utils.WriteList(w, tasks, page) } func (h *TaskHandler) Create(w http.ResponseWriter, r *http.Request) { userID, _ := middleware.GetUserID(r.Context()) var req struct { Title string `json:"title"` Description *string `json:"description"` Status *string `json:"status"` Priority *string `json:"priority"` DueDate *string `json:"due_date"` ProjectID *string `json:"project_id"` ParentID *string `json:"parent_id"` SortOrder *int `json:"sort_order"` RecurrenceRule *string `json:"recurrence_rule"` } if err := utils.DecodeJSON(r, &req); err != nil { utils.WriteError(w, err) return } createReq := service.CreateTaskRequest{ Title: req.Title, Description: req.Description, Status: req.Status, Priority: req.Priority, SortOrder: req.SortOrder, RecurrenceRule: req.RecurrenceRule, } if req.DueDate != nil { if t, err := utils.ParseTime(*req.DueDate); err == nil { createReq.DueDate = &t } } if req.ProjectID != nil { if id, err := utils.ValidateUUID(*req.ProjectID); err == nil { createReq.ProjectID = &id } } if req.ParentID != nil { if id, err := utils.ValidateUUID(*req.ParentID); err == nil { createReq.ParentID = &id } } task, err := h.taskSvc.Create(r.Context(), userID, createReq) if err != nil { utils.WriteError(w, err) return } utils.WriteJSON(w, http.StatusOK, map[string]interface{}{"task": task}) } func (h *TaskHandler) Get(w http.ResponseWriter, r *http.Request) { userID, _ := middleware.GetUserID(r.Context()) taskID, err := utils.ValidateUUID(chi.URLParam(r, "id")) if err != nil { utils.WriteError(w, err) return } task, err := h.taskSvc.Get(r.Context(), userID, taskID) if err != nil { utils.WriteError(w, err) return } utils.WriteJSON(w, http.StatusOK, map[string]interface{}{"task": task}) } func (h *TaskHandler) Update(w http.ResponseWriter, r *http.Request) { userID, _ := middleware.GetUserID(r.Context()) taskID, err := utils.ValidateUUID(chi.URLParam(r, "id")) if err != nil { utils.WriteError(w, err) return } var req struct { Title *string `json:"title"` Description *string `json:"description"` Status *string `json:"status"` Priority *string `json:"priority"` DueDate *string `json:"due_date"` ProjectID *string `json:"project_id"` SortOrder *int `json:"sort_order"` RecurrenceRule *string `json:"recurrence_rule"` } if err := utils.DecodeJSON(r, &req); err != nil { utils.WriteError(w, err) return } updateReq := service.UpdateTaskRequest{ Title: req.Title, Description: req.Description, Status: req.Status, Priority: req.Priority, SortOrder: req.SortOrder, RecurrenceRule: req.RecurrenceRule, } if req.DueDate != nil { if t, err := utils.ParseTime(*req.DueDate); err == nil { updateReq.DueDate = &t } } if req.ProjectID != nil { if id, err := utils.ValidateUUID(*req.ProjectID); err == nil { updateReq.ProjectID = &id } } task, err := h.taskSvc.Update(r.Context(), userID, taskID, updateReq) if err != nil { utils.WriteError(w, err) return } utils.WriteJSON(w, http.StatusOK, map[string]interface{}{"task": task}) } func (h *TaskHandler) Delete(w http.ResponseWriter, r *http.Request) { userID, _ := middleware.GetUserID(r.Context()) taskID, err := utils.ValidateUUID(chi.URLParam(r, "id")) if err != nil { utils.WriteError(w, err) return } permanent := r.URL.Query().Get("permanent") == "true" if err := h.taskSvc.Delete(r.Context(), userID, taskID, permanent); err != nil { utils.WriteError(w, err) return } utils.WriteOK(w) } func (h *TaskHandler) MarkComplete(w http.ResponseWriter, r *http.Request) { userID, _ := middleware.GetUserID(r.Context()) taskID, err := utils.ValidateUUID(chi.URLParam(r, "id")) if err != nil { utils.WriteError(w, err) return } task, err := h.taskSvc.MarkComplete(r.Context(), userID, taskID) if err != nil { utils.WriteError(w, err) return } utils.WriteJSON(w, http.StatusOK, map[string]interface{}{"task": task}) } func (h *TaskHandler) MarkUncomplete(w http.ResponseWriter, r *http.Request) { userID, _ := middleware.GetUserID(r.Context()) taskID, err := utils.ValidateUUID(chi.URLParam(r, "id")) if err != nil { utils.WriteError(w, err) return } var req struct { Status *string `json:"status"` } _ = utils.DecodeJSON(r, &req) task, err := h.taskSvc.MarkUncomplete(r.Context(), userID, taskID, req.Status) if err != nil { utils.WriteError(w, err) return } utils.WriteJSON(w, http.StatusOK, map[string]interface{}{"task": task}) } func (h *TaskHandler) ListSubtasks(w http.ResponseWriter, r *http.Request) { userID, _ := middleware.GetUserID(r.Context()) taskID, err := utils.ValidateUUID(chi.URLParam(r, "id")) if err != nil { utils.WriteError(w, err) return } tasks, err := h.taskSvc.ListSubtasks(r.Context(), userID, taskID) if err != nil { utils.WriteError(w, err) return } utils.WriteList(w, tasks, models.PageInfo{Limit: len(tasks)}) }