Files
gogo2/TESTCASES/web/templates/components/annotation_list.html
2025-10-18 16:37:13 +03:00

189 lines
7.6 KiB
HTML

<div class="card annotation-list mb-3">
<div class="card-header d-flex justify-content-between align-items-center">
<h6 class="mb-0">
<i class="fas fa-tags"></i>
Annotations
</h6>
<button class="btn btn-sm btn-outline-light" id="export-annotations-btn" title="Export">
<i class="fas fa-download"></i>
</button>
</div>
<div class="card-body p-2">
<div class="list-group list-group-flush" id="annotations-list">
<!-- Annotations will be dynamically added here -->
<div class="text-center text-muted py-3" id="no-annotations-msg">
<i class="fas fa-info-circle"></i>
<p class="mb-0 small">No annotations yet</p>
<p class="mb-0 small">Click on charts to create</p>
</div>
</div>
</div>
</div>
<script>
// Export annotations
document.getElementById('export-annotations-btn').addEventListener('click', function() {
fetch('/api/export-annotations', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
symbol: appState.currentSymbol,
format: 'json'
})
})
.then(response => response.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `annotations_${appState.currentSymbol}_${Date.now()}.json`;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
a.remove();
showSuccess('Annotations exported successfully');
})
.catch(error => {
showError('Failed to export annotations: ' + error.message);
});
});
// Function to render annotations list
function renderAnnotationsList(annotations) {
const listContainer = document.getElementById('annotations-list');
const noAnnotationsMsg = document.getElementById('no-annotations-msg');
if (annotations.length === 0) {
noAnnotationsMsg.style.display = 'block';
return;
}
noAnnotationsMsg.style.display = 'none';
// Clear existing items (except the no-annotations message)
Array.from(listContainer.children).forEach(child => {
if (child.id !== 'no-annotations-msg') {
child.remove();
}
});
// Add annotation items
annotations.forEach(annotation => {
const item = document.createElement('div');
item.className = 'list-group-item list-group-item-action p-2';
item.setAttribute('data-annotation-id', annotation.annotation_id);
const profitClass = annotation.profit_loss_pct >= 0 ? 'text-success' : 'text-danger';
const directionIcon = annotation.direction === 'LONG' ? 'fa-arrow-up' : 'fa-arrow-down';
item.innerHTML = `
<div class="d-flex justify-content-between align-items-start">
<div class="flex-grow-1">
<div class="d-flex align-items-center mb-1">
<i class="fas ${directionIcon} me-1"></i>
<strong class="small">${annotation.direction}</strong>
<span class="badge bg-secondary ms-2 small">${annotation.timeframe}</span>
</div>
<div class="small text-muted">
${new Date(annotation.entry.timestamp).toLocaleString()}
</div>
<div class="small ${profitClass} fw-bold">
${annotation.profit_loss_pct >= 0 ? '+' : ''}${annotation.profit_loss_pct.toFixed(2)}%
</div>
</div>
<div class="btn-group-vertical btn-group-sm">
<button class="btn btn-sm btn-outline-primary view-annotation-btn" title="View">
<i class="fas fa-eye"></i>
</button>
<button class="btn btn-sm btn-outline-success generate-testcase-btn" title="Generate Test Case">
<i class="fas fa-file-code"></i>
</button>
<button class="btn btn-sm btn-outline-danger delete-annotation-btn" title="Delete">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
`;
// Add event listeners
item.querySelector('.view-annotation-btn').addEventListener('click', function(e) {
e.stopPropagation();
viewAnnotation(annotation);
});
item.querySelector('.generate-testcase-btn').addEventListener('click', function(e) {
e.stopPropagation();
generateTestCase(annotation.annotation_id);
});
item.querySelector('.delete-annotation-btn').addEventListener('click', function(e) {
e.stopPropagation();
deleteAnnotation(annotation.annotation_id);
});
listContainer.appendChild(item);
});
// Update annotation count
document.getElementById('annotation-count').textContent = annotations.length;
}
function viewAnnotation(annotation) {
// Navigate to annotation time and highlight it
if (appState.timeNavigator) {
appState.timeNavigator.navigateToTime(new Date(annotation.entry.timestamp).getTime());
}
if (appState.chartManager) {
appState.chartManager.highlightAnnotation(annotation.annotation_id);
}
}
function generateTestCase(annotationId) {
fetch('/api/generate-test-case', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({annotation_id: annotationId})
})
.then(response => response.json())
.then(data => {
if (data.success) {
showSuccess('Test case generated successfully');
} else {
showError('Failed to generate test case: ' + data.error.message);
}
})
.catch(error => {
showError('Network error: ' + error.message);
});
}
function deleteAnnotation(annotationId) {
if (!confirm('Are you sure you want to 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 UI
appState.annotations = appState.annotations.filter(a => a.annotation_id !== annotationId);
renderAnnotationsList(appState.annotations);
if (appState.chartManager) {
appState.chartManager.removeAnnotation(annotationId);
}
showSuccess('Annotation deleted');
} else {
showError('Failed to delete annotation: ' + data.error.message);
}
})
.catch(error => {
showError('Network error: ' + error.message);
});
}
</script>