Stabilize live task detail page
This commit is contained in:
@@ -106,23 +106,61 @@ func renderTaskDetailPage(opts HandlerOptions, task Task) string {
|
|||||||
body.WriteString(`</div></div>`)
|
body.WriteString(`</div></div>`)
|
||||||
body.WriteString(`<script>
|
body.WriteString(`<script>
|
||||||
function cancelTaskDetail(id) {
|
function cancelTaskDetail(id) {
|
||||||
fetch('/api/tasks/' + id + '/cancel', {method:'POST'}).then(function(){ window.location.reload(); });
|
fetch('/api/tasks/' + id + '/cancel', {method:'POST'}).then(function(){
|
||||||
|
var term = document.getElementById('task-live-log');
|
||||||
|
if (term) {
|
||||||
|
term.textContent += '\nCancel requested.\n';
|
||||||
|
term.scrollTop = term.scrollHeight;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function renderTaskLiveCharts(taskId, charts) {
|
||||||
|
const host = document.getElementById('task-live-charts');
|
||||||
|
if (!host) return;
|
||||||
|
if (!Array.isArray(charts) || charts.length === 0) {
|
||||||
|
host.innerHTML = 'Waiting for metric samples...';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const seen = {};
|
||||||
|
charts.forEach(function(chart) {
|
||||||
|
seen[chart.file] = true;
|
||||||
|
let img = host.querySelector('img[data-chart-file="' + chart.file + '"]');
|
||||||
|
if (img) {
|
||||||
|
const card = img.closest('.card');
|
||||||
|
if (card) {
|
||||||
|
const title = card.querySelector('.card-head');
|
||||||
|
if (title) title.textContent = chart.title;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const card = document.createElement('div');
|
||||||
|
card.className = 'card';
|
||||||
|
card.style.margin = '0';
|
||||||
|
card.innerHTML = '<div class="card-head"></div><div class="card-body" style="padding:12px"></div>';
|
||||||
|
card.querySelector('.card-head').textContent = chart.title;
|
||||||
|
const body = card.querySelector('.card-body');
|
||||||
|
img = document.createElement('img');
|
||||||
|
img.setAttribute('data-task-chart', '1');
|
||||||
|
img.setAttribute('data-chart-file', chart.file);
|
||||||
|
img.setAttribute('data-base-src', '/api/tasks/' + taskId + '/chart/' + chart.file);
|
||||||
|
img.src = '/api/tasks/' + taskId + '/chart/' + chart.file + '?t=' + Date.now();
|
||||||
|
img.style.width = '100%';
|
||||||
|
img.style.display = 'block';
|
||||||
|
img.style.borderRadius = '6px';
|
||||||
|
img.alt = chart.title;
|
||||||
|
body.appendChild(img);
|
||||||
|
host.appendChild(card);
|
||||||
|
});
|
||||||
|
Array.from(host.querySelectorAll('img[data-task-chart="1"]')).forEach(function(img) {
|
||||||
|
const file = img.getAttribute('data-chart-file') || '';
|
||||||
|
if (seen[file]) return;
|
||||||
|
const card = img.closest('.card');
|
||||||
|
if (card) card.remove();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function loadTaskLiveCharts(taskId) {
|
function loadTaskLiveCharts(taskId) {
|
||||||
fetch('/api/tasks/' + taskId + '/charts').then(function(r){ return r.json(); }).then(function(charts){
|
fetch('/api/tasks/' + taskId + '/charts').then(function(r){ return r.json(); }).then(function(charts){
|
||||||
const host = document.getElementById('task-live-charts');
|
renderTaskLiveCharts(taskId, charts);
|
||||||
if (!host) return;
|
|
||||||
if (!Array.isArray(charts) || charts.length === 0) {
|
|
||||||
host.innerHTML = 'Waiting for metric samples...';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
host.innerHTML = charts.map(function(chart) {
|
|
||||||
return '<div class="card" style="margin:0">' +
|
|
||||||
'<div class="card-head">' + chart.title + '</div>' +
|
|
||||||
'<div class="card-body" style="padding:12px">' +
|
|
||||||
'<img data-task-chart="1" data-base-src="/api/tasks/' + taskId + '/chart/' + chart.file + '" src="/api/tasks/' + taskId + '/chart/' + chart.file + '?t=' + Date.now() + '" style="width:100%;display:block;border-radius:6px" alt="' + chart.title + '">' +
|
|
||||||
'</div></div>';
|
|
||||||
}).join('');
|
|
||||||
}).catch(function(){
|
}).catch(function(){
|
||||||
const host = document.getElementById('task-live-charts');
|
const host = document.getElementById('task-live-charts');
|
||||||
if (host) host.innerHTML = 'Task charts are unavailable.';
|
if (host) host.innerHTML = 'Task charts are unavailable.';
|
||||||
@@ -138,12 +176,31 @@ function refreshTaskLiveCharts() {
|
|||||||
var _taskDetailES = new EventSource('/api/tasks/` + html.EscapeString(task.ID) + `/stream');
|
var _taskDetailES = new EventSource('/api/tasks/` + html.EscapeString(task.ID) + `/stream');
|
||||||
var _taskDetailTerm = document.getElementById('task-live-log');
|
var _taskDetailTerm = document.getElementById('task-live-log');
|
||||||
var _taskChartTimer = null;
|
var _taskChartTimer = null;
|
||||||
|
var _taskChartsFrozen = false;
|
||||||
_taskDetailES.onopen = function(){ _taskDetailTerm.textContent = ''; };
|
_taskDetailES.onopen = function(){ _taskDetailTerm.textContent = ''; };
|
||||||
_taskDetailES.onmessage = function(e){ _taskDetailTerm.textContent += e.data + "\n"; _taskDetailTerm.scrollTop = _taskDetailTerm.scrollHeight; };
|
_taskDetailES.onmessage = function(e){ _taskDetailTerm.textContent += e.data + "\n"; _taskDetailTerm.scrollTop = _taskDetailTerm.scrollHeight; };
|
||||||
_taskDetailES.addEventListener('done', function(){ if (_taskChartTimer) clearInterval(_taskChartTimer); _taskDetailES.close(); setTimeout(function(){ window.location.reload(); }, 1000); });
|
_taskDetailES.addEventListener('done', function(e){
|
||||||
_taskDetailES.onerror = function(){ if (_taskChartTimer) clearInterval(_taskChartTimer); _taskDetailES.close(); };
|
if (_taskChartTimer) clearInterval(_taskChartTimer);
|
||||||
|
_taskDetailES.close();
|
||||||
|
_taskDetailES = null;
|
||||||
|
_taskChartsFrozen = true;
|
||||||
|
_taskDetailTerm.textContent += (e.data ? '\nTask finished with error.\n' : '\nTask finished.\n');
|
||||||
|
_taskDetailTerm.scrollTop = _taskDetailTerm.scrollHeight;
|
||||||
|
refreshTaskLiveCharts();
|
||||||
|
});
|
||||||
|
_taskDetailES.onerror = function(){
|
||||||
|
if (_taskChartTimer) clearInterval(_taskChartTimer);
|
||||||
|
if (_taskDetailES) {
|
||||||
|
_taskDetailES.close();
|
||||||
|
_taskDetailES = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
loadTaskLiveCharts('` + html.EscapeString(task.ID) + `');
|
loadTaskLiveCharts('` + html.EscapeString(task.ID) + `');
|
||||||
_taskChartTimer = setInterval(function(){ refreshTaskLiveCharts(); loadTaskLiveCharts('` + html.EscapeString(task.ID) + `'); }, 2000);
|
_taskChartTimer = setInterval(function(){
|
||||||
|
if (_taskChartsFrozen) return;
|
||||||
|
loadTaskLiveCharts('` + html.EscapeString(task.ID) + `');
|
||||||
|
refreshTaskLiveCharts();
|
||||||
|
}, 2000);
|
||||||
</script>`)
|
</script>`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user