add intelligence features; implement signals and predictions pages in admin panel
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
// intelligence → signals (trade signal cards)
|
||||
// depends on: app.js, intel-shared.js
|
||||
|
||||
async function loadSignals() {
|
||||
let data;
|
||||
try {
|
||||
data = await api("/admin/api/intelligence/signals");
|
||||
} catch (_) {
|
||||
data = [];
|
||||
}
|
||||
|
||||
const grid = document.getElementById("intel-signals-grid");
|
||||
const empty = document.getElementById("intel-signals-empty");
|
||||
|
||||
if (!data || data.length === 0) {
|
||||
grid.innerHTML = "";
|
||||
empty.style.display = "";
|
||||
return;
|
||||
}
|
||||
|
||||
empty.style.display = "none";
|
||||
|
||||
grid.innerHTML = data.map(s => {
|
||||
const firstSentence = (s.summary || "").split(/\.\s+/)[0].replace(/\.$/, "") + ".";
|
||||
const ts = s.generated_at ? s.generated_at.slice(0, 16).replace("T", " ") : "—";
|
||||
|
||||
let drivers = [];
|
||||
let risks = [];
|
||||
let predIds = [];
|
||||
try { drivers = JSON.parse(s.key_drivers || "[]"); } catch (_) {}
|
||||
try { risks = JSON.parse(s.risk_factors || "[]"); } catch (_) {}
|
||||
try { predIds = JSON.parse(s.supporting_prediction_ids || "[]"); } catch (_) {}
|
||||
|
||||
const driverItems = drivers.map(d => `<li>${escapeHtml(d)}</li>`).join("");
|
||||
const riskItems = risks.map(r => `<li>${escapeHtml(r)}</li>`).join("");
|
||||
|
||||
return `
|
||||
<div class="signal-card" id="signal-card-${s.company_id}">
|
||||
<div class="signal-card-glance" onclick="toggleSignalCard(${s.company_id})">
|
||||
<div class="signal-card-header">
|
||||
<div>
|
||||
<div class="signal-company">${escapeHtml(s.company_name)}</div>
|
||||
<div class="signal-ticker">${escapeHtml(s.ticker)}</div>
|
||||
</div>
|
||||
<span class="signal-badge ${s.signal}">${s.signal}</span>
|
||||
</div>
|
||||
<div class="signal-tags">
|
||||
<span class="signal-tag">conf: ${escapeHtml(s.confidence)}</span>
|
||||
<span class="signal-tag">risk: ${escapeHtml(s.risk_level)}</span>
|
||||
<span class="signal-tag">${escapeHtml(s.timeframe)}</span>
|
||||
</div>
|
||||
<div class="signal-summary">${escapeHtml(firstSentence)}</div>
|
||||
<div class="signal-ts">Generated ${ts}</div>
|
||||
</div>
|
||||
<div class="signal-card-detail">
|
||||
<div class="signal-detail-section">
|
||||
<div class="signal-detail-label">Summary</div>
|
||||
<p>${escapeHtml(s.summary || "—")}</p>
|
||||
</div>
|
||||
${driverItems ? `<div class="signal-detail-section"><div class="signal-detail-label">Key drivers</div><ul>${driverItems}</ul></div>` : ""}
|
||||
${riskItems ? `<div class="signal-detail-section"><div class="signal-detail-label">Risk factors</div><ul>${riskItems}</ul></div>` : ""}
|
||||
<div class="signal-detail-section">
|
||||
<div class="signal-detail-label">Data used</div>
|
||||
<p style="color:var(--muted-dark)">${predIds.length} predictions · window: 90 days</p>
|
||||
</div>
|
||||
<button class="signal-regen-btn" onclick="regenerateSignal(event, ${s.company_id})">Regenerate</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}).join("");
|
||||
}
|
||||
|
||||
|
||||
function toggleSignalCard(companyId) {
|
||||
const card = document.getElementById(`signal-card-${companyId}`);
|
||||
if (card) card.classList.toggle("expanded");
|
||||
}
|
||||
|
||||
|
||||
async function regenerateSignal(ev, companyId) {
|
||||
ev.stopPropagation();
|
||||
|
||||
try {
|
||||
await api(`/admin/api/intelligence/signals/${companyId}`, { method: "DELETE" });
|
||||
toast("Signal cleared — worker will regenerate on next cycle");
|
||||
const card = document.getElementById(`signal-card-${companyId}`);
|
||||
if (card) card.remove();
|
||||
|
||||
const grid = document.getElementById("intel-signals-grid");
|
||||
if (!grid.children.length) {
|
||||
document.getElementById("intel-signals-empty").style.display = "";
|
||||
}
|
||||
} catch (_) {
|
||||
toast("Failed to clear signal", true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
document.addEventListener("DOMContentLoaded", async () => {
|
||||
const ok = await loadIntelStatsRow();
|
||||
if (!ok) return;
|
||||
loadSignals();
|
||||
});
|
||||
Reference in New Issue
Block a user