Amg7's picture
Add 2 files
987d0af verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Global Traders Database</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#1e40af',
secondary: '#1e3a8a',
accent: '#3b82f6',
dark: '#0f172a',
light: '#f8fafc'
}
}
}
}
</script>
<style>
.chart-container {
position: relative;
height: 300px;
width: 100%;
}
.sidebar {
transition: all 0.3s ease;
}
@media (max-width: 768px) {
.sidebar {
position: fixed;
left: -100%;
top: 0;
z-index: 50;
height: 100vh;
width: 80%;
}
.sidebar.active {
left: 0;
}
.overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0,0,0,0.5);
z-index: 40;
display: none;
}
.overlay.active {
display: block;
}
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
}
::-webkit-scrollbar-thumb {
background: #888;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #555;
}
</style>
</head>
<body class="bg-gray-50">
<div class="overlay"></div>
<!-- Mobile Header -->
<div class="md:hidden bg-primary text-white p-4 flex justify-between items-center">
<button id="menuToggle" class="text-white">
<i class="fas fa-bars text-xl"></i>
</button>
<h1 class="text-xl font-bold">Traders DB</h1>
<div class="w-8"></div> <!-- Spacer for balance -->
</div>
<!-- Sidebar -->
<div class="sidebar bg-white shadow-lg md:shadow-none md:w-64 fixed md:static h-screen overflow-y-auto">
<div class="p-4 border-b border-gray-200 hidden md:block">
<h1 class="text-xl font-bold text-primary">Global Traders Database</h1>
<p class="text-sm text-gray-500">Track profitable traders worldwide</p>
</div>
<div class="p-4">
<div class="mb-6">
<h2 class="text-lg font-semibold mb-2 text-gray-700">Navigation</h2>
<ul class="space-y-1">
<li>
<a href="#" class="flex items-center p-2 text-primary bg-blue-50 rounded-lg">
<i class="fas fa-chart-line mr-3"></i>
<span>Dashboard</span>
</a>
</li>
<li>
<a href="#" class="flex items-center p-2 text-gray-600 hover:bg-gray-100 rounded-lg">
<i class="fas fa-users mr-3"></i>
<span>Traders</span>
</a>
</li>
<li>
<a href="#" class="flex items-center p-2 text-gray-600 hover:bg-gray-100 rounded-lg">
<i class="fas fa-globe mr-3"></i>
<span>Markets</span>
</a>
</li>
<li>
<a href="#" class="flex items-center p-2 text-gray-600 hover:bg-gray-100 rounded-lg">
<i class="fas fa-bell mr-3"></i>
<span>Alerts</span>
</a>
</li>
<li>
<a href="#" class="flex items-center p-2 text-gray-600 hover:bg-gray-100 rounded-lg">
<i class="fas fa-cog mr-3"></i>
<span>Settings</span>
</a>
</li>
</ul>
</div>
<div class="mb-6">
<h2 class="text-lg font-semibold mb-2 text-gray-700">Filters</h2>
<div class="space-y-3">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Profit Range</label>
<select id="profitRangeFilter" class="w-full p-2 border border-gray-300 rounded-md text-sm">
<option value="all">All</option>
<option value="0-10">0 - $10k</option>
<option value="10-50">$10k - $50k</option>
<option value="50-100">$50k - $100k</option>
<option value="100+">$100k+</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Country</label>
<select id="countryFilter" class="w-full p-2 border border-gray-300 rounded-md text-sm">
<option value="all">All Countries</option>
<option value="United States">United States</option>
<option value="United Kingdom">United Kingdom</option>
<option value="Japan">Japan</option>
<option value="Germany">Germany</option>
<option value="Singapore">Singapore</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Asset Class</label>
<select id="marketFilter" class="w-full p-2 border border-gray-300 rounded-md text-sm">
<option value="all">All</option>
<option value="Stocks">Stocks</option>
<option value="Forex">Forex</option>
<option value="Crypto">Crypto</option>
<option value="Commodities">Commodities</option>
<option value="Options">Options</option>
</select>
</div>
<button id="applyFiltersBtn" class="w-full bg-primary text-white py-2 px-4 rounded-md text-sm hover:bg-secondary transition">
Apply Filters
</button>
<button id="resetFiltersBtn" class="w-full bg-gray-200 text-gray-800 py-2 px-4 rounded-md text-sm hover:bg-gray-300 transition">
Reset Filters
</button>
</div>
</div>
<div class="p-4 bg-blue-50 rounded-lg">
<h3 class="text-sm font-semibold text-primary mb-2">Quick Stats</h3>
<div class="space-y-2">
<div class="flex justify-between text-sm">
<span class="text-gray-600">Total Traders:</span>
<span id="totalTradersStat" class="font-medium">1,248</span>
</div>
<div class="flex justify-between text-sm">
<span class="text-gray-600">Avg. Profit:</span>
<span id="avgProfitStat" class="font-medium text-green-600">$42,780</span>
</div>
<div class="flex justify-between text-sm">
<span class="text-gray-600">Top Country:</span>
<span id="topCountryStat" class="font-medium">USA (32%)</span>
</div>
</div>
</div>
</div>
</div>
<!-- Main Content -->
<div class="md:ml-64 min-h-screen">
<!-- Header -->
<header class="bg-white shadow-sm p-4 hidden md:block">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-bold text-gray-800">Traders Dashboard</h1>
<div class="flex items-center space-x-4">
<div class="relative">
<input type="text" id="searchInput" placeholder="Search traders..." class="pl-10 pr-4 py-2 border border-gray-300 rounded-md text-sm w-64 focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
<i class="fas fa-search absolute left-3 top-3 text-gray-400"></i>
</div>
<button id="openAddTraderModal" class="bg-primary text-white py-2 px-4 rounded-md text-sm hover:bg-secondary transition flex items-center">
<i class="fas fa-plus mr-2"></i>
Add Trader
</button>
<div class="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center">
<i class="fas fa-user text-gray-600"></i>
</div>
</div>
</div>
</header>
<!-- Dashboard Content -->
<main class="p-4 md:p-6">
<!-- Stats Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
<div class="bg-white p-4 rounded-lg shadow-sm border border-gray-100">
<div class="flex justify-between items-start">
<div>
<p class="text-sm text-gray-500">Total Traders</p>
<h3 id="totalTradersCard" class="text-2xl font-bold mt-1">1,248</h3>
</div>
<div class="bg-blue-100 p-2 rounded-full">
<i class="fas fa-users text-blue-600"></i>
</div>
</div>
<p id="totalTradersChange" class="text-xs text-green-600 mt-2"><i class="fas fa-arrow-up mr-1"></i> 12.5% from last month</p>
</div>
<div class="bg-white p-4 rounded-lg shadow-sm border border-gray-100">
<div class="flex justify-between items-start">
<div>
<p class="text-sm text-gray-500">Avg. Profit</p>
<h3 id="avgProfitCard" class="text-2xl font-bold mt-1">$42.7k</h3>
</div>
<div class="bg-green-100 p-2 rounded-full">
<i class="fas fa-chart-line text-green-600"></i>
</div>
</div>
<p id="avgProfitChange" class="text-xs text-green-600 mt-2"><i class="fas fa-arrow-up mr-1"></i> 8.3% from last month</p>
</div>
<div class="bg-white p-4 rounded-lg shadow-sm border border-gray-100">
<div class="flex justify-between items-start">
<div>
<p class="text-sm text-gray-500">Top Market</p>
<h3 id="topMarketCard" class="text-2xl font-bold mt-1">Crypto</h3>
</div>
<div class="bg-purple-100 p-2 rounded-full">
<i class="fas fa-coins text-purple-600"></i>
</div>
</div>
<p id="topMarketPercent" class="text-xs text-gray-500 mt-2">38% of total trades</p>
</div>
<div class="bg-white p-4 rounded-lg shadow-sm border border-gray-100">
<div class="flex justify-between items-start">
<div>
<p class="text-sm text-gray-500">Risk Score</p>
<h3 id="riskScoreCard" class="text-2xl font-bold mt-1">4.2/10</h3>
</div>
<div class="bg-yellow-100 p-2 rounded-full">
<i class="fas fa-shield-alt text-yellow-600"></i>
</div>
</div>
<p id="riskScoreChange" class="text-xs text-green-600 mt-2"><i class="fas fa-arrow-down mr-1"></i> 1.2 from last quarter</p>
</div>
</div>
<!-- Charts Section -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
<div class="bg-white p-4 rounded-lg shadow-sm border border-gray-100">
<div class="flex justify-between items-center mb-4">
<h3 class="font-semibold text-gray-800">Profit Distribution</h3>
<select id="profitTimeRange" class="text-sm border border-gray-300 rounded px-2 py-1">
<option value="30">Last 30 Days</option>
<option value="90">Last 90 Days</option>
<option value="365">This Year</option>
</select>
</div>
<div class="chart-container">
<canvas id="profitChart"></canvas>
</div>
</div>
<div class="bg-white p-4 rounded-lg shadow-sm border border-gray-100">
<div class="flex justify-between items-center mb-4">
<h3 class="font-semibold text-gray-800">Trader Locations</h3>
<select id="locationViewType" class="text-sm border border-gray-300 rounded px-2 py-1">
<option value="country">By Country</option>
<option value="region">By Region</option>
</select>
</div>
<div class="chart-container">
<canvas id="locationChart"></canvas>
</div>
</div>
</div>
<!-- Traders Table -->
<div class="bg-white rounded-lg shadow-sm border border-gray-100 overflow-hidden">
<div class="p-4 border-b border-gray-200 flex justify-between items-center">
<h3 class="font-semibold text-gray-800">Top Performing Traders</h3>
<div class="flex space-x-2">
<button id="exportDataBtn" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-download"></i>
</button>
<button id="showFiltersBtn" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-filter"></i>
</button>
</div>
</div>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Trader</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Country</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Market</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Profit</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Win Rate</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Risk Score</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200" id="tradersTableBody">
<!-- Data will be inserted here by JavaScript -->
</tbody>
</table>
</div>
<div class="px-6 py-4 border-t border-gray-200 flex items-center justify-between">
<div class="text-sm text-gray-500">
Showing <span id="showingFrom" class="font-medium">1</span> to <span id="showingTo" class="font-medium">10</span> of <span id="totalTradersCount" class="font-medium">1248</span> traders
</div>
<div class="flex space-x-2">
<button id="prevPageBtn" class="px-3 py-1 border border-gray-300 rounded-md text-sm bg-white text-gray-700 hover:bg-gray-50">
Previous
</button>
<div id="pageNumbers" class="flex space-x-2">
<!-- Page numbers will be inserted here -->
</div>
<button id="nextPageBtn" class="px-3 py-1 border border-gray-300 rounded-md text-sm bg-white text-gray-700 hover:bg-gray-50">
Next
</button>
</div>
</div>
</div>
</main>
</div>
<!-- Add Trader Modal -->
<div id="addTraderModal" class="fixed inset-0 z-50 hidden overflow-y-auto">
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10">
<i class="fas fa-user-plus text-blue-600"></i>
</div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 class="text-lg leading-6 font-medium text-gray-900">Add New Trader</h3>
<div class="mt-2">
<form id="addTraderForm" class="space-y-4">
<div>
<label for="name" class="block text-sm font-medium text-gray-700">Full Name</label>
<input type="text" id="name" name="name" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm" required>
</div>
<div>
<label for="email" class="block text-sm font-medium text-gray-700">Email</label>
<input type="email" id="email" name="email" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm" required>
</div>
<div>
<label for="country" class="block text-sm font-medium text-gray-700">Country</label>
<select id="country" name="country" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm" required>
<option value="">Select Country</option>
<option value="United States">United States</option>
<option value="United Kingdom">United Kingdom</option>
<option value="Japan">Japan</option>
<option value="Germany">Germany</option>
<option value="Singapore">Singapore</option>
</select>
</div>
<div>
<label for="market" class="block text-sm font-medium text-gray-700">Primary Market</label>
<select id="market" name="market" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm" required>
<option value="">Select Market</option>
<option value="Stocks">Stocks</option>
<option value="Forex">Forex</option>
<option value="Crypto">Cryptocurrency</option>
<option value="Commodities">Commodities</option>
<option value="Options">Options</option>
</select>
</div>
<div>
<label for="profit" class="block text-sm font-medium text-gray-700">Annual Profit ($)</label>
<input type="number" id="profit" name="profit" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm" required>
</div>
<div>
<label for="winRate" class="block text-sm font-medium text-gray-700">Win Rate (%)</label>
<input type="number" id="winRate" name="winRate" min="0" max="100" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm" required>
</div>
<div>
<label for="riskScore" class="block text-sm font-medium text-gray-700">Risk Score (1-10)</label>
<input type="number" id="riskScore" name="riskScore" min="1" max="10" step="0.1" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm" required>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button type="button" id="submitAddTrader" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-primary text-base font-medium text-white hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary sm:ml-3 sm:w-auto sm:text-sm">
Add Trader
</button>
<button type="button" id="cancelAddTrader" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
Cancel
</button>
</div>
</div>
</div>
</div>
<!-- View Trader Modal -->
<div id="viewTraderModal" class="fixed inset-0 z-50 hidden overflow-y-auto">
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10">
<i class="fas fa-user text-blue-600"></i>
</div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 class="text-lg leading-6 font-medium text-gray-900" id="viewTraderName">Trader Details</h3>
<div class="mt-2">
<div id="traderDetails" class="space-y-2">
<!-- Trader details will be inserted here -->
</div>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button type="button" id="closeViewTraderModal" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-primary text-base font-medium text-white hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary sm:ml-3 sm:w-auto sm:text-sm">
Close
</button>
</div>
</div>
</div>
</div>
<!-- Edit Trader Modal -->
<div id="editTraderModal" class="fixed inset-0 z-50 hidden overflow-y-auto">
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10">
<i class="fas fa-edit text-blue-600"></i>
</div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 class="text-lg leading-6 font-medium text-gray-900">Edit Trader</h3>
<div class="mt-2">
<form id="editTraderForm" class="space-y-4">
<input type="hidden" id="editTraderId">
<div>
<label for="editName" class="block text-sm font-medium text-gray-700">Full Name</label>
<input type="text" id="editName" name="editName" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm" required>
</div>
<div>
<label for="editEmail" class="block text-sm font-medium text-gray-700">Email</label>
<input type="email" id="editEmail" name="editEmail" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm" required>
</div>
<div>
<label for="editCountry" class="block text-sm font-medium text-gray-700">Country</label>
<select id="editCountry" name="editCountry" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm" required>
<option value="">Select Country</option>
<option value="United States">United States</option>
<option value="United Kingdom">United Kingdom</option>
<option value="Japan">Japan</option>
<option value="Germany">Germany</option>
<option value="Singapore">Singapore</option>
</select>
</div>
<div>
<label for="editMarket" class="block text-sm font-medium text-gray-700">Primary Market</label>
<select id="editMarket" name="editMarket" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm" required>
<option value="">Select Market</option>
<option value="Stocks">Stocks</option>
<option value="Forex">Forex</option>
<option value="Crypto">Cryptocurrency</option>
<option value="Commodities">Commodities</option>
<option value="Options">Options</option>
</select>
</div>
<div>
<label for="editProfit" class="block text-sm font-medium text-gray-700">Annual Profit ($)</label>
<input type="number" id="editProfit" name="editProfit" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm" required>
</div>
<div>
<label for="editWinRate" class="block text-sm font-medium text-gray-700">Win Rate (%)</label>
<input type="number" id="editWinRate" name="editWinRate" min="0" max="100" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm" required>
</div>
<div>
<label for="editRiskScore" class="block text-sm font-medium text-gray-700">Risk Score (1-10)</label>
<input type="number" id="editRiskScore" name="editRiskScore" min="1" max="10" step="0.1" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm" required>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button type="button" id="submitEditTrader" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-primary text-base font-medium text-white hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary sm:ml-3 sm:w-auto sm:text-sm">
Save Changes
</button>
<button type="button" id="cancelEditTrader" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
Cancel
</button>
</div>
</div>
</div>
</div>
<!-- Delete Confirmation Modal -->
<div id="deleteConfirmationModal" class="fixed inset-0 z-50 hidden overflow-y-auto">
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
<i class="fas fa-exclamation-triangle text-red-600"></i>
</div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 class="text-lg leading-6 font-medium text-gray-900">Delete Trader</h3>
<div class="mt-2">
<p class="text-sm text-gray-500">Are you sure you want to delete this trader? This action cannot be undone.</p>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button type="button" id="confirmDeleteBtn" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">
Delete
</button>
<button type="button" id="cancelDeleteBtn" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
Cancel
</button>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
// Global variables
let traders = [];
let filteredTraders = [];
let currentPage = 1;
const tradersPerPage = 10;
let profitChart, locationChart;
let traderToDelete = null;
// Sample trader data
function initializeTraders() {
traders = [
{
id: 1,
name: "John Smith",
email: "[email protected]",
country: "United States",
market: "Stocks",
profit: 125000,
winRate: 78,
riskScore: 3.2,
avatar: "JS"
},
{
id: 2,
name: "Emma Johnson",
email: "[email protected]",
country: "United Kingdom",
market: "Forex",
profit: 89000,
winRate: 82,
riskScore: 2.8,
avatar: "EJ"
},
{
id: 3,
name: "Hiroshi Tanaka",
email: "[email protected]",
country: "Japan",
market: "Crypto",
profit: 215000,
winRate: 65,
riskScore: 5.1,
avatar: "HT"
},
{
id: 4,
name: "Sophie Müller",
email: "[email protected]",
country: "Germany",
market: "Commodities",
profit: 76000,
winRate: 71,
riskScore: 4.3,
avatar: "SM"
},
{
id: 5,
name: "Wei Chen",
email: "[email protected]",
country: "Singapore",
market: "Options",
profit: 182000,
winRate: 75,
riskScore: 3.9,
avatar: "WC"
},
{
id: 6,
name: "Raj Patel",
email: "[email protected]",
country: "India",
market: "Crypto",
profit: 143000,
winRate: 68,
riskScore: 4.7,
avatar: "RP"
},
{
id: 7,
name: "Maria Garcia",
email: "[email protected]",
country: "Spain",
market: "Forex",
profit: 92000,
winRate: 79,
riskScore: 3.5,
avatar: "MG"
},
{
id: 8,
name: "David Kim",
email: "[email protected]",
country: "South Korea",
market: "Stocks",
profit: 112000,
winRate: 74,
riskScore: 3.8,
avatar: "DK"
},
{
id: 9,
name: "Olivia Brown",
email: "[email protected]",
country: "Canada",
market: "Options",
profit: 155000,
winRate: 81,
riskScore: 3.1,
avatar: "OB"
},
{
id: 10,
name: "Luca Rossi",
email: "[email protected]",
country: "Italy",
market: "Commodities",
profit: 68000,
winRate: 69,
riskScore: 4.5,
avatar: "LR"
},
{
id: 11,
name: "Michael Wilson",
email: "[email protected]",
country: "United States",
market: "Stocks",
profit: 198000,
winRate: 77,
riskScore: 3.7,
avatar: "MW"
},
{
id: 12,
name: "Sarah Lee",
email: "[email protected]",
country: "Australia",
market: "Forex",
profit: 107000,
winRate: 83,
riskScore: 2.9,
avatar: "SL"
},
{
id: 13,
name: "Carlos Hernandez",
email: "[email protected]",
country: "Mexico",
market: "Crypto",
profit: 231000,
winRate: 67,
riskScore: 5.3,
avatar: "CH"
},
{
id: 14,
name: "Anna Kowalski",
email: "[email protected]",
country: "Poland",
market: "Commodities",
profit: 89000,
winRate: 72,
riskScore: 4.1,
avatar: "AK"
},
{
id: 15,
name: "Thomas Müller",
email: "[email protected]",
country: "Germany",
market: "Options",
profit: 175000,
winRate: 76,
riskScore: 3.6,
avatar: "TM"
}
];
// Generate more traders to reach 1248
const countries = ["United States", "United Kingdom", "Japan", "Germany", "Singapore", "India", "Canada", "Australia", "France", "Brazil"];
const markets = ["Stocks", "Forex", "Crypto", "Commodities", "Options"];
for (let i = 16; i <= 1248; i++) {
const country = countries[Math.floor(Math.random() * countries.length)];
const market = markets[Math.floor(Math.random() * markets.length)];
const firstName = ["James", "Robert", "John", "Michael", "David", "William", "Richard", "Joseph", "Thomas", "Charles"][Math.floor(Math.random() * 10)];
const lastName = ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez"][Math.floor(Math.random() * 10)];
traders.push({
id: i,
name: `${firstName} ${lastName}`,
email: `${firstName.toLowerCase()}.${lastName.toLowerCase()}@example.com`,
country: country,
market: market,
profit: Math.floor(Math.random() * 300000) + 10000,
winRate: Math.floor(Math.random() * 30) + 60,
riskScore: (Math.random() * 7 + 2).toFixed(1),
avatar: firstName.charAt(0) + lastName.charAt(0)
});
}
filteredTraders = [...traders];
}
// Format currency
function formatCurrency(amount) {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 0,
maximumFractionDigits: 0
}).format(amount);
}
// Calculate statistics
function calculateStatistics() {
const totalTraders = filteredTraders.length;
const avgProfit = filteredTraders.reduce((sum, trader) => sum + trader.profit, 0) / totalTraders;
// Count traders by country
const countryCounts = {};
filteredTraders.forEach(trader => {
countryCounts[trader.country] = (countryCounts[trader.country] || 0) + 1;
});
// Find top country
let topCountry = "";
let topCount = 0;
for (const country in countryCounts) {
if (countryCounts[country] > topCount) {
topCountry = country;
topCount = countryCounts[country];
}
}
// Count traders by market
const marketCounts = {};
filteredTraders.forEach(trader => {
marketCounts[trader.market] = (marketCounts[trader.market] || 0) + 1;
});
// Find top market
let topMarket = "";
let topMarketCount = 0;
for (const market in marketCounts) {
if (marketCounts[market] > topMarketCount) {
topMarket = market;
topMarketCount = marketCounts[market];
}
}
// Calculate average risk score
const avgRiskScore = filteredTraders.reduce((sum, trader) => sum + parseFloat(trader.riskScore), 0) / totalTraders;
// Update stats
document.getElementById('totalTradersStat').textContent = totalTraders.toLocaleString();
document.getElementById('avgProfitStat').textContent = formatCurrency(avgProfit);
document.getElementById('topCountryStat').textContent = `${topCountry} (${Math.round((topCount / totalTraders) * 100)}%)`;
document.getElementById('totalTradersCard').textContent = totalTraders.toLocaleString();
document.getElementById('avgProfitCard').textContent = formatCurrency(avgProfit);
document.getElementById('topMarketCard').textContent = topMarket;
document.getElementById('topMarketPercent').textContent = `${Math.round((topMarketCount / totalTraders) * 100)}% of total trades`;
document.getElementById('riskScoreCard').textContent = `${avgRiskScore.toFixed(1)}/10`;
// Update charts
updateCharts();
}
// Update charts
function updateCharts() {
// Profit distribution
const profitRanges = [
{ min: 0, max: 10000, label: '0 - $10k', count: 0 },
{ min: 10000, max: 50000, label: '$10k - $50k', count: 0 },
{ min: 50000, max: 100000, label: '$50k - $100k', count: 0 },
{ min: 100000, max: 250000, label: '$100k - $250k', count: 0 },
{ min: 250000, max: Infinity, label: '$250k+', count: 0 }
];
filteredTraders.forEach(trader => {
for (const range of profitRanges) {
if (trader.profit >= range.min && trader.profit < range.max) {
range.count++;
break;
}
}
});
profitChart.data.datasets[0].data = profitRanges.map(range => range.count);
profitChart.update();
// Location distribution
const countryCounts = {};
filteredTraders.forEach(trader => {
countryCounts[trader.country] = (countryCounts[trader.country] || 0) + 1;
});
const countries = Object.keys(countryCounts);
const counts = Object.values(countryCounts);
// Sort by count and take top 5
const sortedIndices = [...Array(countries.length).keys()]
.sort((a, b) => counts[b] - counts[a])
.slice(0, 5);
const topCountries = sortedIndices.map(i => countries[i]);
const topCounts = sortedIndices.map(i => counts[i]);
const otherCount = filteredTraders.length - topCounts.reduce((a, b) => a + b, 0);
locationChart.data.labels = [...topCountries, 'Other'];
locationChart.data.datasets[0].data = [...topCounts, otherCount];
locationChart.update();
}
// Populate traders table
function populateTradersTable() {
const tableBody = document.getElementById('tradersTableBody');
tableBody.innerHTML = '';
const startIndex = (currentPage - 1) * tradersPerPage;
const endIndex = Math.min(startIndex + tradersPerPage, filteredTraders.length);
const paginatedTraders = filteredTraders.slice(startIndex, endIndex);
paginatedTraders.forEach(trader => {
const row = document.createElement('tr');
// Format profit with commas
const formattedProfit = formatCurrency(trader.profit);
row.innerHTML = `
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="flex-shrink-0 h-10 w-10 rounded-full bg-blue-100 flex items-center justify-center">
<span class="text-blue-600 font-medium">${trader.avatar}</span>
</div>
<div class="ml-4">
<div class="text-sm font-medium text-gray-900">${trader.name}</div>
<div class="text-sm text-gray-500">ID: ${trader.id}</div>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-900">${trader.country}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full
${trader.market === 'Stocks' ? 'bg-green-100 text-green-800' :
trader.market === 'Forex' ? 'bg-blue-100 text-blue-800' :
trader.market === 'Crypto' ? 'bg-purple-100 text-purple-800' :
trader.market === 'Commodities' ? 'bg-yellow-100 text-yellow-800' :
'bg-indigo-100 text-indigo-800'}">
${trader.market}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 font-medium">
${formattedProfit}
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="w-16 bg-gray-200 rounded-full h-2">
<div class="bg-green-600 h-2 rounded-full" style="width: ${trader.winRate}%"></div>
</div>
<span class="ml-2 text-sm text-gray-600">${trader.winRate}%</span>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="w-16 bg-gray-200 rounded-full h-2">
<div class="bg-${trader.riskScore < 4 ? 'green' : trader.riskScore < 7 ? 'yellow' : 'red'}-600 h-2 rounded-full" style="width: ${trader.riskScore * 10}%"></div>
</div>
<span class="ml-2 text-sm text-gray-600">${trader.riskScore}</span>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button class="view-trader-btn text-primary hover:text-secondary mr-3" data-id="${trader.id}">
<i class="fas fa-eye"></i>
</button>
<button class="edit-trader-btn text-blue-500 hover:text-blue-700 mr-3" data-id="${trader.id}">
<i class="fas fa-edit"></i>
</button>
<button class="delete-trader-btn text-red-500 hover:text-red-700" data-id="${trader.id}">
<i class="fas fa-trash"></i>
</button>
</td>
`;
tableBody.appendChild(row);
});
// Update pagination info
document.getElementById('showingFrom').textContent = startIndex + 1;
document.getElementById('showingTo').textContent = endIndex;
document.getElementById('totalTradersCount').textContent = filteredTraders.length;
// Update pagination buttons
updatePaginationButtons();
}
// Update pagination buttons
function updatePaginationButtons() {
const totalPages = Math.ceil(filteredTraders.length / tradersPerPage);
const pageNumbersContainer = document.getElementById('pageNumbers');
pageNumbersContainer.innerHTML = '';
// Always show first page
if (currentPage > 3) {
const firstPageBtn = document.createElement('button');
firstPageBtn.className = 'px-3 py-1 border border-gray-300 rounded-md text-sm bg-white text-gray-700 hover:bg-gray-50';
firstPageBtn.textContent = '1';
firstPageBtn.addEventListener('click', () => {
currentPage = 1;
populateTradersTable();
});
pageNumbersContainer.appendChild(firstPageBtn);
if (currentPage > 4) {
const ellipsis = document.createElement('span');
ellipsis.className = 'px-3 py-1';
ellipsis.textContent = '...';
pageNumbersContainer.appendChild(ellipsis);
}
}
// Show pages around current page
const startPage = Math.max(1, currentPage - 2);
const endPage = Math.min(totalPages, currentPage + 2);
for (let i = startPage; i <= endPage; i++) {
const pageBtn = document.createElement('button');
pageBtn.className = `px-3 py-1 border rounded-md text-sm ${i === currentPage ? 'bg-primary text-white border-primary' : 'bg-white text-gray-700 border-gray-300 hover:bg-gray-50'}`;
pageBtn.textContent = i;
pageBtn.addEventListener('click', () => {
currentPage = i;
populateTradersTable();
});
pageNumbersContainer.appendChild(pageBtn);
}
// Always show last page
if (currentPage < totalPages - 2) {
if (currentPage < totalPages - 3) {
const ellipsis = document.createElement('span');
ellipsis.className = 'px-3 py-1';
ellipsis.textContent = '...';
pageNumbersContainer.appendChild(ellipsis);
}
const lastPageBtn = document.createElement('button');
lastPageBtn.className = 'px-3 py-1 border border-gray-300 rounded-md text-sm bg-white text-gray-700 hover:bg-gray-50';
lastPageBtn.textContent = totalPages;
lastPageBtn.addEventListener('click', () => {
currentPage = totalPages;
populateTradersTable();
});
pageNumbersContainer.appendChild(lastPageBtn);
}
// Enable/disable previous and next buttons
document.getElementById('prevPageBtn').disabled = currentPage === 1;
document.getElementById('nextPageBtn').disabled = currentPage === totalPages;
}
// Filter traders based on filters
function filterTraders() {
const profitRange = document.getElementById('profitRangeFilter').value;
const country = document.getElementById('countryFilter').value;
const market = document.getElementById('marketFilter').value;
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
filteredTraders = traders.filter(trader => {
// Filter by profit range
if (profitRange !== 'all') {
if (profitRange === '0-10' && trader.profit >= 10000) return false;
if (profitRange === '10-50' && (trader.profit < 10000 || trader.profit >= 50000)) return false;
if (profitRange === '50-100' && (trader.profit < 50000 || trader.profit >= 100000)) return false;
if (profitRange === '100+' && trader.profit < 100000) return false;
}
// Filter by country
if (country !== 'all' && trader.country !== country) return false;
// Filter by market
if (market !== 'all' && trader.market !== market) return false;
// Filter by search term
if (searchTerm && !(
trader.name.toLowerCase().includes(searchTerm) ||
trader.email.toLowerCase().includes(searchTerm) ||
trader.country.toLowerCase().includes(searchTerm) ||
trader.market.toLowerCase().includes(searchTerm) ||
trader.id.toString().includes(searchTerm)
)) return false;
return true;
});
currentPage = 1;
populateTradersTable();
calculateStatistics();
}
// Initialize charts
function initCharts() {
// Profit Distribution Chart
const profitCtx = document.getElementById('profitChart').getContext('2d');
profitChart = new Chart(profitCtx, {
type: 'bar',
data: {
labels: ['0 - $10k', '$10k - $50k', '$50k - $100k', '$100k - $250k', '$250k+'],
datasets: [{
label: 'Number of Traders',
data: [320, 450, 280, 150, 48],
backgroundColor: [
'rgba(59, 130, 246, 0.6)',
'rgba(59, 130, 246, 0.6)',
'rgba(59, 130, 246, 0.6)',
'rgba(59, 130, 246, 0.6)',
'rgba(59, 130, 246, 0.6)'
],
borderColor: [
'rgba(59, 130, 246, 1)',
'rgba(59, 130, 246, 1)',
'rgba(59, 130, 246, 1)',
'rgba(59, 130, 246, 1)',
'rgba(59, 130, 246, 1)'
],
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true
}
},
plugins: {
legend: {
display: false
}
}
}
});
// Location Distribution Chart
const locationCtx = document.getElementById('locationChart').getContext('2d');
locationChart = new Chart(locationCtx, {
type: 'doughnut',
data: {
labels: ['United States', 'United Kingdom', 'Japan', 'Germany', 'Singapore', 'Other'],
datasets: [{
data: [32, 15, 12, 9, 8, 24],
backgroundColor: [
'rgba(30, 64, 175, 0.7)',
'rgba(59, 130, 246, 0.7)',
'rgba(107, 114, 128, 0.7)',
'rgba(156, 163, 175, 0.7)',
'rgba(209, 213, 219, 0.7)',
'rgba(229, 231, 235, 0.7)'
],
borderColor: [
'rgba(30, 64, 175, 1)',
'rgba(59, 130, 246, 1)',
'rgba(107, 114, 128, 1)',
'rgba(156, 163, 175, 1)',
'rgba(209, 213, 219, 1)',
'rgba(229, 231, 235, 1)'
],
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right',
}
}
}
});
}
// View trader details
function viewTraderDetails(traderId) {
const trader = traders.find(t => t.id === traderId);
if (!trader) return;
document.getElementById('viewTraderName').textContent = trader.name;
const detailsContainer = document.getElementById('traderDetails');
detailsContainer.innerHTML = `
<div class="flex justify-between">
<span class="text-gray-600">ID:</span>
<span class="font-medium">${trader.id}</span>
</div>
<div class="flex justify-between">
<span class="text-gray-600">Email:</span>
<span class="font-medium">${trader.email}</span>
</div>
<div class="flex justify-between">
<span class="text-gray-600">Country:</span>
<span class="font-medium">${trader.country}</span>
</div>
<div class="flex justify-between">
<span class="text-gray-600">Market:</span>
<span class="font-medium">${trader.market}</span>
</div>
<div class="flex justify-between">
<span class="text-gray-600">Annual Profit:</span>
<span class="font-medium">${formatCurrency(trader.profit)}</span>
</div>
<div class="flex justify-between">
<span class="text-gray-600">Win Rate:</span>
<span class="font-medium">${trader.winRate}%</span>
</div>
<div class="flex justify-between">
<span class="text-gray-600">Risk Score:</span>
<span class="font-medium">${trader.riskScore}/10</span>
</div>
<div class="pt-4">
<h4 class="text-sm font-medium text-gray-700 mb-2">Performance Summary</h4>
<p class="text-sm text-gray-600">${trader.name} has been trading ${trader.market} from ${trader.country} with a ${trader.winRate}% win rate and ${formatCurrency(trader.profit)} in annual profits. Their risk score of ${trader.riskScore}/10 indicates ${trader.riskScore < 4 ? 'a conservative' : trader.riskScore < 7 ? 'a moderate' : 'an aggressive'} trading style.</p>
</div>
`;
document.getElementById('viewTraderModal').classList.remove('hidden');
}
// Edit trader
function editTrader(traderId) {
const trader = traders.find(t => t.id === traderId);
if (!trader) return;
document.getElementById('editTraderId').value = trader.id;
document.getElementById('editName').value = trader.name;
document.getElementById('editEmail').value = trader.email;
document.getElementById('editCountry').value = trader.country;
document.getElementById('editMarket').value = trader.market;
document.getElementById('editProfit').value = trader.profit;
document.getElementById('editWinRate').value = trader.winRate;
document.getElementById('editRiskScore').value = trader.riskScore;
document.getElementById('editTraderModal').classList.remove('hidden');
}
// Save edited trader
function saveEditedTrader() {
const traderId = parseInt(document.getElementById('editTraderId').value);
const traderIndex = traders.findIndex(t => t.id === traderId);
if (traderIndex === -1) return;
traders[traderIndex] = {
id: traderId,
name: document.getElementById('editName').value,
email: document.getElementById('editEmail').value,
country: document.getElementById('editCountry').value,
market: document.getElementById('editMarket').value,
profit: parseFloat(document.getElementById('editProfit').value),
winRate: parseFloat(document.getElementById('editWinRate').value),
riskScore: parseFloat(document.getElementById('editRiskScore').value),
avatar: document.getElementById('editName').value.split(' ').map(n => n[0]).join('')
};
document.getElementById('editTraderModal').classList.add('hidden');
filterTraders();
// Show success message
showToast('Trader updated successfully!', 'success');
}
// Delete trader
function deleteTrader(traderId) {
traders = traders.filter(t => t.id !== traderId);
document.getElementById('deleteConfirmationModal').classList.add('hidden');
filterTraders();
// Show success message
showToast('Trader deleted successfully!', 'success');
}
// Add new trader
function addNewTrader() {
const newId = Math.max(...traders.map(t => t.id)) + 1;
traders.push({
id: newId,
name: document.getElementById('name').value,
email: document.getElementById('email').value,
country: document.getElementById('country').value,
market: document.getElementById('market').value,
profit: parseFloat(document.getElementById('profit').value),
winRate: parseFloat(document.getElementById('winRate').value),
riskScore: parseFloat(document.getElementById('riskScore').value),
avatar: document.getElementById('name').value.split(' ').map(n => n[0]).join('')
});
document.getElementById('addTraderModal').classList.add('hidden');
document.getElementById('addTraderForm').reset();
filterTraders();
// Show success message
showToast('New trader added successfully!', 'success');
}
// Show toast notification
function showToast(message, type) {
const toast = document.createElement('div');
toast.className = `fixed bottom-4 right-4 px-4 py-2 rounded-md shadow-md text-white ${
type === 'success' ? 'bg-green-500' :
type === 'error' ? 'bg-red-500' :
'bg-blue-500'
}`;
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => {
toast.classList.add('opacity-0', 'transition-opacity', 'duration-300');
setTimeout(() => toast.remove(), 300);
}, 3000);
}
// Export data
function exportData() {
const data = filteredTraders.map(trader => ({
ID: trader.id,
Name: trader.name,
Email: trader.email,
Country: trader.country,
Market: trader.market,
Profit: trader.profit,
'Win Rate': trader.winRate,
'Risk Score': trader.riskScore
}));
// Convert to CSV
const headers = Object.keys(data[0]);
const csv = [
headers.join(','),
...data.map(row => headers.map(header => JSON.stringify(row[header])).join(','))
].join('\n');
// Create download link
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'traders_export.csv');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
// Show success message
showToast('Data exported successfully!', 'success');
}
// Initialize the app
document.addEventListener('DOMContentLoaded', function() {
initializeTraders();
initCharts();
filterTraders();
// Mobile menu toggle
document.getElementById('menuToggle').addEventListener('click', function() {
document.querySelector('.sidebar').classList.toggle('active');
document.querySelector('.overlay').classList.toggle('active');
});
// Close mobile menu when clicking overlay
document.querySelector('.overlay').addEventListener('click', function() {
document.querySelector('.sidebar').classList.remove('active');
this.classList.remove('active');
});
// Apply filters button
document.getElementById('applyFiltersBtn').addEventListener('click', filterTraders);
// Reset filters button
document.getElementById('resetFiltersBtn').addEventListener('click', function() {
document.getElementById('profitRangeFilter').value = 'all';
document.getElementById('countryFilter').value = 'all';
document.getElementById('marketFilter').value = 'all';
document.getElementById('searchInput').value = '';
filterTraders();
});
// Search input
document.getElementById('searchInput').addEventListener('input', filterTraders);
// Pagination buttons
document.getElementById('prevPageBtn').addEventListener('click', function() {
if (currentPage > 1) {
currentPage--;
populateTradersTable();
}
});
document.getElementById('nextPageBtn').addEventListener('click', function() {
const totalPages = Math.ceil(filteredTraders.length / tradersPerPage);
if (currentPage < totalPages) {
currentPage++;
populateTradersTable();
}
});
// Add trader modal
document.getElementById('openAddTraderModal').addEventListener('click', function() {
document.getElementById('addTraderModal').classList.remove('hidden');
});
document.getElementById('cancelAddTrader').addEventListener('click', function() {
document.getElementById('addTraderModal').classList.add('hidden');
document.getElementById('addTraderForm').reset();
});
document.getElementById('submitAddTrader').addEventListener('click', function() {
if (document.getElementById('addTraderForm').reportValidity()) {
addNewTrader();
}
});
// View trader modal
document.addEventListener('click', function(e) {
if (e.target.classList.contains('view-trader-btn') || e.target.closest('.view-trader-btn')) {
const btn = e.target.classList.contains('view-trader-btn') ? e.target : e.target.closest('.view-trader-btn');
viewTraderDetails(parseInt(btn.dataset.id));
}
});
document.getElementById('closeViewTraderModal').addEventListener('click', function() {
document.getElementById('viewTraderModal').classList.add('hidden');
});
// Edit trader modal
document.addEventListener('click', function(e) {
if (e.target.classList.contains('edit-trader-btn') || e.target.closest('.edit-trader-btn')) {
const btn = e.target.classList.contains('edit-trader-btn') ? e.target : e.target.closest('.edit-trader-btn');
editTrader(parseInt(btn.dataset.id));
}
});
document.getElementById('cancelEditTrader').addEventListener('click', function() {
document.getElementById('editTraderModal').classList.add('hidden');
});
document.getElementById('submitEditTrader').addEventListener('click', function() {
if (document.getElementById('editTraderForm').reportValidity()) {
saveEditedTrader();
}
});
// Delete trader
document.addEventListener('click', function(e) {
if (e.target.classList.contains('delete-trader-btn') || e.target.closest('.delete-trader-btn')) {
const btn = e.target.classList.contains('delete-trader-btn') ? e.target : e.target.closest('.delete-trader-btn');
traderToDelete = parseInt(btn.dataset.id);
document.getElementById('deleteConfirmationModal').classList.remove('hidden');
}
});
document.getElementById('confirmDeleteBtn').addEventListener('click', function() {
deleteTrader(traderToDelete);
});
document.getElementById('cancelDeleteBtn').addEventListener('click', function() {
document.getElementById('deleteConfirmationModal').classList.add('hidden');
</html>