Additional changes for #37
- Updating side bar - Updating nav bar - Adding node details page - Adding controller page - Updating routes
This commit is contained in:
@@ -2,15 +2,61 @@
|
||||
const { DebugBuilder } = require("../utilities/debugBuilder.js");
|
||||
const log = new DebugBuilder("server", "nodesController");
|
||||
// Utilities
|
||||
const {getAllNodes, addNewNode, updateNodeInfo, getNodeInfoFromId, getOnlineNodes } = require("../utilities/mysqlHandler");
|
||||
const { getAllNodes, addNewNode, updateNodeInfo, getNodeInfoFromId, getOnlineNodes } = require("../utilities/mysqlHandler");
|
||||
const utils = require("../utilities/utils");
|
||||
const { sendHttpRequest, requestOptions } = require("../utilities/httpRequests.js");
|
||||
const { nodeObject } = require("../utilities/recordHelper.js");
|
||||
const { joinServerWrapper } = require("../commands/join");
|
||||
const { leaveServerWrapper } = require("../commands/leave");
|
||||
|
||||
const refreshInterval = process.env.NODE_MONITOR_REFRESH_INTERVAL ?? 1200000;
|
||||
|
||||
/**
|
||||
* Check in with a singular node, mark it offline if it's offline and
|
||||
*
|
||||
* @param {*} node The node Object to check in with
|
||||
*/
|
||||
async function checkInWithNode(node) {
|
||||
const reqOptions = new requestOptions("/client/requestCheckIn", "GET", node.ip, node.port)
|
||||
sendHttpRequest(reqOptions, "", (responseObj) => {
|
||||
if (responseObj) {
|
||||
log.DEBUG("Response from: ", node.name, responseObj);
|
||||
const onlineNode = new nodeObject({ _online: false, _id: node.id });
|
||||
log.DEBUG("Node update object: ", onlineNode);
|
||||
updateNodeInfo(onlineNode, (sqlResponse) => {
|
||||
if (!sqlResponse) this.log.ERROR("No response from SQL object");
|
||||
|
||||
log.DEBUG("Updated node: ", sqlResponse);
|
||||
return true
|
||||
})
|
||||
}
|
||||
else {
|
||||
log.DEBUG("No response from node, assuming it's offline");
|
||||
const offlineNode = new nodeObject({ _online: false, _id: node.id });
|
||||
log.DEBUG("Offline node update object: ", offlineNode);
|
||||
updateNodeInfo(offlineNode, (sqlResponse) => {
|
||||
if (!sqlResponse) this.log.ERROR("No response from SQL object");
|
||||
|
||||
log.DEBUG("Updated offline node: ", sqlResponse);
|
||||
return false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
exports.checkInWithNode = checkInWithNode;
|
||||
|
||||
/**
|
||||
* Check in with all online nodes and mark any nodes that are actually offline
|
||||
*/
|
||||
async function checkInWithOnlineNodes() {
|
||||
getOnlineNodes((nodes) => {
|
||||
log.DEBUG("Online Nodes: ", nodes);
|
||||
for (const node of nodes) {
|
||||
checkInWithNode(node);
|
||||
}
|
||||
return;
|
||||
});
|
||||
}
|
||||
exports.checkInWithOnlineNodes = checkInWithOnlineNodes;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} req Default express req from router
|
||||
@@ -28,7 +74,7 @@ exports.listAllNodes = async (req, res) => {
|
||||
* Add a new node to the storage
|
||||
* @param {*} req Default express req from router
|
||||
* @param {*} res Defualt express res from router
|
||||
*/
|
||||
*/
|
||||
exports.newNode = async (req, res) => {
|
||||
if (!req.body.name) return res.status(400).json("No name specified for new node");
|
||||
|
||||
@@ -45,7 +91,7 @@ exports.newNode = async (req, res) => {
|
||||
|
||||
addNewNode(newNode, (newNodeObject) => {
|
||||
// Send back a success if the user has been added and the ID for the client to keep track of
|
||||
res.status(202).json({"nodeId": newNodeObject.id});
|
||||
res.status(202).json({ "nodeId": newNodeObject.id });
|
||||
})
|
||||
}
|
||||
catch (err) {
|
||||
@@ -62,7 +108,7 @@ exports.newNode = async (req, res) => {
|
||||
*
|
||||
* @param {*} req Default express req from router
|
||||
* @param {*} res Defualt express res from router
|
||||
*/
|
||||
*/
|
||||
exports.getNodeInfo = async (req, res) => {
|
||||
if (!req.query.id) return res.status(400).json("No id specified");
|
||||
getNodeInfoFromId(req.query.id, (nodeInfo) => {
|
||||
@@ -75,7 +121,7 @@ exports.getNodeInfo = async (req, res) => {
|
||||
* @param {*} req Default express req from router
|
||||
* @param {*} res Defualt express res from router
|
||||
*/
|
||||
exports.nodeCheckIn = async (req, res) => {
|
||||
exports.nodeCheckIn = async (req, res) => {
|
||||
if (!req.body.id) return res.status(400).json("No id specified");
|
||||
getNodeInfoFromId(req.body.id, (nodeInfo) => {
|
||||
let checkInObject = {};
|
||||
@@ -87,7 +133,7 @@ exports.nodeCheckIn = async (req, res) => {
|
||||
if (req.body.name && req.body.name != nodeInfo.name) {
|
||||
checkInObject._name = req.body.name;
|
||||
isObjectUpdated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (req.body.ip && req.body.ip != nodeInfo.ip) {
|
||||
checkInObject._ip = req.body.ip;
|
||||
@@ -115,9 +161,9 @@ exports.nodeCheckIn = async (req, res) => {
|
||||
}
|
||||
|
||||
// If no changes are made tell the client
|
||||
if (!isObjectUpdated) return res.status(200).json("No keys updated");
|
||||
if (!isObjectUpdated) return res.status(200).json("No keys updated");
|
||||
|
||||
log.INFO("Updating the following keys for ID: ", req.body.id, checkInObject);
|
||||
log.INFO("Updating the following keys for ID: ", req.body.id, checkInObject);
|
||||
|
||||
checkInObject._id = req.body.id;
|
||||
checkInObject = new nodeObject(checkInObject);
|
||||
@@ -125,40 +171,28 @@ exports.nodeCheckIn = async (req, res) => {
|
||||
if (!nodeInfo) {
|
||||
log.WARN("No existing node found with this ID, adding node: ", checkInObject);
|
||||
addNewNode(checkInObject, (newNode) => {
|
||||
return res.status(201).json({"updatedKeys": newNode});
|
||||
return res.status(201).json({ "updatedKeys": newNode });
|
||||
});
|
||||
}
|
||||
else {
|
||||
else {
|
||||
updateNodeInfo(checkInObject, () => {
|
||||
return res.status(202).json({"updatedKeys": checkInObject});
|
||||
return res.status(202).json({ "updatedKeys": checkInObject });
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the node to join the specified server/channel and listen to the specified resource
|
||||
* Requests a specific node to check in with the server, if it's online
|
||||
*
|
||||
* @param {*} req Default express req from router
|
||||
* @param {*} res Defualt express res from router
|
||||
* @param req.body.clientId The client ID to join discord with (NOT dev portal ID, right click user -> Copy ID)
|
||||
* @param req.body.channelId The Channel ID to join in Discord
|
||||
* @param req.body.presetName The preset name to listen to in Discord
|
||||
*/
|
||||
exports.requestNodeJoinServer = async (req, res) => {
|
||||
if (!req.body.clientId || !req.body.channelId || !req.body.presetName) return res.status(400).json("Missing information in request, requires clientId, channelId, presetName");
|
||||
await joinServerWrapper(req.body.presetName, req.body.channelId, req.body.clientId);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} req Default express req from router
|
||||
* @param {*} res Defualt express res from router
|
||||
* @param {*} req.body.clientId The client ID to request to leave the server
|
||||
*/
|
||||
exports.requestNodeLeaveServer = async (req, res) => {
|
||||
if (!req.body.ClientId) return res.status(400).json("Missing client ID in request");
|
||||
await leaveServerWrapper({clientId: req.body.ClientId});
|
||||
exports.requestNodeCheckIn = async (req, res) => {
|
||||
if (!req.params.nodeId) return res.status(400).json("No Node ID supplied in request");
|
||||
const node = await getNodeInfoFromId(req.params.nodeId);
|
||||
if (!node) return res.status(400).json("No Node with the ID given");
|
||||
checkInWithNode(node);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,49 +206,21 @@ exports.nodeMonitorService = class nodeMonitorService {
|
||||
/**
|
||||
* Start the node monitor service in the background
|
||||
*/
|
||||
async start(){
|
||||
// Wait for the a portion of the refresh period before checking in with the nodes, so the rest of the bot can start
|
||||
await new Promise(resolve => setTimeout(resolve, refreshInterval/10));
|
||||
async start() {
|
||||
// Wait for the a portion of the refresh period before checking in with the nodes, so the rest of the bot can start
|
||||
await new Promise(resolve => setTimeout(resolve, refreshInterval / 10));
|
||||
|
||||
log.INFO("Starting Node Monitor Service");
|
||||
// Check in before starting the infinite loop
|
||||
await this.checkInWithOnlineNodes();
|
||||
|
||||
|
||||
while(true){
|
||||
await checkInWithOnlineNodes();
|
||||
|
||||
|
||||
while (true) {
|
||||
// Wait for the refresh interval, then wait for the posts to return, then wait a quarter of the refresh interval to make sure everything is cleared up
|
||||
await new Promise(resolve => setTimeout(resolve, refreshInterval));
|
||||
await this.checkInWithOnlineNodes();
|
||||
await checkInWithOnlineNodes();
|
||||
await new Promise(resolve => setTimeout(resolve, refreshInterval / 4));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check in with all online nodes and mark any nodes that are actually offline
|
||||
*/
|
||||
async checkInWithOnlineNodes(){
|
||||
getOnlineNodes((nodes) => {
|
||||
this.log.DEBUG("Online Nodes: ", nodes);
|
||||
for (const node of nodes) {
|
||||
const reqOptions = new requestOptions("/client/requestCheckIn", "GET", node.ip, node.port)
|
||||
sendHttpRequest(reqOptions, "", (responseObj) => {
|
||||
if (responseObj) {
|
||||
this.log.DEBUG("Response from: ", node.name, responseObj);
|
||||
}
|
||||
else {
|
||||
this.log.DEBUG("No response from node, assuming it's offline");
|
||||
const offlineNode = new nodeObject({ _online: 0, _id: node.id });
|
||||
this.log.DEBUG("Offline node update object: ", offlineNode);
|
||||
updateNodeInfo(offlineNode, (sqlResponse) => {
|
||||
if (!sqlResponse) this.log.ERROR("No response from SQL object");
|
||||
|
||||
this.log.DEBUG("Updated offline node: ", sqlResponse);
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
return;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,4 +139,38 @@ a {
|
||||
/* Global Section */
|
||||
.sidebar-container {
|
||||
min-height: 94.2vh;
|
||||
}
|
||||
}
|
||||
|
||||
/* User table section */
|
||||
|
||||
.label {
|
||||
border-radius: 3px;
|
||||
font-size: 1.1em;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.user-list tbody td .user-subhead {
|
||||
font-size: 1em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.table thead tr th {
|
||||
text-transform: uppercase;
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
.table thead tr th {
|
||||
border-bottom: 2px solid #e7ebee;
|
||||
}
|
||||
|
||||
.table tbody tr td:first-child {
|
||||
font-size: 1.125em;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.table tbody tr td {
|
||||
font-size: 0.875em;
|
||||
vertical-align: middle;
|
||||
border-top: 1px solid #e7ebee;
|
||||
padding: 12px 8px;
|
||||
}
|
||||
|
||||
50
Server/public/res/js/node.js
Normal file
50
Server/public/res/js/node.js
Normal file
@@ -0,0 +1,50 @@
|
||||
function addFrequencyInput(system){
|
||||
// Create new input
|
||||
var icon = document.createElement('i');
|
||||
icon.classList.add('bi');
|
||||
icon.classList.add('bi-x-circle');
|
||||
icon.classList.add('text-black');
|
||||
|
||||
var remove = document.createElement('a');
|
||||
remove.classList.add('align-middle');
|
||||
remove.classList.add('float-left');
|
||||
remove.href = '#'
|
||||
remove.appendChild(icon);
|
||||
|
||||
var childColRemoveIcon = document.createElement('div');
|
||||
childColRemoveIcon.classList.add('col-2');
|
||||
childColRemoveIcon.appendChild(remove);
|
||||
|
||||
var input = document.createElement('input');
|
||||
input.classList.add('form-control');
|
||||
input.id = 'nodeFreq';
|
||||
input.type = 'text';
|
||||
|
||||
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");
|
||||
childRow.appendChild(childColInput);
|
||||
childRow.appendChild(childColRemoveIcon);
|
||||
|
||||
var colParent = document.createElement('div');
|
||||
colParent.classList.add("col-md-6");
|
||||
colParent.classList.add("mb-1");
|
||||
colParent.appendChild(childRow);
|
||||
|
||||
document.getElementById(`frequencyRow_${system.replaceAll(" ", "_")}`).appendChild(colParent);
|
||||
}
|
||||
|
||||
function checkInByNodeId(nodeId){
|
||||
const Http = new XMLHttpRequest();
|
||||
const url='/nodes/'+nodeId;
|
||||
Http.open("GET", url);
|
||||
Http.send();
|
||||
|
||||
Http.onreadystatechange = (e) => {
|
||||
console.log(Http.responseText)
|
||||
}
|
||||
}
|
||||
@@ -28,8 +28,7 @@ router.get('/nodeInfo', nodesController.getNodeInfo);
|
||||
// Client checkin with the server to update information
|
||||
router.post('/nodeCheckIn', nodesController.nodeCheckIn);
|
||||
|
||||
// TODO Need to authenticate this request
|
||||
// Request a particular client to join a particular channel listening to a particular preset
|
||||
router.post('/joinServer', nodesController.requestNodeJoinServer);
|
||||
// Request a node to check in with the server
|
||||
router.get('/:nodeId', nodesController.requestNodeCheckIn);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -118,8 +118,7 @@ class nodeObject {
|
||||
this.ip = _ip;
|
||||
this.port = _port;
|
||||
this.location = _location;
|
||||
this.nearbySystems = _nearbySystems;
|
||||
if (this.nearbySystems) this.presets = Object.keys(_nearbySystems);
|
||||
this.nearbySystems = _nearbySystems;
|
||||
this.online = _online;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +1,154 @@
|
||||
<%- include('partials/htmlHead.ejs') %>
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
Account Details
|
||||
<% if(node.online){%> <span class="badge badge-soft-success mb-0">Online</span>
|
||||
<% } else {%> <span class="badge badge-soft-danger mb-0">Offline</span>
|
||||
<% } %>
|
||||
<div class="container">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<p>
|
||||
<span class="fs-2 fw-semibold">
|
||||
Node Details
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form>
|
||||
<div class="row gx-3 mb-3">
|
||||
<div class="">
|
||||
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="small mb-1" for="nodeId">Node ID (this is the assigned Node ID and cannot be
|
||||
changed)</label>
|
||||
<input class="form-control" id="nodeId" type="text" value="<%=node.id%>" disabled></input>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="small mb-1" for="nodeStatus">Online Status:</label>
|
||||
<% if(node.online){%> <span class="badge badge-soft-success mb-0 align-middle fs-6" id="nodeStatus">Online</span>
|
||||
<% } else {%> <span class="badge badge-soft-danger mb-0 align-middle fs-6">Offline</span>
|
||||
<% } %>
|
||||
<hr>
|
||||
<!-- Join Server button-->
|
||||
<a type="button" class="btn btn-info <% if(!node.online) { %>disabled<% } %>" href="/join/<%=node.id%>">Join Server</a>
|
||||
<!-- Leave Server button -->
|
||||
<a type="button" class="btn btn-danger <% if(!node.online) { %>disabled<% } %>" href="/leave/<%=node.id%>">Leave Server</a>
|
||||
<!-- Checkin with client button -->
|
||||
<a type="button" class="btn btn-secondary" href="#" onclick="checkInByNodeId('<%=node.id%>')">Check-in with Node</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row gx-3 mb-3">
|
||||
<div class="col-md-12">
|
||||
<label class="small mb-1" for="inputNodeName">Node Name:</label>
|
||||
<input class="form-control" id="inputNodeName" type="text" value="<%=node.name%>"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row gx-3 mb-3">
|
||||
<div class="col-md-4">
|
||||
<label class="small mb-1" for="inputNodeIp">Node IP Address (that the server can
|
||||
contact):</label>
|
||||
<input class="form-control" id="inputNodeIp" type="text" value="<%=node.ip%>"></input>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="small mb-1" for="inputOrgName">Node Port (with the API):</label>
|
||||
<input class="form-control" id="inputOrgName" type="number" value="<%=node.port%>"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="small mb-1" for="inputNodeLocation">Node Location (physical location):</label>
|
||||
<input class="form-control" id="inputNodeLocation" type="location" value="<%=node.location%>"></input>
|
||||
</div>
|
||||
<h4>
|
||||
Nearby Systems
|
||||
</h4>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="main-box no-header clearfix">
|
||||
<div class="main-box-body clearfix">
|
||||
<div class="table-responsive">
|
||||
<table class="table user-list <% if(!node.online) { %>disabled<% } %>">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span>System Name</span></th>
|
||||
<th><span>Frequencies</span></th>
|
||||
<th><span>Protocol</span></th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% for(const system in node.nearbySystems){ %>
|
||||
<tr>
|
||||
<td>
|
||||
<%= system %>
|
||||
</td>
|
||||
<td>
|
||||
<% if(node.nearbySystems[system].frequencies.length> 1) { %>
|
||||
<ul>
|
||||
<% for(const frequency of
|
||||
node.nearbySystems[system].frequencies) { %>
|
||||
<li>
|
||||
<%=frequency%> MHz
|
||||
</li>
|
||||
<% } %>
|
||||
</ul>
|
||||
<% } else { const
|
||||
frequency=node.nearbySystems[system].frequencies[0]
|
||||
%>
|
||||
<%=frequency%> MHz
|
||||
<% } %>
|
||||
</td>
|
||||
<td>
|
||||
<span class="label label-default text-uppercase">
|
||||
<%= node.nearbySystems[system].mode %>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<a href="#" class="table-link text-info label"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#updateSystemModal_<%=system.replaceAll(" ", "_")%>">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
<a href="#" class="table-link text-danger label">
|
||||
<i class="bi bi-trash"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<% // Update system modal %>
|
||||
<%- include("partials/modifySystemModal.ejs", {'system': system, 'frequencies': node.nearbySystems[system].frequencies}) %>
|
||||
<% } %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Save changes button-->
|
||||
<button class="btn btn-primary <% if(!node.online) { %>disabled<% } %>" type="button">Save changes</button>
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-primary float-right <% if(!node.online) { %>disabled<% } %>" data-bs-toggle="modal"
|
||||
data-bs-target="#newSystemModal">Add New System</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form>
|
||||
<div class="mb-3">
|
||||
<label class="small mb-1" for="nodeId">Node ID (this is the assigned Node ID and cannot be changed)</label>
|
||||
<input class="form-control" id="nodeId" type="text" value="<%=node.id%>" disabled>
|
||||
</div>
|
||||
<div class="row gx-3 mb-3">
|
||||
<div class="col-md-12">
|
||||
<label class="small mb-1" for="inputNodeName">Node Name</label>
|
||||
<input class="form-control" id="inputNodeName" type="text" value="<%=node.name%>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row gx-3 mb-3">
|
||||
<div class="col-md-4">
|
||||
<label class="small mb-1" for="inputNodeIp">Node IP Address (that the server can contact)</label>
|
||||
<input class="form-control" id="inputNodeIp" type="text" value="<%=node.ip%>">
|
||||
|
||||
<% // new System Modal %>
|
||||
<div class="modal fade" id="newSystemModal" tabindex="-1" aria-labelledby="newSystemModal" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="newSystemModal">Add a New System</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="small mb-1" for="inputOrgName">Node Port (with the API)</label>
|
||||
<input class="form-control" id="inputOrgName" type="number" value="<%=node.port%>">
|
||||
<div class="modal-body">
|
||||
...
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary">Save changes</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="small mb-1" for="inputNodeLocation">Node Location (physical location)</label>
|
||||
<input class="form-control" id="inputNodeLocation" type="location" value="<%=node.location%>">
|
||||
</div>
|
||||
<!-- Save changes button-->
|
||||
<button class="btn btn-primary" type="button">Save changes</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<%- include('partials/bodyEnd.ejs') %>
|
||||
<script src="/res/js/node.js"></script>
|
||||
<%- include('partials/htmlFooter.ejs') %>
|
||||
@@ -7,5 +7,6 @@
|
||||
integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz"
|
||||
crossorigin="anonymous"></script>
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.7.0.slim.min.js"
|
||||
integrity="sha256-tG5mcZUtJsZvyKAxYLVXrmjKBVLd6VpVccqz/r4ypFE=" crossorigin="anonymous"></script>
|
||||
<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>
|
||||
54
Server/views/partials/modifySystemModal.ejs
Normal file
54
Server/views/partials/modifySystemModal.ejs
Normal file
@@ -0,0 +1,54 @@
|
||||
<div class="modal fade" id="updateSystemModal_<%=system.replaceAll(" ", "_")%>" tabindex="-1" aria-labelledby="updateSystemModal_<%=system.replaceAll(" ", "_")%>"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="updateSystemModal_<%=system.replaceAll(" ", "_")%>">Update <%=system%></h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<form>
|
||||
<div class="row gx-3 mb-3" id="frequencyRow_<%=system.replaceAll(" ", "_")%>">
|
||||
<label class="small mb-1 fs-6" for="nodeFreq">Frequencies</label>
|
||||
<% for(const frequency of frequencies) { %>
|
||||
<div class="col-md-6 mb-1">
|
||||
<div class="row px-1">
|
||||
<div class="col-10">
|
||||
<input class="form-control" id="nodeFreq" type="text" value="<%= frequency %>"></input>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<a class="align-middle float-left" href="#"><i class="bi bi-x-circle text-black"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
<button type="button" class="btn btn-info text-white" onclick="addFrequencyInput('<%=system%>')">Add Frequency</button>
|
||||
<hr>
|
||||
<div class="row gx-3 mb-3">
|
||||
<div class="col-md-6">
|
||||
<label class="small mb-1 fs-6" for="modeSelect">Mode</label>
|
||||
<br>
|
||||
<select class="custom-select" id="modeSelect">
|
||||
<option value="<%= node.nearbySystems[system].mode %>" selected><span class="text-uppercase"><%= node.nearbySystems[system].mode %></span></option>
|
||||
<% if(node.nearbySystems[system].mode == "p25") { %>
|
||||
<option value="nbfm">NBFM</option>
|
||||
<% } else if (node.nearbySystems[system].mode == "nbfm") { %>
|
||||
<option value="p25">P25</option>
|
||||
<% } %>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary">Save changes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,12 +1,13 @@
|
||||
<nav class="navbar navbar-expand-lg bg-body-tertiary" data-bs-theme="dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#">Navbar</a>
|
||||
<a class="navbar-brand" href="#">Node Master</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
|
||||
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
<% /*
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="#">Home</a>
|
||||
</li>
|
||||
@@ -30,6 +31,7 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link disabled">Disabled</a>
|
||||
</li>
|
||||
*/%>
|
||||
</ul>
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
|
||||
|
||||
@@ -3,64 +3,18 @@
|
||||
<div class="col-auto col-md-3 col-xl-2 px-sm-2 px-0 bg-dark">
|
||||
<div class="d-flex flex-column align-items-center align-items-sm-start px-3 pt-2 text-white sidebar-container">
|
||||
<ul class="nav nav-pills flex-column mb-sm-auto mb-0 align-items-center align-items-sm-start" id="menu">
|
||||
<li class="nav-item">
|
||||
<a href="#" class="nav-link align-middle px-0">
|
||||
<i class="fs-4 bi-house"></i> <span class="ms-1 d-none d-sm-inline">Home</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#submenu1" data-bs-toggle="collapse" class="nav-link px-0 align-middle">
|
||||
<a href="/" class="nav-link px-0 align-middle">
|
||||
<i class="fs-4 bi-speedometer2"></i> <span class="ms-1 d-none d-sm-inline">Dashboard</span>
|
||||
</a>
|
||||
<ul class="collapse nav flex-column ms-1" id="submenu1" data-bs-parent="#menu">
|
||||
<li class="w-100">
|
||||
<a href="#" class="nav-link px-0"> <span class="d-none d-sm-inline">Item</span> 1 </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="nav-link px-0"> <span class="d-none d-sm-inline">Item</span> 2 </a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="nav-link px-0 align-middle">
|
||||
<i class="fs-4 bi-table"></i> <span class="ms-1 d-none d-sm-inline">Orders</span></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#submenu2" data-bs-toggle="collapse" class="nav-link px-0 align-middle ">
|
||||
<i class="fs-4 bi-bootstrap"></i> <span class="ms-1 d-none d-sm-inline">Bootstrap</span></a>
|
||||
<ul class="collapse nav flex-column ms-1" id="submenu2" data-bs-parent="#menu">
|
||||
<li class="w-100">
|
||||
<a href="#" class="nav-link px-0"> <span class="d-none d-sm-inline">Item</span> 1</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="nav-link px-0"> <span class="d-none d-sm-inline">Item</span> 2</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#submenu3" data-bs-toggle="collapse" class="nav-link px-0 align-middle">
|
||||
<i class="fs-4 bi-grid"></i> <span class="ms-1 d-none d-sm-inline">Products</span> </a>
|
||||
<ul class="collapse nav flex-column ms-1" id="submenu3" data-bs-parent="#menu">
|
||||
<li class="w-100">
|
||||
<a href="#" class="nav-link px-0"> <span class="d-none d-sm-inline">Product</span> 1</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="nav-link px-0"> <span class="d-none d-sm-inline">Product</span> 2</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="nav-link px-0"> <span class="d-none d-sm-inline">Product</span> 3</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="nav-link px-0"> <span class="d-none d-sm-inline">Product</span> 4</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="nav-link px-0 align-middle">
|
||||
<i class="fs-4 bi-people"></i> <span class="ms-1 d-none d-sm-inline">Customers</span> </a>
|
||||
<a href="/controller" class="nav-link px-0 align-middle">
|
||||
<i class="fs-4 bi-grid"></i> <span class="ms-1 d-none d-sm-inline">Controller</span> </a>
|
||||
</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<% /*
|
||||
<div class="dropdown pb-4 fixed-bottom px-3">
|
||||
<a href="#" class="d-flex align-items-center text-white text-decoration-none dropdown-toggle"
|
||||
id="dropdownUser1" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
@@ -78,6 +32,7 @@
|
||||
<li><a class="dropdown-item" href="#">Sign out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
*/%>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col py-3">
|
||||
Reference in New Issue
Block a user