]> git.proxmox.com Git - proxmox-backup.git/blame - www/Dashboard.js
ui: system config: fix leading whitespace in translation
[proxmox-backup.git] / www / Dashboard.js
CommitLineData
84b9eced
TL
1Ext.define('PBS.Dashboard', {
2 extend: 'Ext.panel.Panel',
3 xtype: 'pbsDashboard',
4
5 controller: {
6 xclass: 'Ext.app.ViewController',
7
8 openDashboardOptions: function() {
9 var me = this;
10 var viewModel = me.getViewModel();
11 Ext.create('Ext.window.Window', {
12 modal: true,
13 width: 300,
14 title: gettext('Dashboard Options'),
15 layout: {
65b0cea6 16 type: 'auto',
84b9eced
TL
17 },
18 items: [{
19 xtype: 'form',
20 bodyPadding: '10 10 10 10',
21 defaultButton: 'savebutton',
22 items: [{
23 xtype: 'proxmoxintegerfield',
ad9d1625 24 itemId: 'days',
84b9eced
TL
25 labelWidth: 100,
26 anchor: '100%',
27 allowBlank: false,
28 minValue: 1,
ad9d1625
DC
29 maxValue: 60,
30 value: viewModel.get('days'),
6e0f58e7 31 fieldLabel: gettext('Days to show'),
84b9eced
TL
32 }],
33 buttons: [{
34 text: gettext('Save'),
ad9d1625 35 reference: 'savebutton',
84b9eced
TL
36 formBind: true,
37 handler: function() {
38 var win = this.up('window');
ad9d1625
DC
39 var days = win.down('#days').getValue();
40 me.setDays(days, true);
84b9eced 41 win.close();
65b0cea6
TL
42 },
43 }],
44 }],
84b9eced
TL
45 }).show();
46 },
47
ad9d1625 48 setDays: function(days, setState) {
84b9eced
TL
49 var me = this;
50 var viewModel = me.getViewModel();
ad9d1625 51 viewModel.set('days', days);
84b9eced
TL
52 viewModel.notify();
53
ad9d1625
DC
54 viewModel.getStore('tasks').reload();
55
84b9eced
TL
56 if (setState) {
57 var sp = Ext.state.Manager.getProvider();
ad9d1625 58 sp.set('dashboard-days', days);
84b9eced
TL
59 }
60 },
61
4672273f
FE
62 updateRepositoryStatus: function(store, records, success) {
63 if (!success) { return; }
64 let me = this;
6a7b6738
TL
65 let view = me.getView();
66 view.down('#repositoryStatus').setRepositoryInfo(records[0].data['standard-repos']);
4672273f 67 },
84b9eced
TL
68
69 updateSubscription: function(store, records, success) {
739a5145
DC
70 if (!success) { return; }
71 let me = this;
6a7b6738 72 let view = me.getView();
dcd75edb
TL
73 let status = records[0].data.status || 'unknown';
74 // 2 = all good, 1 = different leves, 0 = none
6a7b6738 75 let subscriptionActive = status.toLowerCase() === 'active';
dcd75edb 76 let subStatus = status.toLowerCase() === 'active' ? 2 : 0;
739a5145 77 me.lookup('subscription').setSubStatus(subStatus);
6a7b6738 78 view.down('#repositoryStatus').setSubscriptionStatus(subscriptionActive);
84b9eced
TL
79 },
80
195d7c90
DC
81 updateTasks: function(store, records, success) {
82 if (!success) return;
83 let me = this;
4acd7229 84 let viewModel = me.getViewModel();
195d7c90
DC
85
86 records.sort((a, b) => a.data.duration - b.data.duration);
87 let top10 = records.slice(-10);
88 me.lookup('longesttasks').updateTasks(top10);
89
90 let data = {
65b0cea6
TL
91 backup: { error: 0, warning: 0, ok: 0 },
92 prune: { error: 0, warning: 0, ok: 0 },
93 garbage_collection: { error: 0, warning: 0, ok: 0 },
94 sync: { error: 0, warning: 0, ok: 0 },
4acd7229 95 verify: { error: 0, warning: 0, ok: 0 },
0763ac14
DC
96 'tape-backup': { error: 0, warning: 0, ok: 0 },
97 'tape-restore': { error: 0, warning: 0, ok: 0 },
195d7c90
DC
98 };
99
100 records.forEach(record => {
21486225
TL
101 let task = record.data;
102 let type = task.worker_type;
195d7c90
DC
103 if (type === 'syncjob') {
104 type = 'sync';
105 }
106
baf9c370 107 if (type.startsWith('verif')) {
4acd7229
DC
108 type = 'verify';
109 }
110
21ff6e59
DC
111 if (type.startsWith('prune')) {
112 type = 'prune';
113 }
114
0763ac14
DC
115 if (type.startsWith('tape-backup')) {
116 type = 'tape-backup';
117 }
118
21486225
TL
119 if (data[type] && task.status) {
120 let parsed = Proxmox.Utils.parse_task_status(task.status);
195d7c90
DC
121 data[type][parsed]++;
122 }
123 });
124
dee74aa4 125 me.lookup('tasksummary').updateTasks(data, viewModel.get('sinceEpoch'));
195d7c90
DC
126 },
127
84b9eced
TL
128 init: function(view) {
129 var me = this;
130 var sp = Ext.state.Manager.getProvider();
ad9d1625
DC
131 var days = sp.get('dashboard-days') || 30;
132 me.setDays(days, false);
01284de0
DC
133
134 view.mon(sp, 'statechange', function(provider, key, value) {
135 if (key !== 'summarycolumns') {
136 return;
137 }
138 Proxmox.Utils.updateColumns(view);
139 });
65b0cea6 140 },
84b9eced
TL
141 },
142
143 viewModel: {
144 data: {
ad9d1625 145 days: 30,
84b9eced
TL
146 },
147
1f0d23f7 148 formulas: {
ad9d1625 149 sinceEpoch: (get) => (Date.now()/1000 - get('days') * 24*3600).toFixed(0),
1f0d23f7
DC
150 },
151
84b9eced 152 stores: {
4672273f
FE
153 repositories: {
154 storeid: 'dash-repositories',
155 type: 'update',
156 interval: 15000,
157 autoStart: true,
158 autoLoad: true,
159 autoDestroy: true,
160 proxy: {
161 type: 'proxmox',
162 url: '/api2/json/nodes/localhost/apt/repositories',
163 },
164 listeners: {
165 load: 'updateRepositoryStatus',
166 },
167 },
84b9eced
TL
168 subscription: {
169 storeid: 'dash-subscription',
170 type: 'update',
171 interval: 10000,
172 autoStart: true,
173 autoLoad: true,
174 autoDestroy: true,
175 proxy: {
176 type: 'proxmox',
65b0cea6 177 url: '/api2/json/nodes/localhost/subscription',
84b9eced
TL
178 },
179 listeners: {
65b0cea6
TL
180 load: 'updateSubscription',
181 },
84b9eced 182 },
195d7c90
DC
183 tasks: {
184 storeid: 'dash-tasks',
185 type: 'update',
186 interval: 15000,
187 autoStart: true,
188 autoLoad: true,
189 autoDestroy: true,
190 model: 'proxmox-tasks',
191 proxy: {
192 type: 'proxmox',
a2a7dd15 193 url: '/api2/json/nodes/localhost/tasks',
ad9d1625 194 extraParams: {
a2a7dd15 195 limit: 0,
ad9d1625
DC
196 since: '{sinceEpoch}',
197 },
195d7c90
DC
198 },
199 listeners: {
65b0cea6
TL
200 load: 'updateTasks',
201 },
195d7c90 202 },
65b0cea6 203 },
84b9eced
TL
204 },
205
01284de0
DC
206 listeners: {
207 resize: function(panel) {
208 Proxmox.Utils.updateColumns(panel);
209 },
210 },
211
ad9d1625 212 title: gettext('Dashboard'),
84b9eced
TL
213
214 layout: {
65b0cea6 215 type: 'column',
84b9eced
TL
216 },
217
218 bodyPadding: '20 0 0 20',
219
01284de0
DC
220 minWidth: 700,
221
84b9eced
TL
222 defaults: {
223 columnWidth: 0.49,
224 xtype: 'panel',
65b0cea6 225 margin: '0 20 20 0',
84b9eced
TL
226 },
227
ad9d1625
DC
228 tools: [
229 {
230 type: 'gear',
a2920c37 231 tooltip: gettext('Edit dashboard settings'),
ad9d1625
DC
232 handler: 'openDashboardOptions',
233 },
234 ],
235
84b9eced
TL
236 scrollable: true,
237
238 items: [
239 {
e584593c 240 xtype: 'pbsNodeInfoPanel',
4672273f 241 reference: 'nodeInfo',
b20368ee 242 height: 280,
84b9eced 243 },
ae66873c
DC
244 {
245 xtype: 'pbsDatastoresStatistics',
b20368ee 246 height: 280,
ae66873c 247 },
84b9eced 248 {
195d7c90 249 xtype: 'pbsLongestTasks',
ad9d1625
DC
250 bind: {
251 title: gettext('Longest Tasks') + ' (' +
252 Ext.String.format(gettext('{0} days'), '{days}') + ')',
253 },
195d7c90 254 reference: 'longesttasks',
e099bd07 255 height: 280,
195d7c90
DC
256 },
257 {
258 xtype: 'pbsRunningTasks',
e099bd07 259 height: 280,
195d7c90
DC
260 },
261 {
ad9d1625
DC
262 bind: {
263 title: gettext('Task Summary') + ' (' +
264 Ext.String.format(gettext('{0} days'), '{days}') + ')',
265 },
195d7c90 266 xtype: 'pbsTaskSummary',
0763ac14 267 height: 250,
195d7c90
DC
268 reference: 'tasksummary',
269 },
270 {
271 iconCls: 'fa fa-ticket',
272 title: 'Subscription',
0763ac14 273 height: 250,
195d7c90 274 reference: 'subscription',
739a5145 275 xtype: 'pbsSubscriptionInfo',
84b9eced 276 },
65b0cea6 277 ],
84b9eced
TL
278});
279
739a5145 280Ext.define('PBS.dashboard.SubscriptionInfo', {
84b9eced 281 extend: 'Ext.panel.Panel',
739a5145 282 xtype: 'pbsSubscriptionInfo',
84b9eced
TL
283
284 style: {
65b0cea6 285 cursor: 'pointer',
84b9eced
TL
286 },
287
739a5145
DC
288 layout: {
289 type: 'hbox',
290 align: 'middle',
291 },
292
293 items: [
294 {
295 xtype: 'box',
296 itemId: 'icon',
297 data: {
298 icon: 'question-circle',
299 },
300 width: 100,
301 tpl: '<center><i class="fa fa-3x fa-{icon}"></i></center>',
302 },
303 {
304 flex: 1,
305 xtype: 'box',
306 data: {
307 message: gettext('Unknown'),
308 },
309 itemId: 'message',
310 tpl: '<center>{message}</center>',
311 },
312 ],
313
84b9eced
TL
314 setSubStatus: function(status) {
315 var me = this;
739a5145
DC
316 let icon = '';
317 let message = '';
84b9eced
TL
318
319 switch (status) {
320 case 2:
739a5145
DC
321 icon = 'check good';
322 message = gettext('Your subscription status is valid.');
84b9eced 323 break;
2e75b6d8 324 case 1:
739a5145
DC
325 icon = 'exclamation-triangle warning';
326 message = gettext('Warning: Your subscription levels are not the same.');
84b9eced 327 break;
2e75b6d8 328 case 0:
739a5145 329 icon = 'times-circle critical';
5176346b 330 message = `<h1>${gettext('No valid subscription')}</h1>${PBS.Utils.noSubKeyHtml}`;
84b9eced
TL
331 break;
332 default:
333 throw 'invalid subscription status';
334 }
739a5145
DC
335 me.getComponent('icon').update({ icon });
336 me.getComponent('message').update({ message });
84b9eced 337 },
84b9eced
TL
338
339 listeners: {
340 click: {
341 element: 'body',
342 fn: function() {
343 var mainview = this.component.up('mainview');
739a5145 344 mainview.getController().redirectTo('pbsSubscription');
65b0cea6
TL
345 },
346 },
347 },
84b9eced 348});