mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2025-12-07 09:36:05 -08:00
Icon selector
This commit is contained in:
@@ -1226,6 +1226,52 @@ input[readonly] {
|
||||
/* Devices page */
|
||||
/* ----------------------------------------------------------------- */
|
||||
|
||||
.modal-header .close
|
||||
{
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.modal-title
|
||||
{
|
||||
display: inline;
|
||||
}
|
||||
|
||||
#iconList
|
||||
{
|
||||
padding: 10px;
|
||||
padding-bottom:30px;
|
||||
}
|
||||
|
||||
.iconPreviewSelector:hover
|
||||
{
|
||||
backdrop-filter: brightness(50%);
|
||||
}
|
||||
|
||||
.iconPreviewSelector
|
||||
{
|
||||
text-align: center;
|
||||
padding: 15px;
|
||||
height: 80px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.iconList
|
||||
{
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.iconPreviewSelector svg
|
||||
{
|
||||
width:40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.iconPreviewSelector i
|
||||
{
|
||||
font-size: 30px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.iconPreview {
|
||||
min-width: 40px;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
require 'php/templates/header.php';
|
||||
?>
|
||||
|
||||
|
||||
<!-- Page ------------------------------------------------------------------ -->
|
||||
<div class="content-wrapper">
|
||||
|
||||
@@ -214,9 +215,7 @@ switch ($UI_THEME) {
|
||||
?>
|
||||
|
||||
<!-- page script ----------------------------------------------------------- -->
|
||||
<script defer>
|
||||
|
||||
|
||||
<script >
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
@@ -238,11 +237,16 @@ switch ($UI_THEME) {
|
||||
var selectedTab = 'tabDetails';
|
||||
var emptyArr = ['undefined', "", undefined, null];
|
||||
|
||||
main();
|
||||
|
||||
// Call renderSmallBoxes, then main
|
||||
(async () => {
|
||||
await renderSmallBoxes();
|
||||
main();
|
||||
})();
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function main () {
|
||||
|
||||
// Initialize MAC
|
||||
var urlParams = new URLSearchParams(window.location.search);
|
||||
if (urlParams.has ('mac') == true) {
|
||||
@@ -670,88 +674,103 @@ function initTable(tableId, mac){
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Render the small boxes on top
|
||||
function renderSmallBoxes(customData) {
|
||||
$.ajax({
|
||||
url: 'php/components/device_cards.php', // PHP script URL
|
||||
type: 'POST', // Use POST method to send data
|
||||
dataType: 'html', // Expect HTML response
|
||||
data: { items: JSON.stringify(customData) }, // Send customData as JSON
|
||||
success: function(response) {
|
||||
$('#TopSmallBoxes').html(response); // Replace container content with fetched HTML
|
||||
hideSpinner()
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('Error fetching small boxes:', error);
|
||||
async function renderSmallBoxes() {
|
||||
|
||||
|
||||
try {
|
||||
// Show loading dialog
|
||||
showSpinner();
|
||||
|
||||
// Get data from the server
|
||||
const response = await fetch(`php/server/devices.php?action=getServerDeviceData&mac=${getMac()}&period=${period}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error fetching device data: ${response.statusText}`);
|
||||
}
|
||||
});
|
||||
|
||||
const deviceData = await response.json();
|
||||
console.log(deviceData);
|
||||
|
||||
// Prepare custom data
|
||||
const customData = [
|
||||
{
|
||||
"onclickEvent": "$('#tabDetails').trigger('click')",
|
||||
"color": "bg-aqua",
|
||||
"headerId": "deviceStatus",
|
||||
"headerStyle": "margin-left: 0em",
|
||||
"labelLang": "DevDetail_Shortcut_CurrentStatus",
|
||||
"iconId": "deviceStatusIcon",
|
||||
"iconClass": deviceData.devPresentLastScan == 1 ? "fa fa-check text-green" : "fa fa-xmark text-red",
|
||||
"dataValue": deviceData.devPresentLastScan == 1 ? getString("Gen_Online") : getString("Gen_Offline")
|
||||
},
|
||||
{
|
||||
"onclickEvent": "$('#tabSessions').trigger('click');",
|
||||
"color": "bg-green",
|
||||
"headerId": "deviceSessions",
|
||||
"headerStyle": "",
|
||||
"labelLang": "DevDetail_Shortcut_Sessions",
|
||||
"iconId": "",
|
||||
"iconClass": "fa fa-plug",
|
||||
"dataValue": deviceData.devSessions
|
||||
},
|
||||
{
|
||||
"onclickEvent": "$('#tabPresence').trigger('click')",
|
||||
"color": "bg-yellow",
|
||||
"headerId": "deviceEvents",
|
||||
"headerStyle": "margin-left: 0em",
|
||||
"labelLang": "DevDetail_Shortcut_Presence",
|
||||
"iconId": "deviceEventsIcon",
|
||||
"iconClass": "fa fa-calendar",
|
||||
"dataValue": `${deviceData.devPresenceHours}h`
|
||||
},
|
||||
{
|
||||
"onclickEvent": "$('#tabEvents').trigger('click');",
|
||||
"color": "bg-red",
|
||||
"headerId": "deviceDownAlerts",
|
||||
"headerStyle": "",
|
||||
"labelLang": "DevDetail_Shortcut_DownAlerts",
|
||||
"iconId": "",
|
||||
"iconClass": "fa fa-warning",
|
||||
"dataValue": deviceData.devDownAlerts
|
||||
}
|
||||
];
|
||||
|
||||
// Send data to render small boxes
|
||||
const cardResponse = await fetch('php/components/device_cards.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ items: customData })
|
||||
});
|
||||
|
||||
if (!cardResponse.ok) {
|
||||
throw new Error(`Error rendering small boxes: ${cardResponse.statusText}`);
|
||||
}
|
||||
|
||||
const html = await cardResponse.text();
|
||||
|
||||
$('#TopSmallBoxes').html(html);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error in renderSmallBoxes:', error);
|
||||
} finally {
|
||||
// Hide loading dialog
|
||||
hideSpinner();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------
|
||||
|
||||
window.onload = function async()
|
||||
{
|
||||
initializeTabs();
|
||||
|
||||
|
||||
// get data from server
|
||||
$.get('php/server/devices.php?action=getServerDeviceData&mac='+ mac + '&period='+ period, function(data) {
|
||||
|
||||
// show loading dialog
|
||||
showSpinner()
|
||||
|
||||
var deviceData = JSON.parse(data);
|
||||
|
||||
console.log(deviceData);
|
||||
|
||||
|
||||
const customData = [
|
||||
{
|
||||
"onclickEvent": "$('#tabDetails').trigger('click')",
|
||||
"color": "bg-aqua",
|
||||
"headerId": "deviceStatus",
|
||||
"headerStyle": "margin-left: 0em",
|
||||
"labelLang": "DevDetail_Shortcut_CurrentStatus",
|
||||
"iconId": "deviceStatusIcon",
|
||||
"iconClass": deviceData.devPresentLastScan == 1 ? "fa fa-check text-green" : "fa fa-xmark text-red",
|
||||
"dataValue": deviceData.devPresentLastScan == 1 ? getString("Gen_Online") : getString("Gen_Offline")
|
||||
},
|
||||
{
|
||||
"onclickEvent": "$('#tabSessions').trigger('click');",
|
||||
"color": "bg-green",
|
||||
"headerId": "deviceSessions",
|
||||
"headerStyle": "",
|
||||
"labelLang": "DevDetail_Shortcut_Sessions",
|
||||
"iconId": "",
|
||||
"iconClass": "fa fa-plug",
|
||||
"dataValue": deviceData.devSessions
|
||||
},
|
||||
{
|
||||
"onclickEvent": "$('#tabPresence').trigger('click')",
|
||||
"color": "bg-yellow",
|
||||
"headerId": "deviceEvents",
|
||||
"headerStyle": "margin-left: 0em",
|
||||
"labelLang": "DevDetail_Shortcut_Presence",
|
||||
"iconId": "deviceEventsIcon",
|
||||
"iconClass": "fa fa-calendar",
|
||||
"dataValue": deviceData.devPresenceHours + 'h'
|
||||
},
|
||||
{
|
||||
"onclickEvent": "$('#tabEvents').trigger('click');",
|
||||
"color": "bg-red",
|
||||
"headerId": "deviceDownAlerts",
|
||||
"headerStyle": "",
|
||||
"labelLang": "DevDetail_Shortcut_DownAlerts",
|
||||
"iconId": "",
|
||||
"iconClass": "fa fa-warning",
|
||||
"dataValue": deviceData.devDownAlerts
|
||||
}
|
||||
];
|
||||
|
||||
renderSmallBoxes(customData);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
@@ -181,6 +181,15 @@
|
||||
</span>`;
|
||||
}
|
||||
|
||||
// handle generate IP for new device
|
||||
if (setting.setKey == "NEWDEV_devIcon") {
|
||||
inlineControl += `<span class="input-group-addon pointer"
|
||||
onclick="showIconSelection()"
|
||||
title="${getString("Gen_Select")}">
|
||||
<i class="fa-solid fa-chevron-down" ></i>
|
||||
</span>`;
|
||||
}
|
||||
|
||||
|
||||
// Generate the input field HTML
|
||||
const inputFormHtml = `<div class="form-group col-xs-12">
|
||||
@@ -413,14 +422,6 @@
|
||||
|
||||
showMessage (msg);
|
||||
|
||||
|
||||
// clear session storage
|
||||
setCache("#dropdownOwner","");
|
||||
setCache("#dropdownDeviceType","");
|
||||
setCache("#dropdownGroup","");
|
||||
setCache("#dropdownLocation","");
|
||||
setCache("#dropdownNetworkNodeMac","");
|
||||
|
||||
// Remove navigation prompt "Are you sure you want to leave..."
|
||||
window.onbeforeunload = null;
|
||||
somethingChanged = false;
|
||||
@@ -438,15 +439,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
// Helper function to clear dropdown cache
|
||||
function clearDropdownCache() {
|
||||
setCache("#dropdownOwner", "");
|
||||
setCache("#dropdownDeviceType", "");
|
||||
setCache("#dropdownGroup", "");
|
||||
setCache("#dropdownLocation", "");
|
||||
setCache("#dropdownNetworkNodeMac", "");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------
|
||||
// Disables or enables network configuration for the root node
|
||||
function toggleNetworkConfiguration(disable) {
|
||||
|
||||
@@ -387,6 +387,89 @@ function addIconAsBase64 (el) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
function showIconSelection() {
|
||||
const selectElement = document.getElementById('NEWDEV_devIcon');
|
||||
const modalId = 'dynamicIconModal';
|
||||
|
||||
// Create modal HTML dynamically
|
||||
const modalHTML = `
|
||||
<div id="${modalId}" class="modal fade" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">${getString("Gen_Select")}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="iconList" class="row"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Append the modal to the body
|
||||
document.body.insertAdjacentHTML('beforeend', modalHTML);
|
||||
|
||||
const iconList = document.getElementById('iconList');
|
||||
|
||||
// Populate the icon list
|
||||
Array.from(selectElement.options).forEach(option => {
|
||||
if (option.value != "") {
|
||||
|
||||
|
||||
const value = option.value;
|
||||
|
||||
// Decode the base64 value
|
||||
let decodedValue;
|
||||
try {
|
||||
decodedValue = atob(value);
|
||||
} catch (e) {
|
||||
console.warn(`Skipping invalid base64 value: ${value}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create an icon container
|
||||
const iconDiv = document.createElement('div');
|
||||
iconDiv.classList.add('iconPreviewSelector','col-md-2' , 'col-sm-3', 'col-xs-4');
|
||||
iconDiv.style.cursor = 'pointer';
|
||||
|
||||
// Render the SVG or HTML content
|
||||
const iconContainer = document.createElement('div');
|
||||
iconContainer.innerHTML = decodedValue;
|
||||
|
||||
// Append the icon to the div
|
||||
iconDiv.appendChild(iconContainer);
|
||||
iconList.appendChild(iconDiv);
|
||||
|
||||
// Add click event to select icon
|
||||
iconDiv.addEventListener('click', () => {
|
||||
selectElement.value = value; // Update the select element value
|
||||
$(`#${modalId}`).modal('hide'); // Hide the modal
|
||||
updateIconPreview();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Show the modal using AJAX
|
||||
$(`#${modalId}`).modal('show');
|
||||
|
||||
// Remove modal from DOM after it's hidden
|
||||
$(`#${modalId}`).on('hidden.bs.modal', function () {
|
||||
document.getElementById(modalId).remove();
|
||||
});
|
||||
|
||||
//
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// initialize
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@@ -3,51 +3,57 @@
|
||||
require '../server/init.php';
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// check if authenticated
|
||||
// Check if authenticated
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
|
||||
|
||||
function renderSmallBox($params) {
|
||||
$onclickEvent = isset($params['onclickEvent']) ? $params['onclickEvent'] : '';
|
||||
$color = isset($params['color']) ? $params['color'] : '';
|
||||
$headerId = isset($params['headerId']) ? $params['headerId'] : '';
|
||||
$headerStyle = isset($params['headerStyle']) ? $params['headerStyle'] : '';
|
||||
$labelLang = isset($params['labelLang']) ? $params['labelLang'] : '';
|
||||
$iconId = isset($params['iconId']) ? $params['iconId'] : '';
|
||||
$iconClass = isset($params['iconClass']) ? $params['iconClass'] : '';
|
||||
$dataValue = isset($params['dataValue']) ? $params['dataValue'] : '';
|
||||
$onclickEvent = isset($params['onclickEvent']) ? $params['onclickEvent'] : '';
|
||||
$color = isset($params['color']) ? $params['color'] : '';
|
||||
$headerId = isset($params['headerId']) ? $params['headerId'] : '';
|
||||
$headerStyle = isset($params['headerStyle']) ? $params['headerStyle'] : '';
|
||||
$labelLang = isset($params['labelLang']) ? $params['labelLang'] : '';
|
||||
$iconId = isset($params['iconId']) ? $params['iconId'] : '';
|
||||
$iconClass = isset($params['iconClass']) ? $params['iconClass'] : '';
|
||||
$dataValue = isset($params['dataValue']) ? $params['dataValue'] : '';
|
||||
|
||||
return '
|
||||
<div class="col-lg-3 col-sm-6 col-xs-6">
|
||||
<a href="#" onclick="javascript: ' . htmlspecialchars($onclickEvent) . '">
|
||||
<div class="small-box ' . htmlspecialchars($color) . '">
|
||||
<div class="inner">
|
||||
<h3 id="' . htmlspecialchars($headerId) . '" style="' . htmlspecialchars($headerStyle) . '"> '.$dataValue.' </h3>
|
||||
<p class="infobox_label">'.lang(htmlspecialchars($labelLang)).'</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i id="' . htmlspecialchars($iconId) . '" class="' . htmlspecialchars($iconClass) . '"></i>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>';
|
||||
return '
|
||||
<div class="col-lg-3 col-sm-6 col-xs-6">
|
||||
<a href="#" onclick="javascript: ' . htmlspecialchars($onclickEvent) . '">
|
||||
<div class="small-box ' . htmlspecialchars($color) . '">
|
||||
<div class="inner">
|
||||
<h3 id="' . htmlspecialchars($headerId) . '" style="' . htmlspecialchars($headerStyle) . '"> ' . htmlspecialchars($dataValue) . ' </h3>
|
||||
<p class="infobox_label">' . lang(htmlspecialchars($labelLang)) . '</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i id="' . htmlspecialchars($iconId) . '" class="' . htmlspecialchars($iconClass) . '"></i>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>';
|
||||
}
|
||||
|
||||
// Load default data from JSON file
|
||||
$defaultDataFile = 'device_cards_defaults.json';
|
||||
$defaultData = file_exists($defaultDataFile) ? json_decode(file_get_contents($defaultDataFile), true) : [];
|
||||
|
||||
// Check if 'items' parameter exists and is valid JSON
|
||||
$items = isset($_POST['items']) ? json_decode($_POST['items'], true) : [];
|
||||
// Decode raw JSON input from body
|
||||
$requestBody = file_get_contents('php://input');
|
||||
$data = json_decode($requestBody, true);
|
||||
|
||||
// Use default data if 'items' is not provided or cannot be decoded
|
||||
if (empty($items)) {
|
||||
$items = $defaultData;
|
||||
// Debugging logs
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
error_log('JSON Decode Error: ' . json_last_error_msg());
|
||||
error_log('Raw body: ' . $requestBody);
|
||||
$data = null;
|
||||
}
|
||||
|
||||
// Extract 'items' or fall back to default data
|
||||
$items = isset($data['items']) ? $data['items'] : $defaultData;
|
||||
|
||||
// Generate HTML
|
||||
$html = '<div class="row">';
|
||||
foreach ($items as $item) {
|
||||
$html .= renderSmallBox($item);
|
||||
$html .= renderSmallBox($item);
|
||||
}
|
||||
$html .= '</div>';
|
||||
|
||||
|
||||
@@ -309,6 +309,7 @@
|
||||
"Gen_Saved": "Saved",
|
||||
"Gen_Search": "Search",
|
||||
"Gen_SelectToPreview": "Select to preview",
|
||||
"Gen_Select": "Select",
|
||||
"Gen_Selected_Devices": "Selected Devices:",
|
||||
"Gen_Switch": "Switch",
|
||||
"Gen_Upd": "Updated successfully",
|
||||
|
||||
@@ -186,11 +186,11 @@ def main ():
|
||||
|
||||
# Footer
|
||||
|
||||
mylog('verbose', ['[MAIN] Process: Wait'])
|
||||
mylog('verbose', ['[MAIN] Process: Idle'])
|
||||
else:
|
||||
# do something
|
||||
# mylog('verbose', ['[MAIN] Waiting to start next loop'])
|
||||
updateState("Process: Wait")
|
||||
updateState("Process: Idle")
|
||||
|
||||
|
||||
#loop
|
||||
|
||||
@@ -64,4 +64,4 @@ def start_server(graphql_port, app_state):
|
||||
thread.start()
|
||||
|
||||
# Update the state to indicate the server has started
|
||||
app_state = updateState("Process: Wait", None, None, None, 1)
|
||||
app_state = updateState("Process: Idle", None, None, None, 1)
|
||||
Reference in New Issue
Block a user