/home/devscfvi/app.devsquantum.com/js/history.js
// ============================================
// TRADE HISTORY & MANAGEMENT
// ============================================
// Exit screenshot state (per trade)
const exitScreenshotState = {};
// Render History View
function renderHistory() {
const trades = state.trades || [];
const hasFilters = false; // We'll add filters later
return `
<div class="min-h-screen p-4">
<div class="max-w-2xl mx-auto">
<!-- Header -->
<div class="flex items-center justify-between mb-6 pt-4">
<div>
<h1 class="text-2xl font-bold text-white">Trade History</h1>
<p class="text-sm text-violet-300">${trades.length} total trades</p>
</div>
<div class="flex gap-2">
${trades.length > 0 ? `
<button onclick="exportTrades()"
class="bg-green-600 hover:bg-green-700 text-white p-3 rounded-xl shadow-lg active:scale-95 transition">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
</button>
` : ''}
<button onclick="showFilterMenu()"
class="bg-violet-600 hover:bg-violet-700 text-white p-3 rounded-xl shadow-lg active:scale-95 transition">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"></path>
</svg>
</button>
</div>
</div>
<!-- Trade List -->
${trades.length === 0 ? renderEmptyTrades() : renderTradesList(trades)}
</div>
</div>
`;
}
// Empty state
function renderEmptyTrades() {
return `
<div class="glass rounded-3xl p-12 text-center border border-violet-500/20">
<svg class="w-16 h-16 mx-auto text-violet-400/50 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
<p class="text-lg text-slate-400 mb-2">No trades yet</p>
<p class="text-sm text-slate-500 mb-4">Start by calculating and saving your first trade</p>
<button onclick="navigateTo('calculator')"
class="gradient-bg text-white px-6 py-3 rounded-xl font-semibold active:scale-95 transition">
Go to Calculator
</button>
</div>
`;
}
// Render trades list
function renderTradesList(trades) {
return `
<div class="space-y-3">
${trades.map(trade => renderTradeCard(trade)).join('')}
</div>
`;
}
// Render individual trade card
function renderTradeCard(trade) {
const statusColor = {
'win': 'green',
'loss': 'red',
'pending': 'yellow'
}[trade.status] || 'slate';
const statusBadge = {
'win': '<span class="px-2 py-1 bg-green-500/20 text-green-400 text-xs font-semibold rounded-full">WIN</span>',
'loss': '<span class="px-2 py-1 bg-red-500/20 text-red-400 text-xs font-semibold rounded-full">LOSS</span>',
'pending': '<span class="px-2 py-1 bg-yellow-500/20 text-yellow-400 text-xs font-semibold rounded-full">PENDING</span>'
}[trade.status] || '<span class="px-2 py-1 bg-slate-500/20 text-slate-400 text-xs font-semibold rounded-full">UNKNOWN</span>';
return `
<div class="glass rounded-2xl p-5 border border-violet-500/20 fade-in">
<!-- Header -->
<div class="flex justify-between items-start mb-3">
<div class="flex-1">
<div class="flex items-center gap-2 flex-wrap mb-1">
<h3 class="text-xl font-bold text-white">${trade.coin}</h3>
${trade.order_type ? `<span class="px-2 py-1 bg-blue-500/20 text-blue-400 text-xs font-semibold rounded-full uppercase">${trade.order_type}</span>` : ''}
${statusBadge}
${trade.trade_category ? `<span class="category-badge category-${trade.trade_category}">${getCategoryLabel(trade.trade_category)}</span>` : ''}
</div>
<p class="text-sm text-violet-300">${formatDateTime(trade.created_at)}</p>
<p class="text-sm text-violet-400 mt-1">R:R 1:${trade.reward_risk_ratio}</p>
${trade.emotion ? `<span class="emotion-badge emotion-${trade.emotion} mt-2 inline-block">${getEmotionLabel(trade.emotion)}</span>` : ''}
</div>
<!-- Actions -->
<div class="flex gap-2">
${trade.status === 'open' || !trade.status ? `
<button onclick='openClosingTradeModal(${JSON.stringify(trade).replace(/'/g, "\\'")})'
class="text-green-400 hover:text-green-300 p-2 transition"
title="Close Trade with P&L">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</button>
` : ''}
<button onclick='openTradeModal(${JSON.stringify(trade).replace(/'/g, "\\'")})'
class="text-blue-400 hover:text-blue-300 p-2 transition">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
</svg>
</button>
<button onclick="confirmDeleteTrade(${trade.id})"
class="text-red-400 hover:text-red-300 p-2 transition">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
</svg>
</button>
</div>
</div>
<!-- Pre-Trade Plan -->
${trade.pre_trade_plan ? `
<div class="mb-3 p-3 bg-blue-500/10 rounded-lg border border-blue-500/20">
<p class="text-xs text-blue-300 mb-1 font-semibold">📝 Pre-Trade Plan:</p>
<p class="text-sm text-white">${trade.pre_trade_plan}</p>
</div>
` : ''}
<!-- Comment -->
${trade.comment ? `
<div class="mb-3 p-3 bg-slate-900/50 rounded-lg">
<p class="text-xs text-slate-400 mb-1">💬 Comment:</p>
<p class="text-sm text-white">${trade.comment}</p>
</div>
` : ''}
<!-- Post-Trade Review -->
${trade.post_trade_review ? `
<div class="mb-3 p-3 bg-violet-500/10 rounded-lg border border-violet-500/20">
<p class="text-xs text-violet-300 mb-1 font-semibold">📊 Post-Trade Review:</p>
<p class="text-sm text-white">${trade.post_trade_review}</p>
${trade.discipline_score ? `<p class="text-xs text-violet-400 mt-2">Discipline: ${trade.discipline_score}/10</p>` : ''}
</div>
` : ''}
<!-- Mistake -->
${trade.mistake_type ? `
<div class="mb-3 p-3 bg-red-500/10 rounded-lg border border-red-500/20">
<p class="text-xs text-red-300 mb-1 font-semibold">⚠️ Mistake:</p>
<p class="text-sm text-white">${trade.mistake_type}</p>
${trade.lesson_learned ? `<p class="text-xs text-red-300 mt-2">💡 Lesson: ${trade.lesson_learned}</p>` : ''}
</div>
` : ''}
<!-- Screenshots -->
${(trade.entry_screenshot || trade.exit_screenshot) ? `
<div class="mb-3 grid ${trade.entry_screenshot && trade.exit_screenshot ? 'grid-cols-2' : 'grid-cols-1'} gap-3">
${trade.entry_screenshot ? `
<div class="bg-slate-900/30 rounded-lg overflow-hidden border border-violet-500/20">
<img src="${trade.entry_screenshot}"
onclick='viewScreenshot(${JSON.stringify(trade.entry_screenshot)})'
class="w-full h-40 object-cover cursor-pointer hover:opacity-80 transition"
alt="Entry Screenshot">
<p class="text-xs text-center text-slate-400 py-2">📸 Entry</p>
</div>
` : ''}
${trade.exit_screenshot ? `
<div class="bg-slate-900/30 rounded-lg overflow-hidden border border-violet-500/20">
<img src="${trade.exit_screenshot}"
onclick='viewScreenshot(${JSON.stringify(trade.exit_screenshot)})'
class="w-full h-40 object-cover cursor-pointer hover:opacity-80 transition"
alt="Exit Screenshot">
<p class="text-xs text-center text-slate-400 py-2">📸 Exit</p>
</div>
` : ''}
</div>
` : ''}
<!-- Trade Stats Grid -->
<div class="grid grid-cols-2 md:grid-cols-4 gap-3 text-sm">
<div class="bg-slate-900/50 rounded-lg p-3">
<p class="text-slate-400 text-xs mb-1">Position</p>
<p class="text-white font-semibold">$${trade.position_size}</p>
</div>
<div class="bg-slate-900/50 rounded-lg p-3">
<p class="text-slate-400 text-xs mb-1">Entry</p>
<p class="text-white font-semibold">$${trade.entry_price}</p>
</div>
<div class="bg-red-500/10 rounded-lg p-3 border border-red-500/20">
<p class="text-red-300 text-xs mb-1">Max Loss</p>
<p class="text-red-400 font-semibold">-$${trade.loss_amount}</p>
</div>
<div class="bg-green-500/10 rounded-lg p-3 border border-green-500/20">
<p class="text-green-300 text-xs mb-1">Max Profit</p>
<p class="text-green-400 font-semibold">+$${trade.profit_amount}</p>
</div>
</div>
</div>
`;
}
// Open trade modal for editing
function openTradeModal(trade) {
const modalContent = `
<div class="mb-4">
<div class="flex items-center justify-between">
<h2 class="text-2xl font-bold text-white">Edit Trade: ${trade.coin}</h2>
<button onclick="closeAllModals()" class="text-slate-400 hover:text-white p-2">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<p class="text-violet-300 text-sm mt-1">${formatDateTime(trade.created_at)}</p>
</div>
<!-- Risk/Reward Display -->
<div class="bg-violet-500/20 rounded-2xl p-4 border border-violet-500/30 mb-4">
<div class="flex items-center justify-between">
<span class="text-sm text-violet-300">Risk/Reward Ratio</span>
<span class="text-2xl font-bold text-white">1:${trade.reward_risk_ratio}</span>
</div>
</div>
<!-- Edit Prices -->
<div class="grid grid-cols-2 gap-4 mb-4">
<div>
<label class="block text-sm font-medium text-red-300 mb-2">Stop Loss Price</label>
<input type="number" id="edit-sl-${trade.id}" value="${trade.sl_price}" step="0.01"
class="w-full px-4 py-3 bg-slate-900/50 border border-red-500/30 rounded-xl text-white placeholder-slate-500 focus:border-red-400 focus:ring-2 focus:ring-red-400/20">
</div>
<div>
<label class="block text-sm font-medium text-green-300 mb-2">Take Profit Price</label>
<input type="number" id="edit-tp-${trade.id}" value="${trade.tp_price}" step="0.01"
class="w-full px-4 py-3 bg-slate-900/50 border border-green-500/30 rounded-xl text-white placeholder-slate-500 focus:border-green-400 focus:ring-2 focus:ring-green-400/20">
</div>
</div>
<button onclick="updateTradePrices(${trade.id})"
class="w-full bg-blue-600 hover:bg-blue-700 text-white py-3 rounded-xl font-semibold active:scale-95 transition mb-4">
Update Prices
</button>
<!-- Update Status -->
<div class="mb-4">
<label class="block text-sm font-medium text-violet-200 mb-2">Trade Status</label>
<div class="grid grid-cols-3 gap-2">
<button onclick="updateStatus(${trade.id}, 'win')"
class="bg-green-600 hover:bg-green-700 text-white py-3 rounded-xl font-semibold active:scale-95 transition">
Win
</button>
<button onclick="updateStatus(${trade.id}, 'loss')"
class="bg-red-600 hover:bg-red-700 text-white py-3 rounded-xl font-semibold active:scale-95 transition">
Loss
</button>
<button onclick="updateStatus(${trade.id}, 'pending')"
class="bg-slate-600 hover:bg-slate-700 text-white py-3 rounded-xl font-semibold active:scale-95 transition">
Pending
</button>
</div>
</div>
<!-- Post-Trade Review -->
<div class="mb-4">
<label class="block text-sm font-medium text-violet-200 mb-2">Post-Trade Review</label>
<textarea id="review-${trade.id}"
class="w-full px-4 py-3 bg-slate-900/50 border border-violet-500/30 rounded-xl text-white placeholder-slate-500 focus:border-violet-400 focus:ring-2 focus:ring-violet-400/20"
rows="3"
placeholder="What happened? Did you follow your plan?">${trade.post_trade_review || ''}</textarea>
</div>
<!-- Discipline Score -->
<div class="mb-4">
<label class="block text-sm font-medium text-violet-200 mb-2">Discipline Score (1-10)</label>
<input type="number" id="discipline-${trade.id}" value="${trade.discipline_score || ''}" min="1" max="10"
class="w-full px-4 py-3 bg-slate-900/50 border border-violet-500/30 rounded-xl text-white placeholder-slate-500 focus:border-violet-400 focus:ring-2 focus:ring-violet-400/20"
placeholder="How well did you stick to your plan?">
</div>
<!-- Mistake Type -->
<div class="mb-4">
<label class="block text-sm font-medium text-violet-200 mb-2">Mistake (if any)</label>
<select id="mistake-${trade.id}"
class="w-full px-4 py-3 bg-slate-900/50 border border-violet-500/30 rounded-xl text-white focus:border-violet-400 focus:ring-2 focus:ring-violet-400/20">
<option value="">No mistake</option>
<option value="Entered too early" ${trade.mistake_type === 'Entered too early' ? 'selected' : ''}>Entered too early</option>
<option value="Entered too late" ${trade.mistake_type === 'Entered too late' ? 'selected' : ''}>Entered too late</option>
<option value="Ignored stop loss" ${trade.mistake_type === 'Ignored stop loss' ? 'selected' : ''}>Ignored stop loss</option>
<option value="Moved stop loss" ${trade.mistake_type === 'Moved stop loss' ? 'selected' : ''}>Moved stop loss</option>
<option value="Took profit too early" ${trade.mistake_type === 'Took profit too early' ? 'selected' : ''}>Took profit too early</option>
<option value="Greed - held too long" ${trade.mistake_type === 'Greed - held too long' ? 'selected' : ''}>Greed - held too long</option>
<option value="FOMO entry" ${trade.mistake_type === 'FOMO entry' ? 'selected' : ''}>FOMO entry</option>
<option value="Revenge trading" ${trade.mistake_type === 'Revenge trading' ? 'selected' : ''}>Revenge trading</option>
<option value="Overleveraged" ${trade.mistake_type === 'Overleveraged' ? 'selected' : ''}>Overleveraged</option>
<option value="No clear plan" ${trade.mistake_type === 'No clear plan' ? 'selected' : ''}>No clear plan</option>
<option value="Ignored trend" ${trade.mistake_type === 'Ignored trend' ? 'selected' : ''}>Ignored trend</option>
<option value="Poor risk/reward" ${trade.mistake_type === 'Poor risk/reward' ? 'selected' : ''}>Poor risk/reward</option>
</select>
</div>
<!-- Lesson Learned -->
<div class="mb-4">
<label class="block text-sm font-medium text-violet-200 mb-2">Lesson Learned</label>
<textarea id="lesson-${trade.id}"
class="w-full px-4 py-3 bg-slate-900/50 border border-violet-500/30 rounded-xl text-white placeholder-slate-500 focus:border-violet-400 focus:ring-2 focus:ring-violet-400/20"
rows="2"
placeholder="What did you learn from this trade?">${trade.lesson_learned || ''}</textarea>
</div>
<!-- Comments -->
<div class="mb-4">
<label class="block text-sm font-medium text-violet-200 mb-2">Comments</label>
<textarea id="comment-${trade.id}"
class="w-full px-4 py-3 bg-slate-900/50 border border-violet-500/30 rounded-xl text-white placeholder-slate-500 focus:border-violet-400 focus:ring-2 focus:ring-violet-400/20"
rows="2"
placeholder="Additional notes...">${trade.comment || ''}</textarea>
</div>
<!-- Exit Screenshot Upload -->
<div class="mb-4 p-4 bg-slate-900/30 rounded-xl border border-violet-500/20">
<label class="block text-sm font-medium text-violet-200 mb-3">
📸 Exit Screenshot (Optional)
</label>
<p class="text-xs text-slate-400 mb-3">Upload a screenshot of your chart at exit</p>
${trade.exit_screenshot ? `
<div class="mb-3">
<img src="${trade.exit_screenshot}"
class="w-full rounded-lg border-2 border-violet-500/30 mb-2"
alt="Current Exit Screenshot">
<p class="text-xs text-green-400">✓ Exit screenshot already uploaded</p>
</div>
` : ''}
<input type="file"
id="exit-screenshot-input-${trade.id}"
accept="image/*"
onchange="handleExitScreenshot(${trade.id}, this)"
class="hidden">
<div id="exit-screenshot-preview-${trade.id}" class="hidden mb-3">
<img id="exit-screenshot-img-${trade.id}"
class="w-full rounded-lg border-2 border-violet-500/30 mb-2"
alt="Exit Screenshot Preview">
<button onclick="clearExitScreenshot(${trade.id})"
class="text-sm text-red-400 hover:text-red-300">
✕ Remove Screenshot
</button>
</div>
<button type="button"
onclick="document.getElementById('exit-screenshot-input-${trade.id}').click()"
class="w-full bg-violet-600 hover:bg-violet-700 text-white py-3 rounded-xl font-semibold active:scale-95 transition">
<span id="exit-screenshot-button-${trade.id}">📷 ${trade.exit_screenshot ? 'Replace' : 'Upload'} Exit Screenshot</span>
</button>
</div>
<!-- Save Review Button -->
<button onclick="saveTradeReview(${trade.id})"
class="w-full gradient-bg text-white py-4 rounded-xl font-bold text-lg shadow-lg active:scale-95 transition mb-3">
Save Changes
</button>
<button onclick="closeAllModals()"
class="w-full bg-slate-700 hover:bg-slate-600 text-white py-3 rounded-xl font-semibold active:scale-95 transition">
Close
</button>
`;
createModal(modalContent);
}
// Update trade prices
async function updateTradePrices(tradeId) {
const slPrice = document.getElementById(`edit-sl-${tradeId}`).value;
const tpPrice = document.getElementById(`edit-tp-${tradeId}`).value;
const result = await apiPost('updateTradePrices', { id: tradeId, slPrice, tpPrice });
if (result.success) {
showToast('Prices updated successfully!', 'success');
await loadTrades();
closeAllModals();
render();
} else {
showToast('Failed to update prices', 'error');
}
}
// Update trade status
async function updateStatus(tradeId, status) {
const comment = document.getElementById(`comment-${tradeId}`)?.value || '';
const result = await apiPost('updateTradeStatus', { id: tradeId, status, comment });
if (result.success) {
showToast(`Trade marked as ${status}!`, 'success');
await loadTrades();
await loadStats();
closeAllModals();
render();
} else {
showToast('Failed to update status', 'error');
}
}
// Save trade review
async function saveTradeReview(tradeId) {
const review = document.getElementById(`review-${tradeId}`).value;
const disciplineScore = document.getElementById(`discipline-${tradeId}`).value;
const mistakeType = document.getElementById(`mistake-${tradeId}`).value;
const lessonLearned = document.getElementById(`lesson-${tradeId}`).value;
const comment = document.getElementById(`comment-${tradeId}`).value;
const exitScreenshot = exitScreenshotState[tradeId] || null;
const result = await apiPost('updateTradeReview', {
id: tradeId,
review,
disciplineScore,
mistakeType,
lessonLearned,
exitScreenshot
});
if (result.success) {
// Also update comment if changed
await apiPost('updateTradeStatus', {
id: tradeId,
status: state.trades.find(t => t.id === tradeId)?.status || 'pending',
comment
});
// Clear screenshot state
delete exitScreenshotState[tradeId];
showToast('Review saved successfully!', 'success');
await loadTrades();
closeAllModals();
render();
} else {
showToast('Failed to save review', 'error');
}
}
// Confirm delete trade
function confirmDeleteTrade(tradeId) {
if (confirm('Are you sure you want to delete this trade? This action cannot be undone.')) {
deleteTrade(tradeId);
}
}
// Delete trade
async function deleteTrade(tradeId) {
const result = await apiPost('deleteTrade', { id: tradeId });
if (result.success) {
showToast('Trade deleted', 'success');
await loadTrades();
await loadStats();
render();
} else {
showToast('Failed to delete trade', 'error');
}
}
// Export trades
function exportTrades() {
if (!state.trades || state.trades.length === 0) {
showToast('No trades to export', 'info');
return;
}
const csv = [
['Date', 'Coin', 'Category', 'Order Type', 'Position', 'Entry', 'SL%', 'TP%', 'SL Price', 'TP Price',
'Max Loss', 'Max Profit', 'Fees', 'R:R', 'Status', 'Emotion', 'Discipline', 'Mistake', 'Pre-Trade Plan', 'Post-Trade Review', 'Lesson', 'Comment'],
...state.trades.map(t => [
t.created_at,
t.coin,
t.trade_category || '',
t.order_type || 'market',
t.position_size,
t.entry_price,
t.stop_loss_percent,
t.take_profit_percent,
t.sl_price,
t.tp_price,
t.loss_amount,
t.profit_amount,
t.fees || 0,
t.reward_risk_ratio,
t.status || 'pending',
t.emotion || '',
t.discipline_score || '',
t.mistake_type || '',
(t.pre_trade_plan || '').replace(/,/g, ';').replace(/\n/g, ' '),
(t.post_trade_review || '').replace(/,/g, ';').replace(/\n/g, ' '),
(t.lesson_learned || '').replace(/,/g, ';').replace(/\n/g, ' '),
(t.comment || '').replace(/,/g, ';').replace(/\n/g, ' ')
])
];
exportToCSV(csv, 'trades');
}
// Show filter menu (placeholder)
function showFilterMenu() {
showToast('Filters coming soon!', 'info');
}
// View screenshot in modal
function viewScreenshot(base64Image) {
const modal = createModal(`
<div class="text-center">
<div class="flex items-center justify-between mb-4">
<h3 class="text-xl font-bold text-white">Screenshot</h3>
<button onclick="closeAllModals()" class="text-slate-400 hover:text-white">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<img src="${base64Image}" class="max-w-full max-h-[70vh] mx-auto rounded-xl shadow-2xl">
<button onclick="closeAllModals()"
class="mt-4 bg-violet-600 hover:bg-violet-700 px-6 py-2 rounded-lg text-white font-semibold active:scale-95 transition">
Close
</button>
</div>
`, 'screenshot-modal');
}
// Open modal to close trade with P&L
function openClosingTradeModal(trade) {
const modalContent = `
<div class="mb-4">
<div class="flex items-center justify-between">
<h2 class="text-2xl font-bold text-white">Close Trade: ${trade.coin}</h2>
<button onclick="closeAllModals()" class="text-slate-400 hover:text-white p-2">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<p class="text-violet-300 text-sm mt-1">Enter your actual results</p>
</div>
<!-- Trade Info -->
<div class="bg-violet-500/10 rounded-xl p-4 mb-4 border border-violet-500/20">
<div class="grid grid-cols-2 gap-4 text-sm">
<div>
<p class="text-slate-400">Entry Price</p>
<p class="text-white font-semibold text-lg">$${trade.entry_price}</p>
</div>
<div>
<p class="text-slate-400">Position Size</p>
<p class="text-white font-semibold text-lg">$${trade.position_size}</p>
</div>
<div>
<p class="text-slate-400">Max Loss</p>
<p class="text-red-400 font-semibold">-$${trade.loss_amount}</p>
</div>
<div>
<p class="text-slate-400">Max Profit</p>
<p class="text-green-400 font-semibold">+$${trade.profit_amount}</p>
</div>
</div>
</div>
<!-- Result Selection -->
<div class="mb-4">
<label class="block text-sm font-medium text-violet-200 mb-2">Trade Result</label>
<div class="grid grid-cols-2 gap-3">
<button onclick="document.getElementById('status-win').checked = true; updatePnLSuggestion('win', ${trade.profit_amount})"
class="p-4 rounded-xl border-2 border-green-500/30 bg-green-500/10 hover:bg-green-500/20 transition">
<p class="text-green-400 font-bold text-lg">WIN ✓</p>
<p class="text-xs text-slate-400 mt-1">Profit taken</p>
</button>
<button onclick="document.getElementById('status-loss').checked = true; updatePnLSuggestion('loss', -${trade.loss_amount})"
class="p-4 rounded-xl border-2 border-red-500/30 bg-red-500/10 hover:bg-red-500/20 transition">
<p class="text-red-400 font-bold text-lg">LOSS ✗</p>
<p class="text-xs text-slate-400 mt-1">Stop loss hit</p>
</button>
</div>
<input type="radio" id="status-win" name="trade-status" value="win" class="hidden">
<input type="radio" id="status-loss" name="trade-status" value="loss" class="hidden">
</div>
<!-- Exit Price -->
<div class="mb-4">
<label class="block text-sm font-medium text-violet-200 mb-2">Actual Exit Price</label>
<input type="number"
id="actual-exit-price-${trade.id}"
step="0.01"
class="w-full px-4 py-3 bg-slate-900/50 border border-violet-500/30 rounded-xl text-white placeholder-slate-500 focus:border-violet-400 focus:ring-2 focus:ring-violet-400/20"
placeholder="Enter exit price">
</div>
<!-- Actual P&L -->
<div class="mb-4">
<label class="block text-sm font-medium text-violet-200 mb-2">Actual P&L ($)</label>
<input type="number"
id="actual-pnl-${trade.id}"
step="0.01"
class="w-full px-4 py-3 bg-slate-900/50 border border-violet-500/30 rounded-xl text-white placeholder-slate-500 focus:border-violet-400 focus:ring-2 focus:ring-violet-400/20"
placeholder="Enter profit (+) or loss (-)">
<p class="text-xs text-slate-400 mt-1">Positive for profit, negative for loss (e.g., 15.50 or -8.25)</p>
</div>
<!-- Budget Impact Preview -->
<div id="budget-impact" class="mb-4 p-4 bg-slate-900/50 rounded-xl border border-slate-700 hidden">
<p class="text-sm text-slate-400 mb-2">Budget Impact:</p>
<div class="flex items-center justify-between">
<span class="text-white">Current Budget:</span>
<span class="text-white font-semibold">$${state.user.budget || 0}</span>
</div>
<div class="flex items-center justify-between mt-1">
<span id="impact-label" class="text-white">New Budget:</span>
<span id="impact-value" class="font-bold text-lg"></span>
</div>
</div>
<!-- Close Trade Button -->
<button onclick="closeTrade(${trade.id})"
class="w-full gradient-bg text-white py-4 rounded-xl font-bold text-lg shadow-lg active:scale-95 transition">
Close Trade & Update Budget
</button>
`;
createModal(modalContent, 'close-trade-modal');
}
// Update P&L suggestion based on win/loss selection
function updatePnLSuggestion(status, suggestedAmount) {
const pnlInput = document.querySelector('[id^="actual-pnl-"]');
if (pnlInput && !pnlInput.value) {
pnlInput.value = suggestedAmount.toFixed(2);
updateBudgetImpact();
}
}
// Update budget impact preview
function updateBudgetImpact() {
const pnlInput = document.querySelector('[id^="actual-pnl-"]');
const budgetImpact = document.getElementById('budget-impact');
const impactValue = document.getElementById('impact-value');
if (pnlInput && pnlInput.value) {
const pnl = parseFloat(pnlInput.value);
const currentBudget = parseFloat(state.user.budget) || 0;
const newBudget = currentBudget + pnl;
budgetImpact.classList.remove('hidden');
impactValue.textContent = formatCurrency(newBudget);
impactValue.className = pnl >= 0 ? 'font-bold text-lg text-green-400' : 'font-bold text-lg text-red-400';
}
}
// Close trade with P&L
async function closeTrade(tradeId) {
const statusWin = document.getElementById('status-win');
const statusLoss = document.getElementById('status-loss');
const exitPriceInput = document.getElementById(`actual-exit-price-${tradeId}`);
const pnlInput = document.getElementById(`actual-pnl-${tradeId}`);
// Validate
if (!statusWin.checked && !statusLoss.checked) {
showToast('Please select WIN or LOSS', 'error');
return;
}
if (!exitPriceInput.value) {
showToast('Please enter exit price', 'error');
return;
}
if (!pnlInput.value) {
showToast('Please enter actual P&L', 'error');
return;
}
const status = statusWin.checked ? 'win' : 'loss';
const actualExitPrice = parseFloat(exitPriceInput.value);
const actualPnl = parseFloat(pnlInput.value);
// Validate P&L matches status
if (status === 'win' && actualPnl < 0) {
const confirm = window.confirm('You selected WIN but P&L is negative. Continue anyway?');
if (!confirm) return;
}
if (status === 'loss' && actualPnl > 0) {
const confirm = window.confirm('You selected LOSS but P&L is positive. Continue anyway?');
if (!confirm) return;
}
// Close trade
const result = await apiPost('closeTradeWithPnL', {
id: tradeId,
status: status,
actualExitPrice: actualExitPrice,
actualPnl: actualPnl
});
if (result.success) {
showToast(`Trade closed! Budget updated to ${formatCurrency(result.newBudget)}`, 'success');
closeAllModals();
// Reload data
await loadTrades();
await loadBudget();
render();
} else {
showToast('Failed to close trade: ' + (result.error || 'Unknown error'), 'error');
}
}
// Exit Screenshot Handling Functions
async function handleExitScreenshot(tradeId, input) {
if (input.files && input.files[0]) {
const file = input.files[0];
// Check file size (max 5MB)
if (file.size > 5 * 1024 * 1024) {
showToast('Image too large! Max 5MB', 'error');
return;
}
// Show preview immediately using FileReader
const reader = new FileReader();
reader.onload = function(e) {
const preview = document.getElementById(`exit-screenshot-preview-${tradeId}`);
const img = document.getElementById(`exit-screenshot-img-${tradeId}`);
if (preview && img) {
img.src = e.target.result;
preview.classList.remove('hidden');
}
};
reader.readAsDataURL(file);
// Upload file to server
const formData = new FormData();
formData.append('screenshot', file);
try {
showToast('Uploading screenshot...', 'info');
const response = await fetch('upload_screenshot.php', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.success) {
// Store file path instead of base64
exitScreenshotState[tradeId] = result.filepath;
// Update button text
const button = document.getElementById(`exit-screenshot-button-${tradeId}`);
if (button) {
button.textContent = '✓ Screenshot Uploaded';
}
showToast('Exit screenshot uploaded!', 'success');
} else {
showToast('Upload failed: ' + result.error, 'error');
clearExitScreenshot(tradeId);
}
} catch (error) {
showToast('Upload error: ' + error.message, 'error');
console.error('Screenshot upload error:', error);
clearExitScreenshot(tradeId);
}
}
}
function clearExitScreenshot(tradeId) {
delete exitScreenshotState[tradeId];
// Hide preview
const preview = document.getElementById(`exit-screenshot-preview-${tradeId}`);
if (preview) {
preview.classList.add('hidden');
}
// Clear file input
const input = document.getElementById(`exit-screenshot-input-${tradeId}`);
if (input) {
input.value = '';
}
// Reset button text
const button = document.getElementById(`exit-screenshot-button-${tradeId}`);
if (button) {
button.textContent = '📷 Upload Exit Screenshot';
}
showToast('Screenshot removed', 'success');
}
// Make functions global
window.renderHistory = renderHistory;
window.openTradeModal = openTradeModal;
window.updateTradePrices = updateTradePrices;
window.updateStatus = updateStatus;
window.saveTradeReview = saveTradeReview;
window.confirmDeleteTrade = confirmDeleteTrade;
window.deleteTrade = deleteTrade;
window.exportTrades = exportTrades;
window.showFilterMenu = showFilterMenu;
window.viewScreenshot = viewScreenshot;
window.handleExitScreenshot = handleExitScreenshot;
window.clearExitScreenshot = clearExitScreenshot;
window.openClosingTradeModal = openClosingTradeModal;
window.updatePnLSuggestion = updatePnLSuggestion;
window.updateBudgetImpact = updateBudgetImpact;
window.closeTrade = closeTrade;
// Add event listener for P&L input to update budget preview
document.addEventListener('input', function(e) {
if (e.target.id && e.target.id.startsWith('actual-pnl-')) {
updateBudgetImpact();
}
});