feat: add JSON serialization, urgency field, and snake_case API contract

Fix latent API bug where multi-word fields (RecurrenceDuration, ParentUUID,
CreatedAt) serialized as PascalCase, breaking the frontend. Add explicit
snake_case json tags and custom MarshalJSON/UnmarshalJSON on Task, Status,
and APIKey to emit unix timestamps and string status codes.

Add Urgency float64 as a derived field on Task, populated via
PopulateUrgency helper in all handlers before serialization. The report
engine's sortByUrgency now also retains the computed score.

Frontend updated with urgency type, color-coded badge in TaskItem, and
mock data values.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-15 14:58:34 +01:00
parent 924b66bc64
commit 3bb2ef2759
9 changed files with 581 additions and 51 deletions
+14
View File
@@ -49,6 +49,9 @@ func ListTasks(w http.ResponseWriter, r *http.Request) {
return
}
// Report sorts may already populate urgency, but ensure it for all paths
engine.PopulateUrgency(tasks...)
jsonResponse(w, http.StatusOK, map[string]interface{}{
"report": reportName,
"tasks": tasks,
@@ -87,6 +90,7 @@ func ListTasks(w http.ResponseWriter, r *http.Request) {
return
}
engine.PopulateUrgency(tasks...)
jsonResponse(w, http.StatusOK, tasks)
}
@@ -159,6 +163,7 @@ func CreateTask(w http.ResponseWriter, r *http.Request) {
return
}
engine.PopulateUrgency(task)
jsonResponse(w, http.StatusCreated, task)
}
@@ -178,6 +183,7 @@ func GetTask(w http.ResponseWriter, r *http.Request) {
return
}
engine.PopulateUrgency(task)
jsonResponse(w, http.StatusOK, task)
}
@@ -271,6 +277,7 @@ func UpdateTask(w http.ResponseWriter, r *http.Request) {
return
}
engine.PopulateUrgency(task)
jsonResponse(w, http.StatusOK, task)
}
@@ -322,6 +329,7 @@ func CompleteTask(w http.ResponseWriter, r *http.Request) {
return
}
engine.PopulateUrgency(task)
jsonResponse(w, http.StatusOK, task)
}
@@ -346,6 +354,7 @@ func StartTask(w http.ResponseWriter, r *http.Request) {
return
}
engine.PopulateUrgency(task)
jsonResponse(w, http.StatusOK, task)
}
@@ -370,6 +379,7 @@ func StopTask(w http.ResponseWriter, r *http.Request) {
return
}
engine.PopulateUrgency(task)
jsonResponse(w, http.StatusOK, task)
}
@@ -429,6 +439,7 @@ func AddTaskTag(w http.ResponseWriter, r *http.Request) {
return
}
engine.PopulateUrgency(task)
jsonResponse(w, http.StatusOK, task)
}
@@ -489,6 +500,7 @@ func ParseTask(w http.ResponseWriter, r *http.Request) {
errorResponse(w, http.StatusBadRequest, err.Error())
return
}
engine.PopulateUrgency(instance)
jsonResponse(w, http.StatusCreated, map[string]interface{}{"task": instance})
return
}
@@ -500,6 +512,7 @@ func ParseTask(w http.ResponseWriter, r *http.Request) {
return
}
engine.PopulateUrgency(task)
jsonResponse(w, http.StatusCreated, map[string]interface{}{"task": task})
}
@@ -525,5 +538,6 @@ func RemoveTaskTag(w http.ResponseWriter, r *http.Request) {
return
}
engine.PopulateUrgency(task)
jsonResponse(w, http.StatusOK, task)
}