<?php
session_start();
require_once 'database/db_config.php';
// Check if user has admin privileges
require_admin_privileges('login.php');
// Get dashboard statistics with error handling
try {
// Get site settings
$site_settings = [];
$site_settings_query = "SELECT * FROM site_settings";
$site_settings_result = $conn->query($site_settings_query);
if ($site_settings_result && $site_settings_result->num_rows > 0) {
while ($row = $site_settings_result->fetch_assoc()) {
$site_settings[$row['setting_key']] = $row['setting_value'];
}
}
// Check if helper messages should be shown
$show_helper_messages = isset($site_settings['show_helper_messages']) ?
($site_settings['show_helper_messages'] === 'true') : true;
// Check if sliders table exists
$sliders_table_exists = $conn->query("SHOW TABLES LIKE 'sliders'")->num_rows > 0;
// Check if tables exist
$tables_exist = true;
$required_tables = ['users', 'courses', 'enrollments', 'payments'];
foreach ($required_tables as $table) {
$result = $conn->query("SHOW TABLES LIKE '$table'");
if ($result->num_rows == 0) {
$tables_exist = false;
break;
}
}
if (!$tables_exist) {
throw new Exception("Required tables do not exist. Please run the installation process first.");
}
$stats = [
'users' => $conn->query("SELECT COUNT(*) as count FROM users")->fetch_assoc()['count'],
'courses' => $conn->query("SELECT COUNT(*) as count FROM courses")->fetch_assoc()['count'],
'enrollments' => $conn->query("SELECT COUNT(*) as count FROM enrollments")->fetch_assoc()['count'],
'revenue' => $conn->query("SELECT COALESCE(SUM(amount), 0) as total FROM payments WHERE status = 'completed'")->fetch_assoc()['total']
];
} catch (Exception $e) {
// Log the error and show a user-friendly message
error_log("Dashboard statistics error: " . $e->getMessage());
$stats = [
'users' => 0,
'courses' => 0,
'enrollments' => 0,
'revenue' => 0
];
// If tables don't exist, redirect to installation
if (strpos($e->getMessage(), "Required tables do not exist") !== false) {
header('Location: install.php');
exit();
}
}
// Include header
include_once 'includes/header.php';
?>
<div class="container-fluid">
<!-- Page Heading -->
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">Dashboard</h1>
<a href="reports.php" class="d-none d-sm-inline-block btn btn-primary shadow-sm">
<i class="fas fa-download fa-sm text-white-50"></i> Generate Report
</a>
</div>
<!-- Messages for new installation -->
<div class="row">
<?php if ($show_helper_messages): ?>
<div class="col-12 mb-4">
<div class="alert alert-info alert-dismissible fade show">
<div class="d-flex justify-content-between align-items-center">
<div>
<strong>Helper Messages</strong>
<p class="mb-0">These messages will guide you through setting up your website. You can dismiss them permanently using the button below.</p>
</div>
<div>
<a href="update_site_settings.php?action=disable" class="btn btn-sm btn-outline-danger me-2">
<i class="fas fa-times-circle me-1"></i> Dismiss Permanently
</a>
<a href="site_settings.php" class="btn btn-sm btn-primary">
<i class="fas fa-cog me-1"></i> Site Settings
</a>
</div>
</div>
</div>
</div>
<?php if (!$sliders_table_exists): ?>
<div class="col-12 mb-4">
<div class="alert alert-warning">
<i class="fas fa-exclamation-triangle"></i> The sliders table has not been created yet.
<a href="create_sliders_table_manual.php" class="alert-link">Click here to create it</a> before managing hero sliders.
</div>
</div>
<?php else: ?>
<div class="col-12 mb-4">
<div class="alert alert-info">
<i class="fas fa-info-circle"></i>
<a href="create_slider_images.php" class="alert-link">Click here to generate default slider images</a> if they are missing.
Or <a href="sliders.php" class="alert-link">manage your hero sliders</a>.
</div>
</div>
<?php endif; ?>
<div class="col-12 mb-4">
<div class="alert alert-info">
<i class="fas fa-database"></i>
<a href="update_database.php" class="alert-link">Click here to update your database structure</a> to ensure all tables have the latest fields.
</div>
</div>
<div class="col-12 mb-4">
<div class="alert alert-info">
<i class="fas fa-users"></i>
<a href="fix_is_team_member.php" class="alert-link">Click here to fix team member database issue</a> if you're seeing errors related to 'is_team_member' column.
</div>
</div>
<div class="col-12 mb-4">
<div class="alert alert-info">
<i class="fas fa-image"></i>
<a href="create_default_course_image.php" class="alert-link">Click here to generate the default course image</a> that will be used when no specific course image is available.
</div>
</div>
<?php endif; ?>
<!-- Total Users Card -->
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-primary shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">
Total Users</div>
<div class="h5 mb-0 font-weight-bold text-gray-800"><?php echo number_format($stats['users']); ?></div>
</div>
<div class="col-auto">
<i class="fas fa-users fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<!-- Total Courses Card -->
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-success shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-success text-uppercase mb-1">
Total Courses</div>
<div class="h5 mb-0 font-weight-bold text-gray-800"><?php echo number_format($stats['courses']); ?></div>
</div>
<div class="col-auto">
<i class="fas fa-book fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<!-- Enrollments Card -->
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-info shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-info text-uppercase mb-1">
Enrollments</div>
<div class="h5 mb-0 font-weight-bold text-gray-800"><?php echo number_format($stats['enrollments']); ?></div>
</div>
<div class="col-auto">
<i class="fas fa-clipboard-list fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<!-- Revenue Card -->
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-warning shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-warning text-uppercase mb-1">
Total Revenue</div>
<div class="h5 mb-0 font-weight-bold text-gray-800">₹<?php echo number_format($stats['revenue'], 2); ?></div>
</div>
<div class="col-auto">
<i class="fas fa-money-bill-wave fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Content Row -->
<div class="row">
<!-- Area Chart -->
<div class="col-xl-8 col-lg-7">
<div class="card shadow mb-4">
<!-- Card Header -->
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold text-primary">Revenue Overview</h6>
</div>
<!-- Card Body -->
<div class="card-body">
<div class="chart-area">
<div id="revenueChart" style="height: 300px;"></div>
</div>
</div>
</div>
</div>
<!-- Pie Chart -->
<div class="col-xl-4 col-lg-5">
<div class="card shadow mb-4">
<!-- Card Header -->
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold text-primary">Course Enrollments</h6>
</div>
<!-- Card Body -->
<div class="card-body">
<div class="chart-pie">
<div id="enrollmentsPieChart" style="height: 300px;"></div>
</div>
</div>
</div>
</div>
</div>
<!-- Recent Activity Row -->
<div class="row">
<!-- Recent Enrollments -->
<div class="col-lg-6 mb-4">
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Recent Enrollments</h6>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" width="100%" cellspacing="0">
<thead>
<tr>
<th>Student</th>
<th>Course</th>
<th>Date</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<?php
$enrollments_query = "SELECT e.*, u.first_name, u.last_name, c.title
FROM enrollments e
JOIN users u ON e.user_id = u.id
JOIN courses c ON e.course_id = c.id
ORDER BY e.enrollment_date DESC LIMIT 5";
$enrollments_result = $conn->query($enrollments_query);
if ($enrollments_result && $enrollments_result->num_rows > 0) {
while ($enrollment = $enrollments_result->fetch_assoc()) {
?>
<tr>
<td><?php echo $enrollment['first_name'] . ' ' . $enrollment['last_name']; ?></td>
<td><?php echo $enrollment['title']; ?></td>
<td><?php echo date('M d, Y', strtotime($enrollment['enrollment_date'])); ?></td>
<td>
<span class="badge bg-success">Enrolled</span>
</td>
</tr>
<?php
}
} else {
?>
<tr>
<td colspan="4" class="text-center">No recent enrollments</td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Recent Payments -->
<div class="col-lg-6 mb-4">
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Recent Payments</h6>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" width="100%" cellspacing="0">
<thead>
<tr>
<th>User</th>
<th>Amount</th>
<th>Date</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<?php
$payments_query = "SELECT p.*, CONCAT(u.first_name, ' ', u.last_name) as user_name
FROM payments p
JOIN users u ON p.user_id = u.id
ORDER BY p.payment_date DESC LIMIT 5";
$payments_result = $conn->query($payments_query);
if ($payments_result && $payments_result->num_rows > 0) {
while ($payment = $payments_result->fetch_assoc()) {
?>
<tr>
<td><?php echo $payment['user_name']; ?></td>
<td>₹<?php echo number_format($payment['amount'], 2); ?></td>
<td><?php echo date('M d, Y', strtotime($payment['payment_date'])); ?></td>
<td>
<span class="badge bg-success">Completed</span>
</td>
</tr>
<?php
}
} else {
?>
<tr>
<td colspan="4" class="text-center">No recent payments</td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Animated background elements -->
<div class="dashboard-bg-elements">
<div class="bg-element element-1"></div>
<div class="bg-element element-2"></div>
<div class="bg-element element-3"></div>
<div class="bg-element element-4"></div>
</div>
<style>
/* Dashboard Styles */
:root {
--dashboard-transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
--glass-bg: rgba(255, 255, 255, 0.25);
--glass-border: rgba(255, 255, 255, 0.18);
--glass-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.15);
}
/* Animated Background */
.dashboard-bg-elements {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
overflow: hidden;
opacity: 0.3;
}
.bg-element {
position: absolute;
border-radius: 50%;
filter: blur(70px);
animation: float 40s infinite ease-in-out;
}
.element-1 {
width: 600px;
height: 600px;
background: var(--primary-color);
top: 10%;
left: -10%;
animation-delay: 0s;
}
.element-2 {
width: 500px;
height: 500px;
background: var(--warning-color);
bottom: -15%;
right: 5%;
animation-delay: -10s;
filter: blur(80px);
}
.element-3 {
width: 450px;
height: 450px;
background: var(--info-color);
top: 40%;
left: 25%;
animation-delay: -20s;
filter: blur(60px);
}
.element-4 {
width: 400px;
height: 400px;
background: var(--success-color);
top: 5%;
right: 15%;
animation-delay: -30s;
filter: blur(50px);
}
@keyframes float {
0%, 100% {
transform: translate(0, 0) scale(1) rotate(0deg);
}
25% {
transform: translate(4%, 4%) scale(1.05) rotate(5deg);
}
50% {
transform: translate(2%, -6%) scale(0.95) rotate(10deg);
}
75% {
transform: translate(-6%, 2%) scale(1.02) rotate(-5deg);
}
}
/* Dashboard header */
.container-fluid {
padding: 2rem;
}
h1.h3.text-gray-800 {
font-weight: 800;
letter-spacing: -0.5px;
background: linear-gradient(120deg, var(--primary-color), var(--info-color));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
margin-bottom: 1.5rem;
font-size: 2.2rem;
animation: text-shimmer 3s infinite linear;
text-shadow: 0px 2px 5px rgba(0,0,0,0.1);
position: relative;
}
@keyframes text-shimmer {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
/* Enhanced Stats Cards */
.card {
border: none;
overflow: hidden;
border-radius: 16px !important;
box-shadow: var(--glass-shadow);
background: var(--glass-bg);
backdrop-filter: blur(10px);
transition: var(--dashboard-transition);
border: 1px solid var(--glass-border);
transform: translateY(20px);
opacity: 0;
animation: card-appear 0.8s forwards cubic-bezier(0.22, 1, 0.36, 1);
}
.card:hover {
transform: translateY(-7px) scale(1.02);
box-shadow: 0 15px 30px rgba(0,0,0,0.1);
border: 1px solid rgba(255,255,255,0.3);
}
@keyframes card-appear {
to {
transform: translateY(0);
opacity: 1;
}
}
.card-body {
padding: 1.5rem;
}
.card-header {
background: rgba(255,255,255,0.1);
border-bottom: 1px solid rgba(255,255,255,0.1);
padding: 1.25rem 1.5rem;
backdrop-filter: blur(5px);
}
.card.border-left-primary {
border-left: 4px solid var(--primary-color) !important;
}
.card.border-left-success {
border-left: 4px solid var(--success-color) !important;
}
.card.border-left-info {
border-left: 4px solid var(--info-color) !important;
}
.card.border-left-warning {
border-left: 4px solid var(--warning-color) !important;
}
.text-xs {
font-size: 0.8rem;
letter-spacing: 1px;
font-weight: 700 !important;
}
.fa-2x {
transition: all 0.3s ease;
}
.card:hover .fa-2x {
transform: scale(1.2);
color: var(--primary-color) !important;
}
/* Enhanced Tables */
.table {
border-collapse: separate;
border-spacing: 0 8px;
margin-top: -8px;
}
.table th {
border-top: none;
border-bottom: 1px solid rgba(var(--primary-rgb), 0.1);
background: transparent;
padding: 1rem 1.5rem;
font-size: 0.85rem;
letter-spacing: 1px;
text-transform: uppercase;
font-weight: 700;
color: var(--gray-600);
}
.table td {
border: none;
background: rgba(255,255,255,0.15);
padding: 1.2rem 1.5rem;
vertical-align: middle;
font-weight: 500;
}
.table tr {
transition: all 0.3s ease;
}
.table tbody tr:first-child td:first-child {
border-top-left-radius: 12px;
border-bottom-left-radius: 12px;
}
.table tbody tr:first-child td:last-child {
border-top-right-radius: 12px;
border-bottom-right-radius: 12px;
}
.table tbody tr:hover {
transform: translateY(-3px);
box-shadow: 0 5px 15px rgba(0,0,0,0.05);
}
.badge {
padding: 0.5em 1em;
border-radius: 8px;
font-weight: 600;
}
.bg-success {
background: linear-gradient(45deg, var(--success-color), #4cd964) !important;
}
.btn-primary {
background: linear-gradient(135deg, var(--primary-color), var(--info-color));
border: none;
box-shadow: 0 5px 15px rgba(var(--primary-rgb), 0.3);
border-radius: 12px;
padding: 0.6rem 1.5rem;
transition: all 0.3s ease;
font-weight: 600;
}
.btn-primary:hover {
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(var(--primary-rgb), 0.4);
}
/* Animation delays for cards */
.col-xl-3:nth-child(1) .card {
animation-delay: 0.1s;
}
.col-xl-3:nth-child(2) .card {
animation-delay: 0.2s;
}
.col-xl-3:nth-child(3) .card {
animation-delay: 0.3s;
}
.col-xl-3:nth-child(4) .card {
animation-delay: 0.4s;
}
.col-xl-8 .card {
animation-delay: 0.5s;
}
.col-xl-4 .card {
animation-delay: 0.6s;
}
.col-lg-6:nth-child(1) .card {
animation-delay: 0.7s;
}
.col-lg-6:nth-child(2) .card {
animation-delay: 0.8s;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Initialize charts with animation
initCharts();
// Add parallax effect on mouse move
document.addEventListener('mousemove', function(e) {
const mouseX = e.clientX / window.innerWidth;
const mouseY = e.clientY / window.innerHeight;
document.querySelectorAll('.bg-element').forEach((element, index) => {
const speed = 0.03 + (index * 0.01);
const x = (mouseX - 0.5) * speed * 100;
const y = (mouseY - 0.5) * speed * 100;
element.style.transform = `translate(${x}px, ${y}px)`;
});
});
// Animation for titles
const dashboardTitle = document.querySelector('h1.h3.text-gray-800');
if (dashboardTitle) {
dashboardTitle.innerHTML = dashboardTitle.textContent.split('').map((char, i) =>
char === ' ' ? ' ' : `<span style="display:inline-block; opacity:0; transform:translateY(20px); transition: all 0.3s ease ${i * 0.05}s;">${char}</span>`
).join('');
setTimeout(() => {
dashboardTitle.querySelectorAll('span').forEach(span => {
span.style.opacity = '1';
span.style.transform = 'translateY(0)';
});
}, 100);
}
// Add subtle hover effects to table rows
const tableRows = document.querySelectorAll('tbody tr');
tableRows.forEach(row => {
row.addEventListener('mouseenter', () => {
row.style.background = 'rgba(255,255,255,0.25)';
row.style.transform = 'translateY(-3px)';
row.style.boxShadow = '0 5px 15px rgba(0,0,0,0.05)';
// Add a subtle glow to the row
row.querySelectorAll('td').forEach(td => {
td.style.background = 'rgba(255,255,255,0.2)';
});
});
row.addEventListener('mouseleave', () => {
row.style.background = '';
row.style.transform = '';
row.style.boxShadow = '';
row.querySelectorAll('td').forEach(td => {
td.style.background = 'rgba(255,255,255,0.15)';
});
});
});
});
// Initialize charts with animation
function initCharts() {
// Revenue chart - simplified example (you can connect to actual data)
if (document.getElementById('revenueChart')) {
const options = {
chart: {
type: 'area',
height: 300,
toolbar: {
show: false
},
animations: {
enabled: true,
easing: 'easeinout',
speed: 800,
animateGradually: {
enabled: true,
delay: 150
},
dynamicAnimation: {
enabled: true,
speed: 350
}
}
},
colors: [getComputedStyle(document.documentElement).getPropertyValue('--primary-color')],
series: [{
name: 'Revenue',
data: [30, 40, 35, 50, 49, 60, 70, 91, 125, 150, 160, 180]
}],
xaxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
},
fill: {
type: 'gradient',
gradient: {
shadeIntensity: 1,
opacityFrom: 0.7,
opacityTo: 0.9,
stops: [0, 90, 100]
}
},
stroke: {
curve: 'smooth'
},
dataLabels: {
enabled: false
},
tooltip: {
x: {
format: 'dd/MM/yy HH:mm'
},
}
};
const chart = new ApexCharts(document.getElementById('revenueChart'), options);
chart.render();
}
// Enrollments pie chart - simplified example
if (document.getElementById('enrollmentsPieChart')) {
const options = {
chart: {
type: 'donut',
height: 300,
animations: {
enabled: true,
easing: 'easeinout',
speed: 800,
animateGradually: {
enabled: true,
delay: 150
},
dynamicAnimation: {
enabled: true,
speed: 350
}
}
},
series: [44, 55, 13, 33],
labels: ['Web Development', 'Data Science', 'Design', 'Mobile Dev'],
colors: [
getComputedStyle(document.documentElement).getPropertyValue('--primary-color'),
getComputedStyle(document.documentElement).getPropertyValue('--success-color'),
getComputedStyle(document.documentElement).getPropertyValue('--info-color'),
getComputedStyle(document.documentElement).getPropertyValue('--warning-color')
],
plotOptions: {
pie: {
donut: {
size: '55%'
}
}
},
legend: {
position: 'bottom'
},
responsive: [{
breakpoint: 480,
options: {
chart: {
width: 200
},
legend: {
position: 'bottom'
}
}
}]
};
const chart = new ApexCharts(document.getElementById('enrollmentsPieChart'), options);
chart.render();
}
}
</script>
<?php
// Include footer
include_once 'includes/footer.php';
?>