568 lines
41 KiB
PHP
Executable File
568 lines
41 KiB
PHP
Executable File
<?php
|
||
ini_set("memory_limit", "512M");
|
||
/**
|
||
* BRAIN ENGINE DASHBOARD - ULTRA DETAILED DRILL-DOWN
|
||
*/
|
||
$pdo = new PDO("pgsql:host=localhost;dbname=adx_system", "admin", "admin123");
|
||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||
|
||
// Handle POST actions
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
$action = $_POST['action'] ?? '';
|
||
if ($action === 'add_config') {
|
||
$stmt = $pdo->prepare("INSERT INTO admin.brain_configs (isp_target, send_method, domain_used, from_email, subject_template, source, notes) VALUES (?, ?, ?, ?, ?, 'manual', ?)");
|
||
$stmt->execute([$_POST['isp_target'], $_POST['send_method'], $_POST['domain_used'], $_POST['from_email'] ?? '', $_POST['subject_template'] ?? '', $_POST['notes'] ?? '']);
|
||
$success = "Config ajoutée !";
|
||
} elseif ($action === 'mark_inbox') {
|
||
$id = (int)$_POST['config_id'];
|
||
$pdo->exec("UPDATE admin.brain_configs SET inbox_count = inbox_count + 1, total_sent = total_sent + 1, inbox_rate = CASE WHEN total_sent > 0 THEN (inbox_count::decimal / total_sent) * 100 ELSE 0 END, consecutive_inbox = consecutive_inbox + 1, consecutive_spam = 0, confidence_score = LEAST(100, confidence_score + 5), last_success_at = NOW(), updated_at = NOW() WHERE id = $id");
|
||
$check = $pdo->query("SELECT inbox_rate, total_sent, is_winner FROM admin.brain_configs WHERE id = $id")->fetch();
|
||
if ($check['inbox_rate'] >= 80 && $check['total_sent'] >= 10 && !$check['is_winner']) {
|
||
$pdo->exec("UPDATE admin.brain_configs SET is_winner = true WHERE id = $id");
|
||
}
|
||
$success = "Marqué INBOX ✅";
|
||
} elseif ($action === 'mark_spam') {
|
||
$id = (int)$_POST['config_id'];
|
||
$pdo->exec("UPDATE admin.brain_configs SET spam_count = spam_count + 1, total_sent = total_sent + 1, inbox_rate = CASE WHEN total_sent > 0 THEN (inbox_count::decimal / total_sent) * 100 ELSE 0 END, consecutive_spam = consecutive_spam + 1, consecutive_inbox = 0, confidence_score = GREATEST(0, confidence_score - 10), updated_at = NOW() WHERE id = $id");
|
||
$success = "Marqué SPAM 📛";
|
||
}
|
||
}
|
||
|
||
$configs = $pdo->query("SELECT * FROM admin.brain_configs ORDER BY inbox_rate DESC, confidence_score DESC LIMIT 100")->fetchAll(PDO::FETCH_ASSOC);
|
||
$winners = $pdo->query("SELECT c.* FROM admin.brain_configs c WHERE c.is_winner = true ORDER BY c.inbox_rate DESC")->fetchAll(PDO::FETCH_ASSOC);
|
||
$seeds = $pdo->query("SELECT * FROM admin.brain_seeds ORDER BY is_active DESC, isp LIMIT 50")->fetchAll(PDO::FETCH_ASSOC);
|
||
$stats = $pdo->query("SELECT COUNT(*) as total_configs, COUNT(CASE WHEN is_winner THEN 1 END) as total_winners, SUM(total_sent) as total_tests, AVG(inbox_rate) as avg_rate, MAX(inbox_rate) as best_rate FROM admin.brain_configs")->fetch(PDO::FETCH_ASSOC);
|
||
$seedStats = $pdo->query("SELECT COUNT(*) as total, COUNT(CASE WHEN is_active THEN 1 END) as active FROM admin.brain_seeds")->fetch(PDO::FETCH_ASSOC);
|
||
$ispStats = $pdo->query("SELECT isp_target, COUNT(*) as configs, AVG(inbox_rate) as avg_rate, MAX(inbox_rate) as best_rate, SUM(total_sent) as tests, COUNT(CASE WHEN is_winner THEN 1 END) as winners FROM admin.brain_configs GROUP BY isp_target ORDER BY avg_rate DESC")->fetchAll(PDO::FETCH_ASSOC);
|
||
$methodStats = $pdo->query("SELECT send_method, COUNT(*) as configs, AVG(inbox_rate) as avg_rate, COUNT(CASE WHEN is_winner THEN 1 END) as winners FROM admin.brain_configs GROUP BY send_method ORDER BY avg_rate DESC")->fetchAll(PDO::FETCH_ASSOC);
|
||
|
||
$isps = ['T-ONLINE', 'GMX', 'ZIGGO', 'GMAIL', 'OUTLOOK', 'HOTMAIL', 'ALICE', 'YAHOO', 'AOL', 'WEB.DE', 'ORANGE', 'FREE'];
|
||
$methods = ['OFFICE_365', 'GSUITE_RELAY', 'DOMAIN_IP', 'PMTA_DIRECT', 'SES', 'SENDGRID'];
|
||
$configsJson = json_encode($configs);
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="fr">
|
||
<head>
|
||
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>🧠 Brain Engine Dashboard</title>
|
||
<script src="https://cdn.tailwindcss.com"></script>
|
||
<style>
|
||
body { background: linear-gradient(135deg, #0a0f1a 0%, #1a1f3a 50%, #0f1629 100%); min-height: 100vh; }
|
||
.glass { background: rgba(255,255,255,0.05); backdrop-filter: blur(10px); border: 1px solid rgba(255,255,255,0.1); }
|
||
.winner-glow { box-shadow: 0 0 20px rgba(34, 197, 94, 0.3); }
|
||
.stat-card { cursor: pointer; transition: all 0.3s; }
|
||
.stat-card:hover { transform: translateY(-4px); border-color: rgba(139, 92, 246, 0.5); }
|
||
.config-card { cursor: pointer; transition: all 0.3s; }
|
||
.config-card:hover { transform: scale(1.01); }
|
||
.modal { display: none; }
|
||
.modal.open { display: flex; }
|
||
.param-badge { display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; border-radius: 6px; font-size: 11px; }
|
||
.tab-btn { padding: 8px 16px; border-radius: 8px 8px 0 0; cursor: pointer; }
|
||
.tab-btn.active { background: rgba(139, 92, 246, 0.3); border-bottom: 2px solid #a855f7; }
|
||
.tab-content { display: none; }
|
||
.tab-content.active { display: block; }
|
||
.detail-section { background: rgba(0,0,0,0.3); border-radius: 12px; padding: 16px; margin-bottom: 16px; }
|
||
.detail-row { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid rgba(255,255,255,0.05); }
|
||
.detail-row:last-child { border-bottom: none; }
|
||
.copy-btn { cursor: pointer; padding: 2px 8px; background: rgba(255,255,255,0.1); border-radius: 4px; font-size: 10px; }
|
||
.copy-btn:hover { background: rgba(255,255,255,0.2); }
|
||
</style>
|
||
|
||
</head>
|
||
<body class="text-white p-6">
|
||
<div class="max-w-7xl mx-auto">
|
||
|
||
<div class="flex justify-between items-center mb-8">
|
||
<div class="flex items-center gap-4">
|
||
<div class="text-5xl">🧠</div>
|
||
<div><h1 class="text-3xl font-bold bg-gradient-to-r from-purple-400 via-pink-400 to-yellow-400 bg-clip-text text-transparent">BRAIN ENGINE</h1>
|
||
<p class="text-gray-400">Winning Configurations & Performance Analysis</p></div>
|
||
</div>
|
||
<div class="flex gap-3">
|
||
<button onclick="document.getElementById('addModal').classList.remove('hidden')" class="px-4 py-2 bg-purple-600 rounded-lg hover:bg-purple-500">➕ Add Config</button>
|
||
<a href="index.php" class="px-4 py-2 bg-gray-700 rounded-lg hover:bg-gray-600">🏠 Home</a>
|
||
</div>
|
||
</div>
|
||
|
||
<?php if (!empty($success)): ?><div class="bg-green-500/20 border border-green-500 text-green-300 p-4 rounded-lg mb-6">✅ <?= $success ?></div><?php endif; ?>
|
||
|
||
<div class="grid grid-cols-6 gap-4 mb-8">
|
||
<div class="glass rounded-xl p-4 text-center stat-card" onclick="showModal('configsModal')"><div class="text-3xl font-bold text-blue-400"><?= $stats['total_configs'] ?></div><div class="text-sm text-gray-400">Configs</div><div class="text-xs text-blue-300 mt-1">Cliquer pour détails →</div></div>
|
||
<div class="glass rounded-xl p-4 text-center stat-card winner-glow" onclick="showModal('winnersModal')"><div class="text-3xl font-bold text-green-400"><?= $stats['total_winners'] ?></div><div class="text-sm text-gray-400">Winners 🏆</div><div class="text-xs text-green-300 mt-1">Voir paramètres →</div></div>
|
||
<div class="glass rounded-xl p-4 text-center stat-card" onclick="showModal('testsModal')"><div class="text-3xl font-bold text-yellow-400"><?= $stats['total_tests'] ?? 0 ?></div><div class="text-sm text-gray-400">Tests</div><div class="text-xs text-yellow-300 mt-1">Historique →</div></div>
|
||
<div class="glass rounded-xl p-4 text-center stat-card" onclick="showModal('ispModal')"><div class="text-3xl font-bold text-purple-400"><?= number_format($stats['avg_rate'] ?? 0, 1) ?>%</div><div class="text-sm text-gray-400">Avg Rate</div><div class="text-xs text-purple-300 mt-1">Par ISP →</div></div>
|
||
<div class="glass rounded-xl p-4 text-center stat-card" onclick="showModal('methodsModal')"><div class="text-3xl font-bold text-pink-400"><?= number_format($stats['best_rate'] ?? 0, 1) ?>%</div><div class="text-sm text-gray-400">Best</div><div class="text-xs text-pink-300 mt-1">Par méthode →</div></div>
|
||
<div class="glass rounded-xl p-4 text-center stat-card" onclick="showModal('seedsModal')"><div class="text-3xl font-bold <?= ($seedStats['active'] ?? 0) > 0 ? 'text-green-400' : 'text-red-400' ?>"><?= $seedStats['active'] ?? 0 ?>/<?= $seedStats['total'] ?? 0 ?></div><div class="text-sm text-gray-400">Seeds</div><div class="text-xs text-cyan-300 mt-1">Gérer seeds →</div></div>
|
||
</div>
|
||
|
||
<div class="glass rounded-xl p-6 mb-8">
|
||
<h2 class="text-2xl font-bold text-green-400 mb-6">🏆 Winning Configurations <span class="text-sm font-normal text-gray-400">(>=80% inbox & >=10 tests)</span></h2>
|
||
<?php if (empty($winners)): ?><div class="text-center py-8 text-gray-400">Aucun winner. Continuez les tests!</div>
|
||
<?php else: foreach ($winners as $i => $w): $medal = $i === 0 ? '🥇' : ($i === 1 ? '🥈' : ($i === 2 ? '🥉' : '🏆')); ?>
|
||
<div class="config-card bg-gradient-to-r from-green-900/40 to-emerald-900/20 p-6 rounded-xl border border-green-500/30 mb-4" onclick="showDeepDrill(<?= $w['id'] ?>)">
|
||
<div class="flex justify-between items-start">
|
||
<div class="flex items-start gap-4">
|
||
<span class="text-4xl"><?= $medal ?></span>
|
||
<div>
|
||
<div class="text-xl font-bold text-white mb-2"><?= $w['isp_target'] ?> <span class="text-sm text-gray-400">#<?= $w['id'] ?></span></div>
|
||
<div class="flex flex-wrap gap-2 mb-3">
|
||
<span class="param-badge bg-blue-500/30 text-blue-300">📧 <?= $w['send_method'] ?></span>
|
||
<span class="param-badge bg-purple-500/30 text-purple-300">🌐 <?= $w['domain_used'] ?: 'N/A' ?></span>
|
||
<?php if ($w['from_email']): ?><span class="param-badge bg-cyan-500/30 text-cyan-300">✉️ <?= $w['from_email'] ?></span><?php endif; ?>
|
||
<?php if ($w['subject_template']): ?><span class="param-badge bg-pink-500/30 text-pink-300">📝 Subject: <?= substr($w['subject_template'], 0, 30) ?>...</span><?php endif; ?>
|
||
</div>
|
||
<div class="grid grid-cols-4 gap-4 text-sm">
|
||
<div><span class="text-gray-400">Inbox:</span> <span class="text-green-400 font-bold"><?= $w['inbox_count'] ?></span></div>
|
||
<div><span class="text-gray-400">Spam:</span> <span class="text-red-400"><?= $w['spam_count'] ?></span></div>
|
||
<div><span class="text-gray-400">Consec:</span> <span class="text-green-300"><?= $w['consecutive_inbox'] ?>🔥</span></div>
|
||
<div><span class="text-gray-400">Conf:</span> <span class="text-yellow-400"><?= $w['confidence_score'] ?>%</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="text-right">
|
||
<div class="text-4xl font-black text-green-400"><?= number_format($w['inbox_rate'], 1) ?>%</div>
|
||
<div class="text-sm text-gray-400"><?= $w['total_sent'] ?> tests</div>
|
||
<div class="text-xs text-purple-400 mt-2">🔍 Cliquer pour tous les détails →</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; endif; ?>
|
||
</div>
|
||
|
||
<div class="glass rounded-xl p-6 mb-8">
|
||
<div class="flex justify-between items-center mb-4">
|
||
<h2 class="text-xl font-bold text-blue-400">📊 Toutes les Configurations</h2>
|
||
<input type="text" id="searchConfigs" placeholder="🔍 Rechercher..." class="bg-gray-800 border border-gray-600 rounded-lg px-4 py-2 text-sm" onkeyup="filterTable()">
|
||
</div>
|
||
<div class="overflow-x-auto">
|
||
<table class="w-full">
|
||
<thead><tr class="border-b border-gray-700 text-left text-sm text-gray-400">
|
||
<th class="p-3">ISP</th><th class="p-3">Méthode</th><th class="p-3">Domaine</th><th class="p-3 text-center">Tests</th><th class="p-3 text-center">Inbox</th><th class="p-3 text-center">Spam</th><th class="p-3 text-center">Rate</th><th class="p-3 text-center">Status</th><th class="p-3">Actions</th>
|
||
</tr></thead>
|
||
<tbody>
|
||
<?php foreach ($configs as $c): $rate = $c['inbox_rate'] ?? 0; $color = $rate >= 80 ? 'text-green-400' : ($rate >= 50 ? 'text-yellow-400' : 'text-red-400'); ?>
|
||
<tr class="border-b border-gray-800 hover:bg-white/5 config-row cursor-pointer" onclick="showDeepDrill(<?= $c['id'] ?>)">
|
||
<td class="p-3 font-bold"><?= $c['isp_target'] ?></td>
|
||
<td class="p-3"><span class="px-2 py-1 rounded text-xs bg-blue-500/20 text-blue-300"><?= $c['send_method'] ?></span></td>
|
||
<td class="p-3 text-gray-400 text-xs font-mono"><?= $c['domain_used'] ?: '-' ?></td>
|
||
<td class="p-3 text-center"><?= $c['total_sent'] ?></td>
|
||
<td class="p-3 text-center text-green-400 font-bold"><?= $c['inbox_count'] ?></td>
|
||
<td class="p-3 text-center text-red-400"><?= $c['spam_count'] ?></td>
|
||
<td class="p-3 text-center font-bold <?= $color ?>"><?= number_format($rate, 1) ?>%</td>
|
||
<td class="p-3 text-center text-xl"><?= $c['is_winner'] ? '🏆' : ($c['total_sent'] < 10 ? '🔄' : '📊') ?></td>
|
||
<td class="p-3" onclick="event.stopPropagation();">
|
||
<form method="POST" class="inline-flex gap-1"><input type="hidden" name="config_id" value="<?= $c['id'] ?>">
|
||
<button name="action" value="mark_inbox" class="px-2 py-1 bg-green-600 hover:bg-green-500 rounded text-xs">📥</button>
|
||
<button name="action" value="mark_spam" class="px-2 py-1 bg-red-600 hover:bg-red-500 rounded text-xs">📛</button>
|
||
</form>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- DEEP DRILL MODAL -->
|
||
<div id="deepDrillModal" class="modal fixed inset-0 bg-black/90 items-center justify-center z-50 p-4 overflow-y-auto">
|
||
<div class="glass rounded-2xl w-full max-w-6xl my-8">
|
||
<div class="p-6 border-b border-gray-700 flex justify-between items-center sticky top-0 bg-gray-900/98 z-10">
|
||
<div><h3 class="text-2xl font-bold" id="drillTitle">Config Details</h3><div class="text-sm text-gray-400" id="drillSubtitle"></div></div>
|
||
<div class="flex gap-2">
|
||
<button onclick="copyConfig()" class="px-3 py-1 bg-gray-700 rounded text-sm">📋 Copy JSON</button>
|
||
<button onclick="exportPMTA()" class="px-3 py-1 bg-purple-600 rounded text-sm">📤 Export PMTA</button>
|
||
<button onclick="closeModal('deepDrillModal')" class="text-3xl text-gray-400 hover:text-white ml-4">×</button>
|
||
</div>
|
||
</div>
|
||
<div class="p-6" id="drillContent"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ISP Modal -->
|
||
<div id="ispModal" class="modal fixed inset-0 bg-black/80 items-center justify-center z-50 p-4">
|
||
<div class="glass rounded-2xl w-full max-w-3xl max-h-[90vh] overflow-y-auto">
|
||
<div class="p-6 border-b border-gray-700 flex justify-between items-center"><h3 class="text-2xl font-bold text-purple-400">📊 Performance par ISP</h3><button onclick="closeModal('ispModal')" class="text-3xl">×</button></div>
|
||
<div class="p-6"><?php foreach ($ispStats as $isp): $color = ($isp['avg_rate'] ?? 0) >= 70 ? 'bg-green-500' : 'bg-yellow-500'; ?>
|
||
<div class="mb-4"><div class="flex justify-between mb-1"><span class="font-bold"><?= $isp['isp_target'] ?></span><span class="text-sm text-gray-400"><?= $isp['configs'] ?> configs | <?= $isp['winners'] ?> winners</span></div>
|
||
<div class="w-full bg-gray-700 rounded-full h-4"><div class="<?= $color ?> h-4 rounded-full" style="width:<?= min(100, $isp['avg_rate'] ?? 0) ?>%"></div></div>
|
||
<div class="text-xs text-gray-500"><?= number_format($isp['avg_rate'] ?? 0, 1) ?>% avg | Best: <?= number_format($isp['best_rate'] ?? 0, 1) ?>%</div></div>
|
||
<?php endforeach; ?></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Methods Modal -->
|
||
<div id="methodsModal" class="modal fixed inset-0 bg-black/80 items-center justify-center z-50 p-4">
|
||
<div class="glass rounded-2xl w-full max-w-3xl max-h-[90vh] overflow-y-auto">
|
||
<div class="p-6 border-b border-gray-700 flex justify-between items-center"><h3 class="text-2xl font-bold text-pink-400">📧 Performance par Méthode</h3><button onclick="closeModal('methodsModal')" class="text-3xl">×</button></div>
|
||
<div class="p-6"><?php foreach ($methodStats as $m): ?>
|
||
<div class="bg-gray-800 p-4 rounded-lg mb-3 flex justify-between items-center"><div><div class="font-bold"><?= $m['send_method'] ?></div><div class="text-sm text-gray-400"><?= $m['configs'] ?> configs | <?= $m['winners'] ?> winners</div></div>
|
||
<div class="text-3xl font-bold <?= ($m['avg_rate'] ?? 0) >= 70 ? 'text-green-400' : 'text-yellow-400' ?>"><?= number_format($m['avg_rate'] ?? 0, 1) ?>%</div></div>
|
||
<?php endforeach; ?></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Other Modals -->
|
||
<div id="winnersModal" class="modal fixed inset-0 bg-black/80 items-center justify-center z-50 p-4"><div class="glass rounded-2xl w-full max-w-3xl max-h-[90vh] overflow-y-auto"><div class="p-6 border-b border-gray-700 flex justify-between items-center"><h3 class="text-2xl font-bold text-green-400">🏆 Winners Analysis</h3><button onclick="closeModal('winnersModal')" class="text-3xl">×</button></div><div class="p-6"><p class="text-gray-400">Cliquez sur un winner dans la liste principale pour voir tous ses détails.</p></div></div></div>
|
||
<div id="seedsModal" class="modal fixed inset-0 bg-black/80 items-center justify-center z-50 p-4"><div class="glass rounded-2xl w-full max-w-3xl max-h-[90vh] overflow-y-auto"><div class="p-6 border-b border-gray-700 flex justify-between items-center"><h3 class="text-2xl font-bold text-cyan-400">🌱 Seeds</h3><button onclick="closeModal('seedsModal')" class="text-3xl">×</button></div><div class="p-6 grid grid-cols-2 gap-3"><?php foreach ($seeds as $seed): ?><div class="p-3 rounded-lg <?= $seed['is_active'] ? 'bg-green-900/20' : 'bg-red-900/20' ?>"><div class="font-bold truncate"><?= $seed['email'] ?></div><div class="text-sm text-gray-400"><?= $seed['isp'] ?> <?= $seed['is_active'] ? '✅' : '❌' ?></div></div><?php endforeach; ?></div></div></div>
|
||
<div id="configsModal" class="modal fixed inset-0 bg-black/80 items-center justify-center z-50 p-4"><div class="glass rounded-2xl w-full max-w-4xl max-h-[90vh] overflow-y-auto"><div class="p-6 border-b border-gray-700 flex justify-between items-center sticky top-0 bg-gray-900/98"><h3 class="text-2xl font-bold text-blue-400">📋 Configs - Level 2 Drill</h3><button onclick="closeModal('configsModal')" class="text-3xl">×</button></div><div class="p-6"><div class="grid grid-cols-3 gap-4 mb-6"><div class="bg-green-500/10 p-4 rounded-lg text-center cursor-pointer hover:bg-green-500/20" onclick="showConfigCategory('winners')"><div class="text-3xl font-bold text-green-400"><?= $stats['total_winners'] ?></div><div class="text-sm">Winners 🏆</div><div class="text-xs text-green-300 mt-1">Click pour liste →</div></div><div class="bg-yellow-500/10 p-4 rounded-lg text-center cursor-pointer hover:bg-yellow-500/20" onclick="showConfigCategory('testing')"><div class="text-3xl font-bold text-yellow-400"><?= count(array_filter($configs, fn($c) => !$c['is_winner'] && $c['total_sent'] >= 5)) ?></div><div class="text-sm">Testing 🔄</div><div class="text-xs text-yellow-300 mt-1">Click pour liste →</div></div><div class="bg-gray-500/10 p-4 rounded-lg text-center cursor-pointer hover:bg-gray-500/20" onclick="showConfigCategory('new')"><div class="text-3xl font-bold text-gray-400"><?= count(array_filter($configs, fn($c) => $c['total_sent'] < 5)) ?></div><div class="text-sm">New 🆕</div><div class="text-xs text-gray-300 mt-1">Click pour liste →</div></div></div><div id="configCategoryDetail" class="hidden"><h4 id="categoryTitle" class="font-bold text-lg mb-4"></h4><div id="categoryList" class="space-y-2 max-h-80 overflow-y-auto"></div></div></div></div></div>
|
||
<div id="testsModal" class="modal fixed inset-0 bg-black/80 items-center justify-center z-50 p-4"><div class="glass rounded-2xl w-full max-w-3xl"><div class="p-6 border-b border-gray-700 flex justify-between items-center"><h3 class="text-2xl font-bold text-yellow-400">🧪 Tests</h3><button onclick="closeModal('testsModal')" class="text-3xl">×</button></div><div class="p-6"><div class="grid grid-cols-2 gap-4"><div class="bg-green-500/10 p-4 rounded-lg text-center"><div class="text-3xl font-bold text-green-400"><?= array_sum(array_column($configs, 'inbox_count')) ?></div><div class="text-sm">Inbox</div></div><div class="bg-red-500/10 p-4 rounded-lg text-center"><div class="text-3xl font-bold text-red-400"><?= array_sum(array_column($configs, 'spam_count')) ?></div><div class="text-sm">Spam</div></div></div></div></div></div>
|
||
|
||
<!-- Add Modal -->
|
||
<div id="addModal" class="hidden fixed inset-0 bg-black/70 flex items-center justify-center z-50">
|
||
<div class="glass rounded-xl p-6 w-full max-w-lg">
|
||
<h3 class="text-xl font-bold mb-4">➕ Nouvelle Config</h3>
|
||
<form method="POST"><input type="hidden" name="action" value="add_config">
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div><label class="text-sm text-gray-400">ISP *</label><select name="isp_target" required class="w-full bg-gray-800 border border-gray-600 rounded p-2"><?php foreach ($isps as $isp): ?><option><?= $isp ?></option><?php endforeach; ?></select></div>
|
||
<div><label class="text-sm text-gray-400">Méthode *</label><select name="send_method" required class="w-full bg-gray-800 border border-gray-600 rounded p-2"><?php foreach ($methods as $m): ?><option><?= $m ?></option><?php endforeach; ?></select></div>
|
||
<div class="col-span-2"><label class="text-sm text-gray-400">Domaine</label><input name="domain_used" class="w-full bg-gray-800 border border-gray-600 rounded p-2"></div>
|
||
<div><label class="text-sm text-gray-400">From Email</label><input name="from_email" class="w-full bg-gray-800 border border-gray-600 rounded p-2"></div>
|
||
<div><label class="text-sm text-gray-400">Subject</label><input name="subject_template" class="w-full bg-gray-800 border border-gray-600 rounded p-2"></div>
|
||
</div>
|
||
<div class="flex justify-end gap-3 mt-4">
|
||
<button type="button" onclick="document.getElementById('addModal').classList.add('hidden')" class="px-4 py-2 bg-gray-600 rounded">Annuler</button>
|
||
<button type="submit" class="px-4 py-2 bg-purple-600 rounded">💾 Save</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<script>
|
||
const allConfigs = <?= $configsJson ?>;
|
||
let currentConfig = null;
|
||
|
||
function showModal(id) { document.getElementById(id).classList.add('open'); }
|
||
function closeModal(id) { document.getElementById(id).classList.remove('open'); }
|
||
|
||
function showDeepDrill(configId) {
|
||
currentConfig = allConfigs.find(c => c.id == configId);
|
||
if (!currentConfig) return;
|
||
const c = currentConfig;
|
||
|
||
document.getElementById('drillTitle').innerHTML = `${c.is_winner ? '🏆' : '📊'} ${c.isp_target} - ${c.send_method}`;
|
||
document.getElementById('drillSubtitle').innerHTML = `Config #${c.id} | ${c.total_sent} tests | ${Number(c.inbox_rate).toFixed(1)}% inbox`;
|
||
|
||
document.getElementById('drillContent').innerHTML = `
|
||
<div class="flex gap-2 mb-6 border-b border-gray-700 pb-2 overflow-x-auto">
|
||
<button class="tab-btn active" onclick="showTab('overview')">📋 Overview</button>
|
||
<button class="tab-btn" onclick="showTab('email')">✉️ Email</button>
|
||
<button class="tab-btn" onclick="showTab('headers')">📝 Headers</button>
|
||
<button class="tab-btn" onclick="showTab('dns')">🔐 DNS/Auth</button>
|
||
<button class="tab-btn" onclick="showTab('body')">📄 Body</button>
|
||
<button class="tab-btn" onclick="showTab('tracking')">📊 Tracking</button>
|
||
<button class="tab-btn" onclick="showTab('pmta')">⚙️ PMTA</button>
|
||
<button class="tab-btn" onclick="showTab('raw')">🔧 Raw</button>
|
||
</div>
|
||
|
||
<div id="tab-overview" class="tab-content active">
|
||
<div class="grid grid-cols-2 gap-6">
|
||
<div class="detail-section">
|
||
<h5 class="text-green-400 font-bold mb-4">📈 Performance Metrics</h5>
|
||
<div class="text-center mb-4">
|
||
<div class="text-6xl font-black ${c.inbox_rate >= 80 ? 'text-green-400' : c.inbox_rate >= 50 ? 'text-yellow-400' : 'text-red-400'}">${Number(c.inbox_rate).toFixed(1)}%</div>
|
||
<div class="text-gray-400">Inbox Rate</div>
|
||
</div>
|
||
<div class="grid grid-cols-2 gap-3">
|
||
<div class="bg-green-900/30 p-3 rounded text-center"><div class="text-2xl font-bold text-green-400">${c.inbox_count}</div><div class="text-xs text-gray-400">Inbox</div></div>
|
||
<div class="bg-red-900/30 p-3 rounded text-center"><div class="text-2xl font-bold text-red-400">${c.spam_count}</div><div class="text-xs text-gray-400">Spam</div></div>
|
||
<div class="bg-yellow-900/30 p-3 rounded text-center"><div class="text-2xl font-bold text-yellow-400">${c.confidence_score}%</div><div class="text-xs text-gray-400">Confidence</div></div>
|
||
<div class="bg-purple-900/30 p-3 rounded text-center"><div class="text-2xl font-bold text-purple-400">${c.stability_score || 0}%</div><div class="text-xs text-gray-400">Stability</div></div>
|
||
</div>
|
||
<div class="mt-4 text-sm space-y-2">
|
||
<div class="detail-row"><span class="text-gray-400">Consecutive Inbox</span><span class="text-green-400 font-bold">${c.consecutive_inbox} 🔥</span></div>
|
||
<div class="detail-row"><span class="text-gray-400">Consecutive Spam</span><span class="text-red-400">${c.consecutive_spam}</span></div>
|
||
<div class="detail-row"><span class="text-gray-400">Bounce Count</span><span>${c.bounce_count || 0}</span></div>
|
||
<div class="detail-row"><span class="text-gray-400">Last Success</span><span>${c.last_success_at || 'Never'}</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h5 class="${c.is_winner ? 'text-green-400' : 'text-yellow-400'} font-bold mb-4">${c.is_winner ? '🏆 Winning Factors' : '📈 Path to Winner'}</h5>
|
||
${c.is_winner ? `
|
||
<div class="bg-green-900/30 p-4 rounded-lg border border-green-500/50 mb-4">
|
||
<ul class="text-sm space-y-2">
|
||
<li>✅ High inbox rate: ${Number(c.inbox_rate).toFixed(1)}%</li>
|
||
<li>✅ Sufficient tests: ${c.total_sent}</li>
|
||
<li>✅ Confidence: ${c.confidence_score}%</li>
|
||
<li>✅ Method: ${c.send_method}</li>
|
||
${c.domain_used ? `<li>✅ Domain: ${c.domain_used}</li>` : ''}
|
||
</ul>
|
||
</div>` : `
|
||
<div class="bg-yellow-900/30 p-4 rounded-lg border border-yellow-500/50 mb-4">
|
||
<ul class="text-sm space-y-2">
|
||
<li>${c.inbox_rate >= 80 ? '✅' : '❌'} Rate >= 80% (${Number(c.inbox_rate).toFixed(1)}%)</li>
|
||
<li>${c.total_sent >= 10 ? '✅' : '❌'} Tests >= 10 (${c.total_sent})</li>
|
||
<li>Need ${Math.max(0, 10 - c.total_sent)} more tests</li>
|
||
</ul>
|
||
</div>`}
|
||
<div class="text-sm space-y-2">
|
||
<div class="detail-row"><span class="text-gray-400">Status</span><span>${c.status || 'learning'}</span></div>
|
||
<div class="detail-row"><span class="text-gray-400">Source</span><span>${c.source || 'manual'}</span></div>
|
||
<div class="detail-row"><span class="text-gray-400">Created</span><span>${c.created_at}</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="tab-email" class="tab-content">
|
||
<div class="grid grid-cols-2 gap-6">
|
||
<div class="detail-section">
|
||
<h5 class="text-blue-400 font-bold mb-4">📧 Sender Configuration</h5>
|
||
<div class="space-y-2 text-sm">
|
||
<div class="detail-row"><span class="text-gray-400">ISP Target</span><span class="font-bold">${c.isp_target}</span></div>
|
||
<div class="detail-row"><span class="text-gray-400">Send Method</span><span class="text-blue-400">${c.send_method}</span></div>
|
||
<div class="detail-row"><span class="text-gray-400">SMTP Type</span><span>${c.smtp_type || 'N/A'}</span></div>
|
||
<div class="detail-row"><span class="text-gray-400">SMTP Account ID</span><span>${c.smtp_account_id || 'N/A'}</span></div>
|
||
<div class="detail-row"><span class="text-gray-400">Domain</span><span class="font-mono text-purple-400">${c.domain_used || 'N/A'}</span></div>
|
||
<div class="detail-row"><span class="text-gray-400">IP Used</span><span class="font-mono text-orange-400">${c.ip_used || 'N/A'}</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h5 class="text-cyan-400 font-bold mb-4">✉️ Email Identity</h5>
|
||
<div class="space-y-2 text-sm">
|
||
<div class="detail-row"><span class="text-gray-400">From Name</span><span>${c.from_name || 'N/A'}</span></div>
|
||
<div class="detail-row"><span class="text-gray-400">From Email</span><span class="text-cyan-400">${c.from_email || 'N/A'}</span></div>
|
||
<div class="detail-row"><span class="text-gray-400">Reply-To</span><span>${c.reply_to || 'N/A'}</span></div>
|
||
<div class="detail-row"><span class="text-gray-400">Return-Path</span><span class="font-mono text-xs">${c.return_path || 'N/A'}</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h5 class="text-pink-400 font-bold mb-4">📝 Content Config</h5>
|
||
<div class="grid grid-cols-3 gap-4 text-sm">
|
||
<div class="detail-row"><span class="text-gray-400">Content-Type</span><span>${c.content_type || 'text/html'}</span></div>
|
||
<div class="detail-row"><span class="text-gray-400">Encoding</span><span>${c.encoding || 'N/A'}</span></div>
|
||
<div class="detail-row"><span class="text-gray-400">Charset</span><span>${c.charset || 'utf-8'}</span></div>
|
||
</div>
|
||
<div class="mt-4"><div class="text-gray-400 text-sm mb-2">Subject Template</div><div class="bg-gray-800 p-3 rounded font-mono text-sm">${c.subject_template || 'N/A'}</div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="tab-headers" class="tab-content">
|
||
<div class="detail-section">
|
||
<h5 class="text-yellow-400 font-bold mb-4">📝 Custom Headers (Critical for Deliverability)</h5>
|
||
<table class="w-full text-sm">
|
||
<thead><tr class="border-b border-gray-700"><th class="p-2 text-left text-gray-400">#</th><th class="p-2 text-left text-gray-400">Header Name</th><th class="p-2 text-left text-gray-400">Value</th><th class="p-2 text-gray-400">Status</th></tr></thead>
|
||
<tbody>
|
||
${[1,2,3,4,5].map(i => `<tr class="border-b border-gray-800">
|
||
<td class="p-2 text-gray-500">${i}</td>
|
||
<td class="p-2 font-mono text-blue-400">${c['header_'+i+'_name'] || '-'}</td>
|
||
<td class="p-2 font-mono text-xs max-w-xs truncate">${c['header_'+i+'_value'] || '-'}</td>
|
||
<td class="p-2"><span class="text-xs px-2 py-1 rounded ${c['header_'+i+'_name'] ? 'bg-green-500/20 text-green-300' : 'bg-gray-500/20 text-gray-400'}">${c['header_'+i+'_name'] ? 'Active' : 'Empty'}</span></td>
|
||
</tr>`).join('')}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h5 class="text-purple-400 font-bold mb-4">🔍 Header Analysis</h5>
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div class="bg-gray-800 p-4 rounded"><div class="text-gray-400 text-sm mb-1">X-Mailer</div><div class="font-mono">${c.header_1_name === 'X-Mailer' ? c.header_1_value : 'Not set'}</div><div class="text-xs text-gray-500 mt-1">Tip: Exchange sans X-Mailer = 97% inbox</div></div>
|
||
<div class="bg-gray-800 p-4 rounded"><div class="text-gray-400 text-sm mb-1">List-Unsubscribe</div><div class="font-mono text-xs truncate">${c.list_unsubscribe || 'Not set'}</div><div class="text-xs text-gray-500 mt-1">Required for Gmail/Outlook bulk</div></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="tab-dns" class="tab-content">
|
||
<div class="detail-section">
|
||
<h5 class="text-green-400 font-bold mb-4">🔐 Authentication Records</h5>
|
||
<div class="space-y-4">
|
||
<div class="bg-gray-800 p-4 rounded-lg">
|
||
<div class="flex justify-between items-center mb-2"><span class="font-bold text-blue-400">SPF Record</span><span class="text-xs px-2 py-1 rounded ${c.spf_record ? 'bg-green-500/20 text-green-300' : 'bg-red-500/20 text-red-300'}">${c.spf_record ? '✅ Set' : '❌ Missing'}</span></div>
|
||
<div class="font-mono text-xs bg-gray-900 p-2 rounded overflow-x-auto">${c.spf_record || 'v=spf1 include:spf.protection.outlook.com -all'}</div>
|
||
</div>
|
||
<div class="bg-gray-800 p-4 rounded-lg">
|
||
<div class="flex justify-between items-center mb-2"><span class="font-bold text-purple-400">DKIM Selector</span><span class="text-xs px-2 py-1 rounded ${c.dkim_selector ? 'bg-green-500/20 text-green-300' : 'bg-yellow-500/20 text-yellow-300'}">${c.dkim_selector ? '✅ Set' : '⚠️ Default'}</span></div>
|
||
<div class="font-mono">${c.dkim_selector || 'selector1'}</div>
|
||
</div>
|
||
<div class="bg-gray-800 p-4 rounded-lg">
|
||
<div class="flex justify-between items-center mb-2"><span class="font-bold text-orange-400">DMARC Policy</span><span class="text-xs px-2 py-1 rounded ${c.dmarc_policy ? 'bg-green-500/20 text-green-300' : 'bg-yellow-500/20 text-yellow-300'}">${c.dmarc_policy ? '✅ Set' : '⚠️ None'}</span></div>
|
||
<div class="font-mono">${c.dmarc_policy || 'none'}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="tab-body" class="tab-content">
|
||
<div class="detail-section">
|
||
<h5 class="text-pink-400 font-bold mb-4">📄 Email Body Template</h5>
|
||
${c.body_template ? `
|
||
<div class="bg-gray-900 p-4 rounded-lg font-mono text-xs max-h-96 overflow-auto whitespace-pre-wrap">${c.body_template.replace(/</g, '<').replace(/>/g, '>')}</div>
|
||
<div class="mt-4 flex gap-2">
|
||
<button onclick="copyBody()" class="px-3 py-1 bg-gray-700 rounded text-sm">📋 Copy Body</button>
|
||
<button onclick="previewBody()" class="px-3 py-1 bg-blue-600 rounded text-sm">👁️ Preview HTML</button>
|
||
</div>
|
||
` : '<p class="text-gray-400">No body template configured</p>'}
|
||
</div>
|
||
<div class="detail-section">
|
||
<h5 class="text-cyan-400 font-bold mb-4">🔗 Links & Assets</h5>
|
||
<div class="space-y-2 text-sm">
|
||
<div class="detail-row"><span class="text-gray-400">Link URL</span><span class="font-mono text-xs truncate max-w-md">${c.link_url || 'N/A'}</span></div>
|
||
<div class="detail-row"><span class="text-gray-400">Image URL</span><span class="font-mono text-xs truncate max-w-md">${c.image_url || 'N/A'}</span></div>
|
||
<div class="detail-row"><span class="text-gray-400">Unsubscribe URL</span><span class="font-mono text-xs truncate max-w-md">${c.unsubscribe_url || 'N/A'}</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="tab-tracking" class="tab-content">
|
||
<div class="detail-section">
|
||
<h5 class="text-orange-400 font-bold mb-4">📊 Tracking Configuration</h5>
|
||
<div class="space-y-2 text-sm">
|
||
<div class="detail-row"><span class="text-gray-400">Tracking Domain</span><span class="font-mono">${c.tracking_domain || 'N/A'}</span></div>
|
||
<div class="detail-row"><span class="text-gray-400">Redirect Domain</span><span class="font-mono">${c.redirect_domain || 'N/A'}</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="tab-pmta" class="tab-content">
|
||
<div class="detail-section">
|
||
<h5 class="text-red-400 font-bold mb-4">⚙️ PMTA Configuration</h5>
|
||
<div class="space-y-2 text-sm mb-4">
|
||
<div class="detail-row"><span class="text-gray-400">PMTA Version</span><span>${c.pmta_version || '5.0r3'}</span></div>
|
||
<div class="detail-row"><span class="text-gray-400">Config Hash</span><span class="font-mono text-xs">${c.config_hash || 'N/A'}</span></div>
|
||
</div>
|
||
<div class="bg-gray-900 p-4 rounded-lg font-mono text-xs">
|
||
<pre># PMTA Config for ${c.isp_target} - ${c.send_method}
|
||
# Generated from Brain Engine Config #${c.id}
|
||
|
||
<domain ${c.isp_target.toLowerCase()}.com>
|
||
max-smtp-out 10
|
||
smtp-pattern-lifetime 1h
|
||
</domain>
|
||
|
||
<source ${c.ip_used || '0.0.0.0'}>
|
||
smtp-source-host ${c.ip_used || '0.0.0.0'} ${c.domain_used || 'example.com'}
|
||
max-msg-rate 100/h
|
||
</source>
|
||
|
||
# Headers
|
||
${c.header_1_name ? `header-${c.header_1_name}: ${c.header_1_value}` : '# No custom headers'}
|
||
${c.header_2_name ? `header-${c.header_2_name}: ${c.header_2_value}` : ''}
|
||
|
||
# Authentication
|
||
dkim-sign yes
|
||
dkim-selector ${c.dkim_selector || 'selector1'}
|
||
</pre>
|
||
</div>
|
||
<button onclick="copyPMTA()" class="mt-4 px-4 py-2 bg-red-600 rounded text-sm">📋 Copy PMTA Config</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="tab-raw" class="tab-content">
|
||
<div class="detail-section">
|
||
<h5 class="text-gray-400 font-bold mb-4">🔧 Raw JSON Data</h5>
|
||
<pre class="bg-gray-900 p-4 rounded text-xs overflow-auto max-h-96">${JSON.stringify(c, null, 2)}</pre>
|
||
<button onclick="copyConfig()" class="mt-4 px-4 py-2 bg-gray-700 rounded text-sm">📋 Copy JSON</button>
|
||
</div>
|
||
</div>
|
||
`;
|
||
showModal('deepDrillModal');
|
||
}
|
||
|
||
function showTab(name) {
|
||
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
|
||
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
|
||
document.querySelector(`[onclick="showTab('${name}')"]`).classList.add('active');
|
||
document.getElementById('tab-' + name).classList.add('active');
|
||
}
|
||
|
||
function copyConfig() { navigator.clipboard.writeText(JSON.stringify(currentConfig, null, 2)); alert('Copied!'); }
|
||
function copyBody() { navigator.clipboard.writeText(currentConfig.body_template || ''); alert('Body copied!'); }
|
||
function copyPMTA() {
|
||
const pmta = document.querySelector('#tab-pmta pre').textContent;
|
||
navigator.clipboard.writeText(pmta);
|
||
alert('PMTA config copied!');
|
||
}
|
||
function exportPMTA() { copyPMTA(); }
|
||
function previewBody() {
|
||
const w = window.open('', '_blank');
|
||
w.document.write(currentConfig.body_template || '<p>No body</p>');
|
||
}
|
||
|
||
function filterTable() {
|
||
function showConfigCategory(cat) {
|
||
const configs = allConfigs;
|
||
let filtered = [];
|
||
let title = "";
|
||
let color = "";
|
||
|
||
if (cat === "winners") {
|
||
filtered = configs.filter(c => c.is_winner);
|
||
title = "🏆 Winners - Configs Gagnantes";
|
||
color = "green";
|
||
} else if (cat === "testing") {
|
||
filtered = configs.filter(c => !c.is_winner && c.total_sent >= 5);
|
||
title = "🔄 Testing - En cours de test";
|
||
color = "yellow";
|
||
} else {
|
||
filtered = configs.filter(c => c.total_sent < 5);
|
||
title = "🆕 New - Nouvelles configs";
|
||
color = "gray";
|
||
}
|
||
|
||
let html = `<div class="mt-4 p-4 bg-gray-800 rounded-lg border border-${color}-500/30">
|
||
<h4 class="font-bold mb-3 text-${color}-400">${title} (${filtered.length})</h4>
|
||
<div class="space-y-2 max-h-60 overflow-y-auto">`;
|
||
|
||
if (filtered.length === 0) {
|
||
html += '<p class="text-gray-500">Aucune config dans cette catégorie</p>';
|
||
} else {
|
||
filtered.forEach(c => {
|
||
html += `<div class="flex justify-between items-center p-3 bg-gray-700/50 rounded cursor-pointer hover:bg-gray-600" onclick="closeModal('configsModal');showDeepDrill(${c.id})">
|
||
<div>
|
||
<span class="font-bold">${c.isp_target}</span>
|
||
<span class="text-gray-400 text-sm ml-2">${c.send_method}</span>
|
||
<span class="text-gray-500 text-xs ml-2">${c.domain_used || ''}</span>
|
||
</div>
|
||
<div class="text-right">
|
||
<span class="font-bold ${c.inbox_rate >= 80 ? 'text-green-400' : c.inbox_rate >= 50 ? 'text-yellow-400' : 'text-red-400'}">${Number(c.inbox_rate).toFixed(1)}%</span>
|
||
<span class="text-gray-500 text-xs ml-2">${c.total_sent} tests</span>
|
||
</div>
|
||
</div>`;
|
||
});
|
||
}
|
||
html += '</div></div>';
|
||
|
||
// Remove previous category detail if exists
|
||
const existing = document.getElementById('categoryDetail');
|
||
if (existing) existing.remove();
|
||
|
||
const container = document.createElement('div');
|
||
container.id = 'categoryDetail';
|
||
container.innerHTML = html;
|
||
document.querySelector('#configsModal .p-6').appendChild(container);
|
||
}
|
||
const q = document.getElementById('searchConfigs').value.toLowerCase();
|
||
document.querySelectorAll('.config-row').forEach(r => r.style.display = r.textContent.toLowerCase().includes(q) ? '' : 'none');
|
||
}
|
||
|
||
function showConfigCategory(cat) {
|
||
const configs = allConfigs;
|
||
let filtered = [];
|
||
let title = "";
|
||
if (cat === "winners") {
|
||
filtered = configs.filter(c => c.is_winner);
|
||
title = "🏆 Winners - Configs Gagnantes";
|
||
} else if (cat === "testing") {
|
||
filtered = configs.filter(c => !c.is_winner && c.total_sent >= 5);
|
||
title = "🔄 Testing - En cours de test";
|
||
} else {
|
||
filtered = configs.filter(c => c.total_sent < 5);
|
||
title = "🆕 New - Nouvelles configs";
|
||
}
|
||
let html = `<div class="mt-4 p-4 bg-gray-800 rounded-lg"><h4 class="font-bold mb-3">${title} (${filtered.length})</h4><div class="space-y-2 max-h-60 overflow-y-auto">`;
|
||
filtered.forEach(c => {
|
||
html += `<div class="flex justify-between items-center p-2 bg-gray-700 rounded cursor-pointer hover:bg-gray-600" onclick="closeModal('configsModal');showDeepDrill(${c.id})">`;
|
||
html += `<div><span class="font-bold">${c.isp_target}</span> <span class="text-gray-400 text-sm">${c.send_method}</span></div>`;
|
||
html += `<span class="font-bold ${c.inbox_rate >= 80 ? "text-green-400" : "text-yellow-400"}">${Number(c.inbox_rate).toFixed(1)}%</span></div>`;
|
||
});
|
||
html += "</div></div>";
|
||
document.querySelector("#configsModal .p-6:last-child").innerHTML += html;
|
||
}
|
||
|
||
document.querySelectorAll(".modal").forEach(m => m.addEventListener('click', e => { if (e.target === m) closeModal(m.id); }));
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|