]> git.proxmox.com Git - proxmox-widget-toolkit.git/blob - src/node/Tasks.js
node/Task: remove leftover 'datastore' reference
[proxmox-widget-toolkit.git] / src / node / Tasks.js
1 Ext.define('Proxmox.node.Tasks', {
2 extend: 'Ext.grid.GridPanel',
3
4 alias: 'widget.proxmoxNodeTasks',
5
6 stateful: true,
7 stateId: 'pve-grid-node-tasks',
8
9 loadMask: true,
10 sortableColumns: false,
11
12 // set extra filter components, must have a 'name' property for the parameter, and must
13 // trigger a 'change' event if the value is 'undefined', it will not be sent to the api
14 extraFilter: [],
15
16
17 // fixed filters which cannot be changed after instantiation, for example:
18 // { vmid: 100 }
19 preFilter: {},
20
21 controller: {
22 xclass: 'Ext.app.ViewController',
23
24 showTaskLog: function() {
25 let me = this;
26 let selection = me.getView().getSelection();
27 if (selection.length < 1) {
28 return;
29 }
30
31 let rec = selection[0];
32
33 Ext.create('Proxmox.window.TaskViewer', {
34 upid: rec.data.upid,
35 endtime: rec.data.endtime,
36 }).show();
37 },
38
39 updateLayout: function() {
40 let me = this;
41 // update the scrollbar on every store load since the total count might be different.
42 // the buffered grid plugin does this only on (user) scrolling itself and even reduces
43 // the scrollheight again when scrolling up
44 me.getView().updateLayout();
45 },
46
47 sinceChange: function(field, newval) {
48 let me = this;
49 let vm = me.getViewModel();
50
51 vm.set('since', newval);
52 },
53
54 untilChange: function(field, newval, oldval) {
55 let me = this;
56 let vm = me.getViewModel();
57
58 vm.set('until', newval);
59 },
60
61 reload: function() {
62 let me = this;
63 let view = me.getView();
64 view.getStore().load();
65 },
66
67 showFilter: function(btn, pressed) {
68 let me = this;
69 let vm = me.getViewModel();
70 vm.set('showFilter', pressed);
71 },
72
73 init: function(view) {
74 let me = this;
75 Proxmox.Utils.monStoreErrors(view, view.getStore(), true);
76 },
77 },
78
79
80 listeners: {
81 itemdblclick: 'showTaskLog',
82 },
83
84 viewModel: {
85 data: {
86 typefilter: '',
87 statusfilter: '',
88 showFilter: false,
89 extraFilter: {},
90 since: null,
91 until: null,
92 },
93
94 formulas: {
95 filterIcon: (get) => 'fa fa-filter' + (get('showFilter') ? ' info-blue' : ''),
96 extraParams: function(get) {
97 let me = this;
98 let params = {};
99 if (get('typefilter')) {
100 params.typefilter = get('typefilter');
101 }
102 if (get('statusfilter')) {
103 params.statusfilter = get('statusfilter');
104 }
105
106 if (get('extraFilter')) {
107 let extraFilter = get('extraFilter');
108 for (const [name, value] of Object.entries(extraFilter)) {
109 if (value !== undefined && value !== null && value !== "") {
110 params[name] = value;
111 }
112 }
113 }
114
115 if (get('since')) {
116 params.since = get('since').valueOf()/1000;
117 }
118
119 if (get('until')) {
120 let until = new Date(get('until').getTime()); // copy object
121 until.setDate(until.getDate() + 1); // end of the day
122 params.until = until.valueOf()/1000;
123 }
124
125 me.getView().getStore().load();
126
127 return params;
128 },
129 },
130
131 stores: {
132 bufferedstore: {
133 type: 'buffered',
134 pageSize: 500,
135 autoLoad: true,
136 remoteFilter: true,
137 model: 'proxmox-tasks',
138 proxy: {
139 type: 'proxmox',
140 startParam: 'start',
141 limitParam: 'limit',
142 extraParams: '{extraParams}',
143 url: "/api2/json/nodes/localhost/tasks",
144 },
145 listeners: {
146 prefetch: 'updateLayout',
147 },
148 },
149 },
150 },
151
152 bind: {
153 store: '{bufferedstore}',
154 },
155
156 dockedItems: [
157 {
158 xtype: 'toolbar',
159 items: [
160 {
161 xtype: 'proxmoxButton',
162 text: gettext('View'),
163 iconCls: 'fa fa-window-restore',
164 disabled: true,
165 handler: 'showTaskLog',
166 },
167 {
168 xtype: 'button',
169 text: gettext('Reload'),
170 iconCls: 'fa fa-refresh',
171 handler: 'reload',
172 },
173 '->',
174 {
175 xtype: 'button',
176 enableToggle: true,
177 bind: {
178 iconCls: '{filterIcon}',
179 },
180 text: gettext('Filter'),
181 stateful: true,
182 stateId: 'task-showfilter',
183 stateEvents: ['toggle'],
184 applyState: function(state) {
185 if (state.pressed !== undefined) {
186 this.setPressed(state.pressed);
187 }
188 },
189 getState: function() {
190 return {
191 pressed: this.pressed,
192 };
193 },
194 listeners: {
195 toggle: 'showFilter',
196 },
197 },
198 ],
199 },
200 {
201 xtype: 'toolbar',
202 dock: 'top',
203 reference: 'filtertoolbar',
204 layout: {
205 type: 'hbox',
206 align: 'top',
207 },
208 bind: {
209 hidden: '{!showFilter}',
210 },
211 items: [
212 {
213 xtype: 'container',
214 padding: 10,
215 layout: {
216 type: 'vbox',
217 align: 'stretch',
218 },
219 defaults: {
220 labelWidth: 80,
221 },
222 // cannot bind the values directly, as it then changes also
223 // on blur, causing wrong reloads of the store
224 items: [
225 {
226 xtype: 'datefield',
227 fieldLabel: gettext('Since'),
228 format: 'Y-m-d',
229 bind: {
230 maxValue: '{until}',
231 },
232 listeners: {
233 change: 'sinceChange',
234 },
235 },
236 {
237 xtype: 'datefield',
238 fieldLabel: gettext('Until'),
239 format: 'Y-m-d',
240 bind: {
241 minValue: '{since}',
242 },
243 listeners: {
244 change: 'untilChange',
245 },
246 },
247 ],
248 },
249 {
250 xtype: 'container',
251 padding: 10,
252 layout: {
253 type: 'vbox',
254 align: 'stretch',
255 },
256 defaults: {
257 labelWidth: 80,
258 },
259 items: [
260 {
261 xtype: 'pmxTaskTypeSelector',
262 fieldLabel: gettext('Task Type'),
263 emptyText: gettext('All'),
264 bind: {
265 value: '{typefilter}',
266 },
267 },
268 {
269 xtype: 'combobox',
270 fieldLabel: gettext('Task Result'),
271 emptyText: gettext('All'),
272 multiSelect: true,
273 store: [
274 ['ok', gettext('OK')],
275 ['unknown', Proxmox.Utils.unknownText],
276 ['warning', gettext('Warnings')],
277 ['error', gettext('Errors')],
278 ],
279 bind: {
280 value: '{statusfilter}',
281 },
282 },
283 ],
284 },
285 ],
286 },
287 ],
288
289 viewConfig: {
290 trackOver: false,
291 stripeRows: false, // does not work with getRowClass()
292 emptyText: gettext('No Tasks found'),
293
294 getRowClass: function(record, index) {
295 let status = record.get('status');
296
297 if (status) {
298 let parsed = Proxmox.Utils.parse_task_status(status);
299 if (parsed === 'error') {
300 return "proxmox-invalid-row";
301 } else if (parsed === 'warning') {
302 return "proxmox-warning-row";
303 }
304 }
305 return '';
306 },
307 },
308
309 columns: [
310 {
311 header: gettext("Start Time"),
312 dataIndex: 'starttime',
313 width: 130,
314 renderer: function(value) {
315 return Ext.Date.format(value, "M d H:i:s");
316 },
317 },
318 {
319 header: gettext("End Time"),
320 dataIndex: 'endtime',
321 width: 130,
322 renderer: function(value, metaData, record) {
323 if (!value) {
324 metaData.tdCls = "x-grid-row-loading";
325 return '';
326 }
327 return Ext.Date.format(value, "M d H:i:s");
328 },
329 },
330 {
331 header: gettext("Duration"),
332 hidden: true,
333 width: 80,
334 renderer: function(value, metaData, record) {
335 let start = record.data.starttime;
336 if (start) {
337 let end = record.data.endtime || Date.now();
338 let duration = end - start;
339 if (duration > 0) {
340 duration /= 1000;
341 }
342 return Proxmox.Utils.format_duration_human(duration);
343 }
344 return Proxmox.Utils.unknownText;
345 },
346 },
347 {
348 header: gettext("User name"),
349 dataIndex: 'user',
350 width: 150,
351 },
352 {
353 header: gettext("Description"),
354 dataIndex: 'upid',
355 flex: 1,
356 renderer: Proxmox.Utils.render_upid,
357 },
358 {
359 header: gettext("Status"),
360 dataIndex: 'status',
361 width: 200,
362 renderer: function(value, metaData, record) {
363 if (value === undefined && !record.data.endtime) {
364 metaData.tdCls = "x-grid-row-loading";
365 return '';
366 }
367
368 let parsed = Proxmox.Utils.parse_task_status(value);
369 switch (parsed) {
370 case 'unknown': return Proxmox.Utils.unknownText;
371 case 'error': return Proxmox.Utils.errorText + ': ' + value;
372 case 'ok': // fall-through
373 case 'warning': // fall-through
374 default: return value;
375 }
376 },
377 },
378 ],
379
380 initComponent: function() {
381 const me = this;
382
383 let updateExtraFilters = function(name, value) {
384 let vm = me.getViewModel();
385 let extraFilter = Ext.clone(vm.get('extraFilter'));
386 extraFilter[name] = value;
387 vm.set('extraFilter', extraFilter);
388 };
389
390 for (const [name, value] of Object.entries(me.preFilter)) {
391 updateExtraFilters(name, value);
392 }
393
394 me.callParent();
395
396 let addFields = function(items) {
397 me.lookup('filtertoolbar').add({
398 xtype: 'container',
399 padding: 10,
400 layout: {
401 type: 'vbox',
402 align: 'stretch',
403 },
404 defaults: {
405 labelWidth: 80,
406 },
407 items,
408 });
409 };
410
411 // start with a userfilter
412 me.extraFilter = [
413 {
414 xtype: 'textfield',
415 fieldLabel: gettext('User name'),
416 changeOptions: {
417 buffer: 500,
418 },
419 name: 'userfilter',
420 },
421 ...me.extraFilter,
422 ];
423 let items = [];
424 for (const filterTemplate of me.extraFilter) {
425 let filter = Ext.clone(filterTemplate);
426
427 filter.listeners = filter.listeners || {};
428 filter.listeners.change = Ext.apply(filter.changeOptions || {}, {
429 fn: function(field, value) {
430 updateExtraFilters(filter.name, value);
431 },
432 });
433
434 items.push(filter);
435 if (items.length === 2) {
436 addFields(items);
437 items = [];
438 }
439 }
440
441 addFields(items);
442 },
443 });