@ntlab/sipd-tu-bridge-ui 1.1.5 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/controller/ui.js CHANGED
@@ -125,10 +125,22 @@ class UiController extends Controller {
125
125
  }
126
126
  res.json(about);
127
127
  });
128
- this.addRoute('task', 'post', '/task/:op', (req, res, next) => {
128
+ this.addRoute('task', 'post', '/task/:op', async (req, res, next) => {
129
129
  const result = {
130
130
  success: false
131
131
  }
132
+ /** @type {import('..').SipdApi} */
133
+ const api = req.app.api;
134
+ switch (req.params.op) {
135
+ case 'remove':
136
+ if (req.body.error) {
137
+ Object.assign(result, await api.query({cmd: 'clean-err', error: req.body.error}));
138
+ }
139
+ break;
140
+ case 'restart':
141
+ Object.assign(result, await api.query({cmd: 'restart'}));
142
+ break;
143
+ }
132
144
  res.json(result);
133
145
  });
134
146
  }
package/index.js CHANGED
@@ -39,6 +39,7 @@
39
39
  * @property {StringPromiseFunction} getActivity Get activity logs
40
40
  * @property {ObjectPromiseFunction} getCount Get activity count
41
41
  * @property {PagedObjectsPromiseFunction} getErrors Get captured errors
42
+ * @property {QueryFunction} query Perform API query
42
43
  */
43
44
 
44
45
  /**
@@ -95,6 +96,14 @@
95
96
  * @returns {Promise<string>}
96
97
  */
97
98
 
99
+ /**
100
+ * A function which returns object Promise.
101
+ *
102
+ * @callback QueryFunction
103
+ * @param {object} data Query data
104
+ * @returns {Promise<object>}
105
+ */
106
+
98
107
  /* --- END API --- */
99
108
 
100
109
  const createError = require('http-errors');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ntlab/sipd-tu-bridge-ui",
3
- "version": "1.1.5",
3
+ "version": "1.3.0",
4
4
  "description": "SIPD Penatausahaan Bridge Web Interface",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -16,8 +16,11 @@ img.logo {
16
16
  }
17
17
 
18
18
  @media only screen and (min-width: 1200px) {
19
+ .ui.container.queue-name {
20
+ max-width: 20em;
21
+ }
19
22
  .ui.container.queue-result {
20
- max-width: 30em;
23
+ max-width: 22.5em;
21
24
  }
22
25
  .ui.container.err-data {
23
26
  max-width: 40em;
@@ -25,8 +28,11 @@ img.logo {
25
28
  }
26
29
 
27
30
  @media only screen and (min-width: 992px) and (max-width: 1199.98px) {
31
+ .ui.container.queue-name {
32
+ max-width: 15em;
33
+ }
28
34
  .ui.container.queue-result {
29
- max-width: 25em;
35
+ max-width: 17.5em;
30
36
  }
31
37
  .ui.container.err-data {
32
38
  max-width: 30em;
@@ -34,8 +40,11 @@ img.logo {
34
40
  }
35
41
 
36
42
  @media only screen and (min-width: 768px) and (max-width: 991.98px) {
43
+ .ui.container.queue-name {
44
+ max-width: 10em;
45
+ }
37
46
  .ui.container.queue-result {
38
- max-width: 20em;
47
+ max-width: 12.5em;
39
48
  }
40
49
  .ui.container.err-data {
41
50
  max-width: 20em;
@@ -43,11 +52,8 @@ img.logo {
43
52
  }
44
53
 
45
54
  @media only screen and (max-width: 767.98px) {
46
- .ui.container.queue-result {
47
- max-width: 75vw;
48
- margin-left: 0 !important;
49
- margin-right: 0 !important;
50
- }
55
+ .ui.container.queue-name,
56
+ .ui.container.queue-result,
51
57
  .ui.container.err-data {
52
58
  max-width: 75vw;
53
59
  margin-left: 0 !important;
@@ -20,6 +20,12 @@
20
20
  divider1: {
21
21
  type: 'divider'
22
22
  },
23
+ restart: {
24
+ title: _('Restart Server'),
25
+ },
26
+ divider2: {
27
+ type: 'divider'
28
+ },
23
29
  logout: {
24
30
  title: _('Logout'),
25
31
  url: route('Security', {name: 'logout'})
@@ -28,7 +34,7 @@
28
34
  } -%>
29
35
  <%- menu(menus, {mainmenu: true, indentation: 2}) %>
30
36
  <%_ script.create('JQuery')
31
- .useDependencies(['JQuery/Util', 'SemanticUI/Notification', 'SemanticUI/Dialog/Message'])
37
+ .useDependencies(['JQuery/Util', 'SemanticUI/Notification', 'SemanticUI/Dialog/Message', 'SemanticUI/Dialog/Confirm'])
32
38
  .add(`
33
39
  $.tasks = {
34
40
  about() {
@@ -57,11 +63,45 @@ $.tasks = {
57
63
  f();
58
64
  }
59
65
  },
66
+ restart() {
67
+ const self = this;
68
+ if (self._restart === undefined) {
69
+ self._restart = true;
70
+ $.ntdlg.confirm(
71
+ 'restart-confirm-dlg',
72
+ '${_('Confirm')}',
73
+ '${_('Are you sure want to restart the server?')}',
74
+ $.ntdlg.ICON_QUESTION,
75
+ function() {
76
+ $.post('${route('Ui', {name: 'task', op: 'restart'})}')
77
+ .done(function(json) {
78
+ self.notify(json);
79
+ })
80
+ .always(function() {
81
+ delete self._restart;
82
+ });
83
+ },
84
+ function() {
85
+ delete self._restart;
86
+ }
87
+ );
88
+ }
89
+ },
90
+ notify(result) {
91
+ if (result.success) {
92
+ $.notify('${_('Operation successfully submitted!')}', 'success');
93
+ } else {
94
+ $.notify('${_('Operation failed!')}', 'error');
95
+ }
96
+ },
60
97
  init() {
61
98
  const self = this;
62
- $('.menu-about').on('click', function(e) {
99
+ $('.menu div.item').on('click', function(e) {
63
100
  e.preventDefault();
64
- self.about();
101
+ const menu = $(this).attr('class').split(' ')[0].split('-')[1];
102
+ if (typeof self[menu] === 'function') {
103
+ self[menu]();
104
+ }
65
105
  });
66
106
  }
67
107
  }
@@ -6,11 +6,12 @@
6
6
  <th><%= _('Screen') %></th>
7
7
  <th><%= _('Error') %></th>
8
8
  <th><%= _('Data') %></th>
9
+ <th><%= _('Action') %></th>
9
10
  </tr>
10
11
  </thead>
11
12
  </table>
12
13
  <%_ script.create('JQuery')
13
- .useDependencies(['SemanticUI/Loader', 'SemanticUI/Dialog'])
14
+ .useDependencies(['SemanticUI/Loader', 'SemanticUI/Dialog/Confirm'])
14
15
  .add(`
15
16
  $.error = $.loader($('div[data-tab="error"] table'), {
16
17
  url: '${route('Ui', {name: 'error', page: 'PAGE'})}',
@@ -27,23 +28,44 @@ $.error = $.loader($('div[data-tab="error"] table'), {
27
28
  counter.addClass('hidden');
28
29
  }
29
30
  }
30
- $('a.err-img-view').on('click', function(e) {
31
+ $('a.err-clicker').on('click', function(e) {
31
32
  e.preventDefault();
32
33
  const a = $(this);
33
- const dlg = $.ntdlg.create(
34
- 'err-img-view-dlg',
35
- a.data('tooltip'),
36
- \`<img class="ui fluid image" src="\${a.find('img').attr('src')}" style="max-height: 70vh;">\`, {
37
- size: 'fullscreen',
38
- buttons: {
39
- okay: {
40
- type: 'green approve',
41
- caption: '<i class="check icon"></i>${_('Ok')}',
34
+ switch (a.data('op')) {
35
+ case 'view':
36
+ const dlg = $.ntdlg.create(
37
+ 'err-img-view-dlg',
38
+ a.data('tooltip'),
39
+ \`<img class="ui fluid image" src="\${a.find('img').attr('src')}" style="max-height: 70vh;">\`, {
40
+ size: 'fullscreen',
41
+ buttons: {
42
+ okay: {
43
+ type: 'green approve',
44
+ caption: '<i class="check icon"></i>${_('Ok')}',
45
+ }
46
+ }
42
47
  }
43
- }
44
- }
45
- );
46
- $.ntdlg.show(dlg);
48
+ );
49
+ $.ntdlg.show(dlg);
50
+ break;
51
+ case 'delete':
52
+ $.ntdlg.confirm(
53
+ 'err-delete-confirm-dlg',
54
+ '${_('Confirm')}',
55
+ '${_('Are you sure want to remove error <code>%ERR%</code>?')}'.replace(/%ERR%/g, a.data('filename')),
56
+ $.ntdlg.ICON_QUESTION,
57
+ function() {
58
+ $.post('${route('Ui', {name: 'task', op: 'remove'})}', {error: a.data('filename')})
59
+ .done(function(json) {
60
+ $.tasks.notify(json);
61
+ if (json.success) {
62
+ self.reload();
63
+ }
64
+ });
65
+ }
66
+ );
67
+ break;
68
+ }
47
69
  });
48
70
  if (self.loading) {
49
71
  self.loading = false;
@@ -53,14 +75,17 @@ $.error = $.loader($('div[data-tab="error"] table'), {
53
75
  $.error.toRow = function(data) {
54
76
  return $(
55
77
  \`<tr><td>\${data.nr}</td>
56
- <td>\${this.toImg($.toStr(data.image), data.filename)}</td>
57
- <td>\${$.toStr(data.error)}</td>
58
- <td><div class="ui scrolling container err-data">\${$.hidePayload($.toStr(data.data))}</div></td>
78
+ <td>\${this.toImg($.toStr(data.image), data.filename)}</td>
79
+ <td>\${$.toStr(data.error)}</td>
80
+ <td><div class="ui scrolling container err-data">\${$.hidePayload($.toStr(data.data))}</div></td>
81
+ <td>
82
+ <a href="#" class="err-clicker" data-op="delete" data-filename="\${data.filename}" role="button"><i class="trash alternate outline red icon"></i></a>
83
+ </td>
59
84
  </tr>\`);
60
85
  }
61
86
  $.error.toImg = function(data, alt) {
62
87
  if (data) {
63
- return \`<a href="#" class="err-img-view" data-tooltip="\${alt}" data-position="right center">
88
+ return \`<a href="#" class="err-clicker" data-op="view" data-tooltip="\${alt}" data-position="right center">
64
89
  <img class="ui medium rounded bordered image" src="\${data}" alt="\${alt}">
65
90
  </a>\`;
66
91
  }
@@ -25,6 +25,7 @@
25
25
  <div class="ui bottom attached tab segment" data-tab="error">
26
26
  <%- include('error') -%>
27
27
  </div>
28
+ <%- include('ticker') -%>
28
29
  <%- include('util') -%>
29
30
  <%_ script.create('JQuery')
30
31
  .useDependencies(['SocketIO'])
@@ -39,6 +40,9 @@ $.uiCon = {
39
40
  .on('connect', function() {
40
41
  self.connected = true;
41
42
  console.log('Socket connected');
43
+ if (self.restarted) {
44
+ window.location.reload();
45
+ }
42
46
  })
43
47
  .on('disconnect', function() {
44
48
  self.connected = false;
@@ -64,12 +68,17 @@ $.uiCon = {
64
68
  })
65
69
  .on('error', function() {
66
70
  $.error.reload();
71
+ })
72
+ .on('restart', function() {
73
+ console.log('Server restarted');
74
+ self.restarted = true;
75
+ $.ticker.start('${_('A server restart is undergo&hellip;')}');
67
76
  });
68
77
  }
69
78
  }
70
79
  `)
71
80
  .addInitializer(`
72
- $('.menu .item').tab({
81
+ $('.menu [data-tab]').tab({
73
82
  autoTabActivation: window.location.hash ? window.location.hash.substr(1) : true,
74
83
  onLoad(path, params, history) {
75
84
  window.location.hash = path;
@@ -38,18 +38,18 @@ $.queue = $.loader($('div[data-tab="queue"] table'), {
38
38
  $.queue.toRow = function(data) {
39
39
  return $(
40
40
  \`<tr><td>\${data.nr}</td>
41
- <td>\${$.toStr(data.id)}</td>
42
- <td>\${$.toStr(data.type)}</td>
43
- <td>\${$.toStr(data.name)}</td>
44
- <td>\${this.toStatus(data.status)}</td>
45
- <td><div class="ui scrolling container queue-result">\${$.hidePayload($.toStr(data.result))}</div></td>
46
- <td>\${$.toStr(data.time)}</td>
41
+ <td>\${$.toStr(data.id)}</td>
42
+ <td>\${$.toStr(data.type)}</td>
43
+ <td><div class="ui scrolling container queue-name">\${$.hidePayload($.toStr(data.name))}</div></td>
44
+ <td>\${this.toStatus(data.status)}</td>
45
+ <td><div class="ui scrolling container queue-result">\${$.hidePayload($.toStr(data.result))}</div></td>
46
+ <td>\${$.toStr(data.time)}</td>
47
47
  </tr>\`);
48
48
  }
49
49
  $.queue.toStatus = function(data) {
50
50
  const icon = {
51
51
  new: 'pause circle outline',
52
- processing: 'spinner',
52
+ processing: 'loading spinner',
53
53
  done: 'green check',
54
54
  error: 'red times',
55
55
  timeout: 'clock outline',
@@ -0,0 +1,67 @@
1
+ <%_ script.create('JQuery')
2
+ .useDependencies(['SemanticUI/Dialog'])
3
+ .add(`
4
+ $.ticker = {
5
+ formatTick(value) {
6
+ return value.toString().padStart(2, '0');
7
+ },
8
+ formatTime(seconds) {
9
+ const self = this;
10
+ if (!self.seconds_in_hour) {
11
+ self.seconds_in_hour = 60 * 60;
12
+ }
13
+ if (!self.seconds_in_minute) {
14
+ self.seconds_in_minute = 60;
15
+ }
16
+ const hour = Math.floor(seconds / self.seconds_in_hour);
17
+ seconds -= hour * self.seconds_in_hour;
18
+ const minute = Math.floor(seconds / self.seconds_in_minute);
19
+ seconds -= minute * self.seconds_in_minute;
20
+ return \`\${self.formatTick(hour)}:\${self.formatTick(minute)}:\${self.formatTick(seconds)}\`;
21
+ },
22
+ build() {
23
+ const self = this;
24
+ self.start = new Date().getTime();
25
+ const f = function() {
26
+ const elapsed = Math.round((new Date().getTime() - self.start) / 1000);
27
+ $('.elapsed').text(self.formatTime(elapsed));
28
+ }
29
+ self.tick = setInterval(f, 1000);
30
+ f();
31
+ },
32
+ start(message) {
33
+ const self = this;
34
+ self.dlg = $.ntdlg.create(
35
+ 'restart-timer-dlg',
36
+ '${_('Please Wait')}',
37
+ \`<div class="ui grid container">
38
+ <div class="row">
39
+ <span class="ui large red text"><i class="clock outline icon"></i><span class="elapsed">00:00:00</span></span>
40
+ <span>&nbsp;&nbsp;&nbsp;</span>
41
+ <span class="ui large text">\${message}</span>
42
+ </div>
43
+ </div>\`,
44
+ {
45
+ size: 'small',
46
+ closable: false,
47
+ show() {
48
+ self.build();
49
+ },
50
+ hide() {
51
+ if (self.tick) {
52
+ clearInterval(self.tick);
53
+ delete self.tick;
54
+ }
55
+ }
56
+ }
57
+ );
58
+ $.ntdlg.show(self.dlg);
59
+ },
60
+ stop() {
61
+ const self = this;
62
+ if (self.dlg) {
63
+ $.ntdlg.close(self.dlg);
64
+ }
65
+ }
66
+ }
67
+ `) -%>