|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>Forex Signal Dashboard</title> |
|
|
<script src="https://cdn.tailwindcss.com"></script> |
|
|
<script src="https://s3.tradingview.com/tv.js"></script> |
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
|
|
<style> |
|
|
.tradingview-widget-container { |
|
|
height: 500px; |
|
|
} |
|
|
.signal-card.buy { |
|
|
border-left: 4px solid #10B981; |
|
|
} |
|
|
.signal-card.sell { |
|
|
border-left: 4px solid #EF4444; |
|
|
} |
|
|
.chart-container { |
|
|
height: 500px; |
|
|
position: relative; |
|
|
} |
|
|
.auth-container { |
|
|
background: linear-gradient(135deg, #1e3a8a 0%, #1e40af 100%); |
|
|
} |
|
|
.blurred { |
|
|
filter: blur(5px); |
|
|
pointer-events: none; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body class="bg-gray-100"> |
|
|
|
|
|
<div id="authModal" class="fixed inset-0 z-50 flex items-center justify-center auth-container"> |
|
|
<div class="bg-white rounded-lg shadow-xl p-8 w-full max-w-md"> |
|
|
<div class="flex justify-between items-center mb-6"> |
|
|
<h2 class="text-2xl font-bold text-gray-800">Welcome to Forex Signals</h2> |
|
|
<button id="closeAuthModal" class="text-gray-500 hover:text-gray-700"> |
|
|
<i class="fas fa-times"></i> |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
<div class="tabs flex mb-6 border-b"> |
|
|
<button id="loginTab" class="tab-btn active px-4 py-2 font-medium text-blue-600 border-b-2 border-blue-600">Login</button> |
|
|
<button id="signupTab" class="tab-btn px-4 py-2 font-medium text-gray-500 hover:text-gray-700">Sign Up</button> |
|
|
</div> |
|
|
|
|
|
<div id="loginForm" class="auth-form"> |
|
|
<div class="mb-4"> |
|
|
<label class="block text-gray-700 mb-2" for="loginEmail">Email</label> |
|
|
<input type="email" id="loginEmail" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"> |
|
|
</div> |
|
|
<div class="mb-6"> |
|
|
<label class="block text-gray-700 mb-2" for="loginPassword">Password</label> |
|
|
<input type="password" id="loginPassword" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"> |
|
|
</div> |
|
|
<button id="loginBtn" class="w-full bg-blue-600 text-white py-2 px-4 rounded-lg hover:bg-blue-700 transition duration-200">Login</button> |
|
|
|
|
|
<div class="mt-4 text-center"> |
|
|
<p class="text-gray-600">Or login with</p> |
|
|
<div class="flex justify-center mt-2 space-x-4"> |
|
|
<button class="p-2 bg-red-100 rounded-full text-red-600 hover:bg-red-200"> |
|
|
<i class="fab fa-google"></i> |
|
|
</button> |
|
|
<button class="p-2 bg-gray-100 rounded-full text-gray-800 hover:bg-gray-200"> |
|
|
<i class="fab fa-apple"></i> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div id="signupForm" class="auth-form hidden"> |
|
|
<div class="mb-4"> |
|
|
<label class="block text-gray-700 mb-2" for="signupName">Full Name</label> |
|
|
<input type="text" id="signupName" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"> |
|
|
</div> |
|
|
<div class="mb-4"> |
|
|
<label class="block text-gray-700 mb-2" for="signupEmail">Email</label> |
|
|
<input type="email" id="signupEmail" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"> |
|
|
</div> |
|
|
<div class="mb-6"> |
|
|
<label class="block text-gray-700 mb-2" for="signupPassword">Password</label> |
|
|
<input type="password" id="signupPassword" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"> |
|
|
</div> |
|
|
<div class="mb-6"> |
|
|
<label class="block text-gray-700 mb-2">Preferred Currency Pairs</label> |
|
|
<div class="flex flex-wrap gap-2"> |
|
|
<label class="inline-flex items-center"> |
|
|
<input type="checkbox" class="form-checkbox text-blue-600" value="EUR/USD"> |
|
|
<span class="ml-2">EUR/USD</span> |
|
|
</label> |
|
|
<label class="inline-flex items-center"> |
|
|
<input type="checkbox" class="form-checkbox text-blue-600" value="GBP/USD"> |
|
|
<span class="ml-2">GBP/USD</span> |
|
|
</label> |
|
|
<label class="inline-flex items-center"> |
|
|
<input type="checkbox" class="form-checkbox text-blue-600" value="USD/JPY"> |
|
|
<span class="ml-2">USD/JPY</span> |
|
|
</label> |
|
|
<label class="inline-flex items-center"> |
|
|
<input type="checkbox" class="form-checkbox text-blue-600" value="AUD/USD"> |
|
|
<span class="ml-2">AUD/USD</span> |
|
|
</label> |
|
|
<label class="inline-flex items-center"> |
|
|
<input type="checkbox" class="form-checkbox text-blue-600" value="XAU/USD"> |
|
|
<span class="ml-2">XAU/USD</span> |
|
|
</label> |
|
|
</div> |
|
|
</div> |
|
|
<button id="signupBtn" class="w-full bg-blue-600 text-white py-2 px-4 rounded-lg hover:bg-blue-700 transition duration-200">Sign Up</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="appContainer" class="blurred"> |
|
|
|
|
|
<header class="bg-white shadow-sm"> |
|
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> |
|
|
<div class="flex justify-between items-center py-4"> |
|
|
<div class="flex items-center"> |
|
|
<div class="w-10 h-10 bg-blue-600 rounded-full flex items-center justify-center text-white font-bold">FS</div> |
|
|
<h1 class="ml-3 text-xl font-semibold text-gray-900">Forex Signals</h1> |
|
|
</div> |
|
|
<div class="flex items-center space-x-4"> |
|
|
<button id="notificationBtn" class="relative p-2 text-gray-500 hover:text-gray-700"> |
|
|
<i class="fas fa-bell"></i> |
|
|
<span class="absolute top-0 right-0 w-2 h-2 bg-red-500 rounded-full"></span> |
|
|
</button> |
|
|
<div class="relative"> |
|
|
<button id="userMenuBtn" class="flex items-center space-x-2 focus:outline-none"> |
|
|
<div class="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center text-blue-600"> |
|
|
<i class="fas fa-user"></i> |
|
|
</div> |
|
|
<span class="hidden md:inline text-gray-700">User</span> |
|
|
</button> |
|
|
<div id="userMenu" class="hidden absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 z-50"> |
|
|
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Profile</a> |
|
|
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Settings</a> |
|
|
<a href="#" id="logoutBtn" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Logout</a> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</header> |
|
|
|
|
|
|
|
|
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6"> |
|
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6"> |
|
|
|
|
|
<div class="lg:col-span-1 space-y-4"> |
|
|
<div class="bg-white rounded-lg shadow p-4"> |
|
|
<div class="flex justify-between items-center mb-4"> |
|
|
<h2 class="text-lg font-semibold">Active Signals</h2> |
|
|
<button class="text-blue-600 hover:text-blue-800"> |
|
|
<i class="fas fa-sync-alt"></i> |
|
|
</button> |
|
|
</div> |
|
|
<div id="activeSignals" class="space-y-3"> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="bg-white rounded-lg shadow p-4"> |
|
|
<h2 class="text-lg font-semibold mb-4">Signal History</h2> |
|
|
<div id="signalHistory" class="space-y-3"> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="lg:col-span-2 space-y-4"> |
|
|
<div class="bg-white rounded-lg shadow p-4"> |
|
|
<div class="flex justify-between items-center mb-4"> |
|
|
<h2 class="text-lg font-semibold">Market Analysis</h2> |
|
|
<div class="flex space-x-2"> |
|
|
<select id="timeframeSelect" class="border rounded px-2 py-1 text-sm"> |
|
|
<option value="1">1m</option> |
|
|
<option value="5">5m</option> |
|
|
<option value="15">15m</option> |
|
|
<option value="60">1h</option> |
|
|
<option value="240" selected>4h</option> |
|
|
<option value="1D">1D</option> |
|
|
</select> |
|
|
<select id="pairSelect" class="border rounded px-2 py-1 text-sm"> |
|
|
<option value="XAUUSDT">XAU/USDT</option> |
|
|
<option value="EURUSD">EUR/USD</option> |
|
|
<option value="GBPUSD">GBP/USD</option> |
|
|
<option value="USDJPY">USD/JPY</option> |
|
|
<option value="AUDUSD">AUD/USD</option> |
|
|
<option value="USDCAD">USD/CAD</option> |
|
|
<option value="XAUUSD">XAU/USD</option> |
|
|
</select> |
|
|
</div> |
|
|
</div> |
|
|
<div class="chart-container"> |
|
|
<div id="tradingview-chart"></div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="bg-white rounded-lg shadow p-4"> |
|
|
<h2 class="text-lg font-semibold mb-4">Signal Details</h2> |
|
|
<div id="signalDetails" class="grid grid-cols-1 md:grid-cols-2 gap-4"> |
|
|
<div> |
|
|
<h3 class="font-medium text-gray-700 mb-2">ICT Analysis</h3> |
|
|
<p class="text-gray-600">This signal is based on ICT concepts including FVG, liquidity grabs, and market structure shifts.</p> |
|
|
</div> |
|
|
<div> |
|
|
<h3 class="font-medium text-gray-700 mb-2">Key Levels</h3> |
|
|
<div class="space-y-2"> |
|
|
<div class="flex justify-between"> |
|
|
<span class="text-gray-600">Entry:</span> |
|
|
<span class="font-medium" id="detailEntry">1.0850</span> |
|
|
</div> |
|
|
<div class="flex justify-between"> |
|
|
<span class="text-gray-600">Stop Loss:</span> |
|
|
<span class="font-medium text-red-500" id="detailSL">1.0820</span> |
|
|
</div> |
|
|
<div class="flex justify-between"> |
|
|
<span class="text-gray-600">Take Profit 1:</span> |
|
|
<span class="font-medium text-green-500" id="detailTP1">1.0880</span> |
|
|
</div> |
|
|
<div class="flex justify-between"> |
|
|
<span class="text-gray-600">Take Profit 2:</span> |
|
|
<span class="font-medium text-green-500" id="detailTP2">1.0920</span> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</main> |
|
|
|
|
|
|
|
|
<div id="adminPanel" class="hidden fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center"> |
|
|
<div class="bg-white rounded-lg shadow-xl p-6 w-full max-w-4xl max-h-[90vh] overflow-y-auto"> |
|
|
<div class="flex justify-between items-center mb-6"> |
|
|
<h2 class="text-2xl font-bold">Admin Panel</h2> |
|
|
<button id="closeAdminPanel" class="text-gray-500 hover:text-gray-700"> |
|
|
<i class="fas fa-times"></i> |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
<div class="mb-6"> |
|
|
<h3 class="text-lg font-semibold mb-3">Create New Signal</h3> |
|
|
<form id="signalForm" class="grid grid-cols-1 md:grid-cols-2 gap-4"> |
|
|
<div> |
|
|
<label class="block text-gray-700 mb-1">Currency Pair</label> |
|
|
<select id="signalPair" class="w-full border rounded px-3 py-2"> |
|
|
<option value="EUR/USD">EUR/USD</option> |
|
|
<option value="GBP/USD">GBP/USD</option> |
|
|
<option value="USD/JPY">USD/JPY</option> |
|
|
<option value="AUD/USD">AUD/USD</option> |
|
|
<option value="XAU/USD">XAU/USD</option> |
|
|
</select> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-gray-700 mb-1">Direction</label> |
|
|
<select id="signalDirection" class="w-full border rounded px-3 py-2"> |
|
|
<option value="buy">Buy</option> |
|
|
<option value="sell">Sell</option> |
|
|
</select> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-gray-700 mb-1">Entry Price</label> |
|
|
<input type="number" step="0.0001" id="signalEntry" class="w-full border rounded px-3 py-2"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-gray-700 mb-1">Stop Loss</label> |
|
|
<input type="number" step="0.0001" id="signalSL" class="w-full border rounded px-3 py-2"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-gray-700 mb-1">Take Profit 1</label> |
|
|
<input type="number" step="0.0001" id="signalTP1" class="w-full border rounded px-3 py-2"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-gray-700 mb-1">Take Profit 2</label> |
|
|
<input type="number" step="0.0001" id="signalTP2" class="w-full border rounded px-3 py-2"> |
|
|
</div> |
|
|
<div class="md:col-span-2"> |
|
|
<label class="block text-gray-700 mb-1">Analysis Notes</label> |
|
|
<textarea id="signalNotes" rows="3" class="w-full border rounded px-3 py-2"></textarea> |
|
|
</div> |
|
|
<div class="md:col-span-2 flex justify-end space-x-3"> |
|
|
<button type="button" id="cancelSignal" class="px-4 py-2 border rounded-lg text-gray-700 hover:bg-gray-100">Cancel</button> |
|
|
<button type="submit" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">Publish Signal</button> |
|
|
</div> |
|
|
</form> |
|
|
</div> |
|
|
|
|
|
<div> |
|
|
<h3 class="text-lg font-semibold mb-3">Manage Signals</h3> |
|
|
<div class="overflow-x-auto"> |
|
|
<table class="min-w-full divide-y divide-gray-200"> |
|
|
<thead class="bg-gray-50"> |
|
|
<tr> |
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Pair</th> |
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Direction</th> |
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Entry</th> |
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th> |
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th> |
|
|
</tr> |
|
|
</thead> |
|
|
<tbody id="adminSignalList" class="bg-white divide-y divide-gray-200"> |
|
|
|
|
|
</tbody> |
|
|
</table> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
|
|
|
const activeSignals = [ |
|
|
{ |
|
|
id: 1, |
|
|
pair: 'XAU/USDT', |
|
|
direction: 'buy', |
|
|
entry: 2035.50, |
|
|
sl: 2020.00, |
|
|
tp1: 2050.00, |
|
|
tp2: 2075.00, |
|
|
timestamp: new Date(), |
|
|
notes: 'Bullish engulfing pattern at key support level with RSI divergence' |
|
|
}, |
|
|
{ |
|
|
id: 2, |
|
|
pair: 'EUR/USD', |
|
|
direction: 'buy', |
|
|
entry: 1.0850, |
|
|
sl: 1.0820, |
|
|
tp1: 1.0880, |
|
|
tp2: 1.0920, |
|
|
timestamp: new Date(), |
|
|
notes: 'ICT FVG with liquidity grab below previous low' |
|
|
}, |
|
|
{ |
|
|
id: 3, |
|
|
pair: 'GBP/USD', |
|
|
direction: 'sell', |
|
|
entry: 1.2650, |
|
|
sl: 1.2685, |
|
|
tp1: 1.2600, |
|
|
tp2: 1.2550, |
|
|
timestamp: new Date(Date.now() - 3600000), |
|
|
notes: 'Bearish order block with premium array' |
|
|
} |
|
|
]; |
|
|
|
|
|
const signalHistory = [ |
|
|
{ |
|
|
id: 3, |
|
|
pair: 'USD/JPY', |
|
|
direction: 'buy', |
|
|
entry: 150.80, |
|
|
sl: 150.50, |
|
|
tp1: 151.20, |
|
|
tp2: 151.80, |
|
|
timestamp: new Date(Date.now() - 86400000), |
|
|
closedAt: new Date(Date.now() - 82800000), |
|
|
outcome: 'win', |
|
|
closedPrice: 151.25, |
|
|
notes: 'Liquidity sweep of previous low' |
|
|
}, |
|
|
{ |
|
|
id: 4, |
|
|
pair: 'AUD/USD', |
|
|
direction: 'sell', |
|
|
entry: 0.6580, |
|
|
sl: 0.6610, |
|
|
tp1: 0.6550, |
|
|
tp2: 0.6520, |
|
|
timestamp: new Date(Date.now() - 172800000), |
|
|
closedAt: new Date(Date.now() - 169200000), |
|
|
outcome: 'loss', |
|
|
closedPrice: 0.6615, |
|
|
notes: 'Failed breakdown of support' |
|
|
} |
|
|
]; |
|
|
|
|
|
|
|
|
const authModal = document.getElementById('authModal'); |
|
|
const appContainer = document.getElementById('appContainer'); |
|
|
const loginTab = document.getElementById('loginTab'); |
|
|
const signupTab = document.getElementById('signupTab'); |
|
|
const loginForm = document.getElementById('loginForm'); |
|
|
const signupForm = document.getElementById('signupForm'); |
|
|
const loginBtn = document.getElementById('loginBtn'); |
|
|
const signupBtn = document.getElementById('signupBtn'); |
|
|
const closeAuthModal = document.getElementById('closeAuthModal'); |
|
|
const logoutBtn = document.getElementById('logoutBtn'); |
|
|
const userMenuBtn = document.getElementById('userMenuBtn'); |
|
|
const userMenu = document.getElementById('userMenu'); |
|
|
const adminPanel = document.getElementById('adminPanel'); |
|
|
const closeAdminPanel = document.getElementById('closeAdminPanel'); |
|
|
const activeSignalsContainer = document.getElementById('activeSignals'); |
|
|
const signalHistoryContainer = document.getElementById('signalHistory'); |
|
|
const pairSelect = document.getElementById('pairSelect'); |
|
|
const timeframeSelect = document.getElementById('timeframeSelect'); |
|
|
const signalForm = document.getElementById('signalForm'); |
|
|
const adminSignalList = document.getElementById('adminSignalList'); |
|
|
const detailEntry = document.getElementById('detailEntry'); |
|
|
const detailSL = document.getElementById('detailSL'); |
|
|
const detailTP1 = document.getElementById('detailTP1'); |
|
|
const detailTP2 = document.getElementById('detailTP2'); |
|
|
|
|
|
|
|
|
let tvWidget = null; |
|
|
|
|
|
|
|
|
let currentUser = null; |
|
|
let isAdmin = false; |
|
|
|
|
|
|
|
|
function initApp() { |
|
|
|
|
|
const loggedIn = localStorage.getItem('forexUserLoggedIn'); |
|
|
|
|
|
if (loggedIn) { |
|
|
|
|
|
currentUser = { |
|
|
name: 'John Doe', |
|
|
email: 'john@example.com', |
|
|
isAdmin: localStorage.getItem('forexUserIsAdmin') === 'true' |
|
|
}; |
|
|
isAdmin = currentUser.isAdmin; |
|
|
|
|
|
|
|
|
authModal.classList.add('hidden'); |
|
|
appContainer.classList.remove('blurred'); |
|
|
|
|
|
|
|
|
loadSignals(); |
|
|
initChart(); |
|
|
} else { |
|
|
|
|
|
authModal.classList.remove('hidden'); |
|
|
appContainer.classList.add('blurred'); |
|
|
} |
|
|
|
|
|
|
|
|
setupEventListeners(); |
|
|
} |
|
|
|
|
|
|
|
|
function setupEventListeners() { |
|
|
|
|
|
loginTab.addEventListener('click', () => { |
|
|
loginTab.classList.add('active', 'text-blue-600', 'border-blue-600'); |
|
|
loginTab.classList.remove('text-gray-500'); |
|
|
signupTab.classList.add('text-gray-500'); |
|
|
signupTab.classList.remove('active', 'text-blue-600', 'border-blue-600'); |
|
|
loginForm.classList.remove('hidden'); |
|
|
signupForm.classList.add('hidden'); |
|
|
}); |
|
|
|
|
|
signupTab.addEventListener('click', () => { |
|
|
signupTab.classList.add('active', 'text-blue-600', 'border-blue-600'); |
|
|
signupTab.classList.remove('text-gray-500'); |
|
|
loginTab.classList.add('text-gray-500'); |
|
|
loginTab.classList.remove('active', 'text-blue-600', 'border-blue-600'); |
|
|
signupForm.classList.remove('hidden'); |
|
|
loginForm.classList.add('hidden'); |
|
|
}); |
|
|
|
|
|
|
|
|
loginBtn.addEventListener('click', (e) => { |
|
|
e.preventDefault(); |
|
|
const email = document.getElementById('loginEmail').value; |
|
|
const password = document.getElementById('loginPassword').value; |
|
|
|
|
|
|
|
|
if (email && password) { |
|
|
|
|
|
currentUser = { |
|
|
name: 'John Doe', |
|
|
email: email, |
|
|
isAdmin: email.includes('admin') |
|
|
}; |
|
|
isAdmin = currentUser.isAdmin; |
|
|
|
|
|
|
|
|
localStorage.setItem('forexUserLoggedIn', 'true'); |
|
|
localStorage.setItem('forexUserIsAdmin', isAdmin.toString()); |
|
|
|
|
|
|
|
|
authModal.classList.add('hidden'); |
|
|
appContainer.classList.remove('blurred'); |
|
|
|
|
|
|
|
|
loadSignals(); |
|
|
initChart(); |
|
|
|
|
|
|
|
|
showNotification('Welcome back! Signals loaded successfully.'); |
|
|
} else { |
|
|
alert('Please enter both email and password'); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
signupBtn.addEventListener('click', (e) => { |
|
|
e.preventDefault(); |
|
|
const name = document.getElementById('signupName').value; |
|
|
const email = document.getElementById('signupEmail').value; |
|
|
const password = document.getElementById('signupPassword').value; |
|
|
|
|
|
|
|
|
if (name && email && password) { |
|
|
|
|
|
currentUser = { |
|
|
name: name, |
|
|
email: email, |
|
|
isAdmin: false |
|
|
}; |
|
|
isAdmin = false; |
|
|
|
|
|
|
|
|
localStorage.setItem('forexUserLoggedIn', 'true'); |
|
|
localStorage.setItem('forexUserIsAdmin', 'false'); |
|
|
|
|
|
|
|
|
authModal.classList.add('hidden'); |
|
|
appContainer.classList.remove('blurred'); |
|
|
|
|
|
|
|
|
loadSignals(); |
|
|
initChart(); |
|
|
|
|
|
|
|
|
showNotification('Welcome to Forex Signals! Your account has been created.'); |
|
|
} else { |
|
|
alert('Please fill in all fields'); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
closeAuthModal.addEventListener('click', () => { |
|
|
|
|
|
if (!localStorage.getItem('forexUserLoggedIn')) { |
|
|
alert('Please login or sign up to continue'); |
|
|
} else { |
|
|
authModal.classList.add('hidden'); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
userMenuBtn.addEventListener('click', () => { |
|
|
userMenu.classList.toggle('hidden'); |
|
|
|
|
|
|
|
|
document.addEventListener('click', (e) => { |
|
|
if (!userMenu.contains(e.target) && e.target !== userMenuBtn) { |
|
|
userMenu.classList.add('hidden'); |
|
|
} |
|
|
}, { once: true }); |
|
|
}); |
|
|
|
|
|
|
|
|
logoutBtn.addEventListener('click', () => { |
|
|
localStorage.removeItem('forexUserLoggedIn'); |
|
|
localStorage.removeItem('forexUserIsAdmin'); |
|
|
currentUser = null; |
|
|
isAdmin = false; |
|
|
|
|
|
|
|
|
authModal.classList.remove('hidden'); |
|
|
appContainer.classList.add('blurred'); |
|
|
|
|
|
|
|
|
document.getElementById('loginEmail').value = ''; |
|
|
document.getElementById('loginPassword').value = ''; |
|
|
document.getElementById('signupName').value = ''; |
|
|
document.getElementById('signupEmail').value = ''; |
|
|
document.getElementById('signupPassword').value = ''; |
|
|
|
|
|
|
|
|
loginTab.click(); |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('notificationBtn').addEventListener('click', () => { |
|
|
showNotification('You have no new notifications'); |
|
|
}); |
|
|
|
|
|
|
|
|
pairSelect.addEventListener('change', reloadChart); |
|
|
timeframeSelect.addEventListener('change', reloadChart); |
|
|
|
|
|
|
|
|
signalForm.addEventListener('submit', (e) => { |
|
|
e.preventDefault(); |
|
|
createNewSignal(); |
|
|
}); |
|
|
|
|
|
document.getElementById('cancelSignal').addEventListener('click', () => { |
|
|
signalForm.reset(); |
|
|
}); |
|
|
|
|
|
|
|
|
closeAdminPanel.addEventListener('click', () => { |
|
|
adminPanel.classList.add('hidden'); |
|
|
}); |
|
|
|
|
|
|
|
|
if (isAdmin) { |
|
|
|
|
|
const header = document.querySelector('header .flex.items-center.space-x-4'); |
|
|
const adminBtn = document.createElement('button'); |
|
|
adminBtn.className = 'p-2 text-gray-500 hover:text-gray-700'; |
|
|
adminBtn.innerHTML = '<i class="fas fa-cog"></i>'; |
|
|
adminBtn.addEventListener('click', () => { |
|
|
adminPanel.classList.remove('hidden'); |
|
|
loadAdminSignals(); |
|
|
}); |
|
|
header.insertBefore(adminBtn, header.firstChild); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function loadSignals() { |
|
|
|
|
|
activeSignalsContainer.innerHTML = ''; |
|
|
signalHistoryContainer.innerHTML = ''; |
|
|
|
|
|
|
|
|
activeSignals.forEach(signal => { |
|
|
const signalCard = createSignalCard(signal, false); |
|
|
activeSignalsContainer.appendChild(signalCard); |
|
|
}); |
|
|
|
|
|
|
|
|
signalHistory.forEach(signal => { |
|
|
const historyCard = createSignalCard(signal, true); |
|
|
signalHistoryContainer.appendChild(historyCard); |
|
|
}); |
|
|
|
|
|
|
|
|
if (activeSignals.length > 0) { |
|
|
showSignalDetails(activeSignals[0]); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function createSignalCard(signal, isHistory) { |
|
|
const card = document.createElement('div'); |
|
|
card.className = `signal-card ${signal.direction} bg-white rounded shadow p-3 cursor-pointer hover:shadow-md transition duration-200`; |
|
|
card.dataset.id = signal.id; |
|
|
|
|
|
const directionClass = signal.direction === 'buy' ? 'text-green-500' : 'text-red-500'; |
|
|
const directionIcon = signal.direction === 'buy' ? 'fa-arrow-up' : 'fa-arrow-down'; |
|
|
|
|
|
let outcomeBadge = ''; |
|
|
if (isHistory) { |
|
|
const outcomeClass = signal.outcome === 'win' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'; |
|
|
outcomeBadge = `<span class="text-xs px-2 py-1 rounded-full ${outcomeClass} ml-2">${signal.outcome}</span>`; |
|
|
} |
|
|
|
|
|
card.innerHTML = ` |
|
|
<div class="flex justify-between items-start"> |
|
|
<div> |
|
|
<h3 class="font-semibold">${signal.pair}</h3> |
|
|
<p class="text-sm text-gray-500">${formatDate(signal.timestamp)}</p> |
|
|
</div> |
|
|
<div class="flex items-center"> |
|
|
<i class="fas ${directionIcon} ${directionClass}"></i> |
|
|
${outcomeBadge} |
|
|
</div> |
|
|
</div> |
|
|
<div class="mt-2 grid grid-cols-3 gap-2 text-sm"> |
|
|
<div> |
|
|
<p class="text-gray-500">Entry</p> |
|
|
<p>${signal.entry}</p> |
|
|
</div> |
|
|
<div> |
|
|
<p class="text-gray-500">SL</p> |
|
|
<p class="text-red-500">${signal.sl}</p> |
|
|
</div> |
|
|
<div> |
|
|
<p class="text-gray-500">TP</p> |
|
|
<p class="text-green-500">${signal.tp1}</p> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
|
|
|
card.addEventListener('click', () => { |
|
|
showSignalDetails(signal); |
|
|
|
|
|
|
|
|
document.querySelectorAll('.signal-card').forEach(c => { |
|
|
c.classList.remove('ring-2', 'ring-blue-500'); |
|
|
}); |
|
|
card.classList.add('ring-2', 'ring-blue-500'); |
|
|
}); |
|
|
|
|
|
return card; |
|
|
} |
|
|
|
|
|
|
|
|
function showSignalDetails(signal) { |
|
|
detailEntry.textContent = signal.entry; |
|
|
detailSL.textContent = signal.sl; |
|
|
detailTP1.textContent = signal.tp1; |
|
|
detailTP2.textContent = signal.tp2 || 'N/A'; |
|
|
|
|
|
|
|
|
pairSelect.value = signal.pair.replace('/', ''); |
|
|
reloadChart(); |
|
|
} |
|
|
|
|
|
|
|
|
function formatDate(date) { |
|
|
return new Date(date).toLocaleString('en-US', { |
|
|
month: 'short', |
|
|
day: 'numeric', |
|
|
hour: '2-digit', |
|
|
minute: '2-digit' |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
function initChart() { |
|
|
if (tvWidget !== null) { |
|
|
tvWidget.remove(); |
|
|
tvWidget = null; |
|
|
} |
|
|
|
|
|
const widgetOptions = { |
|
|
symbol: pairSelect.value, |
|
|
interval: timeframeSelect.value, |
|
|
theme: 'light', |
|
|
container_id: 'tradingview-chart', |
|
|
datafeed: new Datafeeds.UDFCompatibleDatafeed(`https://api.twelvedata.com/time_series?symbol=${pairSelect.value}&interval=${timeframeSelect.value}&apikey=cc9af226f2dd41b9af948da524333bd5`), |
|
|
library_path: 'https://s3.tradingview.com/tv.js', |
|
|
locale: 'en', |
|
|
disabled_features: ['header_widget', 'header_compare', 'header_screenshot', 'header_undo_redo', 'header_interval_dialog_button'], |
|
|
enabled_features: ['study_templates'], |
|
|
charts_storage_url: 'https://saveload.tradingview.com', |
|
|
charts_storage_api_version: '1.1', |
|
|
client_id: 'tradingview.com', |
|
|
user_id: 'public_user_id', |
|
|
fullscreen: false, |
|
|
autosize: true, |
|
|
studies_overrides: {}, |
|
|
overrides: { |
|
|
'paneProperties.background': '#ffffff', |
|
|
'paneProperties.vertGridProperties.color': '#f0f0f0', |
|
|
'paneProperties.horzGridProperties.color': '#f0f0f0', |
|
|
'mainSeriesProperties.candleStyle.upColor': '#388e3c', |
|
|
'mainSeriesProperties.candleStyle.downColor': '#d32f2f', |
|
|
'mainSeriesProperties.candleStyle.borderUpColor': '#388e3c', |
|
|
'mainSeriesProperties.candleStyle.borderDownColor': '#d32f2f', |
|
|
'mainSeriesProperties.candleStyle.wickUpColor': '#388e3c', |
|
|
'mainSeriesProperties.candleStyle.wickDownColor': '#d32f2f', |
|
|
'mainSeriesProperties.priceAxisProperties.autoScale': false, |
|
|
'mainSeriesProperties.priceAxisProperties.percentage': false, |
|
|
'mainSeriesProperties.priceAxisProperties.log': false |
|
|
} |
|
|
}; |
|
|
|
|
|
tvWidget = new TradingView.widget(widgetOptions); |
|
|
|
|
|
tvWidget.onChartReady(() => { |
|
|
console.log('Chart has loaded!'); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
function reloadChart() { |
|
|
initChart(); |
|
|
} |
|
|
|
|
|
|
|
|
function showNotification(message) { |
|
|
alert(message); |
|
|
} |
|
|
|
|
|
|
|
|
function loadAdminSignals() { |
|
|
adminSignalList.innerHTML = ''; |
|
|
|
|
|
const allSignals = [...activeSignals, ...signalHistory]; |
|
|
|
|
|
allSignals.forEach(signal => { |
|
|
const row = document.createElement('tr'); |
|
|
|
|
|
const directionClass = signal.direction === 'buy' ? 'text-green-500' : 'text-red-500'; |
|
|
const directionText = signal.direction === 'buy' ? 'BUY' : 'SELL'; |
|
|
|
|
|
let statusBadge = ''; |
|
|
if (signalHistory.find(s => s.id === signal.id)) { |
|
|
statusBadge = `<span class="px-2 py-1 text-xs rounded-full bg-gray-100 text-gray-800">Closed</span>`; |
|
|
} else { |
|
|
statusBadge = `<span class="px-2 py-1 text-xs rounded-full bg-blue-100 text-blue-800">Active</span>`; |
|
|
} |
|
|
|
|
|
row.innerHTML = ` |
|
|
<td class="px-6 py-4 whitespace-nowrap">${signal.pair}</td> |
|
|
<td class="px-6 py-4 whitespace-nowrap"><span class="${directionClass}">${directionText}</span></td> |
|
|
<td class="px-6 py-4 whitespace-nowrap">${signal.entry}</td> |
|
|
<td class="px-6 py-4 whitespace-nowrap">${statusBadge}</td> |
|
|
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> |
|
|
<button class="text-blue-600 hover:text-blue-900 mr-2 edit-signal" data-id="${signal.id}">Edit</button> |
|
|
<button class="text-red-600 hover:text-red-900 delete-signal" data-id="${signal.id}">Delete</button> |
|
|
</td> |
|
|
`; |
|
|
|
|
|
adminSignalList.appendChild(row); |
|
|
}); |
|
|
|
|
|
|
|
|
document.querySelectorAll('.edit-signal').forEach(btn => { |
|
|
btn.addEventListener('click', (e) => { |
|
|
const signalId = parseInt(e.target.dataset.id); |
|
|
editSignal(signalId); |
|
|
}); |
|
|
}); |
|
|
|
|
|
document.querySelectorAll('.delete-signal').forEach(btn => { |
|
|
btn.addEventListener('click', (e) => { |
|
|
const signalId = parseInt(e.target.dataset.id); |
|
|
if (confirm('Are you sure you want to delete this signal?')) { |
|
|
deleteSignal(signalId); |
|
|
} |
|
|
}); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
function editSignal(signalId) { |
|
|
const allSignals = [...activeSignals, ...signalHistory]; |
|
|
const signal = allSignals.find(s => s.id === signalId); |
|
|
|
|
|
if (signal) { |
|
|
document.getElementById('signalPair').value = signal.pair; |
|
|
document.getElementById('signalDirection').value = signal.direction; |
|
|
document.getElementById('signalEntry').value = signal.entry; |
|
|
document.getElementById('signalSL').value = signal.sl; |
|
|
document.getElementById('signalTP1').value = signal.tp1; |
|
|
document.getElementById('signalTP2').value = signal.tp2 || ''; |
|
|
document.getElementById('signalNotes').value = signal.notes; |
|
|
|
|
|
|
|
|
document.getElementById('signalForm').scrollIntoView(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function deleteSignal(signalId) { |
|
|
|
|
|
console.log(`Signal ${signalId} deleted`); |
|
|
showNotification('Signal deleted successfully'); |
|
|
loadAdminSignals(); |
|
|
} |
|
|
|
|
|
|
|
|
function createNewSignal() { |
|
|
const pair = document.getElementById('signalPair').value; |
|
|
const direction = document.getElementById('signalDirection').value; |
|
|
const entry = parseFloat(document.getElementById('signalEntry').value); |
|
|
const sl = parseFloat(document.getElementById('signalSL').value); |
|
|
const tp1 = parseFloat(document.getElementById('signalTP1').value); |
|
|
const tp2 = parseFloat(document.getElementById('signalTP2').value) || null; |
|
|
const notes = document.getElementById('signalNotes').value; |
|
|
|
|
|
|
|
|
if (!pair || !direction || isNaN(entry) || isNaN(sl) || isNaN(tp1)) { |
|
|
alert('Please fill in all required fields'); |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
const newSignal = { |
|
|
id: Math.max(...activeSignals.map(s => s.id), ...signalHistory.map(s => s.id)) + 1, |
|
|
pair, |
|
|
direction, |
|
|
entry, |
|
|
sl, |
|
|
tp1, |
|
|
tp2, |
|
|
timestamp: new Date(), |
|
|
notes |
|
|
}; |
|
|
|
|
|
|
|
|
activeSignals.push(newSignal); |
|
|
|
|
|
|
|
|
signalForm.reset(); |
|
|
|
|
|
|
|
|
showNotification('New signal published successfully!'); |
|
|
|
|
|
|
|
|
loadSignals(); |
|
|
loadAdminSignals(); |
|
|
} |
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', initApp); |
|
|
</script> |
|
|
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Abel222/fx" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
|
</html> |