package admin import ( "github.com/gin-gonic/gin" "github.com/mattnite/forgejo-tickets/internal/forgejo" "github.com/mattnite/forgejo-tickets/internal/models" "github.com/rs/zerolog/log" ) type DashboardHandler struct { deps Dependencies } func (h *DashboardHandler) Index(c *gin.Context) { var userCount int64 if err := h.deps.DB.Model(&models.User{}).Count(&userCount).Error; err != nil { log.Error().Err(err).Msg("count users error") } var totalTickets int64 if err := h.deps.DB.Model(&models.Ticket{}).Count(&totalTickets).Error; err != nil { log.Error().Err(err).Msg("count tickets error") } // Get distinct repos that have tickets type repoInfo struct { ForgejoOwner string ForgejoRepo string } var repos []repoInfo h.deps.DB.Table("tickets"). Select("DISTINCT repos.forgejo_owner, repos.forgejo_repo"). Joins("JOIN repos ON repos.id = tickets.repo_id"). Scan(&repos) // Get all ticket issue numbers grouped by repo for matching type ticketIssue struct { ForgejoOwner string ForgejoRepo string ForgejoIssueNumber int64 } var ticketIssues []ticketIssue h.deps.DB.Table("tickets"). Select("repos.forgejo_owner, repos.forgejo_repo, tickets.forgejo_issue_number"). Joins("JOIN repos ON repos.id = tickets.repo_id"). Scan(&ticketIssues) // Build a set of known issue numbers per repo knownIssues := map[string]map[int64]bool{} for _, ti := range ticketIssues { key := ti.ForgejoOwner + "/" + ti.ForgejoRepo if knownIssues[key] == nil { knownIssues[key] = map[int64]bool{} } knownIssues[key][ti.ForgejoIssueNumber] = true } var openTickets, inProgressTickets, closedTickets int64 for _, repo := range repos { issues, err := h.deps.ForgejoClient.ListIssues(repo.ForgejoOwner, repo.ForgejoRepo, "all", "") if err != nil { log.Error().Err(err).Str("repo", repo.ForgejoOwner+"/"+repo.ForgejoRepo).Msg("dashboard: forgejo list issues error") continue } key := repo.ForgejoOwner + "/" + repo.ForgejoRepo known := knownIssues[key] for i := range issues { if !known[issues[i].Number] { continue } switch forgejo.DeriveStatus(&issues[i]) { case "open": openTickets++ case "in_progress": inProgressTickets++ case "closed": closedTickets++ } } } h.deps.Renderer.Render(c.Writer, c.Request, "admin/dashboard", map[string]interface{}{ "UserCount": userCount, "TotalTickets": totalTickets, "OpenTickets": openTickets, "InProgressTickets": inProgressTickets, "ClosedTickets": closedTickets, }) }