conn = $conn; } /** * Get count of open tickets */ public function getOpenTicketCount(): int { $sql = "SELECT COUNT(*) as count FROM tickets WHERE status IN ('Open', 'Pending', 'In Progress')"; $result = $this->conn->query($sql); $row = $result->fetch_assoc(); return (int)$row['count']; } /** * Get count of closed tickets */ public function getClosedTicketCount(): int { $sql = "SELECT COUNT(*) as count FROM tickets WHERE status = 'Closed'"; $result = $this->conn->query($sql); $row = $result->fetch_assoc(); return (int)$row['count']; } /** * Get tickets grouped by priority */ public function getTicketsByPriority(): array { $sql = "SELECT priority, COUNT(*) as count FROM tickets WHERE status != 'Closed' GROUP BY priority ORDER BY priority"; $result = $this->conn->query($sql); $data = []; while ($row = $result->fetch_assoc()) { $data['P' . $row['priority']] = (int)$row['count']; } return $data; } /** * Get tickets grouped by status */ public function getTicketsByStatus(): array { $sql = "SELECT status, COUNT(*) as count FROM tickets GROUP BY status ORDER BY FIELD(status, 'Open', 'Pending', 'In Progress', 'Closed')"; $result = $this->conn->query($sql); $data = []; while ($row = $result->fetch_assoc()) { $data[$row['status']] = (int)$row['count']; } return $data; } /** * Get tickets grouped by category */ public function getTicketsByCategory(): array { $sql = "SELECT category, COUNT(*) as count FROM tickets WHERE status != 'Closed' GROUP BY category ORDER BY count DESC"; $result = $this->conn->query($sql); $data = []; while ($row = $result->fetch_assoc()) { $data[$row['category']] = (int)$row['count']; } return $data; } /** * Get average resolution time in hours */ public function getAverageResolutionTime(): float { $sql = "SELECT AVG(TIMESTAMPDIFF(HOUR, created_at, updated_at)) as avg_hours FROM tickets WHERE status = 'Closed' AND created_at IS NOT NULL AND updated_at IS NOT NULL AND updated_at > created_at"; $result = $this->conn->query($sql); $row = $result->fetch_assoc(); return $row['avg_hours'] ? round($row['avg_hours'], 1) : 0; } /** * Get count of tickets created today */ public function getTicketsCreatedToday(): int { $sql = "SELECT COUNT(*) as count FROM tickets WHERE DATE(created_at) = CURDATE()"; $result = $this->conn->query($sql); $row = $result->fetch_assoc(); return (int)$row['count']; } /** * Get count of tickets created this week */ public function getTicketsCreatedThisWeek(): int { $sql = "SELECT COUNT(*) as count FROM tickets WHERE YEARWEEK(created_at, 1) = YEARWEEK(CURDATE(), 1)"; $result = $this->conn->query($sql); $row = $result->fetch_assoc(); return (int)$row['count']; } /** * Get count of tickets closed today */ public function getTicketsClosedToday(): int { $sql = "SELECT COUNT(*) as count FROM tickets WHERE status = 'Closed' AND DATE(updated_at) = CURDATE()"; $result = $this->conn->query($sql); $row = $result->fetch_assoc(); return (int)$row['count']; } /** * Get tickets by assignee (top 5) */ public function getTicketsByAssignee(int $limit = 5): array { $sql = "SELECT u.display_name, u.username, COUNT(t.ticket_id) as ticket_count FROM tickets t JOIN users u ON t.assigned_to = u.user_id WHERE t.status != 'Closed' GROUP BY t.assigned_to ORDER BY ticket_count DESC LIMIT ?"; $stmt = $this->conn->prepare($sql); $stmt->bind_param('i', $limit); $stmt->execute(); $result = $stmt->get_result(); $data = []; while ($row = $result->fetch_assoc()) { $name = $row['display_name'] ?: $row['username']; $data[$name] = (int)$row['ticket_count']; } $stmt->close(); return $data; } /** * Get unassigned ticket count */ public function getUnassignedTicketCount(): int { $sql = "SELECT COUNT(*) as count FROM tickets WHERE assigned_to IS NULL AND status != 'Closed'"; $result = $this->conn->query($sql); $row = $result->fetch_assoc(); return (int)$row['count']; } /** * Get critical (P1) ticket count */ public function getCriticalTicketCount(): int { $sql = "SELECT COUNT(*) as count FROM tickets WHERE priority = 1 AND status != 'Closed'"; $result = $this->conn->query($sql); $row = $result->fetch_assoc(); return (int)$row['count']; } /** * Get all stats as a single array * * Uses caching to reduce database load. Stats are cached for STATS_CACHE_TTL seconds. * * @param bool $forceRefresh Force a cache refresh * @return array All dashboard statistics */ public function getAllStats(bool $forceRefresh = false): array { $cacheKey = 'dashboard_all'; if ($forceRefresh) { CacheHelper::delete(self::CACHE_PREFIX, $cacheKey); } return CacheHelper::remember( self::CACHE_PREFIX, $cacheKey, function() { return $this->fetchAllStats(); }, self::STATS_CACHE_TTL ); } /** * Fetch all stats from database (uncached) * * @return array All dashboard statistics */ private function fetchAllStats(): array { return [ 'open_tickets' => $this->getOpenTicketCount(), 'closed_tickets' => $this->getClosedTicketCount(), 'created_today' => $this->getTicketsCreatedToday(), 'created_this_week' => $this->getTicketsCreatedThisWeek(), 'closed_today' => $this->getTicketsClosedToday(), 'unassigned' => $this->getUnassignedTicketCount(), 'critical' => $this->getCriticalTicketCount(), 'avg_resolution_hours' => $this->getAverageResolutionTime(), 'by_priority' => $this->getTicketsByPriority(), 'by_status' => $this->getTicketsByStatus(), 'by_category' => $this->getTicketsByCategory(), 'by_assignee' => $this->getTicketsByAssignee() ]; } /** * Invalidate cached stats * * Call this method when ticket data changes to ensure fresh stats. */ public function invalidateCache(): void { CacheHelper::delete(self::CACHE_PREFIX, null); } }