summaryrefslogtreecommitdiff
path: root/internal/handlers/handlers.go
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-08-12 23:35:47 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-08-12 23:35:47 +0000
commit2509dde6aa372a505b186657706f4d21bd391807 (patch)
treee4147991747b8dd14d0b8654b2b8e50717b666bd /internal/handlers/handlers.go
parent3e8ad60431d6cc783f9f7c555bfde5db54ebec75 (diff)
Add task title editing/deletion, timeline click-to-open, widget app launch
Task-detail modal was description-only with no delete affordance; HandleUpdateTask now saves the title too and a Delete button hits a new DELETE /tasks/{id} route backed by store.DeleteNativeTask, which repairs chain_position/unlocks the successor when the deleted task belongs to a chain. Timeline tab task/card/gtask rows now open the same detail modal as the Tasks tab. Android widget's "TODAY" header is now a tap target that launches DashboardActivity, since nothing previously opened the full app from the widget.
Diffstat (limited to 'internal/handlers/handlers.go')
-rw-r--r--internal/handlers/handlers.go43
1 files changed, 41 insertions, 2 deletions
diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go
index ed15e89..30aaf67 100644
--- a/internal/handlers/handlers.go
+++ b/internal/handlers/handlers.go
@@ -15,6 +15,8 @@ import (
"sync"
"time"
+ "github.com/go-chi/chi/v5"
+
"task-dashboard/internal/api"
"task-dashboard/internal/auth"
"task-dashboard/internal/config"
@@ -951,6 +953,7 @@ func (h *Handler) HandleUpdateTask(w http.ResponseWriter, r *http.Request) {
id := r.FormValue("id")
source := r.FormValue("source")
+ title := r.FormValue("title")
description := r.FormValue("description")
if id == "" || source == "" {
@@ -961,9 +964,17 @@ func (h *Handler) HandleUpdateTask(w http.ResponseWriter, r *http.Request) {
var err error
switch source {
case "doot":
- err = h.store.UpdateNativeTaskDescription(id, description)
+ if title != "" {
+ err = h.store.UpdateNativeTask(id, title, description)
+ } else {
+ err = h.store.UpdateNativeTaskDescription(id, description)
+ }
case "trello":
- err = h.trelloClient.UpdateCard(r.Context(), id, map[string]interface{}{"desc": description})
+ updates := map[string]interface{}{"desc": description}
+ if title != "" {
+ updates["name"] = title
+ }
+ err = h.trelloClient.UpdateCard(r.Context(), id, updates)
default:
JSONError(w, http.StatusBadRequest, "Unknown source", nil)
return
@@ -981,6 +992,34 @@ func (h *Handler) HandleUpdateTask(w http.ResponseWriter, r *http.Request) {
}
}
+// HandleDeleteTask permanently deletes a doot-native task. Other sources
+// (Trello cards, Google Tasks, calendar events) aren't deletable through
+// doot -- the task-detail modal only shows the Delete button for source ==
+// "doot" (task-detail.html's IsDoot flag), matching this scoping.
+func (h *Handler) HandleDeleteTask(w http.ResponseWriter, r *http.Request) {
+ id := chi.URLParam(r, "id")
+ source := r.URL.Query().Get("source")
+ if id == "" {
+ JSONError(w, http.StatusBadRequest, "Missing id", nil)
+ return
+ }
+ if source != "doot" {
+ JSONError(w, http.StatusBadRequest, "Delete is only supported for doot-native tasks", nil)
+ return
+ }
+
+ if err := h.store.DeleteNativeTask(id); err != nil {
+ if errors.Is(err, store.ErrNativeTaskNotFound) {
+ JSONError(w, http.StatusNotFound, "task not found", err)
+ return
+ }
+ JSONError(w, http.StatusInternalServerError, "Failed to delete task", err)
+ return
+ }
+
+ w.WriteHeader(http.StatusOK)
+}
+
// HandleTabTasks renders the unified Tasks tab (native tasks + Trello cards with due dates + Google Tasks)
func (h *Handler) HandleTabTasks(w http.ResponseWriter, r *http.Request) {
atoms, boards, err := BuildUnifiedAtomList(h.store, h.claudomatorClient)