Skip to content

Commit

Permalink
Explorer UI - review improvements for connection
Browse files Browse the repository at this point in the history
* Enable logs display changed
* Confirm before deleting connection or thing
* Allow to edit connection before storing
* Retrieve logs, status and metrics on several events

Signed-off-by: thfries <thomas.fries0@gmail.com>
  • Loading branch information
thfries committed Jul 24, 2022
1 parent cb88ffc commit 073b724
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 57 deletions.
12 changes: 12 additions & 0 deletions ui/index.html
Expand Up @@ -112,6 +112,18 @@
<!-- Modals ------------------------------------------------------------------------------->
<div id="authorizationHTML"></div>
<div id="fieldsHTML"></div>
<div class="modal fade" id="modalConfirm" tabindex="-1">
<div class="modal-dialog dialog-sm">
<div class="modal-content">
<div class="modal-body" id="modalBodyConfirm"></div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal" id="buttonConfirmed">Delete</button>
<button type="button" class="btn btn-outline-secondary btn-sm" data-bs-dismiss="modal">Cancel</button>
</div>
</div>
</div>
</div>

</body>

</html>
2 changes: 2 additions & 0 deletions ui/main.js
Expand Up @@ -20,6 +20,7 @@ import * as SearchFilter from './modules/things/searchFilter.js';
import * as Things from './modules/things/things.js';
import * as Connections from './modules/connections/connections.js';
import * as API from './modules/api.js';
import * as Utils from './modules/utils.js';


