Files
gogo2/ANNOTATE/web/templates/annotation_dashboard.html
2025-10-18 23:44:02 +03:00

302 lines
12 KiB
HTML

{% extends "base_layout.html" %}
{% block title %}Trade Annotation Dashboard{% endblock %}
{% block content %}
<!-- Live Mode Banner -->
<div id="live-mode-banner" class="alert alert-success mb-0" style="display: none; border-radius: 0;">
<div class="container-fluid">
<div class="d-flex align-items-center justify-content-between">
<div>
<span class="badge bg-danger me-2">🔴 LIVE</span>
<strong>Real-Time Inference Active</strong>
<span class="ms-3 small">Charts updating with live data every second</span>
</div>
<div>
<span class="badge bg-light text-dark" id="live-update-count">0 updates</span>
</div>
</div>
</div>
</div>
<div class="row mt-3">
<!-- Left Sidebar - Controls -->
<div class="col-md-2">
{% include 'components/control_panel.html' %}
</div>
<!-- Main Chart Area -->
<div class="col-md-8">
{% include 'components/chart_panel.html' %}
</div>
<!-- Right Sidebar - Annotations & Training -->
<div class="col-md-2">
{% include 'components/annotation_list.html' %}
{% include 'components/training_panel.html' %}
</div>
</div>
<!-- Inference Simulation Modal -->
<div class="modal fade" id="inferenceModal" tabindex="-1">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<i class="fas fa-brain"></i>
Inference Simulation
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
{% include 'components/inference_panel.html' %}
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
// Initialize application state
window.appState = {
currentSymbol: '{{ current_symbol }}',
currentTimeframes: {{ timeframes | tojson }},
annotations: {{ annotations | tojson }},
pendingAnnotation: null,
chartManager: null,
annotationManager: null,
timeNavigator: null,
trainingController: null
};
// Initialize components when DOM is ready
document.addEventListener('DOMContentLoaded', function() {
// Initialize chart manager
window.appState.chartManager = new ChartManager('chart-container', window.appState.currentTimeframes);
// Initialize annotation manager
window.appState.annotationManager = new AnnotationManager(window.appState.chartManager);
// Initialize time navigator
window.appState.timeNavigator = new TimeNavigator(window.appState.chartManager);
// Initialize training controller
window.appState.trainingController = new TrainingController();
// Load initial data
loadInitialData();
// Setup keyboard shortcuts
setupKeyboardShortcuts();
// Setup global functions
setupGlobalFunctions();
});
function loadInitialData() {
// Fetch initial chart data
fetch('/api/chart-data', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
symbol: appState.currentSymbol,
timeframes: appState.currentTimeframes,
start_time: null,
end_time: null
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
window.appState.chartManager.initializeCharts(data.chart_data);
// Load existing annotations
console.log('Loading', window.appState.annotations.length, 'existing annotations');
window.appState.annotations.forEach(annotation => {
window.appState.chartManager.addAnnotation(annotation);
});
// Update annotation list
if (typeof renderAnnotationsList === 'function') {
renderAnnotationsList(window.appState.annotations);
}
} else {
showError('Failed to load chart data: ' + data.error.message);
}
})
.catch(error => {
showError('Network error: ' + error.message);
});
}
function setupKeyboardShortcuts() {
document.addEventListener('keydown', function(e) {
// Arrow left - navigate backward
if (e.key === 'ArrowLeft') {
e.preventDefault();
if (window.appState.timeNavigator) {
window.appState.timeNavigator.scrollBackward();
}
}
// Arrow right - navigate forward
else if (e.key === 'ArrowRight') {
e.preventDefault();
if (window.appState.timeNavigator) {
window.appState.timeNavigator.scrollForward();
}
}
// Space - mark point (if chart is focused)
else if (e.key === ' ' && e.target.tagName !== 'INPUT') {
e.preventDefault();
// Trigger mark at current crosshair position
if (window.appState.annotationManager) {
window.appState.annotationManager.markCurrentPosition();
}
}
// Escape - cancel pending annotation
else if (e.key === 'Escape') {
e.preventDefault();
if (window.appState.annotationManager) {
window.appState.annotationManager.pendingAnnotation = null;
document.getElementById('pending-annotation-status').style.display = 'none';
showSuccess('Annotation cancelled');
}
}
// Enter - complete annotation (if pending)
else if (e.key === 'Enter' && e.target.tagName !== 'INPUT') {
e.preventDefault();
if (window.appState.annotationManager && window.appState.annotationManager.pendingAnnotation) {
showSuccess('Click on chart to mark exit point');
}
}
});
}
function showError(message) {
// Create toast notification
const toast = document.createElement('div');
toast.className = 'toast align-items-center text-white bg-danger border-0';
toast.setAttribute('role', 'alert');
toast.innerHTML = `
<div class="d-flex">
<div class="toast-body">
<i class="fas fa-exclamation-circle"></i>
${message}
</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
</div>
`;
// Add to page and show
document.body.appendChild(toast);
const bsToast = new bootstrap.Toast(toast);
bsToast.show();
// Remove after hidden
toast.addEventListener('hidden.bs.toast', () => toast.remove());
}
function showSuccess(message) {
const toast = document.createElement('div');
toast.className = 'toast align-items-center text-white bg-success border-0';
toast.setAttribute('role', 'alert');
toast.innerHTML = `
<div class="d-flex">
<div class="toast-body">
<i class="fas fa-check-circle"></i>
${message}
</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
</div>
`;
document.body.appendChild(toast);
const bsToast = new bootstrap.Toast(toast);
bsToast.show();
toast.addEventListener('hidden.bs.toast', () => toast.remove());
}
function setupGlobalFunctions() {
// Make functions globally available
window.showError = showError;
window.showSuccess = showSuccess;
window.renderAnnotationsList = renderAnnotationsList;
window.deleteAnnotation = deleteAnnotation;
window.highlightAnnotation = highlightAnnotation;
}
function renderAnnotationsList(annotations) {
const listElement = document.getElementById('annotations-list');
if (!listElement) return;
listElement.innerHTML = '';
annotations.forEach(annotation => {
const item = document.createElement('div');
item.className = 'annotation-item mb-2 p-2 border rounded';
item.innerHTML = `
<div class="d-flex justify-content-between align-items-center">
<div>
<small class="text-muted">${annotation.timeframe}</small>
<div class="fw-bold ${annotation.profit_loss_pct >= 0 ? 'text-success' : 'text-danger'}">
${annotation.direction} ${annotation.profit_loss_pct >= 0 ? '+' : ''}${annotation.profit_loss_pct.toFixed(2)}%
</div>
<small class="text-muted">
${new Date(annotation.entry.timestamp).toLocaleString()}
</small>
</div>
<div class="btn-group btn-group-sm">
<button class="btn btn-outline-primary btn-sm" onclick="highlightAnnotation('${annotation.annotation_id}')" title="Highlight">
<i class="fas fa-eye"></i>
</button>
<button class="btn btn-outline-danger btn-sm" onclick="deleteAnnotation('${annotation.annotation_id}')" title="Delete">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
`;
listElement.appendChild(item);
});
}
function deleteAnnotation(annotationId) {
if (!confirm('Delete this annotation?')) return;
fetch('/api/delete-annotation', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({annotation_id: annotationId})
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Remove from app state
window.appState.annotations = window.appState.annotations.filter(a => a.annotation_id !== annotationId);
// Update UI
renderAnnotationsList(window.appState.annotations);
// Remove from chart
if (window.appState.chartManager) {
window.appState.chartManager.removeAnnotation(annotationId);
}
showSuccess('Annotation deleted');
} else {
showError('Failed to delete annotation: ' + data.error.message);
}
})
.catch(error => {
showError('Network error: ' + error.message);
});
}
function highlightAnnotation(annotationId) {
if (window.appState.chartManager) {
window.appState.chartManager.highlightAnnotation(annotationId);
}
}
</script>
{% endblock %}