package handlers import ( "encoding/json" "net/http" "strconv" "github.com/rede5/gohorsejobs/backend/internal/dto" "github.com/rede5/gohorsejobs/backend/internal/services" ) type AdminHandlers struct { adminService *services.AdminService auditService *services.AuditService jobService *services.JobService } type RoleAccess struct { Role string `json:"role"` Description string `json:"description"` Actions []string `json:"actions"` } type UpdateCompanyStatusRequest struct { Active *bool `json:"active,omitempty"` Verified *bool `json:"verified,omitempty"` } type UpdateJobStatusRequest struct { Status string `json:"status"` } type CreateTagRequest struct { Name string `json:"name"` Category string `json:"category"` } type UpdateTagRequest struct { Name *string `json:"name,omitempty"` Active *bool `json:"active,omitempty"` } func NewAdminHandlers(adminService *services.AdminService, auditService *services.AuditService, jobService *services.JobService) *AdminHandlers { return &AdminHandlers{ adminService: adminService, auditService: auditService, jobService: jobService, } } func (h *AdminHandlers) ListAccessRoles(w http.ResponseWriter, r *http.Request) { roles := []RoleAccess{ { Role: "admin", Description: "Administrador geral da plataforma", Actions: []string{ "criar/editar/excluir usuários", "aprovar empresas", "moderar vagas", "gerir tags e categorias", }, }, { Role: "moderador", Description: "Moderação de vagas e conteúdo", Actions: []string{ "aprovar, recusar ou pausar vagas", "marcar vagas denunciadas", "revisar empresas pendentes", }, }, { Role: "suporte", Description: "Suporte ao usuário", Actions: []string{ "acessar perfil de usuário", "resetar senhas", "analisar logs de acesso", }, }, { Role: "financeiro", Description: "Gestão financeira e faturamento", Actions: []string{ "ver planos e pagamentos", "exportar relatórios financeiros", "controlar notas e cobranças", }, }, } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(roles) } func (h *AdminHandlers) ListLoginAudits(w http.ResponseWriter, r *http.Request) { limit, _ := strconv.Atoi(r.URL.Query().Get("limit")) entries, err := h.auditService.ListLogins(r.Context(), limit) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(entries) } func (h *AdminHandlers) ListCompanies(w http.ResponseWriter, r *http.Request) { page, _ := strconv.Atoi(r.URL.Query().Get("page")) if page < 1 { page = 1 } limit, _ := strconv.Atoi(r.URL.Query().Get("limit")) if limit < 1 { limit = 10 } var verified *bool if verifiedParam := r.URL.Query().Get("verified"); verifiedParam != "" { value := verifiedParam == "true" verified = &value } companies, total, err := h.adminService.ListCompanies(r.Context(), verified, page, limit) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } response := dto.PaginatedResponse{ Data: companies, Pagination: dto.Pagination{ Page: page, Limit: limit, Total: total, }, } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(response) } func (h *AdminHandlers) UpdateCompanyStatus(w http.ResponseWriter, r *http.Request) { id := r.PathValue("id") var req UpdateCompanyStatusRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid Request", http.StatusBadRequest) return } company, err := h.adminService.UpdateCompanyStatus(r.Context(), id, req.Active, req.Verified) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(company) } func (h *AdminHandlers) ListJobs(w http.ResponseWriter, r *http.Request) { page, _ := strconv.Atoi(r.URL.Query().Get("page")) limit, _ := strconv.Atoi(r.URL.Query().Get("limit")) status := r.URL.Query().Get("status") filter := dto.JobFilterQuery{ PaginationQuery: dto.PaginationQuery{ Page: page, Limit: limit, }, } if status != "" { filter.Status = &status } jobs, total, err := h.jobService.GetJobs(filter) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } response := dto.PaginatedResponse{ Data: jobs, Pagination: dto.Pagination{ Page: page, Limit: limit, Total: total, }, } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(response) } func (h *AdminHandlers) UpdateJobStatus(w http.ResponseWriter, r *http.Request) { id := r.PathValue("id") var req UpdateJobStatusRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid Request", http.StatusBadRequest) return } if req.Status == "" { http.Error(w, "Status is required", http.StatusBadRequest) return } status := req.Status job, err := h.jobService.UpdateJob(id, dto.UpdateJobRequest{Status: &status}) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(job) } func (h *AdminHandlers) DuplicateJob(w http.ResponseWriter, r *http.Request) { id := r.PathValue("id") job, err := h.adminService.DuplicateJob(r.Context(), id) 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(job) } func (h *AdminHandlers) ListTags(w http.ResponseWriter, r *http.Request) { category := r.URL.Query().Get("category") if category == "" { category = "" } var categoryFilter *string if category != "" { categoryFilter = &category } tags, err := h.adminService.ListTags(r.Context(), categoryFilter) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(tags) } func (h *AdminHandlers) CreateTag(w http.ResponseWriter, r *http.Request) { var req CreateTagRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid Request", http.StatusBadRequest) return } if req.Name == "" || req.Category == "" { http.Error(w, "Name and category are required", http.StatusBadRequest) return } tag, err := h.adminService.CreateTag(r.Context(), req.Name, req.Category) 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(tag) } func (h *AdminHandlers) UpdateTag(w http.ResponseWriter, r *http.Request) { idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { http.Error(w, "Invalid tag ID", http.StatusBadRequest) return } var req UpdateTagRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid Request", http.StatusBadRequest) return } tag, err := h.adminService.UpdateTag(r.Context(), id, req.Name, req.Active) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(tag) } func (h *AdminHandlers) ListCandidates(w http.ResponseWriter, r *http.Request) { candidates, stats, err := h.adminService.ListCandidates(r.Context()) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } response := dto.CandidateListResponse{ Stats: stats, Candidates: candidates, } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(response) }