feat(kanban): stickers feature + dashboard null guards (#0063)
- backend: Sticker type, idempotent stickers column, PUT /api/cards/:id/stickers, 4 tests - frontend: emoji-mart picker, toolbar button + ESC, draggable overlay with right-click delete, % coords for resize survival - dashboard: null guards on metrics arrays Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+20
@@ -210,6 +210,25 @@ func handleUpdateCard(db *DB) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// PUT /api/cards/{id}/stickers { stickers: [{emoji,x,y}, ...] }
|
||||
func handleUpdateCardStickers(db *DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.PathValue("id")
|
||||
var body struct {
|
||||
Stickers []Sticker `json:"stickers"`
|
||||
}
|
||||
if err := infra.HTTPParseBody(r, &body, maxBodyBytes); err != nil {
|
||||
badRequest(w, err.Error())
|
||||
return
|
||||
}
|
||||
if err := db.UpdateStickers(id, body.Stickers); err != nil {
|
||||
serverError(w, err)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE /api/cards/{id}
|
||||
func handleDeleteCard(db *DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -314,6 +333,7 @@ func apiRoutes(db *DB, chatWorkdir string, logger *ChatLogger) []infra.Route {
|
||||
{Method: "DELETE", Path: "/api/columns/{id}", Handler: handleDeleteColumn(db)},
|
||||
{Method: "POST", Path: "/api/cards", Handler: handleCreateCard(db)},
|
||||
{Method: "PATCH", Path: "/api/cards/{id}", Handler: handleUpdateCard(db)},
|
||||
{Method: "PUT", Path: "/api/cards/{id}/stickers", Handler: handleUpdateCardStickers(db)},
|
||||
{Method: "DELETE", Path: "/api/cards/{id}", Handler: handleDeleteCard(db)},
|
||||
{Method: "POST", Path: "/api/cards/{id}/move", Handler: handleMoveCard(db)},
|
||||
{Method: "GET", Path: "/api/cards/{id}/history", Handler: handleCardHistory(db)},
|
||||
|
||||
Reference in New Issue
Block a user