primeira versão do e-li-nps construido com IA
This commit is contained in:
commit
06950d6e2c
34 changed files with 2524 additions and 0 deletions
183
internal/elinps/handlers.go
Normal file
183
internal/elinps/handlers.go
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
package elinps
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"e-li.nps/internal/db"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type Handlers struct {
|
||||
store *Store
|
||||
tpl *TemplateRenderer
|
||||
}
|
||||
|
||||
func NewHandlers(pool *pgxpool.Pool) *Handlers {
|
||||
return &Handlers{
|
||||
store: NewStore(pool),
|
||||
tpl: NewTemplateRenderer(mustParseTemplates()),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handlers) PostPedido(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
var in PedidoInput
|
||||
if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]any{"error": "json_invalido"})
|
||||
return
|
||||
}
|
||||
if err := ValidatePedidoInput(&in); err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]any{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure per-product table exists (also normalizes produto).
|
||||
table, err := h.store.EnsureTableForProduto(ctx, in.ProdutoNome)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]any{"error": "produto_invalido"})
|
||||
return
|
||||
}
|
||||
|
||||
// Keep normalized form for the widget to build URLs safely.
|
||||
// table = "nps_" + produto_normalizado
|
||||
produtoNormalizado := strings.TrimPrefix(table, "nps_")
|
||||
|
||||
// Rules
|
||||
respRecente, err := h.store.HasRespostaValidaRecente(ctx, table, in.InquilinoCodigo, in.UsuarioCodigo)
|
||||
if err != nil {
|
||||
// Fail-closed
|
||||
writeJSON(w, http.StatusOK, PedidoResponse{PodeAbrir: false, Motivo: "erro"})
|
||||
return
|
||||
}
|
||||
if respRecente {
|
||||
writeJSON(w, http.StatusOK, PedidoResponse{PodeAbrir: false, Motivo: "resposta_recente"})
|
||||
return
|
||||
}
|
||||
|
||||
pedidoAberto, err := h.store.HasPedidoEmAbertoRecente(ctx, table, in.InquilinoCodigo, in.UsuarioCodigo)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusOK, PedidoResponse{PodeAbrir: false, Motivo: "erro"})
|
||||
return
|
||||
}
|
||||
if pedidoAberto {
|
||||
writeJSON(w, http.StatusOK, PedidoResponse{PodeAbrir: false, Motivo: "pedido_em_aberto"})
|
||||
return
|
||||
}
|
||||
|
||||
id, err := h.store.CreatePedido(ctx, table, in, r)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusOK, PedidoResponse{PodeAbrir: false, Motivo: "erro"})
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, map[string]any{"pode_abrir": true, "id": id, "produto": produtoNormalizado})
|
||||
}
|
||||
|
||||
func (h *Handlers) PatchResposta(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
produtoParam := chi.URLParam(r, "produto")
|
||||
id := chi.URLParam(r, "id")
|
||||
|
||||
// produtoParam already in path; sanitize again.
|
||||
prod, err := db.NormalizeProduto(produtoParam)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]any{"error": "produto_invalido"})
|
||||
return
|
||||
}
|
||||
table := db.TableNameForProduto(prod)
|
||||
if err := db.EnsureNPSTable(ctx, h.store.pool, table); err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]any{"error": "db"})
|
||||
return
|
||||
}
|
||||
|
||||
var in PatchInput
|
||||
if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]any{"error": "json_invalido"})
|
||||
return
|
||||
}
|
||||
if err := ValidatePatchInput(&in); err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]any{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if in.Nota == nil && in.Justificativa == nil && !in.Finalizar {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]any{"error": "nada_para_atualizar"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.store.PatchRegistro(ctx, table, id, in); err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]any{"error": "db"})
|
||||
return
|
||||
}
|
||||
|
||||
// If called via HTMX, respond with refreshed HTML fragment.
|
||||
if r.Header.Get("HX-Request") == "true" {
|
||||
reg, err := h.store.GetRegistro(ctx, table, id)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte("db"))
|
||||
return
|
||||
}
|
||||
data := FormPageData{Produto: prod, ID: id, Reg: reg}
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
h.tpl.Render(w, "form_inner.html", data)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, map[string]any{"ok": true})
|
||||
}
|
||||
|
||||
func (h *Handlers) GetForm(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
produtoParam := chi.URLParam(r, "produto")
|
||||
id := chi.URLParam(r, "id")
|
||||
|
||||
prod, err := db.NormalizeProduto(produtoParam)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte("produto invalido"))
|
||||
return
|
||||
}
|
||||
table := db.TableNameForProduto(prod)
|
||||
if err := db.EnsureNPSTable(ctx, h.store.pool, table); err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte("db"))
|
||||
return
|
||||
}
|
||||
|
||||
reg, err := h.store.GetRegistro(ctx, table, id)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte("nao encontrado"))
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte("db"))
|
||||
return
|
||||
}
|
||||
|
||||
data := FormPageData{
|
||||
Produto: prod,
|
||||
ID: id,
|
||||
Reg: reg,
|
||||
}
|
||||
|
||||
// Always return a standalone HTML page so the widget can use iframe.
|
||||
// But the inner container is also HTMX-friendly (it swaps itself).
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
h.tpl.Render(w, "form_page.html", data)
|
||||
}
|
||||
|
||||
func writeJSON(w http.ResponseWriter, status int, v any) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(status)
|
||||
_ = json.NewEncoder(w).Encode(v)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue