Fix broken dashboard, history, and send screens

Co-authored-by: Yacineutt <Yacineutt@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-03-06 01:05:21 +00:00
parent 9808676512
commit a059673d21
12 changed files with 183 additions and 114 deletions

View File

@@ -47,9 +47,9 @@
<li><a href="/widgets/cloud_health.html" target="_blank"><span class="title">â˜<EFBFBD>ï¸<EFBFBD> Cloud Health</span></a></li>
<li><a href="/brain_editor.php" target="_blank"><span class="title">âœ<EFBFBD>ï¸<EFBFBD> Brain Config Editor</span></a></li>
<li><a href="/sidebar-admin.php" target="_blank"><span class="title">📋 Sidebar Admin</span></a></li>
<li><a href="/deliverads/dashboard.php" target="_blank"><span class="title">🚀 Deliverads Dashboard</span></a></li>
<li><a href="/deliverads/" target="_blank"><span class="title">🚀 Deliverads Dashboard</span></a></li>
<li><a href="/deliverads/brain-admin.php" target="_blank"><span class="title">🧠 Brain Admin</span></a></li>
<li><a href="/deliverads/seeds.php" target="_blank"><span class="title">🌱 Seeds Manager</span></a></li>
<li><a href="/deliverads/seeds-manager.php" target="_blank"><span class="title">🌱 Seeds Manager</span></a></li>
<li><a href="/deliverads/harvest.php" target="_blank"><span class="title">🌾 Harvest</span></a></li>
<li><a href="/deliverads/warmup.php" target="_blank"><span class="title">🔥 Warmup</span></a></li>
<li><a href="/deliverads/send-control.php" target="_blank"><span class="title">📤 Send Control</span></a></li>

View File

@@ -182,8 +182,8 @@ async function loadConfigs() {
// Extract unique ISPs
const isps = [...new Set(allConfigs.map(c => c.isp_target).filter(Boolean))];
const filterEl = document.getElementById('ispFilters');
filterEl.innerHTML = '<div class="fpill active" onclick="filterISP(\'all\')">ALL</div>' +
isps.map(i => '<div class="fpill" onclick="filterISP(\'' + esc(i) + '\')">' + esc(i) + '</div>').join('');
filterEl.innerHTML = '<div class="fpill active" data-isp="all" onclick="filterISP(\'all\')">ALL</div>' +
isps.map(i => '<div class="fpill" data-isp="' + esc(i) + '" onclick="filterISP(\'' + esc(i) + '\')">' + esc(i) + '</div>').join('');
document.getElementById('configsBadge').textContent = allConfigs.length + ' configs';
document.getElementById('winnersBadge').textContent = allConfigs.filter(c => c.status === 'winner').length + ' winners';
@@ -196,8 +196,9 @@ async function loadConfigs() {
function filterISP(isp) {
filterIsp = isp;
document.querySelectorAll('#ispFilters .fpill').forEach(p => p.classList.remove('active'));
event.target.classList.add('active');
document.querySelectorAll('#ispFilters .fpill').forEach(p => {
p.classList.toggle('active', p.dataset.isp === isp);
});
renderWinners();
}
@@ -243,7 +244,8 @@ function renderWinners() {
function selectConfig(idx, jsonStr) {
selectedConfig = JSON.parse(jsonStr);
document.querySelectorAll('.winner').forEach(w => w.classList.remove('selected'));
event.currentTarget.classList.add('selected');
const cards = document.querySelectorAll('.winner');
if (cards[idx]) cards[idx].classList.add('selected');
document.getElementById('btnSend').disabled = false;
if (selectedConfig.from_domain && !document.getElementById('fromEmail').value) {
@@ -309,7 +311,6 @@ function esc(s) { if (!s) return ''; const d = document.createElement('div'); d.
loadConfigs();
</script>
<script src="arsenal-common.js?v1770778169">
<script src="arsenal-common.js?v1770778169"></script>
</body>
</html>
</script>

View File

@@ -1,6 +1,75 @@
<?php
/** AUTO-GENERATED WRAPPER — HAMID History */
$GLOBALS["no_auth_check_wrapper"] = false; // Keep auth
if (isset($_GET['action']) || isset($_POST['action'])) {
header('Content-Type: application/json');
try {
$pdo = new PDO('pgsql:host=localhost;dbname=adx_system', 'admin', 'admin123');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->exec("CREATE TABLE IF NOT EXISTS hamid_conversations (
id SERIAL PRIMARY KEY,
session_id VARCHAR(100),
title VARCHAR(255),
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
)");
$pdo->exec("CREATE TABLE IF NOT EXISTS hamid_messages (
id SERIAL PRIMARY KEY,
conversation_id INT,
role VARCHAR(20),
content TEXT,
provider VARCHAR(50),
created_at TIMESTAMP DEFAULT NOW()
)");
$action = $_POST['action'] ?? $_GET['action'] ?? '';
switch ($action) {
case 'new_conversation':
$stmt = $pdo->prepare("INSERT INTO hamid_conversations (session_id, title) VALUES (?, ?) RETURNING id");
$stmt->execute([uniqid('conv_'), $_POST['title'] ?? 'Nouvelle conversation']);
echo json_encode(['success' => true, 'id' => $stmt->fetchColumn()]);
break;
case 'list_conversations':
$convs = $pdo->query("SELECT * FROM hamid_conversations ORDER BY updated_at DESC LIMIT 20")->fetchAll(PDO::FETCH_ASSOC);
echo json_encode(['conversations' => $convs]);
break;
case 'search':
$q = trim($_GET['q'] ?? $_POST['q'] ?? '');
$stmt = $pdo->prepare("SELECT * FROM hamid_messages WHERE content ILIKE ? ORDER BY created_at DESC LIMIT 20");
$stmt->execute(["%{$q}%"]);
echo json_encode(['results' => $stmt->fetchAll(PDO::FETCH_ASSOC)]);
break;
case 'status':
echo json_encode(['status' => 'online', 'service' => 'HAMID History', 'timestamp' => date('c')]);
break;
case 'stats':
echo json_encode([
'conversations' => (int) $pdo->query("SELECT COUNT(*) FROM hamid_conversations")->fetchColumn(),
'messages' => (int) $pdo->query("SELECT COUNT(*) FROM hamid_messages")->fetchColumn(),
'status' => 'online'
]);
break;
default:
echo json_encode(['actions' => ['new_conversation', 'list_conversations', 'search', 'stats', 'status']]);
break;
}
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
}
exit;
}
?>
<!DOCTYPE html>
<html data-theme="dark" lang="fr">
@@ -78,7 +147,7 @@ pre.response{background:#0d1117;border:1px solid #1e293b;border-radius:8px;paddi
<script>
const FILE = "hamid-history.php";
const ENDPOINT = "/hamid-history-raw.php";
const ENDPOINT = "/hamid-history.php";
let lastResponse = null;
async function testEndpoint(params="") {

View File

@@ -103,7 +103,7 @@ document.addEventListener('click', function(e) {
<div style="color:#4ade80;font-weight:bold;padding:5px 12px;">🎯 WEVAL SEND</div>
<a href="/deliverads/">📊 Dashboard</a>
<a href="/deliverads/campaign-builder.php">📧 Campaigns</a>
<a href="/deliverads/send-engine.php">🚀 Send Engine</a>
<a href="/deliverads/send-control.php">🚀 Send Engine</a>
<a href="/deliverads/unified-inbox.php">📬 Inbox</a>
<a href="/deliverads/config-generator.php">⚙️ Configs</a>
<a href="/deliverads/learning-engine.php">🧠 Learning</a>

View File

@@ -184,7 +184,7 @@ body{font-family:var(--font);background:#060a14;color:var(--text);min-height:100
<div class="card-title"><span class="icon">📧</span> Recipient</div>
<div class="form-group">
<label class="form-label">To Email</label>
<input type="email" class="form-input" id="toEmail" ="test@t-online.de" oninput="onEmailInput()">
<input type="email" class="form-input" id="toEmail" placeholder="test@t-online.de" oninput="onEmailInput()">
</div>
<div class="isp-zone" id="ispZone">
<div style="color:var(--dim);font-size:13px">Type an email to auto-detect ISP</div>
@@ -200,11 +200,11 @@ body{font-family:var(--font);background:#060a14;color:var(--text);min-height:100
<div class="card-title"><span class="icon">✉️</span> Message</div>
<div class="form-group">
<label class="form-label">From Email</label>
<input type="email" class="form-input" id="fromEmail" ="info@culturellemejean.charity">
<input type="email" class="form-input" id="fromEmail" placeholder="info@culturellemejean.charity">
</div>
<div class="form-group">
<label class="form-label">Subject</label>
<input type="text" class="form-input" id="subject" ="Your subject line">
<input type="text" class="form-input" id="subject" placeholder="Your subject line">
</div>
<div class="form-group">
<label class="form-label">Body (HTML)</label>
@@ -273,7 +273,7 @@ body{font-family:var(--font);background:#060a14;color:var(--text);min-height:100
</div>
<div class="form-group">
<label class="form-label">X-Mailer</label>
<input class="form-input" id="manXmailer" ="Microsoft Outlook 16.0">
<input class="form-input" id="manXmailer" placeholder="Microsoft Outlook 16.0">
</div>
<div class="form-group">
<label class="form-label">Priority</label>
@@ -569,7 +569,6 @@ function toast(msg, type) {
setTimeout(() => t.remove(), 3000);
}
</script>
<script src="arsenal-common.js?v1770778169">
<script src="arsenal-common.js?v1770778169"></script>
</body>
</html>
</script>

View File

@@ -168,8 +168,4 @@ setInterval(arsenalLoad, 30000);
async function detectISP(email){const r=await fetch('/deliverads/send-engine.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'detect_isp',email:email})});return r.json();}
async function sendEmail(payload){const r=await fetch('/deliverads/send-engine.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'send',...payload})});return r.json();}
</script><script src="arsenal-common.js?v1770778169"></script>
</script>
</body></html>

View File

@@ -72,6 +72,5 @@ try{const r=await fetch(API+'?action=system_health');const d=await r.json();if(d
const hist=d.data.executions||[];document.getElementById('tb').innerHTML=hist.slice(0,15).map(e=>`<tr><td>${e.id||'—'}</td><td>${e.workflow||'full_automation'}</td><td><span class="badge ${e.status==='completed'?'gn':e.status==='running'?'cy':e.status==='failed'?'rd':'or'}">${e.status||'—'}</span></td><td>${e.steps_done||0}/${e.steps_total||7}</td><td>${e.items_processed||0}</td><td>${e.duration||'—'}</td><td>${e.date||'—'}</td></tr>`).join('')}else{renderCanvas({})}}catch(e){renderCanvas({})}}
load();setInterval(load,15000);
</script>
<script src="arsenal-common.js?v1770778169">
<script src="arsenal-common.js?v1770778169"></script>
</body></html>
</script>

View File

@@ -392,7 +392,6 @@ function loadChannels(){fetch('/api/youtube-factory.php?action=list_channels').t
function fetchNewTrends(){loadTrends();var t=document.createElement('div');t.className='toast';t.textContent='Trends refreshed';document.body.appendChild(t);setTimeout(function(){t.remove()},3000)}
function runPipeline(){fetch('/api/youtube-factory.php?action=run',{method:'POST'}).then(r=>r.json()).then(d=>{alert('Pipeline: '+(d.status||'started'))}).catch(()=>alert('Pipeline queued'))}
</script>
<script src="arsenal-common.js?v1770778169">
<script src="arsenal-common.js?v1770778169"></script>
</body>
</html>
</script>

View File

@@ -479,84 +479,58 @@ function previewBody() {
}
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');
document.querySelectorAll('.config-row').forEach(row => {
row.style.display = row.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";
let title = '';
let accentClass = 'text-gray-400';
if (cat === 'winners') {
filtered = allConfigs.filter(c => c.is_winner);
title = '🏆 Winners - Configs gagnantes';
accentClass = 'text-green-400';
} else if (cat === 'testing') {
filtered = allConfigs.filter(c => !c.is_winner && c.total_sent >= 5);
title = '🔄 Testing - En cours de test';
accentClass = 'text-yellow-400';
} else {
filtered = configs.filter(c => c.total_sent < 5);
title = "🆕 New - Nouvelles configs";
filtered = allConfigs.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;
const wrapper = document.getElementById('configCategoryDetail');
const titleEl = document.getElementById('categoryTitle');
const listEl = document.getElementById('categoryList');
if (!wrapper || !titleEl || !listEl) return;
wrapper.classList.remove('hidden');
titleEl.className = `font-bold text-lg mb-4 ${accentClass}`;
titleEl.textContent = `${title} (${filtered.length})`;
if (!filtered.length) {
listEl.innerHTML = '<div class="bg-gray-800 rounded-lg p-4 text-gray-500">Aucune config dans cette catégorie.</div>';
return;
}
listEl.innerHTML = filtered.map(c => `
<div class="flex justify-between items-center p-3 bg-gray-800 rounded-lg cursor-pointer hover:bg-gray-700 transition-colors"
onclick="closeModal('configsModal');showDeepDrill(${c.id})">
<div>
<div class="font-bold">${c.isp_target || 'N/A'}</div>
<div class="text-sm text-gray-400">${c.send_method || 'N/A'}${c.domain_used ? ` · ${c.domain_used}` : ''}</div>
</div>
<div class="text-right">
<div class="font-bold ${c.inbox_rate >= 80 ? 'text-green-400' : c.inbox_rate >= 50 ? 'text-yellow-400' : 'text-red-400'}">${Number(c.inbox_rate || 0).toFixed(1)}%</div>
<div class="text-xs text-gray-500">${c.total_sent || 0} tests</div>
</div>
</div>
`).join('');
}
document.querySelectorAll(".modal").forEach(m => m.addEventListener('click', e => { if (e.target === m) closeModal(m.id); }));

View File

@@ -20,7 +20,7 @@ $s['health'] = round($s['o365'] / max(1, $s['o365'] + 404) * 100);
<?php
$m = [
['campaign-builder.php','paper-plane','green','Campaigns'],
['send-engine.php','rocket','blue','Send'],
['send-control.php','rocket','blue','Send'],
['spintax-engine.php','sync','purple','Spintax'],
['unified-inbox.php','inbox','cyan','Inbox'],
['email-verifier.php','check','emerald','Verify'],

View File

@@ -21,13 +21,26 @@ $stats = [
$sponsors = $db->query("SELECT * FROM admin.sponsors WHERE is_active = true ORDER BY name")->fetchAll(PDO::FETCH_ASSOC);
// Offers
$offers = [];
try {
$offers = $db->query("
SELECT
o.*,
s.id AS sponsor_id
FROM admin.offers o
LEFT JOIN admin.sponsors s
ON LOWER(TRIM(s.name)) = LOWER(TRIM(o.sponsor))
WHERE COALESCE(o.status, 'active') = 'active'
ORDER BY o.name
")->fetchAll(PDO::FETCH_ASSOC);
} catch (Exception $e) {
$offers = [];
}
// Listes disponibles (schemas ISP)
$lists = $dbClients->query("SELECT schemaname as schema, tablename as table_name FROM pg_tables WHERE schemaname IN ('gmail', 'hotmail', 'gmx', 'tonline', 'webde', 'videotron', 'yahoo', 'aol')")->fetchAll(PDO::FETCH_ASSOC);
// Serveurs disponibles
$servers = $db->query("SELECT id, name, main_ip, status, provider_name FROM admin.mta_servers WHERE status = 'active' ORDER BY name LIMIT 50")->fetchAll(PDO::FETCH_ASSOC);
$servers = $db->query("SELECT id, name, main_ip AS ip_address, status, provider_name FROM admin.mta_servers WHERE status = 'active' ORDER BY name LIMIT 50")->fetchAll(PDO::FETCH_ASSOC);
// Sends actifs
$activeSends = $db->query("SELECT c.*, s.name as sponsor_name, o.name as offer_name
@@ -202,7 +215,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
exit;
}
$servers = $db->query("SELECT id, ip_address FROM admin.mta_servers WHERE id IN ($serverIds)")->fetchAll(PDO::FETCH_ASSOC);
$servers = $db->query("SELECT id, COALESCE(ip_address, main_ip) AS ip_address FROM admin.mta_servers WHERE id IN ($serverIds)")->fetchAll(PDO::FETCH_ASSOC);
$results = [];
$listedCount = 0;
@@ -603,8 +616,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
const offerSelect = document.getElementById('offerSelect');
Array.from(offerSelect.options).forEach(opt => {
if (opt.value === '') return;
opt.style.display = opt.dataset.sponsor === sponsorId ? '' : 'none';
opt.style.display = !sponsorId || !opt.dataset.sponsor || opt.dataset.sponsor === sponsorId ? '' : 'none';
});
offerSelect.value = '';
});
// Get list count

View File

@@ -7,6 +7,18 @@
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
// Old dashboards still link directly to this API endpoint.
// Redirect browser navigations to the actual UI instead of showing raw JSON.
if (
($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'GET'
&& !isset($_GET['action'])
&& (strpos($_SERVER['HTTP_ACCEPT'] ?? '', 'text/html') !== false)
) {
header('Content-Type: text/html; charset=utf-8');
header('Location: /deliverads/send-control.php');
exit;
}
// DB Connection
try {
$pdo = new PDO('pgsql:host=localhost;dbname=adx_system', 'admin', 'admin123');
@@ -89,21 +101,27 @@ switch ($action) {
break;
case 'get_winners':
$isp = $input['isp'] ?? $_GET['isp'] ?? '';
if (empty($isp)) {
echo json_encode(['error' => 'ISP parameter required']);
exit;
$isp = trim($input['isp'] ?? $_GET['isp'] ?? 'all');
if ($isp === '' || strtolower($isp) === 'all') {
$stmt = $pdo->query("
SELECT * FROM brain_send_configs
WHERE is_winner = true
ORDER BY inbox_rate DESC
");
$configs = $stmt->fetchAll(PDO::FETCH_ASSOC);
$isp = 'all';
} else {
$stmt = $pdo->prepare("
SELECT * FROM brain_send_configs
WHERE isp_target ILIKE :isp
AND is_winner = true
ORDER BY inbox_rate DESC
");
$stmt->execute(['isp' => "%$isp%"]);
$configs = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
$stmt = $pdo->prepare("
SELECT * FROM brain_send_configs
WHERE isp_target ILIKE :isp
AND is_winner = true
ORDER BY inbox_rate DESC
");
$stmt->execute(['isp' => "%$isp%"]);
$configs = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode([
'isp' => $isp,
'configs' => $configs,