Finalizing Server webUI
Still needed: - Way to update clients' versions - Way to delete nodes - working dashboard - working search function
This commit is contained in:
@@ -86,6 +86,8 @@ exports.leaveServer = async (req, res) => {
|
||||
const currentConnection = await getConnectionByNodeId(nodeId);
|
||||
log.DEBUG("Current Connection for node: ", currentConnection);
|
||||
|
||||
if (!currentConnection) return res.status(400).json("Node is not connected")
|
||||
|
||||
await leaveServerWrapper(currentConnection.clientObject)
|
||||
|
||||
return res.status(200).json(currentConnection.clientObject.name);
|
||||
|
||||
@@ -20,7 +20,7 @@ async function checkInWithNode(node) {
|
||||
sendHttpRequest(reqOptions, "", (responseObj) => {
|
||||
if (responseObj) {
|
||||
log.DEBUG("Response from: ", node.name, responseObj);
|
||||
const onlineNode = new nodeObject({ _online: false, _id: node.id });
|
||||
const onlineNode = new nodeObject({ _online: true, _id: node.id });
|
||||
log.DEBUG("Node update object: ", onlineNode);
|
||||
updateNodeInfo(onlineNode, (sqlResponse) => {
|
||||
if (!sqlResponse) this.log.ERROR("No response from SQL object");
|
||||
@@ -193,6 +193,39 @@ exports.updateNodeSystem = async (req, res) => {
|
||||
})
|
||||
}
|
||||
|
||||
/** Deletes a specific system/preset from a given node
|
||||
*
|
||||
* @param {*} req Default express req from router
|
||||
* @param {*} res Defualt express res from router
|
||||
* @param {*} req.params.nodeId The Node ID to update the preset/system on
|
||||
* @param {*} req.body.systemName The name of the system to update
|
||||
*/
|
||||
exports.removeNodeSystem = async (req, res) => {
|
||||
if (!req.params.nodeId) return res.status(400).json("No id specified");
|
||||
if (!req.body.systemName) return res.status(400).json("No system specified");
|
||||
log.DEBUG("Updating system for node: ", req.params.nodeId, req.body);
|
||||
getNodeInfoFromId(req.params.nodeId, (node) => {
|
||||
const reqOptions = new requestOptions("/client/removePreset", "POST", node.ip, node.port);
|
||||
const reqBody = {
|
||||
'systemName': req.body.systemName
|
||||
}
|
||||
|
||||
log.DEBUG("Request body for deleting preset: ", reqBody, reqOptions);
|
||||
sendHttpRequest(reqOptions, JSON.stringify(reqBody), async (responseObj) => {
|
||||
if(responseObj){
|
||||
// Good
|
||||
log.DEBUG("Response from deleting preset: ", reqBody, responseObj);
|
||||
return res.sendStatus(200)
|
||||
} else {
|
||||
// Bad
|
||||
log.DEBUG("No Response from deleting preset");
|
||||
return res.status(400).json("No Response from deleting preset, could be offline");
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/** Updates the information received from the client based on ID
|
||||
*
|
||||
* @param {*} req Default express req from router
|
||||
@@ -247,14 +280,14 @@ exports.updateExistingNode = async = (req, res) => {
|
||||
|
||||
if (!nodeInfo) {
|
||||
log.WARN("No existing node found with this ID, adding node: ", checkInObject);
|
||||
addNewNode(checkInObject, (newNode) => {
|
||||
this.requestNodeCheckIn({"params": {"nodeId": checkInObject.id}});
|
||||
addNewNode(checkInObject, async (newNode) => {
|
||||
await checkInWithNode(newNode);
|
||||
return res.status(201).json({ "updatedKeys": newNode });
|
||||
});
|
||||
}
|
||||
else {
|
||||
updateNodeInfo(checkInObject, () => {
|
||||
this.requestNodeCheckIn({"params": {"nodeId": checkInObject.id}});
|
||||
updateNodeInfo(checkInObject, async () => {
|
||||
await checkInWithNode(nodeInfo);
|
||||
return res.status(202).json({ "updatedKeys": checkInObject });
|
||||
});
|
||||
}
|
||||
@@ -291,7 +324,7 @@ exports.requestNodeCheckIn = async (req, res) => {
|
||||
const node = await getNodeInfoFromId(req.params.nodeId);
|
||||
if (!node) return res.status(400).json("No Node with the ID given");
|
||||
await checkInWithNode(node);
|
||||
res.sendStatus(200);
|
||||
if (res) res.sendStatus(200);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,11 +1,119 @@
|
||||
$(document).ready(async () => {
|
||||
console.log("Loading stored notifications...");
|
||||
await loadStoredToasts();
|
||||
console.log("Showing stored notifications...");
|
||||
await showStoredToasts();
|
||||
});
|
||||
|
||||
/**
|
||||
* Gets all toasts stored in local storage
|
||||
*
|
||||
* @returns {Object} Object of toasts in storage
|
||||
*/
|
||||
function getStoredToasts() {
|
||||
if (localStorage.getItem("toasts")) {
|
||||
const storedToasts = JSON.parse(localStorage.getItem("toasts"));
|
||||
console.log("LOADED STORED TOASTS: ", storedToasts);
|
||||
navbarUpdateNotificationBellCount(storedToasts);
|
||||
return storedToasts;
|
||||
}
|
||||
else return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a toast to storage, will not allow duplicates
|
||||
*
|
||||
* @param {Date} time The date object from when the toast was created
|
||||
* @param {*} message The message of the toast
|
||||
*/
|
||||
function addToastToStorage(time, message) {
|
||||
var toasts = [{ 'time': time, 'message': message }]
|
||||
var storedToasts = getStoredToasts();
|
||||
console.log("Adding new notification to storage: ", toasts);
|
||||
toasts = toasts.concat(storedToasts);
|
||||
console.log("Combined new and stored notifications: ", toasts);
|
||||
toasts = toasts.filter((value, index, self) =>
|
||||
index === self.findIndex((t) => (
|
||||
t.time === value.time && t.message === value.message
|
||||
))
|
||||
)
|
||||
console.log("Deduped stored notifications: ", toasts);
|
||||
localStorage.setItem("toasts", JSON.stringify(toasts));
|
||||
navbarUpdateNotificationBellCount(toasts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a toast from the local storage
|
||||
*
|
||||
* @param {Date} time The date object from when the toast was created
|
||||
* @param {*} message The message of the toast
|
||||
*/
|
||||
function removeToastFromStorage(time, message) {
|
||||
const toastToRemove = { 'time': time, 'message': message }
|
||||
console.log("Toast to remove: ", toastToRemove);
|
||||
var toasts = getStoredToasts();
|
||||
console.log("Stored toasts: ", toasts);
|
||||
if (toasts.indexOf(toastToRemove)) toasts.splice(toasts.indexOf(toastToRemove) - 1, 1)
|
||||
console.log("Toasts with selected toast removed: ", toasts);
|
||||
localStorage.setItem("toasts", JSON.stringify(toasts));
|
||||
navbarUpdateNotificationBellCount(toasts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows all stored toasts
|
||||
*/
|
||||
function showStoredToasts() {
|
||||
const storedToasts = getStoredToasts();
|
||||
if (!storedToasts) return
|
||||
console.log("Loaded stored notifications to show: ", storedToasts);
|
||||
for (const toast of storedToasts) {
|
||||
const toastId = `${toast.time}-toast`;
|
||||
console.log("Showing stored toast: ", toast, toastId);
|
||||
const toastElement = bootstrap.Toast.getOrCreateInstance(document.getElementById(toastId));
|
||||
toastElement.show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all toasts stored in the local storage into the DOM of the webpage
|
||||
*/
|
||||
function loadStoredToasts() {
|
||||
const storedToasts = getStoredToasts();
|
||||
if (!storedToasts) return
|
||||
console.log("Loaded stored notifications: ", storedToasts);
|
||||
for (const toast of storedToasts) {
|
||||
createToast(toast.message, { time: toast.time })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will update the count of notifications on the bell icon in the navbar
|
||||
*
|
||||
* @param {Array} storedToasts An array of stored toasts to be counted and updated in the navbar
|
||||
*/
|
||||
function navbarUpdateNotificationBellCount(storedToasts) {
|
||||
const notificationBellIcon = document.getElementById("navbar-notification-bell");
|
||||
var notificationBellCount = document.getElementById("notification-bell-icon-count");
|
||||
if (!notificationBellCount) {
|
||||
notificationBellCount = document.createElement('span');
|
||||
notificationBellCount.id = "notification-bell-icon-count";
|
||||
notificationBellCount.classList.add('badge');
|
||||
notificationBellCount.classList.add('text-bg-secondary');
|
||||
notificationBellCount.appendChild(document.createTextNode(storedToasts.length));
|
||||
}
|
||||
else notificationBellCount.innerHTML = storedToasts.length;
|
||||
|
||||
notificationBellIcon.appendChild(notificationBellCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a frequency input from the DOM
|
||||
*
|
||||
* @param {string} system The system name to add the frequency to
|
||||
* @param {string} inputId [OPTIONAL] The ID of input, this can be anything unique to this input. If this is not provided the number of frequencies will be used as the ID
|
||||
*/
|
||||
function addFrequencyInput(system, inputId = null){
|
||||
if (!inputId) inputId = $(`[id^="${system}_systemFreqRow_"]`).length;
|
||||
function addFrequencyInput(system, inputId = null) {
|
||||
if (!inputId) inputId = $(`[id^="${system}_systemFreqRow_"]`).length;
|
||||
// Create new input
|
||||
var icon = document.createElement('i');
|
||||
icon.classList.add('bi');
|
||||
@@ -16,7 +124,7 @@ function addFrequencyInput(system, inputId = null){
|
||||
remove.classList.add('align-middle');
|
||||
remove.classList.add('float-left');
|
||||
remove.href = '#'
|
||||
remove.onclick = () => {removeFrequencyInput(`${system}_systemFreqRow_${inputId}`)}
|
||||
remove.onclick = () => { removeFrequencyInput(`${system}_systemFreqRow_${inputId}`) }
|
||||
remove.appendChild(icon);
|
||||
|
||||
var childColRemoveIcon = document.createElement('div');
|
||||
@@ -31,7 +139,7 @@ function addFrequencyInput(system, inputId = null){
|
||||
var childColInput = document.createElement('div');
|
||||
childColInput.classList.add('col-10');
|
||||
childColInput.appendChild(input);
|
||||
|
||||
|
||||
var childRow = document.createElement('div');
|
||||
childRow.classList.add("row");
|
||||
childRow.classList.add("px-1");
|
||||
@@ -47,19 +155,30 @@ function addFrequencyInput(system, inputId = null){
|
||||
document.getElementById(`frequencyRow_${system.replaceAll(" ", "_")}`).appendChild(colParent);
|
||||
}
|
||||
|
||||
function createToast(notificationMessage){
|
||||
/**
|
||||
* Add a toast element to the DOM
|
||||
*
|
||||
* @param {*} notificationMessage The message of the notification
|
||||
* @param {Date} param1.time The date object for when the toast was created, blank if creating new
|
||||
* @param {boolean} param1.showNow Show the toast now or just store it
|
||||
* @returns
|
||||
*/
|
||||
function createToast(notificationMessage, { time = undefined, showNow = false } = {}) {
|
||||
if (!time) time = new Date(Date.now());
|
||||
else time = new Date(Date.parse(time));
|
||||
const toastTitle = document.createElement('strong');
|
||||
toastTitle.classList.add('me-auto');
|
||||
toastTitle.appendChild(document.createTextNode("Server Notification"));
|
||||
|
||||
const toastTime = document.createElement('small');
|
||||
toastTime.appendChild(document.createTextNode(new Date(Date.now()).toLocaleString()));
|
||||
toastTime.appendChild(document.createTextNode(time.toLocaleString()));
|
||||
|
||||
const toastClose = document.createElement('button');
|
||||
toastClose.type = 'button';
|
||||
toastClose.classList.add('btn-close');
|
||||
toastClose.ariaLabel = 'Close';
|
||||
toastClose.setAttribute('data-bs-dismiss', 'toast');
|
||||
toastClose.onclick = () => { removeToastFromStorage(time.toISOString(), notificationMessage); };
|
||||
|
||||
const toastHeader = document.createElement('div');
|
||||
toastHeader.classList.add('toast-header');
|
||||
@@ -67,7 +186,6 @@ function createToast(notificationMessage){
|
||||
toastHeader.appendChild(toastTime);
|
||||
toastHeader.appendChild(toastClose);
|
||||
|
||||
|
||||
const toastMessage = document.createElement('p');
|
||||
toastMessage.classList.add("px-2");
|
||||
toastMessage.appendChild(document.createTextNode(notificationMessage));
|
||||
@@ -78,31 +196,39 @@ function createToast(notificationMessage){
|
||||
|
||||
const wrapperDiv = document.createElement('div');
|
||||
wrapperDiv.classList.add('toast');
|
||||
wrapperDiv.role ='alert';
|
||||
//wrapperDiv.classList.add('position-fixed');
|
||||
wrapperDiv.id = `${time.toISOString()}-toast`;
|
||||
wrapperDiv.role = 'alert';
|
||||
wrapperDiv.ariaLive = 'assertive';
|
||||
wrapperDiv.ariaAtomic = true;
|
||||
wrapperDiv.setAttribute('data-bs-delay', "7500");
|
||||
wrapperDiv.setAttribute('data-bs-animation', true);
|
||||
wrapperDiv.appendChild(toastHeader);
|
||||
wrapperDiv.appendChild(toastMessage);
|
||||
|
||||
document.getElementById("toastZone").appendChild(wrapperDiv);
|
||||
|
||||
$('.toast').toast('show');
|
||||
return $('.toast');
|
||||
addToastToStorage(time.toISOString(), notificationMessage);
|
||||
if (showNow) {
|
||||
const toastElement = bootstrap.Toast.getOrCreateInstance(document.getElementById(`${time.toISOString()}-toast`));
|
||||
toastElement.show();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function sendNodeHeartbeat(nodeId){
|
||||
function sendNodeHeartbeat(nodeId) {
|
||||
const Http = new XMLHttpRequest();
|
||||
const url='/nodes/nodeCheckIn/'+nodeId;
|
||||
const url = '/nodes/nodeCheckIn/' + nodeId;
|
||||
Http.open("GET", url);
|
||||
Http.send();
|
||||
|
||||
Http.onloadend = (e) => {
|
||||
console.log(Http.responseText)
|
||||
createToast(Http.responseText);
|
||||
console.log(Http.responseText)
|
||||
createToast(Http.responseText, { showNow: true });
|
||||
}
|
||||
}
|
||||
|
||||
function joinServer(){
|
||||
function joinServer() {
|
||||
const preset = document.getElementById("selectRadioPreset").value;
|
||||
const nodeId = document.getElementById("nodeId").value;
|
||||
const clientId = document.getElementById("inputDiscordClientId").value;
|
||||
@@ -118,7 +244,7 @@ function joinServer(){
|
||||
console.log(reqBody);
|
||||
|
||||
const Http = new XMLHttpRequest();
|
||||
const url='/admin/join';
|
||||
const url = '/admin/join';
|
||||
Http.open("POST", url);
|
||||
Http.setRequestHeader("Content-Type", "application/json");
|
||||
Http.send(JSON.stringify(reqBody));
|
||||
@@ -127,19 +253,19 @@ function joinServer(){
|
||||
const responseObject = JSON.parse(Http.responseText)
|
||||
console.log(Http.status);
|
||||
console.log(responseObject);
|
||||
createToast(`${responseObject.name} will join shortly`);
|
||||
createToast(`${responseObject.name} will join shortly`, { showNow: true });
|
||||
$("#joinModal").modal('toggle');
|
||||
}
|
||||
}
|
||||
|
||||
function leaveServer(){
|
||||
function leaveServer() {
|
||||
const nodeId = document.getElementById("nodeId").value;
|
||||
const reqBody = {
|
||||
'nodeId': nodeId
|
||||
};
|
||||
|
||||
const Http = new XMLHttpRequest();
|
||||
const url='/admin/leave';
|
||||
const url = '/admin/leave';
|
||||
Http.open("POST", url);
|
||||
Http.setRequestHeader("Content-Type", "application/json");
|
||||
Http.send(JSON.stringify(reqBody));
|
||||
@@ -148,8 +274,7 @@ function leaveServer(){
|
||||
const responseObject = JSON.parse(Http.responseText)
|
||||
console.log(Http.status);
|
||||
console.log(responseObject);
|
||||
createToast(`${responseObject} is leaving`);
|
||||
setTimeout(() => {}, 45000);
|
||||
createToast(`${responseObject} is leaving`, { showNow: true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +296,7 @@ function saveNodeDetails() {
|
||||
console.log("Request Body: ", reqBody);
|
||||
|
||||
const Http = new XMLHttpRequest();
|
||||
const url='/nodes/'+nodeId;
|
||||
const url = '/nodes/' + nodeId;
|
||||
Http.open("PUT", url);
|
||||
Http.setRequestHeader("Content-Type", "application/json");
|
||||
Http.send(JSON.stringify(reqBody));
|
||||
@@ -181,15 +306,17 @@ function saveNodeDetails() {
|
||||
console.log(Http.status);
|
||||
console.log(responseObject);
|
||||
createToast(`Node Updated!`);
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
function addNewSystem(systemName) {
|
||||
function addNewSystem() {
|
||||
const nodeId = document.getElementById("nodeId").value;
|
||||
const systemMode = document.getElementById(`${systemName}_systemMode`).value;
|
||||
const inputSystemFreqs = $(`[id^="${systemName}_systemFreq_"]`);
|
||||
const systemName = document.getElementById(`New System_systemName`).value;
|
||||
const systemMode = document.getElementById(`New System_systemMode`).value;
|
||||
const inputSystemFreqs = $(`[id^="New System_systemFreq_"]`);
|
||||
let systemFreqs = [];
|
||||
for (const inputFreq of inputSystemFreqs){
|
||||
for (const inputFreq of inputSystemFreqs) {
|
||||
systemFreqs.push(inputFreq.value);
|
||||
}
|
||||
|
||||
@@ -201,13 +328,13 @@ function addNewSystem(systemName) {
|
||||
|
||||
console.log("Request Body: ", reqBody);
|
||||
const Http = new XMLHttpRequest();
|
||||
const url='/nodes/'+nodeId+"/systems";
|
||||
const url = '/nodes/' + nodeId + "/systems";
|
||||
Http.open("POST", url);
|
||||
Http.setRequestHeader("Content-Type", "application/json");
|
||||
Http.send(JSON.stringify(reqBody));
|
||||
|
||||
Http.onloadend = (e) => {
|
||||
const responseObject = JSON.parse(Http.responseText)
|
||||
const responseObject = Http.responseText
|
||||
console.log(Http.status);
|
||||
console.log(responseObject);
|
||||
createToast(`${systemName} Added!`);
|
||||
@@ -218,9 +345,9 @@ function addNewSystem(systemName) {
|
||||
function updateSystem(systemName) {
|
||||
const nodeId = document.getElementById("nodeId").value;
|
||||
const systemMode = document.getElementById(`${systemName}_systemMode`).value;
|
||||
const inputSystemFreqs = $(`[id^="${systemName}_systemFreq_"]`);
|
||||
const inputSystemFreqs = $(`[id^="${systemName}_systemFreq_"]`);
|
||||
let systemFreqs = [];
|
||||
for (const inputFreq of inputSystemFreqs){
|
||||
for (const inputFreq of inputSystemFreqs) {
|
||||
systemFreqs.push(inputFreq.value);
|
||||
}
|
||||
|
||||
@@ -232,13 +359,13 @@ function updateSystem(systemName) {
|
||||
|
||||
console.log("Request Body: ", reqBody);
|
||||
const Http = new XMLHttpRequest();
|
||||
const url='/nodes/'+nodeId+"/systems";
|
||||
const url = '/nodes/' + nodeId + "/systems";
|
||||
Http.open("PUT", url);
|
||||
Http.setRequestHeader("Content-Type", "application/json");
|
||||
Http.send(JSON.stringify(reqBody));
|
||||
|
||||
Http.onloadend = (e) => {
|
||||
const responseObject = JSON.parse(Http.responseText)
|
||||
const responseObject = Http.responseText;
|
||||
console.log(Http.status);
|
||||
console.log(responseObject);
|
||||
createToast(`${systemName} Updated!`);
|
||||
@@ -246,6 +373,28 @@ function updateSystem(systemName) {
|
||||
}
|
||||
}
|
||||
|
||||
function removeSystem(systemName) {
|
||||
const nodeId = document.getElementById("nodeId").value;
|
||||
|
||||
const reqBody = {
|
||||
'systemName': systemName,
|
||||
}
|
||||
|
||||
console.log("Request Body: ", reqBody);
|
||||
const Http = new XMLHttpRequest();
|
||||
const url = '/nodes/' + nodeId + "/systems";
|
||||
Http.open("DELETE", url);
|
||||
Http.setRequestHeader("Content-Type", "application/json");
|
||||
Http.send(JSON.stringify(reqBody));
|
||||
Http.onloadend = (e) => {
|
||||
const responseObject = Http.responseText;
|
||||
console.log(Http.status);
|
||||
console.log(responseObject);
|
||||
createToast(`${systemName} Removed!`);
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
function requestNodeUpdate() {
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ router.post('/:nodeId/systems', nodesController.addNodeSystem);
|
||||
// Update a system on an existing node
|
||||
router.put('/:nodeId/systems', nodesController.updateNodeSystem);
|
||||
|
||||
// Delete a system from an existing node
|
||||
router.delete('/:nodeId/systems', nodesController.removeNodeSystem);
|
||||
|
||||
// TODO Need to authenticate this request
|
||||
/* POST a new node to the server
|
||||
*
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<!-- Checkin with client button -->
|
||||
<a type="button" class="btn btn-secondary" href="#" onclick="sendNodeHeartbeat('<%=node.id%>')">Check-in with Node</a>
|
||||
<!-- Update Client button -->
|
||||
<a type="button" class="btn btn-warning" href="#" onclick="requestNodeUpdate('<%=node.id%>')">Update Node</a>
|
||||
<a type="button" class="btn btn-warning disabled" href="#" onclick="requestNodeUpdate('<%=node.id%>')">Update Node</a>
|
||||
</div>
|
||||
<hr>
|
||||
<form>
|
||||
@@ -107,7 +107,7 @@
|
||||
data-bs-target="#updateSystemModal_<%=system.replaceAll(" ", "_")%>">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
<a href="#" class="table-link text-danger label">
|
||||
<a class="table-link text-danger label" onclick="removeSystem('<%=system%>')">
|
||||
<i class="bi bi-trash"></i>
|
||||
</a>
|
||||
</td>
|
||||
@@ -135,5 +135,4 @@
|
||||
<% // new System Modal %>
|
||||
<%- include("partials/modifySystemModal.ejs", {'system': "New System", 'frequencies': [], 'mode': ''}) %>
|
||||
<%- include('partials/bodyEnd.ejs') %>
|
||||
<script src="/res/js/node.js"></script>
|
||||
<%- include('partials/htmlFooter.ejs') %>
|
||||
@@ -9,4 +9,6 @@
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.0/jquery.min.js"
|
||||
integrity="sha512-3gJwYpMe3QewGELv8k/BX9vcqhryRdzRMxVfq6ngyWXwo03GFEzjsUm8Q7RZcHPHksttq7/GFoxjCVUjkjvPdw=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
|
||||
<script src="/res/js/node.js"></script>
|
||||
@@ -1,7 +1,6 @@
|
||||
<!doctype html>
|
||||
<html lang="en" data-bs-theme="auto">
|
||||
<%- include('head.ejs') %>
|
||||
<body>
|
||||
<div class="toast-container mx-2" id="toastZone"></div>
|
||||
<body>
|
||||
<%- include('navbar.ejs') %>
|
||||
<%- include('sidebar.ejs') %>
|
||||
@@ -12,7 +12,7 @@
|
||||
<form>
|
||||
<div class="row gx-3 mb-3">
|
||||
<label class="small mb-1 fs-6" for="systemName">System Name</label>
|
||||
<input class="form-control" id="systemName" type="text" value="<%if (!system == "New System") {%><%= system %><%} else {%>Local Radio System<%}%>"></input>
|
||||
<input class="form-control" id="<%=system%>_systemName" type="text" value="<%if (!system == "New System") {%><%= system %><%} else {%>Local Radio System<%}%>"></input>
|
||||
</div>
|
||||
<div class="row gx-3 mb-3" id="frequencyRow_<%=system.replaceAll(" ", "_")%>">
|
||||
<label class="small mb-1 fs-6" for="systemFreq">Frequencies</label>
|
||||
@@ -54,7 +54,7 @@
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" onclick="location.reload()">Close</button>
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" <%if(!system == "New System") {%>onclick="updateSystem('<%=system%>')"<%} else {%>onclick="addNewSystem('<%=system%>')"<%}%>>Save changes</button>
|
||||
<button type="button" class="btn btn-primary" <%if(!system == "New System") {%>onclick="updateSystem('<%=system%>')"<%} else {%>onclick="addNewSystem('<%=system%>')"<%}%>>Save changes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -28,10 +28,10 @@
|
||||
<li><a class="dropdown-item" href="#">Something else here</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link disabled">Disabled</a>
|
||||
</li>
|
||||
*/%>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="navbar-notification-bell" onclick="showStoredToasts()"><i class="bi bi-bell-fill"></i></a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
<div class="container-fluid mt-5">
|
||||
<div aria-live="polite" aria-atomic="true" class="position-relative">
|
||||
<!-- Position it: -->
|
||||
<!-- - `.toast-container` for spacing between toasts -->
|
||||
<!-- - `top-0` & `end-0` to position the toasts in the upper right corner -->
|
||||
<!-- - `.p-3` to prevent the toasts from sticking to the edge of the container -->
|
||||
<div class="toast-container top-0 end-0 p-3 max" id="toastZone">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row flex-nowrap">
|
||||
<div class="col-auto col-md-3 col-xl-2 px-sm-2 px-0 bg-dark sidebar-container">
|
||||
<div
|
||||
@@ -34,6 +42,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
*/%>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col py-3">
|
||||
<div class="col py-3">
|
||||
|
||||
Reference in New Issue
Block a user