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) } // ---- LOGISTICA DAILY INVITATIONS & PROFESSIONALS LIST ---- // func (h *Handler) ListProfissionaisDisponiveis(c *gin.Context) { var query ListDisponiveisInput if err := c.ShouldBindQuery(&query); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } regiao := c.GetString("regiao") res, err := h.service.ListProfissionaisDisponiveisLogistica(c.Request.Context(), query, regiao) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } formatted := make([]map[string]interface{}, len(res.Data)) for i, r := range res.Data { formatted[i] = map[string]interface{}{ "id": uuid.UUID(r.ID.Bytes).String(), "nome": r.Nome, "role": r.FuncaoNome.String, "avatar_url": r.AvatarUrl.String, "whatsapp": r.Whatsapp.String, "cidade": r.Cidade.String, "carro_disponivel": r.CarroDisponivel.Bool, } } c.JSON(http.StatusOK, gin.H{ "data": formatted, "total": res.Total, "page": res.Page, "total_pages": res.TotalPages, }) } func (h *Handler) CreateConvite(c *gin.Context) { var req CreateConviteInput if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } regiao := c.GetString("regiao") convite, err := h.service.CreateConviteDiario(c.Request.Context(), req, regiao) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusCreated, gin.H{"id": uuid.UUID(convite.ID.Bytes).String()}) } // Get Invitations for logged professional func (h *Handler) ListConvitesProfissional(c *gin.Context) { // For MVP: Let the frontend send the prof ID or use a dedicated endpoint where frontend provides it. // Actually frontend can provide ?prof_id=... pID := c.Query("prof_id") if pID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "prof_id is required"}) return } convites, err := h.service.ListConvitesDoProfissional(c.Request.Context(), pID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Format response nicely resp := make([]map[string]interface{}, len(convites)) for i, c := range convites { // convert time.Time to YYYY-MM-DD var dateStr string if c.Data.Valid { dateStr = c.Data.Time.Format("2006-01-02") } resp[i] = map[string]interface{}{ "id": uuid.UUID(c.ID.Bytes).String(), "profissional_id": uuid.UUID(c.ProfissionalID.Bytes).String(), "data": dateStr, "status": c.Status.String, "motivo_rejeicao": c.MotivoRejeicao.String, "criado_em": c.CriadoEm.Time.Format("2006-01-02T15:04:05Z07:00"), } } c.JSON(http.StatusOK, resp) } // Respond to an invitation func (h *Handler) ResponderConvite(c *gin.Context) { conviteID := c.Param("id") if conviteID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "id required"}) return } var req ResponderConviteInput if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } convite, err := h.service.ResponderConvite(c.Request.Context(), conviteID, req) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"status": convite.Status.String}) } // Get Invitations by Date for Logistics Manager func (h *Handler) ListConvitesPorData(c *gin.Context) { dataStr := c.Query("data") if dataStr == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "data is required"}) return } regiao := c.GetString("regiao") convites, err := h.service.ListConvitesPorData(c.Request.Context(), dataStr, regiao) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Format response resp := make([]map[string]interface{}, len(convites)) for i, conv := range convites { var dateStr string if conv.Data.Valid { dateStr = conv.Data.Time.Format("2006-01-02") } resp[i] = map[string]interface{}{ "id": uuid.UUID(conv.ID.Bytes).String(), "profissional_id": uuid.UUID(conv.ProfissionalID.Bytes).String(), "data": dateStr, "status": conv.Status.String, "motivo_rejeicao": conv.MotivoRejeicao.String, "criado_em": conv.CriadoEm.Time.Format("2006-01-02T15:04:05Z07:00"), } } c.JSON(http.StatusOK, resp) }