what does the chart suppose to show?
<?php
session_start();
date_default_timezone_set('Asia/Manila');
global $wpdb;
// Using your exact table structure
$tickets_table = 'tickets';
$users_table = 'users';
$departments_table = 'departments';
// Initialize default stats to prevent null errors
$default_stats = [
'total_tickets' => 0,
'avg_resolution_time' => null,
'min_resolution_time' => null,
'max_resolution_time' => null,
'solved_count' => 0,
'open_count' => 0,
'assigned_count' => 0,
'pending_count' => 0,
'closed_count' => 0
];
// Fetch and calculate statistics based on your schema
$stats_query = $wpdb->get_row("
SELECT
COUNT(*) as total_tickets,
AVG(TIMESTAMPDIFF(MINUTE, created_at, updated_at)) as avg_resolution_time,
MIN(TIMESTAMPDIFF(MINUTE, created_at, updated_at)) as min_resolution_time,
MAX(TIMESTAMPDIFF(MINUTE, created_at, updated_at)) as max_resolution_time,
SUM(CASE WHEN status = 'solved' THEN 1 ELSE 0 END) as solved_count,
SUM(CASE WHEN status = 'open' THEN 1 ELSE 0 END) as open_count,
SUM(CASE WHEN status = 'assigned' THEN 1 ELSE 0 END) as assigned_count,
SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending_count,
SUM(CASE WHEN status = 'closed' THEN 1 ELSE 0 END) as closed_count
FROM $tickets_table
", ARRAY_A);
// Merge with defaults to ensure all keys exist
$stats = array_merge($default_stats, (array)$stats_query);
// Convert minutes to hours for display
function minutesToHours($minutes) {
if ($minutes === null || $minutes === 0) return 'N/A';
$hours = floor($minutes / 60);
$mins = $minutes % 60;
return sprintf("%dh %02dm", $hours, $mins);
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard - RJL DeskTrack</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0; padding: 0;
background-color: #f4f7f9; color: #333;
}
.dashboard-container { display: flex; }
.sidebar {
width: 240px;
background-color: #c1d8f0;
color: #000;
height: 100vh;
position: fixed;
left: 0; top: 0;
overflow-y: auto;
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
z-index: 1000;
}
.sidebar-header {
padding: 20px;
text-align: center;
}
.sidebar-header .inventory-name {
font-size: 24px;
font-weight: bold;
color: #000;
}
.sidebar-menu {
padding: 0;
}
.sidebar-menu ul {
list-style: none;
margin: 0;
padding: 0;
}
.sidebar-menu li a {
display: flex;
align-items: center;
padding: 12px 20px;
text-decoration: none;
color: #000;
transition: 0.3s;
font-size: 16px;
}
.sidebar-menu li a i {
margin-right: 12px;
width: 20px;
text-align: center;
}
.sidebar-menu li a:hover,
.sidebar-menu li a.active {
background-color: #ffffff;
color: #000;
}
.header {
position: fixed;
top: 0;
left: 240px;
right: 0;
display: flex;
justify-content: space-between;
align-items: center;
background-color: #4663ac;
padding: 10px 30px;
height: 60px;
z-index: 999;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.header-left {
display: flex;
align-items: center;
}
.header-left .date-time {
font-size: 15px;
color: #fff;
font-weight: 500;
}
.header-right {
display: flex;
align-items: center;
font-size: 16px;
color: #ffffff;
}
.user-dropdown {
position: relative;
display: inline-block;
cursor: pointer;
}
.user-dropdown-content {
display: none;
position: absolute;
right: 0;
background-color: #D1B48C;
min-width: 100px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
border-radius: 4px;
}
.user-dropdown-header {
padding: 8px 12px;
background-color: #D1B48C;
border-bottom: 1px solid #e9ecef;
display: flex;
align-items: center;
gap: 8px;
}
.user-dropdown-header i {
font-size: 20px;
color: #000000;
}
.user-dropdown-content.user-info {
display: flex;
align-items: right;
gap: 1px;
color: #000000;
}
.user-dropdown-content a {
display: flex;
padding: 8px 15px;
align-items: center;
color: #000000;
text-decoration: none;
font-size: 16px;
transition: all 0.2s;
}
.user-dropdown-content a i {
font-size: 16px;
margin-right: 8px;
}
.user-dropdown-content a:hover {
text-decoration: underline;
color: #000000;
}
.user-dropdown:hover .user-dropdown-content {
display: block;
}
.header-right i {
color: #ffffff;
font-size:40px;
}
.header-right span {
font-size: 15px;
color: #ffffff;
}
.main-content {
margin-left: 240px;
padding: 80px 20px 20px 20px;
flex-grow: 1;
}
/* Strict 2x2 Grid Layout */
.dashboard-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(2, auto);
gap: 20px;
width: 100%;
max-width: 800px;
margin: 0 auto;
}
.dashboard-box {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.06);
border: 1px solid #e9ecef;
display: flex;
align-items: center;
gap: 15px;
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
}
.dashboard-box:hover {
transform: translateY(-3px);
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.09);
}
.dashboard-box i.icon {
font-size: 24px;
color: #fff;
border-radius: 50%;
padding: 12px;
width: 50px;
height: 50px;
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.dashboard-box:nth-child(1) i.icon { background-color: #17a2b8; }
.dashboard-box:nth-child(2) i.icon { background-color: #28a745; }
.dashboard-box:nth-child(3) i.icon { background-color: #ffc107; }
.dashboard-box:nth-child(4) i.icon { background-color: #dc3545; }
.dashboard-box .detail {
text-align: left;
overflow: hidden;
}
.dashboard-box .count {
font-size: 24px;
font-weight: 600;
color: #343a40;
line-height: 1.1;
margin-bottom: 5px;
}
.dashboard-box .label {
font-size: 14px;
color: #6c757d;
}
/* Report Styles */
.report-container {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.stat-card h3 {
margin-top: 0;
color: #555;
font-size: 16px;
}
.stat-value {
font-size: 28px;
font-weight: bold;
margin: 10px 0;
color: #333;
}
.stat-description {
color: #777;
font-size: 14px;
}
.chart-container {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 30px;
}
.chart-container canvas {
width: 100% !important;
height: 400px !important;
}
/* Responsive Adjustments */
@media (max-width: 768px) {
.dashboard-grid,
.report-container {
grid-template-columns: 1fr;
max-width: 400px;
}
}
</style>
</head>
<body>
<div class="dashboard-container">
<aside class="sidebar">
<div class="sidebar-header">
<div class="inventory-name">RJL DeskTrack</div>
</div>
<div class="sidebar-menu">
<ul>
<?php if ($_SESSION['role'] !== 'user'): ?>
<li><a href="http://localhost/helpdesk/?page_id=57"><i class="fas fa-tachometer-alt"></i> Dashboard</a></li>
<li><a href="http://localhost/helpdesk/?page_id=62"><i class="fas fa-users-cog"></i> Users</a></li>
<li><a href="http://localhost/helpdesk/?page_id=66"><i class="fas fa-building"></i> Departments</a></li>
<?php endif; ?>
<li><a href="http://localhost/helpdesk/?page_id=70"><i class="fas fa-ticket-alt"></i> Tickets</a></li>
<li><a href="http://localhost/helpdesk/?page_id=74"><i class="fas fa-envelope"></i> Messages</a></li>
<?php if ($_SESSION['role'] !== 'user'): ?>
<li><a href="http://localhost/helpdesk/?page_id=78"><i class="fas fa-chart-bar"></i> Reports</a></li>
<?php endif; ?>
</ul>
</div>
</aside>
<main class="main-content">
<header class="header">
<div class="header-left">
<span class="date-time" id="current-date-time"></span>
</div>
<div class="header-right">
<div class="user-dropdown">
<div class="user-info">
<span id="current-username-header">
<?= htmlspecialchars($_SESSION['username']) ?>
<i class="fas fa-user-circle"></i>
</span>
</div>
<div class="user-dropdown-content">
<div class="user-dropdown-header">
<i class="fas fa-user-circle" style="color:#000000;"></i>
<span style="color:#000000;"><?= htmlspecialchars($_SESSION['username']) ?></span>
</div>
<a href="#" onclick="return confirm('Are you sure you want to log out?');">
<i class="fas fa-sign-out-alt" style="font-size: 16px; color:#000000;"></i>Logout
</a>
</div>
</div>
</div>
</header>
<h1>Performance Reports</h1>
<div class="report-container">
<div class="stat-card">
<h3>Total Tickets</h3>
<div class="stat-value"><?= (int)$stats['total_tickets'] ?></div>
<div class="stat-description">All tickets in the system</div>
</div>
<div class="stat-card">
<h3>Solved Tickets</h3>
<div class="stat-value"><?= (int)$stats['solved_count'] ?></div>
<div class="stat-description">Successfully solved tickets</div>
</div>
<div class="stat-card">
<h3>Open Tickets</h3>
<div class="stat-value"><?= (int)$stats['open_count'] ?></div>
<div class="stat-description">Tickets awaiting resolution</div>
</div>
<div class="stat-card">
<h3>Avg. Resolution Time</h3>
<div class="stat-value"><?= minutesToHours($stats['avg_resolution_time']) ?></div>
<div class="stat-description">Average time to resolve tickets</div>
</div>
</div>
<!-- Charts Container - Only show if we have data -->
<?php if ($stats['total_tickets'] > 0): ?>
<div class="chart-container">
<canvas id="resolutionTimeChart"></canvas>
</div>
<div class="chart-container">
<canvas id="ticketsByStatusChart"></canvas>
</div>
<?php endif; ?>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Debug - check if chart containers exist
console.log('Chart containers:', {
resolution: document.getElementById('resolutionTimeChart'),
status: document.getElementById('ticketsByStatusChart')
});
<?php if ($stats['total_tickets'] > 0): ?>
// Resolution Time Chart - with more checks
const resolutionCtx = document.getElementById('resolutionTimeChart');
if (resolutionCtx) {
console.log('Creating resolution time chart');
try {
new Chart(resolutionCtx, {
type: 'bar',
data: {
labels: ['Minimum', 'Average', 'Maximum'],
datasets: [{
label: 'Resolution Time (hours)',
data: [
<?= $stats['min_resolution_time'] ?? 0 ?>,
<?= $stats['avg_resolution_time'] ?? 0 ?>,
<?= $stats['max_resolution_time'] ?? 0 ?>
].map(min => min ? (min/60).toFixed(1) : 0),
backgroundColor: [
'rgba(75, 192, 192, 0.6)',
'rgba(54, 162, 235, 0.6)',
'rgba(255, 99, 132, 0.6)'
]
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
title: {
display: true,
text: 'Hours'
}
}
}
}
});
} catch (e) {
console.error('Chart error:', e);
}
}
// Status Chart
const statusCtx = document.getElementById('ticketsByStatusChart');
if (statusCtx) {
console.log('Creating status chart');
try {
new Chart(statusCtx, {
type: 'doughnut',
data: {
labels: ['Solved', 'Open', 'Assigned', 'Pending', 'Closed'],
datasets: [{
data: [
<?= $stats['solved_count'] ?? 0 ?>,
<?= $stats['open_count'] ?? 0 ?>,
<?= $stats['assigned_count'] ?? 0 ?>,
<?= $stats['pending_count'] ?? 0 ?>,
<?= $stats['closed_count'] ?? 0 ?>
],
backgroundColor: [
'rgba(40, 167, 69, 0.6)',
'rgba(255, 193, 7, 0.6)',
'rgba(13, 110, 253, 0.6)',
'rgba(255, 152, 0, 0.6)',
'rgba(108, 117, 125, 0.6)'
]
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right',
}
}
}
});
} catch (e) {
console.error('Status chart error:', e);
}
}
<?php endif; ?>
});
</script>
</body>
</html>