plan/10: Add /api/stats endpoint and dashboard stats badge
- Add GetStats handler returning agent counts, plan/task/queue breakdowns - Wire GET /api/stats route in api/main.go - Add stats_test.go with handler unit tests - Add StatsWidget.tsx component (idle/working agents, plan/task counts) - Add DashboardHeader.tsx displaying stats badge in the dashboard header
This commit is contained in:
93
api/handlers/stats.go
Normal file
93
api/handlers/stats.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type StatsResponse struct {
|
||||
AgentCount int `json:"agent_count"`
|
||||
WorkingAgents int `json:"working_agents"`
|
||||
PlanCounts map[string]int `json:"plan_counts"`
|
||||
TaskCounts map[string]int `json:"task_counts"`
|
||||
QueueCounts map[string]int `json:"queue_counts"`
|
||||
}
|
||||
|
||||
func (h *Handler) GetStats(c *gin.Context) {
|
||||
// Get total agent count
|
||||
agentCount, err := h.db.Query(`SELECT COUNT(*) FROM agents`)
|
||||
if err != nil {
|
||||
c.JSON(500, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
defer agentCount.Close()
|
||||
var totalAgents int
|
||||
if agentCount.Next() {
|
||||
agentCount.Scan(&totalAgents)
|
||||
}
|
||||
|
||||
// Get working agent count
|
||||
workingAgents, err := h.db.Query(`SELECT COUNT(*) FROM agents WHERE status = 'working'`)
|
||||
if err != nil {
|
||||
c.JSON(500, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
defer workingAgents.Close()
|
||||
var workingAgentCount int
|
||||
if workingAgents.Next() {
|
||||
workingAgents.Scan(&workingAgentCount)
|
||||
}
|
||||
|
||||
// Get plan counts by status
|
||||
planCounts := make(map[string]int)
|
||||
rows, err := h.db.Query(`SELECT status, COUNT(*) FROM plans GROUP BY status`)
|
||||
if err != nil {
|
||||
c.JSON(500, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var status string
|
||||
var count int
|
||||
rows.Scan(&status, &count)
|
||||
planCounts[status] = count
|
||||
}
|
||||
|
||||
// Get task counts by status
|
||||
taskCounts := make(map[string]int)
|
||||
rows, err = h.db.Query(`SELECT status, COUNT(*) FROM tasks GROUP BY status`)
|
||||
if err != nil {
|
||||
c.JSON(500, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var status string
|
||||
var count int
|
||||
rows.Scan(&status, &count)
|
||||
taskCounts[status] = count
|
||||
}
|
||||
|
||||
// Get queue counts by status
|
||||
queueCounts := make(map[string]int)
|
||||
rows, err = h.db.Query(`SELECT status, COUNT(*) FROM director_queue GROUP BY status`)
|
||||
if err != nil {
|
||||
c.JSON(500, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var status string
|
||||
var count int
|
||||
rows.Scan(&status, &count)
|
||||
queueCounts[status] = count
|
||||
}
|
||||
|
||||
// Return stats
|
||||
c.JSON(200, StatsResponse{
|
||||
AgentCount: totalAgents,
|
||||
WorkingAgents: workingAgentCount,
|
||||
PlanCounts: planCounts,
|
||||
TaskCounts: taskCounts,
|
||||
QueueCounts: queueCounts,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user