package handlers import ( "encoding/json" "net/http" "strconv" "github.com/rede5/gohorsejobs/backend/internal/models" "github.com/rede5/gohorsejobs/backend/internal/services" ) type TicketHandler struct { service *services.TicketService } func NewTicketHandler(service *services.TicketService) *TicketHandler { return &TicketHandler{service: service} } // CreateTicket creates a new support ticket // @Summary Create Ticket // @Description Create a new support ticket // @Tags Tickets // @Accept json // @Produce json // @Param ticket body models.CreateTicketRequest true "Ticket data" // @Success 201 {object} models.Ticket // @Failure 400 {string} string "Invalid Request" // @Router /api/v1/tickets [post] func (h *TicketHandler) CreateTicket(w http.ResponseWriter, r *http.Request) { var req models.CreateTicketRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request", http.StatusBadRequest) return } if req.Subject == "" || req.Description == "" { http.Error(w, "Subject and description are required", http.StatusBadRequest) return } // TODO: Get user ID from auth context var userID *int ticket, err := h.service.Create(userID, nil, req) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(ticket) } // GetTickets lists tickets // @Summary List Tickets // @Description Get all tickets with optional filters // @Tags Tickets // @Produce json // @Param status query string false "Filter by status" // @Param priority query string false "Filter by priority" // @Param limit query int false "Limit results" // @Param offset query int false "Offset for pagination" // @Success 200 {array} models.Ticket // @Router /api/v1/tickets [get] func (h *TicketHandler) GetTickets(w http.ResponseWriter, r *http.Request) { status := r.URL.Query().Get("status") priority := r.URL.Query().Get("priority") limit, _ := strconv.Atoi(r.URL.Query().Get("limit")) offset, _ := strconv.Atoi(r.URL.Query().Get("offset")) if limit == 0 { limit = 50 } tickets, err := h.service.List(status, priority, limit, offset) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(tickets) } // GetTicketByID gets a specific ticket // @Summary Get Ticket // @Description Get a ticket by ID // @Tags Tickets // @Produce json // @Param id path int true "Ticket ID" // @Success 200 {object} models.Ticket // @Failure 404 {string} string "Not Found" // @Router /api/v1/tickets/{id} [get] func (h *TicketHandler) GetTicketByID(w http.ResponseWriter, r *http.Request) { idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { http.Error(w, "Invalid ticket ID", http.StatusBadRequest) return } ticket, err := h.service.GetByID(id) if err != nil { http.Error(w, "Ticket not found", http.StatusNotFound) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(ticket) } // UpdateTicket updates a ticket // @Summary Update Ticket // @Description Update ticket status, priority or assignment // @Tags Tickets // @Accept json // @Produce json // @Param id path int true "Ticket ID" // @Param ticket body models.UpdateTicketRequest true "Update data" // @Success 200 {object} models.Ticket // @Router /api/v1/tickets/{id} [put] func (h *TicketHandler) UpdateTicket(w http.ResponseWriter, r *http.Request) { idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { http.Error(w, "Invalid ticket ID", http.StatusBadRequest) return } var req models.UpdateTicketRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request", http.StatusBadRequest) return } ticket, err := h.service.Update(id, req) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(ticket) } // GetTicketMessages gets messages for a ticket // @Summary Get Ticket Messages // @Description Get all messages for a ticket // @Tags Tickets // @Produce json // @Param id path int true "Ticket ID" // @Success 200 {array} models.TicketMessage // @Router /api/v1/tickets/{id}/messages [get] func (h *TicketHandler) GetTicketMessages(w http.ResponseWriter, r *http.Request) { idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { http.Error(w, "Invalid ticket ID", http.StatusBadRequest) return } // TODO: Check if user is admin for internal messages includeInternal := true messages, err := h.service.GetMessages(id, includeInternal) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(messages) } // AddTicketMessage adds a message to a ticket // @Summary Add Ticket Message // @Description Add a message/reply to a ticket // @Tags Tickets // @Accept json // @Produce json // @Param id path int true "Ticket ID" // @Param message body models.AddTicketMessageRequest true "Message" // @Success 201 {object} models.TicketMessage // @Router /api/v1/tickets/{id}/messages [post] func (h *TicketHandler) AddTicketMessage(w http.ResponseWriter, r *http.Request) { idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { http.Error(w, "Invalid ticket ID", http.StatusBadRequest) return } var req models.AddTicketMessageRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request", http.StatusBadRequest) return } if req.Message == "" { http.Error(w, "Message is required", http.StatusBadRequest) return } // TODO: Get user ID from auth context var userID *int msg, err := h.service.AddMessage(id, userID, req) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(msg) } // GetTicketStats gets ticket statistics // @Summary Get Ticket Stats // @Description Get ticket statistics for dashboard // @Tags Tickets // @Produce json // @Success 200 {object} models.TicketStats // @Router /api/v1/tickets/stats [get] func (h *TicketHandler) GetTicketStats(w http.ResponseWriter, r *http.Request) { stats, err := h.service.GetStats() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(stats) }