@ntlab/sipd-tu-bridge-ui 1.5.0 → 1.6.1
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 +32 -18
- package/index.js +41 -31
- package/package.json +2 -2
- package/public/css/app.css +16 -3
- package/views/ui/bridge.ejs +23 -5
- package/views/ui/error.ejs +3 -3
- package/views/ui/index.ejs +16 -3
- package/views/ui/queue.ejs +33 -2
- package/views/ui/status.ejs +11 -7
- package/views/ui/util.ejs +34 -1
package/controller/ui.js
CHANGED
|
@@ -43,6 +43,7 @@ class UiController extends Controller {
|
|
|
43
43
|
bridge.stat = await bridge.getStats();
|
|
44
44
|
bridge.last = await bridge.getLast();
|
|
45
45
|
bridge.current = await bridge.getCurrent();
|
|
46
|
+
bridge.logs = await bridge.getLogFiles();
|
|
46
47
|
}
|
|
47
48
|
const socketOptions = {};
|
|
48
49
|
if (req.app.get('root') !== '/') {
|
|
@@ -114,6 +115,37 @@ class UiController extends Controller {
|
|
|
114
115
|
result.pages = req.app.locals.pager(result.count, result.size, result.page);
|
|
115
116
|
res.json(result);
|
|
116
117
|
});
|
|
118
|
+
this.addRoute('task', 'post', '/task/:op', async (req, res, next) => {
|
|
119
|
+
const result = {
|
|
120
|
+
success: false
|
|
121
|
+
}
|
|
122
|
+
let retval;
|
|
123
|
+
/** @type {import('..').SipdApi} */
|
|
124
|
+
const api = req.app.api;
|
|
125
|
+
switch (req.params.op) {
|
|
126
|
+
case 'log':
|
|
127
|
+
if (req.body.seq) {
|
|
128
|
+
if (req.body.log) {
|
|
129
|
+
retval = await api.query({cmd: 'log-file', log: req.body.log, seq: req.body.seq});
|
|
130
|
+
} else {
|
|
131
|
+
retval = await api.query({cmd: 'log-file', seq: req.body.seq});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
break;
|
|
135
|
+
case 'remove':
|
|
136
|
+
if (req.body.queue) {
|
|
137
|
+
retval = await api.query({cmd: 'remove-queue', queue: req.body.queue});
|
|
138
|
+
}
|
|
139
|
+
if (req.body.error) {
|
|
140
|
+
retval = await api.query({cmd: 'clean-err', error: req.body.error});
|
|
141
|
+
}
|
|
142
|
+
break;
|
|
143
|
+
case 'restart':
|
|
144
|
+
retval = await api.query({cmd: 'restart'});
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
res.json({...result, ...(retval || {})});
|
|
148
|
+
});
|
|
117
149
|
this.addRoute('about', 'get', '/about', (req, res, next) => {
|
|
118
150
|
let about;
|
|
119
151
|
if (req.app.about) {
|
|
@@ -130,24 +162,6 @@ class UiController extends Controller {
|
|
|
130
162
|
}
|
|
131
163
|
res.json(about);
|
|
132
164
|
});
|
|
133
|
-
this.addRoute('task', 'post', '/task/:op', async (req, res, next) => {
|
|
134
|
-
const result = {
|
|
135
|
-
success: false
|
|
136
|
-
}
|
|
137
|
-
/** @type {import('..').SipdApi} */
|
|
138
|
-
const api = req.app.api;
|
|
139
|
-
switch (req.params.op) {
|
|
140
|
-
case 'remove':
|
|
141
|
-
if (req.body.error) {
|
|
142
|
-
Object.assign(result, await api.query({cmd: 'clean-err', error: req.body.error}));
|
|
143
|
-
}
|
|
144
|
-
break;
|
|
145
|
-
case 'restart':
|
|
146
|
-
Object.assign(result, await api.query({cmd: 'restart'}));
|
|
147
|
-
break;
|
|
148
|
-
}
|
|
149
|
-
res.json(result);
|
|
150
|
-
});
|
|
151
165
|
}
|
|
152
166
|
|
|
153
167
|
/**
|
package/index.js
CHANGED
|
@@ -22,7 +22,20 @@
|
|
|
22
22
|
* SOFTWARE.
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
const createError = require('http-errors');
|
|
26
|
+
const express = require('express');
|
|
27
|
+
const path = require('path');
|
|
28
|
+
const logger = require('morgan');
|
|
29
|
+
const session = require('express-session');
|
|
30
|
+
const FileStore = require('session-file-store')(session);
|
|
31
|
+
const { Helper, Security, Factory } = require('@ntlab/express-middleware');
|
|
32
|
+
const { ScriptManager, ScriptAsset } = require('@ntlab/ntjs');
|
|
33
|
+
const { Assets, CDN } = require('@ntlab/ntjs-assets');
|
|
34
|
+
|
|
35
|
+
// register script repository
|
|
36
|
+
require('@ntlab/ntjs-repo')();
|
|
37
|
+
|
|
38
|
+
/* --- BEGIN API V1 --- */
|
|
26
39
|
|
|
27
40
|
/**
|
|
28
41
|
* SIPD Penatausahaan Bridge main application.
|
|
@@ -35,10 +48,10 @@
|
|
|
35
48
|
* @property {object} config Configuration
|
|
36
49
|
* @property {SipdBridge[]} bridges Bridges
|
|
37
50
|
* @property {AuthenticateFunction} authenticate Perform usename and password authentication
|
|
38
|
-
* @property {
|
|
39
|
-
* @property {
|
|
40
|
-
* @property {
|
|
41
|
-
* @property {
|
|
51
|
+
* @property {PagedObjectsFunction} getQueues Get queues
|
|
52
|
+
* @property {ActivityFunction} getActivity Get activity logs
|
|
53
|
+
* @property {ObjectFunction} getCount Get activity count
|
|
54
|
+
* @property {PagedObjectsFunction} getErrors Get captured errors
|
|
42
55
|
* @property {QueryFunction} query Perform API query
|
|
43
56
|
*/
|
|
44
57
|
|
|
@@ -48,10 +61,11 @@
|
|
|
48
61
|
* @typedef {Object} SipdBridge
|
|
49
62
|
* @property {string} name Name
|
|
50
63
|
* @property {number} year Year
|
|
51
|
-
* @property {
|
|
52
|
-
* @property {
|
|
53
|
-
* @property {
|
|
54
|
-
* @property {
|
|
64
|
+
* @property {ObjectFunction} getStats Get bridge stats
|
|
65
|
+
* @property {ActivityFunction} getLogs Get bridge logs
|
|
66
|
+
* @property {LogFilesFunction} getLogFiles Get bridge addiitonal log files
|
|
67
|
+
* @property {ObjectFunction} getLast Get last queue
|
|
68
|
+
* @property {ObjectFunction} getCurrent Get current processing queue
|
|
55
69
|
*/
|
|
56
70
|
|
|
57
71
|
/**
|
|
@@ -74,50 +88,46 @@
|
|
|
74
88
|
*/
|
|
75
89
|
|
|
76
90
|
/**
|
|
77
|
-
*
|
|
91
|
+
* Get objects at specified page with size of limit. If none specified
|
|
92
|
+
* it returns first page with default size limit (either 10 or 25).
|
|
78
93
|
*
|
|
79
|
-
* @callback
|
|
80
|
-
* @param {number} page Page number
|
|
81
|
-
* @param {number} size Page size
|
|
94
|
+
* @callback PagedObjectsFunction
|
|
95
|
+
* @param {?number} page Page number
|
|
96
|
+
* @param {?number} size Page size
|
|
82
97
|
* @returns {Promise<object[]>}
|
|
83
98
|
*/
|
|
84
99
|
|
|
85
100
|
/**
|
|
86
|
-
*
|
|
101
|
+
* Get miscellanous object such as queue or log.
|
|
87
102
|
*
|
|
88
|
-
* @callback
|
|
103
|
+
* @callback ObjectFunction
|
|
89
104
|
* @returns {Promise<object>}
|
|
90
105
|
*/
|
|
91
106
|
|
|
92
107
|
/**
|
|
93
|
-
*
|
|
108
|
+
* Get string content such as activity logs.
|
|
94
109
|
*
|
|
95
|
-
* @callback
|
|
110
|
+
* @callback ActivityFunction
|
|
111
|
+
* @param {?string} seq Sequence number
|
|
96
112
|
* @returns {Promise<string>}
|
|
97
113
|
*/
|
|
98
114
|
|
|
99
115
|
/**
|
|
100
|
-
*
|
|
116
|
+
* Query api and return result object.
|
|
101
117
|
*
|
|
102
118
|
* @callback QueryFunction
|
|
103
119
|
* @param {object} data Query data
|
|
104
120
|
* @returns {Promise<object>}
|
|
105
121
|
*/
|
|
106
122
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const session = require('express-session');
|
|
114
|
-
const FileStore = require('session-file-store')(session);
|
|
115
|
-
const { Helper, Security, Factory } = require('@ntlab/express-middleware');
|
|
116
|
-
const { ScriptManager, ScriptAsset } = require('@ntlab/ntjs');
|
|
117
|
-
const { Assets, CDN } = require('@ntlab/ntjs-assets');
|
|
123
|
+
/**
|
|
124
|
+
* Get additional log files.
|
|
125
|
+
*
|
|
126
|
+
* @callback LogFilesFunction
|
|
127
|
+
* @returns {Promise<[{name: string, seq: string, time: number}]>}
|
|
128
|
+
*/
|
|
118
129
|
|
|
119
|
-
|
|
120
|
-
require('@ntlab/ntjs-repo')();
|
|
130
|
+
/* --- END API --- */
|
|
121
131
|
|
|
122
132
|
/**
|
|
123
133
|
* Express application.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ntlab/sipd-tu-bridge-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"description": "SIPD Penatausahaan Bridge Web Interface",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"@ntlab/express-controller": "^1.3.0",
|
|
35
35
|
"@ntlab/express-middleware": "^2.6.0",
|
|
36
36
|
"@ntlab/ntjs": "^3.1.0",
|
|
37
|
-
"@ntlab/ntjs-assets": "^2.
|
|
37
|
+
"@ntlab/ntjs-assets": "^2.144.0",
|
|
38
38
|
"@ntlab/ntjs-repo": "^3.1.0",
|
|
39
39
|
"@ntlab/ntlib": "^2.9.1",
|
|
40
40
|
"ejs": "^3.1.10",
|
package/public/css/app.css
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--viewport-h-limit: 70vh;
|
|
3
|
+
--viewport-w-limit: 70vw;
|
|
4
|
+
}
|
|
5
|
+
|
|
1
6
|
body.with-menu {
|
|
2
7
|
padding-top: 5em;
|
|
3
8
|
}
|
|
@@ -15,6 +20,14 @@ img.logo {
|
|
|
15
20
|
margin-right: 1em !important;
|
|
16
21
|
}
|
|
17
22
|
|
|
23
|
+
.v-max {
|
|
24
|
+
max-height: var(--viewport-h-limit) !important;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.v-min {
|
|
28
|
+
min-height: var(--viewport-h-limit) !important;
|
|
29
|
+
}
|
|
30
|
+
|
|
18
31
|
@media only screen and (min-width: 1200px) {
|
|
19
32
|
.ui.container.queue-name {
|
|
20
33
|
max-width: 20em;
|
|
@@ -47,10 +60,10 @@ img.logo {
|
|
|
47
60
|
|
|
48
61
|
@media only screen and (min-width: 768px) and (max-width: 991.98px) {
|
|
49
62
|
.ui.container.queue-name {
|
|
50
|
-
max-width:
|
|
63
|
+
max-width: 8em;
|
|
51
64
|
}
|
|
52
65
|
.ui.container.queue-result {
|
|
53
|
-
max-width:
|
|
66
|
+
max-width: 10em;
|
|
54
67
|
}
|
|
55
68
|
.ui.container.err-message {
|
|
56
69
|
max-width: 12.5em;
|
|
@@ -65,7 +78,7 @@ img.logo {
|
|
|
65
78
|
.ui.container.queue-result,
|
|
66
79
|
.ui.container.err-message,
|
|
67
80
|
.ui.container.err-data {
|
|
68
|
-
max-width:
|
|
81
|
+
max-width: var(--viewport-w-limit);
|
|
69
82
|
margin-left: 0 !important;
|
|
70
83
|
margin-right: 0 !important;
|
|
71
84
|
}
|
package/views/ui/bridge.ejs
CHANGED
|
@@ -12,12 +12,29 @@
|
|
|
12
12
|
<div class="twelve wide column">
|
|
13
13
|
<%_ bridges.forEach(bridge => { -%>
|
|
14
14
|
<div class="ui form bridge <%= bridge.name %>" data-bridge="<%= bridge.name %>" style="display: none;">
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
<div class="ui two column stackable grid">
|
|
16
|
+
<div class="column">
|
|
17
|
+
<%_ Object.keys(bridge.stat).forEach(stat => { -%>
|
|
18
|
+
<div class="two fields">
|
|
19
|
+
<div class="field"><label><%= _(bridge.stat[stat].label) %></label></div>
|
|
20
|
+
<div class="field"><input type="text" value="<%- bridge.stat[stat].value %>" data-key="<%= stat %>" readonly></div>
|
|
21
|
+
</div>
|
|
22
|
+
<%_ }) -%>
|
|
23
|
+
</div>
|
|
24
|
+
<div class="column">
|
|
25
|
+
<div class="two fields">
|
|
26
|
+
<div class="field"><label><%= _('Archived logs') %></label></div>
|
|
27
|
+
<div class="field">
|
|
28
|
+
<select class="ui dropdown" data-log="<%= bridge.name %>" data-title="<%= _('Bridge <span class="ui info text">%NAME%</span> log %LOG%') %>">
|
|
29
|
+
<option value=""><%= _('- Select log -') %></option>
|
|
30
|
+
<%_ bridge.logs.forEach(log => { -%>
|
|
31
|
+
<option value="<%= log.seq %>"><%= log.name %></option>
|
|
32
|
+
<%_ }) -%>
|
|
33
|
+
</select>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
19
37
|
</div>
|
|
20
|
-
<%_ }) -%>
|
|
21
38
|
<div class="field">
|
|
22
39
|
<label><%= _('Logs') %></label>
|
|
23
40
|
<textarea class="log <%= bridge.name %>" rows="20" readonly></textarea>
|
|
@@ -39,4 +56,5 @@ $('a[data-bridge]').on('click', function(e) {
|
|
|
39
56
|
$(\`.ui.form.bridge\`).hide();
|
|
40
57
|
$(\`.ui.form.bridge.\${a.data('bridge')}\`).show();
|
|
41
58
|
}).filter(':first').click();
|
|
59
|
+
$('.ui.dropdown').dropdown();
|
|
42
60
|
`) -%>
|
package/views/ui/error.ejs
CHANGED
|
@@ -92,11 +92,11 @@ $.error.handle = function(el) {
|
|
|
92
92
|
let content, size = 'large';
|
|
93
93
|
const img = el.find('img'), pre = el.find('pre');
|
|
94
94
|
if (img.length) {
|
|
95
|
-
content = \`<img class="ui fluid image" src="\${img.attr('src')}"
|
|
95
|
+
content = \`<img class="ui fluid image v-max" src="\${img.attr('src')}">\`;
|
|
96
96
|
size = 'fullscreen';
|
|
97
97
|
}
|
|
98
98
|
if (pre.length) {
|
|
99
|
-
content = \`<div class="ui fluid scrolling container"><pre>\${pre.text()}</pre></div>\`;
|
|
99
|
+
content = \`<div class="ui fluid scrolling container"><pre>\${$.safeStr(pre.text())}</pre></div>\`;
|
|
100
100
|
}
|
|
101
101
|
const dlg = $.ntdlg.create('err-view-dlg', el.data('title'), content, {
|
|
102
102
|
size,
|
|
@@ -113,7 +113,7 @@ $.error.handle = function(el) {
|
|
|
113
113
|
$.ntdlg.confirm(
|
|
114
114
|
'err-delete-confirm-dlg',
|
|
115
115
|
'${_('Confirm')}',
|
|
116
|
-
'${_('Are you sure want to remove error <
|
|
116
|
+
'${_('Are you sure want to remove error <span class="ui error text">%ERR%</span>?')}'.replace(/%ERR%/g, el.data('filename')),
|
|
117
117
|
$.ntdlg.ICON_QUESTION,
|
|
118
118
|
function() {
|
|
119
119
|
$.post('${route('Ui', {name: 'task', op: 'remove'})}', {error: el.data('filename')})
|
package/views/ui/index.ejs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<div class="ui top attached stackable tabular menu">
|
|
2
2
|
<a class="item" data-tab="status"><%= _('Status') %></a>
|
|
3
3
|
<a class="item" data-tab="activity"><%= _('Activity') %></a>
|
|
4
|
-
<a class="item" data-tab="queue"><%= _('Queue') %> <span class="ui hidden tiny label"></span></a>
|
|
4
|
+
<a class="item" data-tab="queue"><%= _('Queue') %> <span class="ui hidden blue tiny label"></span></a>
|
|
5
5
|
<%_ if (api.bridges.length) { -%>
|
|
6
|
-
<a class="item" data-tab="bridge"><%= _('Bridge') %> <span class="ui tiny label"><%= api.bridges.length %></span></a>
|
|
6
|
+
<a class="item" data-tab="bridge"><%= _('Bridge') %> <span class="ui green tiny label"><%= api.bridges.length %></span></a>
|
|
7
7
|
<%_ } -%>
|
|
8
|
-
<a class="item" data-tab="error"><%= _('Error') %> <span class="ui hidden tiny label"></span></a>
|
|
8
|
+
<a class="item" data-tab="error"><%= _('Error') %> <span class="ui hidden red tiny label"></span></a>
|
|
9
9
|
</div>
|
|
10
10
|
<div class="ui bottom attached tab segment" data-tab="status">
|
|
11
11
|
<%- indent(include('status'), 2) -%>
|
|
@@ -84,5 +84,18 @@ $('.menu [data-tab]').tab({
|
|
|
84
84
|
window.location.hash = path;
|
|
85
85
|
}
|
|
86
86
|
});
|
|
87
|
+
$('select[data-log]').on('change', function(e) {
|
|
88
|
+
e.preventDefault();
|
|
89
|
+
const el = $(this);
|
|
90
|
+
const seq = el.val();
|
|
91
|
+
if (seq !== '') {
|
|
92
|
+
const log = el.data('log');
|
|
93
|
+
const logname = el.find(\`option[value="\${seq}"]\`).text();
|
|
94
|
+
const title = el.data('title')
|
|
95
|
+
.replace(/%LOG%/g, logname)
|
|
96
|
+
.replace(/%NAME%/g, log?.toUpperCase());
|
|
97
|
+
$.viewLog(title, log, seq);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
87
100
|
$.uiCon.init();
|
|
88
101
|
`) -%>
|
package/views/ui/queue.ejs
CHANGED
|
@@ -9,11 +9,12 @@
|
|
|
9
9
|
<th><%= _('Status') %></th>
|
|
10
10
|
<th><%= _('Result') %></th>
|
|
11
11
|
<th><%= _('Time') %></th>
|
|
12
|
+
<th><%= _('Action') %></th>
|
|
12
13
|
</tr>
|
|
13
14
|
</thead>
|
|
14
15
|
</table>
|
|
15
16
|
<%_ script.create('JQuery')
|
|
16
|
-
.useDependencies('SemanticUI/Loader')
|
|
17
|
+
.useDependencies(['SemanticUI/Loader', 'SemanticUI/Dialog/Confirm'])
|
|
17
18
|
.add(`
|
|
18
19
|
$.queue = $.loader($('div[data-tab="queue"] table'), {
|
|
19
20
|
url: '${route('Ui', {name: 'queue', page: 'PAGE'})}',
|
|
@@ -30,6 +31,11 @@ $.queue = $.loader($('div[data-tab="queue"] table'), {
|
|
|
30
31
|
counter.addClass('hidden');
|
|
31
32
|
}
|
|
32
33
|
}
|
|
34
|
+
$('[data-status!="processing"] [data-op="delete"]').hide();
|
|
35
|
+
$('a.queue-clicker').on('click', function(e) {
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
self.handle($(this));
|
|
38
|
+
});
|
|
33
39
|
if (self.loading) {
|
|
34
40
|
self.loading = false;
|
|
35
41
|
}
|
|
@@ -37,13 +43,16 @@ $.queue = $.loader($('div[data-tab="queue"] table'), {
|
|
|
37
43
|
});
|
|
38
44
|
$.queue.toRow = function(data) {
|
|
39
45
|
return $(\`
|
|
40
|
-
<tr><td>\${data.nr}</td>
|
|
46
|
+
<tr data-status="\${data.status}"><td>\${data.nr}</td>
|
|
41
47
|
<td>\${$.toStr(data.id)}</td>
|
|
42
48
|
<td>\${$.toStr(data.type)}</td>
|
|
43
49
|
<td><div class="ui scrolling container queue-name">\${$.hidePayload($.toStr(data.name))}</div></td>
|
|
44
50
|
<td>\${this.toStatus(data.status)}</td>
|
|
45
51
|
<td><div class="ui scrolling container queue-result">\${$.hidePayload($.toStr(data.result))}</div></td>
|
|
46
52
|
<td>\${$.toStr(data.time)}</td>
|
|
53
|
+
<td>
|
|
54
|
+
<a href="#" class="queue-clicker" data-op="delete" data-queue="\${data.id}" role="button"><i class="trash alternate outline red icon"></i></a>
|
|
55
|
+
</td>
|
|
47
56
|
</tr>\`);
|
|
48
57
|
}
|
|
49
58
|
$.queue.toStatus = function(data) {
|
|
@@ -58,6 +67,28 @@ $.queue.toStatus = function(data) {
|
|
|
58
67
|
return icon ? \`<div data-tooltip="\${data}" data-position="right center"><i class="\${icon} icon"></i></div>\` :
|
|
59
68
|
$.toStr(data);
|
|
60
69
|
}
|
|
70
|
+
$.queue.handle = function(el) {
|
|
71
|
+
const self = this;
|
|
72
|
+
switch (el.data('op')) {
|
|
73
|
+
case 'delete':
|
|
74
|
+
$.ntdlg.confirm(
|
|
75
|
+
'queue-delete-confirm-dlg',
|
|
76
|
+
'${_('Confirm')}',
|
|
77
|
+
'${_('Are you sure want to remove queue <span class="ui error text">%QUEUE%</span>?')}'.replace(/%QUEUE%/g, el.data('queue')),
|
|
78
|
+
$.ntdlg.ICON_QUESTION,
|
|
79
|
+
function() {
|
|
80
|
+
$.post('${route('Ui', {name: 'task', op: 'remove'})}', {queue: el.data('queue')})
|
|
81
|
+
.done(function(json) {
|
|
82
|
+
$.tasks.notify(json);
|
|
83
|
+
if (json.success) {
|
|
84
|
+
self.reload();
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
61
92
|
$.queue.reload = function() {
|
|
62
93
|
const self = this;
|
|
63
94
|
if (!self.loading) {
|
package/views/ui/status.ejs
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<div class="twelve wide column">
|
|
19
19
|
<div class="ui fluid card">
|
|
20
20
|
<div class="content">
|
|
21
|
-
<div class="ui right floated tiny label"><%= bridge.year %></div>
|
|
21
|
+
<div class="ui red right floated tiny label"><%= bridge.year %></div>
|
|
22
22
|
<div class="item header"><i class="ui laptop code icon"></i><%= bridge.name.toUpperCase() %></div>
|
|
23
23
|
</div>
|
|
24
24
|
<div class="extra content" data-bridge="<%= bridge.name %>">
|
|
@@ -71,12 +71,16 @@
|
|
|
71
71
|
</div>
|
|
72
72
|
<%_ }) -%>
|
|
73
73
|
<%_ } else { -%>
|
|
74
|
-
<div class="
|
|
75
|
-
<div class="
|
|
76
|
-
<
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
74
|
+
<div class="row">
|
|
75
|
+
<div class="twelve wide column">
|
|
76
|
+
<div class="ui placeholder segment v-min">
|
|
77
|
+
<div class="ui icon header">
|
|
78
|
+
<i class="laptop code icon"></i>
|
|
79
|
+
<div class="ui big hidden divider"></div>
|
|
80
|
+
<%= _('Currently, no configured bridge available.') %><br/>
|
|
81
|
+
<%= _('Once a bridge is configured, it will appear here.') %>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
80
84
|
</div>
|
|
81
85
|
</div>
|
|
82
86
|
<%_ } -%>
|
package/views/ui/util.ejs
CHANGED
|
@@ -38,6 +38,30 @@ $.addLog = function(el, message) {
|
|
|
38
38
|
}
|
|
39
39
|
return false;
|
|
40
40
|
}
|
|
41
|
+
$.viewLog = function(title, log, seq) {
|
|
42
|
+
$.post('${route('Ui', {name: 'task', op: 'log'})}', {log, seq})
|
|
43
|
+
.done(function(json) {
|
|
44
|
+
if (json.success) {
|
|
45
|
+
const logs = document.createTextNode(json.logs);
|
|
46
|
+
const content = \`<div class="ui fluid scrolling container v-min v-max"><pre></pre></div>\`;
|
|
47
|
+
const dlg = $.ntdlg.create('log-view-dlg', title, content, {
|
|
48
|
+
size: 'fullscreen',
|
|
49
|
+
show() {
|
|
50
|
+
dlg.find('pre').append(logs);
|
|
51
|
+
},
|
|
52
|
+
buttons: {
|
|
53
|
+
okay: {
|
|
54
|
+
type: 'green approve',
|
|
55
|
+
caption: '<i class="check icon"></i>${_('Ok')}',
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
$.ntdlg.show(dlg);
|
|
60
|
+
} else {
|
|
61
|
+
$.tasks.notify(json);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
41
65
|
$.hidePayload = function(message) {
|
|
42
66
|
if (message) {
|
|
43
67
|
message = message.replace(
|
|
@@ -50,11 +74,20 @@ $.hidePayload = function(message) {
|
|
|
50
74
|
$.isPayload = function(data) {
|
|
51
75
|
return Array.isArray(data) || typeof data === 'object' && data.constructor.name === 'Object';
|
|
52
76
|
}
|
|
77
|
+
$.safeStr = function(s) {
|
|
78
|
+
if (s !== '') {
|
|
79
|
+
const tn = document.createTextNode(s);
|
|
80
|
+
const pn = document.createElement('p');
|
|
81
|
+
pn.appendChild(tn);
|
|
82
|
+
s = pn.innerHTML;
|
|
83
|
+
}
|
|
84
|
+
return s;
|
|
85
|
+
}
|
|
53
86
|
$.toStr = function(o) {
|
|
54
87
|
if ($.isPayload(o)) {
|
|
55
88
|
return JSON.stringify(o, null, ' ');
|
|
56
89
|
} else if (o !== undefined && o !== null) {
|
|
57
|
-
return o.toString();
|
|
90
|
+
return $.safeStr(o.toString());
|
|
58
91
|
}
|
|
59
92
|
return '';
|
|
60
93
|
}
|