225 lines
5.8 KiB
PHP
Executable File
225 lines
5.8 KiB
PHP
Executable File
<?php
|
|
/**
|
|
* MSPE Dashboard Stats API
|
|
*/
|
|
|
|
require_once 'config.php';
|
|
|
|
$method = $_SERVER['REQUEST_METHOD'];
|
|
|
|
if ($method !== 'GET') {
|
|
jsonResponse(['success' => false, 'message' => 'Method not allowed'], 405);
|
|
}
|
|
|
|
// Require authentication for stats
|
|
requireAuth();
|
|
|
|
$action = $_GET['action'] ?? 'all';
|
|
$forceRefresh = !empty($_GET['refresh']);
|
|
|
|
switch ($action) {
|
|
case 'all':
|
|
getAllStats();
|
|
break;
|
|
case 'bookings':
|
|
getBookingStats();
|
|
break;
|
|
case 'messages':
|
|
getMessageStats();
|
|
break;
|
|
case 'news':
|
|
getNewsStats();
|
|
break;
|
|
default:
|
|
getAllStats();
|
|
}
|
|
|
|
function getAllStats() {
|
|
$cacheKey = 'stats_all_v1';
|
|
if (!$GLOBALS['forceRefresh']) {
|
|
$cached = getStatsCache($cacheKey, 30);
|
|
if ($cached !== null) {
|
|
jsonResponse($cached);
|
|
}
|
|
}
|
|
|
|
$bookingStats = calculateBookingStats();
|
|
$messageStats = calculateMessageStats();
|
|
$newsStats = calculateNewsStats();
|
|
$subscriberStats = calculateSubscriberStats();
|
|
|
|
$payload = [
|
|
'success' => true,
|
|
'data' => [
|
|
'bookings' => $bookingStats,
|
|
'messages' => $messageStats,
|
|
'news' => $newsStats,
|
|
'subscribers' => $subscriberStats
|
|
]
|
|
];
|
|
|
|
setStatsCache($cacheKey, $payload);
|
|
jsonResponse($payload);
|
|
}
|
|
|
|
function calculateBookingStats() {
|
|
global $db;
|
|
|
|
$today = date('Y-m-d');
|
|
$weekStart = date('Y-m-d', strtotime('monday this week'));
|
|
$weekEnd = date('Y-m-d', strtotime('sunday this week'));
|
|
|
|
$totalBookings = $db->count('bookings');
|
|
$thisWeekCount = $db->countByDateRange('bookings', 'booking_date', $weekStart, $weekEnd);
|
|
|
|
// Last week's bookings (for trend)
|
|
$lastWeekStart = date('Y-m-d', strtotime('monday last week'));
|
|
$lastWeekEnd = date('Y-m-d', strtotime('sunday last week'));
|
|
$lastWeekCount = $db->countByDateRange('bookings', 'booking_date', $lastWeekStart, $lastWeekEnd);
|
|
|
|
$pendingCount = $db->count('bookings', ['status' => 'pending']);
|
|
$confirmedCount = $db->count('bookings', ['status' => 'confirmed']);
|
|
|
|
// JSON boolean true is compared as string "true" after JSON_UNQUOTE
|
|
$availableSlotsCount = $db->countByFieldComparison('availability_slots', 'date', '>=', $today, [
|
|
'is_available' => 'true'
|
|
]);
|
|
|
|
$confirmationRate = $totalBookings > 0 ? round(($confirmedCount / $totalBookings) * 100) : 0;
|
|
|
|
// Calculate trend percentages
|
|
$bookingTrend = $lastWeekCount > 0 ? round((($thisWeekCount - $lastWeekCount) / $lastWeekCount) * 100) : ($thisWeekCount > 0 ? 100 : 0);
|
|
|
|
return [
|
|
'this_week' => $thisWeekCount,
|
|
'pending' => $pendingCount,
|
|
'confirmed' => $confirmedCount,
|
|
'available_slots' => $availableSlotsCount,
|
|
'confirmation_rate' => $confirmationRate,
|
|
'trend' => $bookingTrend,
|
|
'total' => $totalBookings
|
|
];
|
|
}
|
|
|
|
function calculateMessageStats() {
|
|
global $db;
|
|
|
|
$total = $db->count('messages');
|
|
$unread = $db->count('messages', ['status' => 'unread']);
|
|
|
|
$weekStart = date('Y-m-d', strtotime('monday this week'));
|
|
$weekEnd = date('Y-m-d', strtotime('sunday this week'));
|
|
$thisWeek = $db->countByDateRange('messages', 'created_at', $weekStart . ' 00:00:00', $weekEnd . ' 23:59:59');
|
|
|
|
return [
|
|
'total' => $total,
|
|
'unread' => $unread,
|
|
'this_week' => $thisWeek
|
|
];
|
|
}
|
|
|
|
function calculateNewsStats() {
|
|
global $db;
|
|
|
|
return [
|
|
'total' => $db->count('news'),
|
|
'published' => $db->count('news', ['status' => 'published']),
|
|
'draft' => $db->count('news', ['status' => 'draft'])
|
|
];
|
|
}
|
|
|
|
function calculateSubscriberStats() {
|
|
global $db;
|
|
|
|
return [
|
|
'total' => $db->count('subscribers'),
|
|
'active' => $db->count('subscribers', ['status' => 'active'])
|
|
];
|
|
}
|
|
|
|
function getBookingStats() {
|
|
$cacheKey = 'stats_bookings_v1';
|
|
if (!$GLOBALS['forceRefresh']) {
|
|
$cached = getStatsCache($cacheKey, 30);
|
|
if ($cached !== null) {
|
|
jsonResponse($cached);
|
|
}
|
|
}
|
|
|
|
$payload = [
|
|
'success' => true,
|
|
'data' => calculateBookingStats()
|
|
];
|
|
|
|
setStatsCache($cacheKey, $payload);
|
|
jsonResponse($payload);
|
|
}
|
|
|
|
function getMessageStats() {
|
|
$cacheKey = 'stats_messages_v1';
|
|
if (!$GLOBALS['forceRefresh']) {
|
|
$cached = getStatsCache($cacheKey, 30);
|
|
if ($cached !== null) {
|
|
jsonResponse($cached);
|
|
}
|
|
}
|
|
|
|
$payload = [
|
|
'success' => true,
|
|
'data' => calculateMessageStats()
|
|
];
|
|
|
|
setStatsCache($cacheKey, $payload);
|
|
jsonResponse($payload);
|
|
}
|
|
|
|
function getNewsStats() {
|
|
$cacheKey = 'stats_news_v1';
|
|
if (!$GLOBALS['forceRefresh']) {
|
|
$cached = getStatsCache($cacheKey, 30);
|
|
if ($cached !== null) {
|
|
jsonResponse($cached);
|
|
}
|
|
}
|
|
|
|
$payload = [
|
|
'success' => true,
|
|
'data' => calculateNewsStats()
|
|
];
|
|
|
|
setStatsCache($cacheKey, $payload);
|
|
jsonResponse($payload);
|
|
}
|
|
|
|
function getStatsCache($cacheKey, $ttlSeconds) {
|
|
$cacheDir = DATA_DIR . 'cache/';
|
|
$cacheFile = $cacheDir . 'stats_' . md5($cacheKey) . '.json';
|
|
|
|
if (!file_exists($cacheFile)) {
|
|
return null;
|
|
}
|
|
|
|
$age = time() - (int)filemtime($cacheFile);
|
|
if ($age > (int)$ttlSeconds) {
|
|
return null;
|
|
}
|
|
|
|
$content = @file_get_contents($cacheFile);
|
|
if ($content === false || $content === '') {
|
|
return null;
|
|
}
|
|
|
|
$decoded = json_decode($content, true);
|
|
return is_array($decoded) ? $decoded : null;
|
|
}
|
|
|
|
function setStatsCache($cacheKey, $payload) {
|
|
$cacheDir = DATA_DIR . 'cache/';
|
|
if (!is_dir($cacheDir)) {
|
|
@mkdir($cacheDir, 0755, true);
|
|
}
|
|
|
|
$cacheFile = $cacheDir . 'stats_' . md5($cacheKey) . '.json';
|
|
@file_put_contents($cacheFile, json_encode($payload), LOCK_EX);
|
|
}
|