let resized = false;
Expand All @@ -36,6 +37,7 @@ document.addEventListener('DOMContentLoaded', async function() {
document.getElementById('authorizationHTML').innerHTML =
await (await fetch('modules/environments/authorization.html')).text();

Utils.ready();
await Things.ready();
Attributes.ready();
await Fields.ready();
Expand Down
21 changes: 8 additions & 13 deletions ui/modules/connections/connections.html
Expand Up @@ -75,26 +75,21 @@ <h6>Outgoing JavaScript Mapping</h6>
<div class="modal-dialog dialog-sm">
<div class="modal-content">
<div class="modal-header">
Create Connection
Select Connection Template
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<label>Select Connection Template</label>
<hr />
<fieldset class="form-group">
<div class="row">
<label class="col-form-label col-sm-2 pt-0">Type</label>
<div class="col-sm-10" id="connectionTemplateRadios"></div>
</div>
</fieldset>
<!-- <div class="input-group input-group-sm mb-1">
<label class="input-group-text">Type</label>
<select class="form-control form-control-sm" id="connectionTemplate"></select>
</div> -->
<div class="input-group input-group-sm" style="flex-direction: row-reverse;">
<button class="btn btn-outline-secondary btn-sm" data-bs-dismiss="modal"
id="buttonCreateConnection">Create</button>
</div>
Edit the template and click "Save" to finally create the connection
</div>
<div class="modal-footer">
<button class="btn btn-outline-secondary btn-sm" data-bs-dismiss="modal"
id="buttonCreateConnection">Select Template</button>
</div>
</div>
</div>
Expand Down Expand Up @@ -173,9 +168,9 @@ <h5 data-bs-toggle="collapse" data-bs-target="#tabConnectionHelper">
<h6>Connection Logs</h6>
<div class="input-group input-group-sm mb-1">
<button class="btn btn-outline-secondary btn-sm button_round_left" id="buttonEnableConnectionLogs"
data-bs-toggle="tooltip" title="Enable connection logs for the selected connection">
data-bs-toggle="tooltip" title="Click to enable connection logs for the selected connection">
<i class="bi bi-toggle-off"></i>
Enable
<span>Disabled</span>
</button>
<button class="btn btn-outline-secondary btn-sm button_round_right" id="buttonResetConnectionLogs"
data-bs-toggle="tooltip" title="Reset connection logs for the selected connection">
Expand Down
82 changes: 52 additions & 30 deletions ui/modules/connections/connections.js
Expand Up @@ -76,7 +76,6 @@ export function ready() {

const mergedConnection = {...templateConnection, ...newConnection};
setConnection(mergedConnection);
API.callConnectionsAPI('createConnection', loadConnections, null, mergedConnection);
};

dom.tbodyConnections.addEventListener('click', (event) => {
Expand Down Expand Up @@ -116,24 +115,23 @@ export function ready() {

dom.buttonDeleteConnection.onclick = () => {
Utils.assert(dom.inputConnectionId.value, 'Please select a connection');
API.callConnectionsAPI('deleteConnection', () => {
setConnection(null);
loadConnections();
},
dom.inputConnectionId.value);
Utils.confirm(`Are you sure you want to delete connection<br>'${theConnection.name}'?`, 'Delete', () => {
API.callConnectionsAPI('deleteConnection', () => {
setConnection(null);
loadConnections();
},
dom.inputConnectionId.value);
});
};

dom.buttonRetrieveConnectionStatus.onclick = () => {
Utils.assert(dom.inputConnectionId.value, 'Please select a connection');
API.callConnectionsAPI('retrieveStatus', (connectionStatus) => {
connectionStatusDetail.setValue(JSON.stringify(connectionStatus, null, 2), -1);
},
dom.inputConnectionId.value);
};
dom.buttonRetrieveConnectionStatus.onclick = retrieveConnectionStatus;
document.querySelector('a[data-bs-target="#tabConnectionStatus"]').onclick = retrieveConnectionStatus;
dom.buttonRetrieveConnectionMetrics.onclick = retrieveConnectionMetrics;
document.querySelector('a[data-bs-target="#tabConnectionMetrics"]').onclick = retrieveConnectionMetrics;

dom.buttonEnableConnectionLogs.onclick = () => {
Utils.assert(dom.inputConnectionId.value, 'Please select a connection');
API.callConnectionsAPI('connectionCommand', null, dom.inputConnectionId.value, null, 'connectivity.commands:enableConnectionLogs');
API.callConnectionsAPI('connectionCommand', retrieveConnectionLogs, dom.inputConnectionId.value, null, 'connectivity.commands:enableConnectionLogs');
};

dom.buttonResetConnectionLogs.onclick = () => {
Expand All @@ -143,40 +141,61 @@ export function ready() {

dom.buttonRetrieveConnectionLogs.onclick = retrieveConnectionLogs;

dom.buttonRetrieveConnectionMetrics.onclick = () => {
Utils.assert(dom.inputConnectionId.value, 'Please select a connection');
dom.tbodyConnectionMetrics.innerHTML = '';
API.callConnectionsAPI('retrieveConnectionMetrics', (response) => {
if (response.connectionMetrics.outbound) {
Object.keys(response.connectionMetrics.outbound).forEach((type) => {
let entry = response.connectionMetrics.outbound[type];
Utils.addTableRow(dom.tbodyConnectionMetrics, type, false, false, 'success', entry.success.PT1M, entry.success.PT1H, entry.success.PT24H);
Utils.addTableRow(dom.tbodyConnectionMetrics, type, false, false, 'failure', entry.failure.PT1M, entry.failure.PT1H, entry.failure.PT24H);
});
}
},
dom.inputConnectionId.value);
};

dom.buttonResetConnectionMetrics.onclick = () => {
Utils.assert(dom.inputConnectionId.value, 'Please select a connection');
API.callConnectionsAPI('connectionCommand', null, dom.inputConnectionId.value, null, 'connectivity.commands:resetConnectionMetrics');
};
}

function retrieveConnectionMetrics() {
Utils.assert(dom.inputConnectionId.value, 'Please select a connection');
dom.tbodyConnectionMetrics.innerHTML = '';
API.callConnectionsAPI('retrieveConnectionMetrics', (response) => {
if (response.connectionMetrics.outbound) {
Object.keys(response.connectionMetrics.outbound).forEach((type) => {
let entry = response.connectionMetrics.outbound[type];
Utils.addTableRow(dom.tbodyConnectionMetrics, type, false, false, 'success', entry.success.PT1M, entry.success.PT1H, entry.success.PT24H);
Utils.addTableRow(dom.tbodyConnectionMetrics, type, false, false, 'failure', entry.failure.PT1M, entry.failure.PT1H, entry.failure.PT24H);
});
}
},
dom.inputConnectionId.value);
}

function retrieveConnectionStatus() {
Utils.assert(dom.inputConnectionId.value, 'Please select a connection');
API.callConnectionsAPI('retrieveStatus', (connectionStatus) => {
connectionStatusDetail.setValue(JSON.stringify(connectionStatus, null, 2), -1);
},
dom.inputConnectionId.value);
}

function retrieveConnectionLogs() {
Utils.assert(dom.inputConnectionId.value, 'Please select a connection');
dom.tbodyConnectionLogs.innerHTML = '';
connectionLogDetail.setValue('');
API.callConnectionsAPI('retrieveConnectionLogs', (response) => {
connectionLogs = response.connectionLogs;
adjustEnableButton(response);
response.connectionLogs.forEach((entry) => {
const timestampDisplay = entry.timestamp.replace('T', ' ').replace('Z', '').replace('.', ' ');
Utils.addTableRow(dom.tbodyConnectionLogs, timestampDisplay, false, false, entry.type, entry.level);
Utils.addTableRow(dom.tbodyConnectionLogs, Utils.formatDate(entry.timestamp, true), false, false, entry.type, entry.level);
});
dom.tbodyConnectionLogs.scrollTop = dom.tbodyConnectionLogs.scrollHeight - dom.tbodyConnectionLogs.clientHeight;
},
dom.inputConnectionId.value);

function adjustEnableButton(response) {
if (response.enabledUntil) {
dom.buttonEnableConnectionLogs.querySelector('i').classList.replace('bi-toggle-off', 'bi-toggle-on');
dom.buttonEnableConnectionLogs.querySelector('span').innerText = 'Enabled';
dom.buttonEnableConnectionLogs.setAttribute('title', `Enabled until ${Utils.formatDate(response.enabledUntil)}`);
} else {
dom.buttonEnableConnectionLogs.querySelector('i').classList.replace('bi-toggle-on', 'bi-toggle-off');
dom.buttonEnableConnectionLogs.querySelector('span').innerText = 'Disabled';
dom.buttonEnableConnectionLogs.setAttribute('title', 'Click to enable connection logs for the selected connection');
}
}
}

function setConnection(connection) {
Expand All @@ -194,6 +213,9 @@ function setConnection(connection) {
connectionLogDetail.setValue('');
dom.tbodyConnectionMetrics.innerHTML = '';
dom.tbodyConnectionLogs.innerHTML = '';
if (theConnection && theConnection.id) {
retrieveConnectionLogs();
}
}

function loadConnections() {
Expand Down
32 changes: 18 additions & 14 deletions ui/modules/things/things.js
Expand Up @@ -69,8 +69,17 @@ export async function ready() {
});
};

document.getElementById('putThing').onclick = clickModifyThing('PUT');
document.getElementById('deleteThing').onclick = clickModifyThing('DELETE');
document.getElementById('putThing').onclick = () => {
Utils.assert(dom.thingId.value, 'Thing ID is empty');
modifyThing('PUT');
};

document.getElementById('deleteThing').onclick = () => {
Utils.assert(dom.thingId.value, 'Thing ID is empty');
Utils.confirm(`Are you sure you want to delete thing<br>'${theThing.thingId}'?`, 'Delete', () => {
modifyThing('DELETE');
});
};

dom.thingsTableBody.addEventListener('click', (event) => {
if (event.target && event.target.nodeName === 'TD') {
Expand Down Expand Up @@ -189,19 +198,14 @@ export function getThings(thingIds) {
/**
* Returns a click handler for Update thing and delete thing
* @param {String} method PUT or DELETE
* @return {function} Click handler function
*/
function clickModifyThing(method) {
return function() {
Utils.assert(dom.thingId.value, 'Thing ID is empty');
API.callDittoREST(method,
'/things/' + dom.thingId.value,
method === 'PUT' ? JSON.parse(thingJsonEditor.getValue()) : null,
).then(() => {
// todo: perform last things table update
method === 'PUT' ? refreshThing(dom.thingId.value) : searchThings();
});
};
function modifyThing(method) {
API.callDittoREST(method,
'/things/' + dom.thingId.value,
method === 'PUT' ? JSON.parse(thingJsonEditor.getValue()) : null,
).then(() => {
method === 'PUT' ? refreshThing(dom.thingId.value) : searchThings();
});
};

/**
Expand Down
42 changes: 42 additions & 0 deletions ui/modules/utils.js
Expand Up @@ -13,6 +13,18 @@

/* eslint-disable quotes */

const dom = {
modalBodyConfirm: null,
buttonConfirmed: null,
};

/**
* Initializes components. Should be called after DOMContentLoaded event
*/
export function ready() {
getAllElementsById(dom);
}

/**
* Adds a table to a table element
* @param {HTMLElement} table tbody element the row is added to
Expand Down Expand Up @@ -201,6 +213,36 @@ export function assert(condition, message, validatedElement) {
}
}

/**
* Simple Date format that makes UTC string more readable and cuts off the milliseconds
* @param {Date} date to format
* @param {boolean} withMilliseconds don t cut off milliseconds if true
* @return {String} formatted date
*/
export function formatDate(date, withMilliseconds) {
if (withMilliseconds) {
return date.replace('T', ' ').replace('Z', '').replace('.', ' ');
} else {
return date.split('.')[0].replace('T', ' ');
}
}

let modalConfirm;

/**
* Like from bootbox or bootprompt
* @param {String} message confirm message
* @param {String} action button text
* @param {function} callback true if confirmed
*/
export function confirm(message, action, callback) {
modalConfirm = modalConfirm ?? new bootstrap.Modal('#modalConfirm');
dom.modalBodyConfirm.innerHTML = message;
dom.buttonConfirmed.innerText = action;
dom.buttonConfirmed.onclick = callback;
modalConfirm.show();
}

/**
* Creates and configures an ace editor
* @param {String} domId id of the dom element for the ace editor
Expand Down

0 comments on commit 073b724

Please sign in to comment.