- Responsivo ≤760px: sidebar vira gaveta com backdrop, painel de versões em tela cheia, toolbar/menu rolam sem rótulos, alvos de toque, dvh + safe-area, fonte do editor ≥16px (anti-zoom iOS) - Abas: várias notas abertas com estado sujo por aba, ✕ com confirmação, clique do meio, Alt+W / Alt+←/→ / Alt+1..9; abas e rascunhos sobrevivem à sessão via localStorage - Categorias: modelo vira árvore (categories.json) com notas em múltiplas categorias; explorador no sidebar com expandir/renomear/mover/excluir (sem ciclos, GC de vínculos) e seletor multi-marca no editor; filtro por pasta recursivo por padrão; migração automática do campo único legado - API: POST/PUT/DELETE /api/categories; notas aceitam categories: [ids] - Testes: store (árvore, ciclo, migração, GC) e api (CRUD de categorias)
51 lines
1.2 KiB
Go
51 lines
1.2 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
)
|
|
|
|
type categoryInput struct {
|
|
Name string `json:"name"`
|
|
Parent *string `json:"parent"` // ausente = não move; "" = raiz
|
|
}
|
|
|
|
func (s *Server) handleCreateCategory(w http.ResponseWriter, r *http.Request) {
|
|
var in categoryInput
|
|
if err := decodeJSON(r, &in, 1<<20); err != nil {
|
|
writeError(w, http.StatusBadRequest, "corpo JSON inválido")
|
|
return
|
|
}
|
|
parent := ""
|
|
if in.Parent != nil {
|
|
parent = *in.Parent
|
|
}
|
|
c, err := s.st.CreateCategory(in.Name, parent)
|
|
if err != nil {
|
|
mapStoreError(w, err)
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusCreated, c)
|
|
}
|
|
|
|
func (s *Server) handleUpdateCategory(w http.ResponseWriter, r *http.Request) {
|
|
var in categoryInput
|
|
if err := decodeJSON(r, &in, 1<<20); err != nil {
|
|
writeError(w, http.StatusBadRequest, "corpo JSON inválido")
|
|
return
|
|
}
|
|
c, err := s.st.UpdateCategory(r.PathValue("id"), in.Name, in.Parent)
|
|
if err != nil {
|
|
mapStoreError(w, err)
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, c)
|
|
}
|
|
|
|
func (s *Server) handleDeleteCategory(w http.ResponseWriter, r *http.Request) {
|
|
if err := s.st.DeleteCategory(r.PathValue("id")); err != nil {
|
|
mapStoreError(w, err)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusNoContent)
|
|
}
|