5
0
mirror of git://git.proxmox.com/git/proxmox-backup.git synced 2025-08-22 21:49:23 +03:00
Files
proxmox-backup/www/dashboard/TaskSummary.js
Thomas Lamprecht 099c260534 use ngettext where other UIs already have plurals
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-07-31 13:50:15 +02:00

300 lines
9.7 KiB
JavaScript

Ext.define('PBS.TaskSummary', {
extend: 'Ext.panel.Panel',
alias: 'widget.pbsTaskSummary',
title: gettext('Task Summary'),
states: ['', 'error', 'warning', 'ok'],
types: [
'backup',
'prune',
'garbage_collection',
'sync',
'verify',
'tape-backup',
'tape-restore',
],
typeFilterMap: {
verify: 'verif', // some verify task start with 'verification''
},
titles: {
backup: gettext('Backups'),
prune: gettext('Prunes'),
garbage_collection: gettext('Garbage collections'),
sync: gettext('Syncs'),
verify: gettext('Verify'),
'tape-backup': gettext('Tape Backup'),
'tape-restore': gettext('Tape Restore'),
},
// set true to show the onclick panel as modal grid
subPanelModal: false,
// the datastore the onclick panel is filtered by
datastore: undefined,
controller: {
xclass: 'Ext.app.ViewController',
openTaskList: function (grid, td, cellindex, record, tr, rowindex) {
let me = this;
let view = me.getView();
if (cellindex > 0) {
let tasklist = view.tasklist;
let state = view.states[cellindex];
let type = view.types[rowindex];
let typefilter = type;
if (view.typeFilterMap[type] !== undefined) {
typefilter = view.typeFilterMap[type];
}
let filterParam = {
limit: 0,
statusfilter: state,
typefilter: typefilter,
};
if (me.since) {
filterParam.since = me.since;
}
if (view.datastore) {
filterParam.store = view.datastore;
}
if (record.data[state] === 0 || record.data[state] === undefined) {
return;
}
if (tasklist === undefined) {
tasklist = Ext.create('Ext.grid.Panel', {
tools: [
{
type: 'close',
handler: () => tasklist.setVisible(false),
},
],
floating: true,
scrollable: true,
height: 400,
width: 600,
columns: [
{
text: gettext('Task'),
dataIndex: 'upid',
renderer: Proxmox.Utils.render_upid,
flex: 1,
},
{
header: gettext('Start Time'),
dataIndex: 'starttime',
width: 130,
renderer: function (value) {
return Ext.Date.format(value, 'M d H:i:s');
},
},
{
xtype: 'actioncolumn',
width: 40,
items: [
{
iconCls: 'fa fa-chevron-right',
tooltip: gettext('View Task'),
handler: function (g, rowIndex) {
let rec = tasklist.getStore().getAt(rowIndex);
tasklist.setVisible(false);
Ext.create('Proxmox.window.TaskViewer', {
upid: rec.data.upid,
endtime: rec.data.endtime,
listeners: {
close: () => tasklist.setVisible(true),
},
}).show();
},
},
],
},
],
store: {
sorters: [
{
property: 'starttime',
direction: 'DESC',
},
],
type: 'store',
model: 'proxmox-tasks',
proxy: {
type: 'proxmox',
url: '/api2/json/nodes/localhost/tasks',
},
},
});
view.on('destroy', function () {
tasklist.setVisible(false);
tasklist.destroy();
tasklist = undefined;
});
view.tasklist = tasklist;
} else {
let cidx = tasklist.cidx;
let ridx = tasklist.ridx;
if (cidx === cellindex && ridx === rowindex && tasklist.isVisible()) {
tasklist.setVisible(false);
return;
}
}
tasklist.cidx = cellindex;
tasklist.ridx = rowindex;
let task = view.titles[type];
let status = '';
switch (state) {
case 'ok':
status = gettext('OK');
break;
case 'warnings':
status = ngettext('Warning', 'Warnings', 1); // TODO: use actual count
break;
case 'error':
status = Proxmox.Utils.errorText;
break;
}
let icon = me.render_icon(state, 1);
tasklist.setTitle(`${task} - ${status} ${icon}`);
tasklist.getStore().getProxy().setExtraParams(filterParam);
tasklist.getStore().removeAll();
if (view.subPanelModal) {
tasklist.modal = true;
tasklist.showBy(Ext.getBody(), 'c-c');
} else {
tasklist.modal = false;
tasklist.showBy(td, 'bl-tl');
}
setTimeout(() => tasklist.getStore().reload(), 10);
}
},
render_icon: function (state, count) {
let cls = 'question';
let color = 'faded';
switch (state) {
case 'error':
cls = 'times-circle';
color = 'critical';
break;
case 'warning':
cls = 'exclamation-circle';
color = 'warning';
break;
case 'ok':
cls = 'check-circle';
color = 'good';
break;
default:
break;
}
if (count < 1) {
color = 'faded';
}
cls += ' ' + color;
return `<i class="fa fa-${cls}"></i>`;
},
render_count: function (value, md, record, rowindex, colindex) {
let me = this;
let view = me.getView();
let count = value || 0;
if (count > 0) {
md.tdCls = 'pointer';
}
let icon = me.render_icon(view.states[colindex], count);
return `${icon} ${value || 0}`;
},
},
updateTasks: function (source, since) {
let me = this;
let controller = me.getController();
let data = [];
if (!source) {
source = {};
}
me.types.forEach((type) => {
if (!source[type]) {
source[type] = {
error: 0,
warning: 0,
ok: 0,
};
}
source[type].type = me.titles[type];
data.push(source[type]);
});
me.lookup('grid').getStore().setData(data);
controller.since = since;
},
layout: 'fit',
bodyPadding: 15,
minHeight: 166,
// we have to wrap the grid in a panel to get the padding right
items: [
{
xtype: 'grid',
reference: 'grid',
hideHeaders: true,
border: false,
bodyBorder: false,
rowLines: false,
viewConfig: {
stripeRows: false,
trackOver: true,
},
scrollable: false,
disableSelection: true,
store: {
data: [],
},
listeners: {
cellclick: 'openTaskList',
},
columns: [
{
dataIndex: 'type',
flex: 1,
},
{
dataIndex: 'error',
renderer: 'render_count',
},
{
dataIndex: 'warning',
renderer: 'render_count',
},
{
dataIndex: 'ok',
renderer: 'render_count',
},
],
},
],
});