photum/backend/internal/logistica/handler.go
NANDO9322 804a566095 feat(ops): implementa modulo operacional completo (escala, logistica, equipe)
- Backend: Migrations para tabelas 'escalas' e 'logistica' (transporte)
- Backend: Handlers e Services completos para gestão de escalas e logística
- Backend: Suporte a auth vinculado a perfil profissional
- Frontend: Nova página de Detalhes Operacionais (/agenda/:id)
- Frontend: Componente EventScheduler com verificação robusta de conflitos
- Frontend: Componente EventLogistics para gestão de motoristas e caronas
- Frontend: Modal de Detalhes de Profissional unificado (Admin + Self-view)
- Frontend: Dashboard com modal de gestão de equipe e filtros avançados
- Fix: Correção crítica de timezone (UTC) em horários de agendamento
- Fix: Tratamento de URLs no campo de local do evento
- Fix: Filtros de profissional com carro na logística
2025-12-29 16:01:17 -03:00

164 lines
4.6 KiB
Go

package logistica
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
type Handler struct {
service *Service
}
func NewHandler(service *Service) *Handler {
return &Handler{service: service}
}
// CreateCarro
func (h *Handler) CreateCarro(c *gin.Context) {
var req CreateCarroInput
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
carro, err := h.service.CreateCarro(c.Request.Context(), req)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, gin.H{"id": uuid.UUID(carro.ID.Bytes).String()})
}
// ListCarros
func (h *Handler) ListCarros(c *gin.Context) {
agendaID := c.Query("agenda_id")
if agendaID == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "agenda_id is required"})
return
}
carros, err := h.service.ListCarros(c.Request.Context(), agendaID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// Fetch passengers for each car? Or let frontend do it on demand?
// For main list, efficient to just list cars, or maybe passengers embedded.
// For simplicity, let's embed passengers if possible, but list returns row.
// We will return list of cars. The frontend can fetch passengers per car or we should use a transaction/composite query.
// MVP: Check if we can just return the cars now.
resp := make([]map[string]interface{}, len(carros))
for i, car := range carros {
var driverName string
// Logic: if system driver name exists, use it. Else use custom name.
if car.MotoristaNomeSistema.String != "" {
driverName = car.MotoristaNomeSistema.String
} else {
driverName = car.NomeMotorista.String
}
var avatar string
if car.MotoristaAvatar.Valid {
avatar = car.MotoristaAvatar.String
}
resp[i] = map[string]interface{}{
"id": uuid.UUID(car.ID.Bytes).String(),
"agenda_id": uuid.UUID(car.AgendaID.Bytes).String(),
"driver_id": uuid.UUID(car.MotoristaID.Bytes).String(), // May be empty UUID or nil representation
"driver_name": driverName,
"driver_avatar": avatar,
"arrival_time": car.HorarioChegada.String,
"notes": car.Observacoes.String,
}
}
c.JSON(http.StatusOK, resp)
}
// DeleteCarro
func (h *Handler) DeleteCarro(c *gin.Context) {
id := c.Param("id")
if id == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "id required"})
return
}
if err := h.service.DeleteCarro(c.Request.Context(), id); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "deleted"})
}
// UpdateCarro
func (h *Handler) UpdateCarro(c *gin.Context) {
id := c.Param("id")
var req UpdateCarroInput
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
_, err := h.service.UpdateCarro(c.Request.Context(), id, req)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "updated"})
}
// Passengers
type AddPassengerInput struct {
ProfissionalID string `json:"profissional_id"`
}
func (h *Handler) AddPassenger(c *gin.Context) {
carID := c.Param("id")
var req AddPassengerInput
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
err := h.service.AddPassageiro(c.Request.Context(), carID, req.ProfissionalID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "passenger added"})
}
func (h *Handler) RemovePassenger(c *gin.Context) {
carID := c.Param("id")
profID := c.Param("profID")
err := h.service.RemovePassageiro(c.Request.Context(), carID, profID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "passenger removed"})
}
func (h *Handler) ListPassengers(c *gin.Context) {
carID := c.Param("id")
passengers, err := h.service.ListPassageiros(c.Request.Context(), carID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
resp := make([]map[string]interface{}, len(passengers))
for i, p := range passengers {
resp[i] = map[string]interface{}{
"id": uuid.UUID(p.ID.Bytes).String(),
"profissional_id": uuid.UUID(p.ProfissionalID.Bytes).String(),
"name": p.Nome,
"avatar_url": p.AvatarUrl.String,
"role": p.FuncaoNome.String,
}
}
c.JSON(http.StatusOK, resp)
}