120 lines
4.1 KiB
PHP
120 lines
4.1 KiB
PHP
<?php
|
|
/**
|
|
* dashboard_data.php
|
|
* API موحّد للداشبورد التحليلي — يقرأ من ملفات JSON المؤرشفة + بيانات حيّة من Redis.
|
|
*
|
|
* Parameters:
|
|
* date (optional) — YYYY-MM-DD, default: today
|
|
* section (optional) — realtime|gap|heatmap|pricing|revenue|growth|market|complaints|funnel|hourly|weekly|zones|retention|all
|
|
* default: all
|
|
*/
|
|
|
|
require_once __DIR__ . '/../../../connect.php';
|
|
|
|
if ($role !== 'admin' && $role !== 'super_admin') {
|
|
http_response_code(403);
|
|
echo json_encode(['status' => 'error', 'message' => 'Unauthorized']);
|
|
exit;
|
|
}
|
|
|
|
$requestedDate = filterRequest('date') ?: date('Y-m-d');
|
|
$section = filterRequest('section') ?: 'all';
|
|
|
|
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $requestedDate)) {
|
|
http_response_code(400);
|
|
echo json_encode(['status' => 'error', 'message' => 'Invalid date format']);
|
|
exit;
|
|
}
|
|
|
|
$cacheBase = __DIR__ . '/../../../cache/analytics';
|
|
$dayDir = "$cacheBase/$requestedDate";
|
|
|
|
$response = [
|
|
'status' => 'success',
|
|
'date' => $requestedDate,
|
|
'section' => $section,
|
|
'data' => [],
|
|
];
|
|
|
|
function loadSnapshot(string $dir, string $name): ?array {
|
|
$path = "$dir/$name.json";
|
|
if (!file_exists($path)) return null;
|
|
$data = json_decode(file_get_contents($path), true);
|
|
return is_array($data) ? $data : null;
|
|
}
|
|
|
|
function loadLatestRealtime(string $dir): ?array {
|
|
$files = glob("$dir/realtime_*.json");
|
|
if (empty($files)) return null;
|
|
sort($files);
|
|
$latest = end($files);
|
|
$data = json_decode(file_get_contents($latest), true);
|
|
return is_array($data) ? $data : null;
|
|
}
|
|
|
|
$sectionMap = [
|
|
'realtime' => fn() => loadLatestRealtime($dayDir),
|
|
'gap' => fn() => loadSnapshot($dayDir, 'supply_demand_gap'),
|
|
'heatmap' => fn() => loadSnapshot($dayDir, 'heatmap'),
|
|
'pricing' => fn() => loadSnapshot($dayDir, 'pricing_grids'),
|
|
'demand' => fn() => loadSnapshot($dayDir, 'predictive_demand'),
|
|
'revenue' => fn() => loadSnapshot($dayDir, 'revenue_30d'),
|
|
'growth' => fn() => loadSnapshot($dayDir, 'growth_30d'),
|
|
'market' => fn() => loadSnapshot($dayDir, 'market_health'),
|
|
'complaints' => fn() => loadSnapshot($dayDir, 'complaints_open'),
|
|
'funnel' => fn() => loadSnapshot($dayDir, 'ride_funnel'),
|
|
'hourly' => fn() => loadSnapshot($dayDir, 'hourly_pattern'),
|
|
'weekly' => fn() => loadSnapshot($dayDir, 'weekly_comparison'),
|
|
'zones' => fn() => loadSnapshot($dayDir, 'top_zones'),
|
|
'retention' => fn() => loadSnapshot($dayDir, 'retention_cohort'),
|
|
'competitor' => fn() => loadSnapshot($dayDir, 'competitor_prices_24h'),
|
|
];
|
|
|
|
try {
|
|
if (!is_dir($dayDir)) {
|
|
$response['data'] = null;
|
|
$response['note'] = "No snapshot data for $requestedDate";
|
|
|
|
$indexPath = "$cacheBase/index.json";
|
|
if (file_exists($indexPath)) {
|
|
$idx = json_decode(file_get_contents($indexPath), true);
|
|
$response['available_dates'] = $idx['available_dates'] ?? [];
|
|
}
|
|
|
|
echo json_encode($response, JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
if ($section === 'all') {
|
|
foreach ($sectionMap as $key => $loader) {
|
|
$result = $loader();
|
|
if ($result !== null) {
|
|
$response['data'][$key] = $result;
|
|
}
|
|
}
|
|
} elseif (isset($sectionMap[$section])) {
|
|
$response['data'] = $sectionMap[$section]();
|
|
} else {
|
|
http_response_code(400);
|
|
echo json_encode([
|
|
'status' => 'error',
|
|
'message' => "Unknown section: $section",
|
|
'available' => array_keys($sectionMap),
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
$indexPath = "$cacheBase/index.json";
|
|
if (file_exists($indexPath)) {
|
|
$idx = json_decode(file_get_contents($indexPath), true);
|
|
$response['available_dates'] = $idx['available_dates'] ?? [];
|
|
}
|
|
|
|
echo json_encode($response, JSON_UNESCAPED_UNICODE);
|
|
|
|
} catch (Exception $e) {
|
|
http_response_code(500);
|
|
error_log("[dashboard_data.php] " . $e->getMessage());
|
|
echo json_encode(['status' => 'error', 'message' => 'Internal error']);
|
|
}
|