353 lines
18 KiB
PHP
Executable File
353 lines
18 KiB
PHP
Executable File
<?php
|
|
/**
|
|
* WEVAL SEND - WORLD DASHBOARD V2
|
|
* Real data from database + flexible import
|
|
*/
|
|
$pdo = new PDO("pgsql:host=localhost;dbname=adx_system", "admin", "admin123", [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
|
|
|
|
// Get real stats from DB
|
|
$totalLeads = $pdo->query("SELECT COUNT(*) FROM admin.leads")->fetchColumn();
|
|
$totalDomains = $pdo->query("SELECT COUNT(*) FROM admin.domains")->fetchColumn();
|
|
$totalCampaigns = $pdo->query("SELECT COUNT(*) FROM admin.campaigns")->fetchColumn();
|
|
$completedCampaigns = $pdo->query("SELECT COUNT(*) FROM admin.campaigns WHERE status = 'completed'")->fetchColumn();
|
|
|
|
// Get country stats
|
|
$countryStats = $pdo->query("SELECT * FROM admin.country_daily_stats ORDER BY sent DESC")->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
// Get brain winners
|
|
$brainWinners = $pdo->query("SELECT isp_target, inbox_rate, stability_score FROM admin.brain_winners ORDER BY inbox_rate DESC LIMIT 6")->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
// Get country profiles
|
|
$countryProfiles = $pdo->query("SELECT * FROM admin.country_profiles ORDER BY avg_open_rate DESC")->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
// Calculate totals
|
|
$totalSent = $pdo->query("SELECT COALESCE(SUM(sent), 0) FROM admin.country_daily_stats")->fetchColumn();
|
|
$totalOpens = $pdo->query("SELECT COALESCE(SUM(opens), 0) FROM admin.country_daily_stats")->fetchColumn();
|
|
$totalClicks = $pdo->query("SELECT COALESCE(SUM(clicks), 0) FROM admin.country_daily_stats")->fetchColumn();
|
|
$totalLeadsGen = $pdo->query("SELECT COALESCE(SUM(leads), 0) FROM admin.country_daily_stats")->fetchColumn();
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>WEVAL SEND - World Dashboard</title>
|
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body { font-family: 'Segoe UI', sans-serif; background: #0a0a1a; color: #fff; min-height: 100vh; }
|
|
.header { background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); padding: 15px 25px; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #333; }
|
|
.header h1 { font-size: 22px; display: flex; align-items: center; gap: 10px; }
|
|
.header h1 span { color: #00d4ff; }
|
|
.live-badge { display: flex; align-items: center; gap: 8px; background: rgba(0,255,136,0.1); padding: 8px 15px; border-radius: 20px; }
|
|
.live-dot { width: 8px; height: 8px; background: #00ff88; border-radius: 50%; animation: pulse 1s infinite; }
|
|
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
|
|
.nav-tabs { display: flex; gap: 10px; }
|
|
.nav-tab { padding: 8px 20px; background: rgba(255,255,255,0.05); border: none; color: #888; border-radius: 8px; cursor: pointer; transition: all 0.3s; }
|
|
.nav-tab:hover, .nav-tab.active { background: #00d4ff; color: #000; }
|
|
|
|
.main-container { display: grid; grid-template-columns: 1fr 380px; gap: 20px; padding: 20px; }
|
|
.map-section { background: #1a1a2e; border-radius: 15px; padding: 20px; }
|
|
#worldMap { height: 450px; border-radius: 10px; background: #0d1b2a; }
|
|
|
|
.stats-bar { display: grid; grid-template-columns: repeat(4, 1fr); gap: 15px; margin-bottom: 20px; }
|
|
.stat-card { background: linear-gradient(135deg, #1a1a2e, #16213e); padding: 20px; border-radius: 12px; text-align: center; border: 1px solid #333; }
|
|
.stat-card .icon { font-size: 24px; margin-bottom: 8px; }
|
|
.stat-card .value { font-size: 32px; font-weight: bold; }
|
|
.stat-card .label { font-size: 12px; color: #888; margin-top: 5px; }
|
|
.stat-card.blue .value { color: #00d4ff; }
|
|
.stat-card.green .value { color: #00ff88; }
|
|
.stat-card.orange .value { color: #ff9500; }
|
|
.stat-card.purple .value { color: #a855f7; }
|
|
|
|
.sidebar { display: flex; flex-direction: column; gap: 15px; }
|
|
.card { background: #1a1a2e; border-radius: 12px; padding: 18px; border: 1px solid #333; }
|
|
.card h3 { color: #888; font-size: 11px; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 12px; display: flex; align-items: center; gap: 8px; }
|
|
|
|
.country-item { display: flex; justify-content: space-between; align-items: center; padding: 10px 0; border-bottom: 1px solid rgba(255,255,255,0.05); }
|
|
.country-item:last-child { border: none; }
|
|
.country-info { display: flex; align-items: center; gap: 10px; }
|
|
.country-flag { font-size: 20px; }
|
|
.country-name { font-size: 14px; }
|
|
.country-stats { display: flex; gap: 12px; font-size: 12px; }
|
|
.country-stats span { padding: 3px 8px; background: rgba(255,255,255,0.05); border-radius: 4px; }
|
|
|
|
.tz-item { display: flex; justify-content: space-between; align-items: center; padding: 10px; background: rgba(255,255,255,0.03); border-radius: 8px; margin-bottom: 8px; }
|
|
.tz-time { font-size: 18px; font-weight: bold; font-family: monospace; }
|
|
.tz-status { padding: 4px 12px; border-radius: 15px; font-size: 10px; font-weight: bold; }
|
|
.tz-status.optimal { background: #00ff88; color: #000; }
|
|
.tz-status.waiting { background: #ff9500; color: #000; }
|
|
.tz-status.sleeping { background: #444; color: #888; }
|
|
|
|
.isp-item { display: flex; justify-content: space-between; align-items: center; padding: 8px 0; }
|
|
.isp-name { font-size: 14px; }
|
|
.isp-rate { font-size: 16px; font-weight: bold; color: #00ff88; }
|
|
.isp-bar { height: 4px; background: #333; border-radius: 2px; margin-top: 4px; }
|
|
.isp-bar-fill { height: 100%; background: linear-gradient(90deg, #00d4ff, #00ff88); border-radius: 2px; }
|
|
|
|
.btn-action { width: 100%; padding: 15px; background: linear-gradient(135deg, #00d4ff, #00ff88); border: none; border-radius: 10px; font-size: 14px; font-weight: bold; color: #000; cursor: pointer; margin-top: 10px; display: flex; align-items: center; justify-content: center; gap: 8px; }
|
|
.btn-action:hover { transform: translateY(-2px); box-shadow: 0 5px 20px rgba(0,212,255,0.3); }
|
|
.btn-secondary { background: rgba(255,255,255,0.1); color: #fff; }
|
|
.btn-secondary:hover { background: rgba(255,255,255,0.2); box-shadow: none; }
|
|
|
|
.legend { display: flex; gap: 20px; margin-top: 15px; justify-content: center; }
|
|
.legend-item { display: flex; align-items: center; gap: 6px; font-size: 12px; color: #888; }
|
|
.legend-dot { width: 10px; height: 10px; border-radius: 50%; }
|
|
|
|
.resources-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
|
|
.resource-item { background: rgba(255,255,255,0.03); padding: 12px; border-radius: 8px; text-align: center; }
|
|
.resource-value { font-size: 20px; font-weight: bold; color: #00d4ff; }
|
|
.resource-label { font-size: 11px; color: #888; margin-top: 4px; }
|
|
</style>
|
|
|
|
</head>
|
|
<body>
|
|
<div class="header">
|
|
<h1>🌍 <span>WEVAL SEND</span> World Command Center</h1>
|
|
<div class="nav-tabs">
|
|
<button class="nav-tab active">📊 Dashboard</button>
|
|
<button class="nav-tab" onclick="location.href='data-import.php'">📥 Import Data</button>
|
|
<button class="nav-tab" onclick="location.href='api-config.php'">⚙️ Config</button>
|
|
</div>
|
|
<div class="live-badge">
|
|
<div class="live-dot"></div>
|
|
<span>LIVE</span>
|
|
<span id="currentTime"></span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stats Bar -->
|
|
<div style="padding: 20px 20px 0;">
|
|
<div class="stats-bar">
|
|
<div class="stat-card blue">
|
|
<div class="icon">📤</div>
|
|
<div class="value"><?= number_format($totalSent) ?></div>
|
|
<div class="label">EMAILS SENT</div>
|
|
</div>
|
|
<div class="stat-card green">
|
|
<div class="icon">📬</div>
|
|
<div class="value"><?= number_format($totalOpens) ?></div>
|
|
<div class="label">OPENS</div>
|
|
</div>
|
|
<div class="stat-card orange">
|
|
<div class="icon">🖱️</div>
|
|
<div class="value"><?= number_format($totalClicks) ?></div>
|
|
<div class="label">CLICKS</div>
|
|
</div>
|
|
<div class="stat-card purple">
|
|
<div class="icon">🎯</div>
|
|
<div class="value"><?= number_format($totalLeadsGen) ?></div>
|
|
<div class="label">LEADS GENERATED</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="main-container">
|
|
<!-- Map Section -->
|
|
<div class="map-section">
|
|
<h3 style="margin-bottom:15px; color:#888; font-size:12px;">📍 GLOBAL TRAFFIC HEATMAP</h3>
|
|
<div id="worldMap"></div>
|
|
<div class="legend">
|
|
<div class="legend-item"><div class="legend-dot" style="background:#00ff88;"></div> High Engagement (>25%)</div>
|
|
<div class="legend-item"><div class="legend-dot" style="background:#ff9500;"></div> Medium (15-25%)</div>
|
|
<div class="legend-item"><div class="legend-dot" style="background:#ff4444;"></div> Low (<15%)</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sidebar -->
|
|
<div class="sidebar">
|
|
<!-- Resources -->
|
|
<div class="card">
|
|
<h3>📦 Platform Resources</h3>
|
|
<div class="resources-grid">
|
|
<div class="resource-item">
|
|
<div class="resource-value"><?= number_format($totalLeads) ?></div>
|
|
<div class="resource-label">Total Leads</div>
|
|
</div>
|
|
<div class="resource-item">
|
|
<div class="resource-value"><?= $totalDomains ?></div>
|
|
<div class="resource-label">Domains</div>
|
|
</div>
|
|
<div class="resource-item">
|
|
<div class="resource-value"><?= $totalCampaigns ?></div>
|
|
<div class="resource-label">Campaigns</div>
|
|
</div>
|
|
<div class="resource-item">
|
|
<div class="resource-value"><?= $completedCampaigns ?></div>
|
|
<div class="resource-label">Completed</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Top Countries -->
|
|
<div class="card">
|
|
<h3>🏆 Top Performing Countries</h3>
|
|
<?php
|
|
$flags = ['DE'=>'🇩🇪','FR'=>'🇫🇷','NL'=>'🇳🇱','US'=>'🇺🇸','UK'=>'🇬🇧','ES'=>'🇪🇸','IT'=>'🇮🇹','CA'=>'🇨🇦','AU'=>'🇦🇺','BR'=>'🇧🇷'];
|
|
foreach(array_slice($countryStats, 0, 5) as $c):
|
|
$flag = $flags[$c['country_code']] ?? '🌍';
|
|
?>
|
|
<div class="country-item">
|
|
<div class="country-info">
|
|
<span class="country-flag"><?= $flag ?></span>
|
|
<span class="country-name"><?= $c['country_code'] ?></span>
|
|
</div>
|
|
<div class="country-stats">
|
|
<span>📬 <?= number_format($c['opens']) ?></span>
|
|
<span>🖱️ <?= number_format($c['clicks']) ?></span>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
<?php if(empty($countryStats)): ?>
|
|
<div style="text-align:center; color:#666; padding:20px;">
|
|
No data yet. <a href="data-import.php" style="color:#00d4ff;">Import data</a>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<!-- Timezone Scheduler -->
|
|
<div class="card">
|
|
<h3>⏰ Timezone Auto-Schedule</h3>
|
|
<div id="tzScheduler"></div>
|
|
</div>
|
|
|
|
<!-- Brain Winners -->
|
|
<div class="card" style="background: linear-gradient(135deg, rgba(0,212,255,0.1), rgba(0,255,136,0.1)); border-color: #00d4ff44;">
|
|
<h3>🧠 Brain Winners (Best ISPs)</h3>
|
|
<?php foreach(array_slice($brainWinners, 0, 4) as $w):
|
|
$pct = min(100, $w['inbox_rate']);
|
|
?>
|
|
<div class="isp-item">
|
|
<div>
|
|
<div class="isp-name"><?= $w['isp_target'] ?></div>
|
|
<div class="isp-bar" style="width:150px;">
|
|
<div class="isp-bar-fill" style="width:<?= $pct ?>%;"></div>
|
|
</div>
|
|
</div>
|
|
<div class="isp-rate"><?= $w['inbox_rate'] ?>%</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
|
|
<!-- Actions -->
|
|
<button class="btn-action" onclick="location.href='data-import.php'">
|
|
📥 IMPORT DATA
|
|
</button>
|
|
<button class="btn-action btn-secondary" onclick="launchCampaign()">
|
|
🚀 LAUNCH AUTO CAMPAIGN
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Initialize map
|
|
const map = L.map('worldMap', {
|
|
center: [30, 10],
|
|
zoom: 2,
|
|
minZoom: 2,
|
|
maxZoom: 6
|
|
});
|
|
|
|
L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
|
|
attribution: '©WEVAL SEND'
|
|
}).addTo(map);
|
|
|
|
// Country data from PHP
|
|
const countryData = <?= json_encode(array_column($countryStats, null, 'country_code')) ?>;
|
|
const countryCoords = {
|
|
'DE': [51.1657, 10.4515],
|
|
'FR': [46.2276, 2.2137],
|
|
'NL': [52.1326, 5.2913],
|
|
'US': [39.8283, -98.5795],
|
|
'UK': [55.3781, -3.4360],
|
|
'ES': [40.4637, -3.7492],
|
|
'IT': [41.8719, 12.5674],
|
|
'CA': [56.1304, -106.3468],
|
|
'AU': [-25.2744, 133.7751],
|
|
'BR': [-14.2350, -51.9253]
|
|
};
|
|
const countryFlags = {
|
|
'DE': '🇩🇪', 'FR': '🇫🇷', 'NL': '🇳🇱', 'US': '🇺🇸', 'UK': '🇬🇧',
|
|
'ES': '🇪🇸', 'IT': '🇮🇹', 'CA': '🇨🇦', 'AU': '🇦🇺', 'BR': '🇧🇷'
|
|
};
|
|
|
|
// Add markers
|
|
Object.entries(countryData).forEach(([code, data]) => {
|
|
if (!countryCoords[code]) return;
|
|
|
|
const openRate = data.sent > 0 ? (data.opens / data.sent * 100) : 0;
|
|
const color = openRate > 25 ? '#00ff88' : openRate > 15 ? '#ff9500' : '#ff4444';
|
|
const size = Math.min(25, Math.max(8, data.sent / 3000));
|
|
|
|
L.circleMarker(countryCoords[code], {
|
|
radius: size,
|
|
fillColor: color,
|
|
color: '#fff',
|
|
weight: 2,
|
|
opacity: 1,
|
|
fillOpacity: 0.7
|
|
}).addTo(map).bindPopup(`
|
|
<div style="min-width:150px;">
|
|
<b>${countryFlags[code] || ''} ${code}</b><br>
|
|
<hr style="margin:5px 0;border-color:#333;">
|
|
📤 Sent: <b>${parseInt(data.sent).toLocaleString()}</b><br>
|
|
📬 Opens: <b>${parseInt(data.opens).toLocaleString()}</b> (${openRate.toFixed(1)}%)<br>
|
|
🖱️ Clicks: <b>${parseInt(data.clicks).toLocaleString()}</b><br>
|
|
🎯 Leads: <b>${parseInt(data.leads).toLocaleString()}</b>
|
|
</div>
|
|
`);
|
|
});
|
|
|
|
// Timezone scheduler
|
|
function updateTimezones() {
|
|
const timezones = [
|
|
{ name: 'Europe (CET)', offset: 1 },
|
|
{ name: 'US East (EST)', offset: -5 },
|
|
{ name: 'US West (PST)', offset: -8 },
|
|
{ name: 'Asia (SGT)', offset: 8 }
|
|
];
|
|
|
|
const now = new Date();
|
|
const html = timezones.map(tz => {
|
|
const localHour = (now.getUTCHours() + tz.offset + 24) % 24;
|
|
const localTime = `${String(localHour).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
|
|
const isOptimal = [9,10,11,14,15,16].includes(localHour);
|
|
const isSleeping = localHour < 7 || localHour > 21;
|
|
|
|
let status = 'waiting', statusText = 'Waiting';
|
|
if (isOptimal) { status = 'optimal'; statusText = 'OPTIMAL'; }
|
|
if (isSleeping) { status = 'sleeping'; statusText = 'Sleep'; }
|
|
|
|
return `
|
|
<div class="tz-item">
|
|
<div>
|
|
<div style="font-size:11px;color:#666;">${tz.name}</div>
|
|
<div class="tz-time">${localTime}</div>
|
|
</div>
|
|
<div class="tz-status ${status}">${statusText}</div>
|
|
</div>
|
|
`;
|
|
}).join('');
|
|
document.getElementById('tzScheduler').innerHTML = html;
|
|
}
|
|
|
|
function updateTime() {
|
|
document.getElementById('currentTime').textContent = new Date().toLocaleTimeString();
|
|
}
|
|
|
|
function launchCampaign() {
|
|
if(confirm('🚀 Launch autonomous campaign to optimal countries?')) {
|
|
alert('Campaign queued! Check the scheduler for status.');
|
|
}
|
|
}
|
|
|
|
updateTimezones();
|
|
updateTime();
|
|
setInterval(updateTime, 1000);
|
|
setInterval(updateTimezones, 60000);
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|