Icon selector

This commit is contained in:
jokob-sk
2024-12-04 21:03:35 +11:00
parent 989d5dde8a
commit e92d1bb0ad
8 changed files with 272 additions and 125 deletions

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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) {

View File

@@ -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">&times;</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
// -----------------------------------------------------------------------------

View File

@@ -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>';

View File

@@ -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",

View File

@@ -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

View File

@@ -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)