|
<!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; |
|
} |
|
} |
|
|
|
|
|
::-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> |
|
|
|
|
|
<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> |
|
</div> |
|
|
|
|
|
<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> |
|
|
|
|
|
<div class="md:ml-64 min-h-screen"> |
|
|
|
<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> |
|
|
|
|
|
<main class="p-4 md:p-6"> |
|
|
|
<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> |
|
|
|
|
|
<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> |
|
|
|
|
|
<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"> |
|
|
|
</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"> |
|
|
|
</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> |
|
|
|
|
|
<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">​</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> |
|
|
|
|
|
<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">​</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"> |
|
|
|
</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> |
|
|
|
|
|
<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">​</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> |
|
|
|
|
|
<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">​</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> |
|
|
|
let traders = []; |
|
let filteredTraders = []; |
|
let currentPage = 1; |
|
const tradersPerPage = 10; |
|
let profitChart, locationChart; |
|
let traderToDelete = null; |
|
|
|
|
|
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" |
|
} |
|
]; |
|
|
|
|
|
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]; |
|
} |
|
|
|
|
|
function formatCurrency(amount) { |
|
return new Intl.NumberFormat('en-US', { |
|
style: 'currency', |
|
currency: 'USD', |
|
minimumFractionDigits: 0, |
|
maximumFractionDigits: 0 |
|
}).format(amount); |
|
} |
|
|
|
|
|
function calculateStatistics() { |
|
const totalTraders = filteredTraders.length; |
|
const avgProfit = filteredTraders.reduce((sum, trader) => sum + trader.profit, 0) / totalTraders; |
|
|
|
|
|
const countryCounts = {}; |
|
filteredTraders.forEach(trader => { |
|
countryCounts[trader.country] = (countryCounts[trader.country] || 0) + 1; |
|
}); |
|
|
|
|
|
let topCountry = ""; |
|
let topCount = 0; |
|
for (const country in countryCounts) { |
|
if (countryCounts[country] > topCount) { |
|
topCountry = country; |
|
topCount = countryCounts[country]; |
|
} |
|
} |
|
|
|
|
|
const marketCounts = {}; |
|
filteredTraders.forEach(trader => { |
|
marketCounts[trader.market] = (marketCounts[trader.market] || 0) + 1; |
|
}); |
|
|
|
|
|
let topMarket = ""; |
|
let topMarketCount = 0; |
|
for (const market in marketCounts) { |
|
if (marketCounts[market] > topMarketCount) { |
|
topMarket = market; |
|
topMarketCount = marketCounts[market]; |
|
} |
|
} |
|
|
|
|
|
const avgRiskScore = filteredTraders.reduce((sum, trader) => sum + parseFloat(trader.riskScore), 0) / totalTraders; |
|
|
|
|
|
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`; |
|
|
|
|
|
updateCharts(); |
|
} |
|
|
|
|
|
function updateCharts() { |
|
|
|
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(); |
|
|
|
|
|
const countryCounts = {}; |
|
filteredTraders.forEach(trader => { |
|
countryCounts[trader.country] = (countryCounts[trader.country] || 0) + 1; |
|
}); |
|
|
|
const countries = Object.keys(countryCounts); |
|
const counts = Object.values(countryCounts); |
|
|
|
|
|
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(); |
|
} |
|
|
|
|
|
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'); |
|
|
|
|
|
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); |
|
}); |
|
|
|
|
|
document.getElementById('showingFrom').textContent = startIndex + 1; |
|
document.getElementById('showingTo').textContent = endIndex; |
|
document.getElementById('totalTradersCount').textContent = filteredTraders.length; |
|
|
|
|
|
updatePaginationButtons(); |
|
} |
|
|
|
|
|
function updatePaginationButtons() { |
|
const totalPages = Math.ceil(filteredTraders.length / tradersPerPage); |
|
const pageNumbersContainer = document.getElementById('pageNumbers'); |
|
pageNumbersContainer.innerHTML = ''; |
|
|
|
|
|
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); |
|
} |
|
} |
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
document.getElementById('prevPageBtn').disabled = currentPage === 1; |
|
document.getElementById('nextPageBtn').disabled = currentPage === totalPages; |
|
} |
|
|
|
|
|
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 => { |
|
|
|
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; |
|
} |
|
|
|
|
|
if (country !== 'all' && trader.country !== country) return false; |
|
|
|
|
|
if (market !== 'all' && trader.market !== market) return false; |
|
|
|
|
|
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(); |
|
} |
|
|
|
|
|
function initCharts() { |
|
|
|
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 |
|
} |
|
} |
|
} |
|
}); |
|
|
|
|
|
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', |
|
} |
|
} |
|
} |
|
}); |
|
} |
|
|
|
|
|
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'); |
|
} |
|
|
|
|
|
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'); |
|
} |
|
|
|
|
|
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(); |
|
|
|
|
|
showToast('Trader updated successfully!', 'success'); |
|
} |
|
|
|
|
|
function deleteTrader(traderId) { |
|
traders = traders.filter(t => t.id !== traderId); |
|
document.getElementById('deleteConfirmationModal').classList.add('hidden'); |
|
filterTraders(); |
|
|
|
|
|
showToast('Trader deleted successfully!', 'success'); |
|
} |
|
|
|
|
|
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(); |
|
|
|
|
|
showToast('New trader added successfully!', 'success'); |
|
} |
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
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 |
|
})); |
|
|
|
|
|
const headers = Object.keys(data[0]); |
|
const csv = [ |
|
headers.join(','), |
|
...data.map(row => headers.map(header => JSON.stringify(row[header])).join(',')) |
|
].join('\n'); |
|
|
|
|
|
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); |
|
|
|
|
|
showToast('Data exported successfully!', 'success'); |
|
} |
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() { |
|
initializeTraders(); |
|
initCharts(); |
|
filterTraders(); |
|
|
|
|
|
document.getElementById('menuToggle').addEventListener('click', function() { |
|
document.querySelector('.sidebar').classList.toggle('active'); |
|
document.querySelector('.overlay').classList.toggle('active'); |
|
}); |
|
|
|
|
|
document.querySelector('.overlay').addEventListener('click', function() { |
|
document.querySelector('.sidebar').classList.remove('active'); |
|
this.classList.remove('active'); |
|
}); |
|
|
|
|
|
document.getElementById('applyFiltersBtn').addEventListener('click', filterTraders); |
|
|
|
|
|
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(); |
|
}); |
|
|
|
|
|
document.getElementById('searchInput').addEventListener('input', filterTraders); |
|
|
|
|
|
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(); |
|
} |
|
}); |
|
|
|
|
|
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(); |
|
} |
|
}); |
|
|
|
|
|
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'); |
|
}); |
|
|
|
|
|
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(); |
|
} |
|
}); |
|
|
|
|
|
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> |