`;
item.querySelector('.btn-edit').addEventListener('click', () => startEdit(t.id));
item.querySelector('.btn-delete').addEventListener('click', () => deleteTrader(t.id));
traderList.appendChild(item);
});
}
function renderCharts() {
const labels = traders.map(t => t.name);
const winRates = traders.map(t => t.winRate);
const riskScores = traders.map(t => t.riskScore);
const profitFactors = traders.map(t => t.profitFactor);
const followers = traders.map(t => t.followers);
const backgroundColors = ['#4F46E5', '#10B981', '#F59E0B', '#EF4444', '#6366F1', '#22C55E', '#FBBF24', '#F87171'];
// Win Rate Bar Chart
const winRateCtx = document.getElementById('winRateChart').getContext('2d');
if (winRateChart) winRateChart.destroy();
winRateChart = new Chart(winRateCtx, {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: 'Win Rate (%)',
data: winRates,
backgroundColor: backgroundColors.slice(0, traders.length),
borderColor: '#fff',
borderWidth: 1
}]
},
options: { scales: { y: { beginAtZero: true, max: 100 } }, plugins: { legend: { display: false } } }
});
// Risk vs Profit Bubble Chart
const riskProfitCtx = document.getElementById('riskProfitChart').getContext('2d');
const bubbleData = traders.map((t, i) => ({
x: t.riskScore,
y: t.profitFactor,
r: Math.max(5, Math.sqrt(t.followers) / 25), // Scale bubble size
label: t.name
}));
if (riskProfitChart) riskProfitChart.destroy();
riskProfitChart = new Chart(riskProfitCtx, {
type: 'bubble',
data: {
datasets: [{
label: 'Traders',
data: bubbleData,
backgroundColor: backgroundColors.map(c => c + 'B3').slice(0, traders.length) // Add alpha
}]
},
options: {
scales: {
x: { title: { display: true, text: 'Risk Score (1-10)' } },
y: { title: { display: true, text: 'Profit Factor' } }
},
plugins: { legend: { display: false } }
}
});
}
// --- FORM & DATA LOGIC ---
function handleFormSubmit(e) {
e.preventDefault();
const id = parseInt(traderIdInput.value);
const traderData = {
name: document.getElementById('traderName').value,
winRate: parseInt(document.getElementById('winRate').value),
followers: parseInt(document.getElementById('followers').value),
riskScore: parseInt(document.getElementById('riskScore').value),
profitFactor: parseFloat(document.getElementById('profitFactor').value),
};
if (id === -1) { // Add new
traderData.id = traders.length > 0 ? Math.max(...traders.map(t => t.id)) + 1 : 1;
traders.push(traderData);
} else { // Update existing
const index = traders.findIndex(t => t.id === id);
if (index !== -1) {
traders[index] = { ...traders[index], ...traderData };
}
}
renderAll();
resetForm();
}
function startEdit(id) {
const trader = traders.find(t => t.id === id);
if (!trader) return;
traderIdInput.value = trader.id;
document.getElementById('traderName').value = trader.name;
document.getElementById('winRate').value = trader.winRate;
document.getElementById('followers').value = trader.followers;
document.getElementById('riskScore').value = trader.riskScore;
document.getElementById('profitFactor').value = trader.profitFactor;
formTitle.textContent = 'Edit Trader';
submitBtn.textContent = 'Update Trader';
cancelBtn.classList.remove('hidden');
}
function cancelEdit() {
resetForm();
}
function resetForm() {
traderForm.reset();
traderIdInput.value = -1;
formTitle.textContent = 'Add New Trader';
submitBtn.textContent = 'Add Trader';
cancelBtn.classList.add('hidden');
}
function deleteTrader(id) {
traders = traders.filter(t => t.id !== id);
renderAll();
resetForm(); // In case we were editing the one we deleted
}
// --- UI FUNCTIONS ---
function switchTab(tabName) {
if (tabName === 'dashboard') {
dashboardContent.classList.remove('hidden');
configContent.classList.add('hidden');
tabBtnDashboard.classList.add('active');
tabBtnDashboard.classList.remove('inactive');
tabBtnConfig.classList.add('inactive');
tabBtnConfig.classList.remove('active');
} else {
dashboardContent.classList.add('hidden');
configContent.classList.remove('hidden');
tabBtnDashboard.classList.add('inactive');
tabBtnDashboard.classList.remove('active');
tabBtnConfig.classList.add('active');
tabBtnConfig.classList.remove('inactive');
}
}
// --- PDF GENERATION ---
function downloadPDF() {
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
const pageWidth = doc.internal.pageSize.getWidth();
doc.setFont("helvetica", "bold");
doc.setFontSize(20);
doc.setTextColor(17, 24, 39);
doc.text("Social Trading Network Analysis", pageWidth / 2, 20, { align: "center" });
doc.setFontSize(10);
doc.setFont("helvetica", "normal");
doc.setTextColor(107, 114, 128);
doc.text(`Report Generated: ${new Date().toLocaleDateString('en-US')}`, pageWidth / 2, 28, { align: "center" });
doc.autoTable({
startY: 40,
head: [['Trader Name', 'Win Rate (%)', 'Followers', 'Risk Score', 'Profit Factor']],
body: traders.map(t => [t.name, t.winRate, t.followers.toLocaleString('en-US'), t.riskScore, t.profitFactor.toFixed(2)]),
theme: 'grid',
headStyles: { fillColor: [31, 41, 55] },
styles: { cellPadding: 2.5, fontSize: 9 },
});
doc.save("Social_Trading_Analysis_Report.pdf");
}
// --- START THE APP ---
initialize();
});