🎨 Device tiles setting

This commit is contained in:
jokob-sk
2024-06-16 12:23:14 +10:00
parent 5302673a96
commit 3b7cbba32b
20 changed files with 258 additions and 101 deletions

View File

@@ -7,7 +7,7 @@ When opening an issue please:
1. Include a screenshot of what you see when accessing `HTTP://<your rpi IP>/20211` (or your custom port)
1. [Follow steps 1, 2, 3, 4 on this page](https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEBUG_TIPS.md)
1. Execute the following in the container to see the processes and their ports and submit a screenshot of the result:
1. `sudo apt-get install lsof`
1. `sudo apk add lsof`
1. `sudo lsof -i`
1. Try running the `nginx` command in the container
1. if you get `nginx: [emerg] bind() to 0.0.0.0:20211 failed (98: Address in use)` try using a different port number

View File

@@ -1106,6 +1106,10 @@ input[readonly] {
height: 1.5em !important;
}
#TileCards .tile .inner
{
color: white;
}
#dropdownIcon li svg, #dropdownIcon li i{
height: 1.5em !important;

View File

@@ -42,77 +42,7 @@
<!-- Tile toggle cards ------------------------------------------------------- -->
<div class="row" id="TileCards">
<!-- top small box 1 ------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: initializeDatatable('my');">
<div class="small-box bg-aqua">
<div class="inner"><h3 id="devicesMy"> -- </h3>
<p class="infobox_label"><?= lang('Device_Shortcut_AllDevices');?></p>
</div>
<div class="icon"><i class="fa fa-laptop text-aqua-40"></i></div>
</div>
</a>
</div>
<!-- top small box 2 ------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: initializeDatatable('connected');">
<div class="small-box bg-green">
<div class="inner"><h3 id="devicesConnected"> -- </h3>
<p class="infobox_label"><?= lang('Device_Shortcut_Connected');?></p>
</div>
<div class="icon"><i class="fa fa-plug text-green-40"></i></div>
</div>
</a>
</div>
<!-- top small box 3 ------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: initializeDatatable('favorites');">
<div class="small-box bg-yellow">
<div class="inner"><h3 id="devicesFavorites"> -- </h3>
<p class="infobox_label"><?= lang('Device_Shortcut_Favorites');?></p>
</div>
<div class="icon"><i class="fa fa-star text-yellow-40"></i></div>
</div>
</a>
</div>
<!-- top small box 4 ------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: initializeDatatable('new');">
<div class="small-box bg-yellow">
<div class="inner"><h3 id="devicesNew"> -- </h3>
<p class="infobox_label"><?= lang('Device_Shortcut_NewDevices');?></p>
</div>
<div class="icon"><i class="ion ion-plus-round text-yellow-40"></i></div>
</div>
</a>
</div>
<!-- top small box 5 ------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: initializeDatatable('down');">
<div class="small-box bg-red">
<div class="inner"><h3 id="devicesDown"> -- </h3>
<p class="infobox_label"><?= lang('Device_Shortcut_DownOnly');?></p>
</div>
<div class="icon"><i class="fa fa-warning text-red-40"></i></div>
</div>
</a>
</div>
<!-- top small box 6 ------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: initializeDatatable('archived');">
<div class="small-box bg-gray top_small_box_gray_text">
<div class="inner"><h3 id="devicesArchived"> -- </h3>
<p class="infobox_label"><?= lang('Device_Shortcut_Archived');?></p>
</div>
<div class="icon"><i class="fa fa-eye-slash text-gray-40"></i></div>
</div>
</a>
</div>
<!-- Placeholder ------------------------------------------------------- -->
</div>
@@ -321,9 +251,7 @@ function main () {
// Initialize components with parameters
initializeDatatable(getUrlAnchor('my'));
initializeDatatable(getUrlAnchor('my_devices'));
// check if data outdated and show spinner if so
handleLoadingDialog()
@@ -358,21 +286,49 @@ function getDevicesTotals(devicesData) {
if (getCache("getDevicesTotals") !== "") {
resultJSON = getCache("getDevicesTotals");
} else {
// combined query
const devices = filterDataByStatus(devicesData, 'my');
const connectedDevices = filterDataByStatus(devicesData, 'connected');
const favoritesDevices = filterDataByStatus(devicesData, 'favorites');
const newDevices = filterDataByStatus(devicesData, 'new');
const downDevices = filterDataByStatus(devicesData, 'down');
const archivedDevices = filterDataByStatus(devicesData, 'archived');
// Define filter conditions and corresponding objects
const filters = [
{ status: 'my_devices', color: 'bg-aqua', label: getString('Device_Shortcut_AllDevices'), icon: 'fa-laptop' },
{ status: 'all', color: 'bg-aqua', label: getString('Gen_All_Devices'), icon: 'fa-laptop' },
{ status: 'connected', color: 'bg-green', label: getString('Device_Shortcut_Connected'), icon: 'fa-plug' },
{ status: 'favorites', color: 'bg-yellow', label: getString('Device_Shortcut_Favorites'), icon: 'fa-star' },
{ status: 'new', color: 'bg-yellow', label: getString('Device_Shortcut_NewDevices'), icon: 'fa-plus' },
{ status: 'down', color: 'bg-red', label: getString('Device_Shortcut_DownOnly'), icon: 'fa-warning' },
{ status: 'archived', color: 'bg-gray', label: getString('Device_Shortcut_Archived'), icon: 'fa-eye-slash' },
{ status: 'offline', color: 'bg-gray', label: getString('Gen_Offline'), icon: 'fa-xmark' }
];
$('#devicesMy').html (devices.length);
$('#devicesConnected').html (connectedDevices.length);
$('#devicesFavorites').html (favoritesDevices.length);
$('#devicesNew').html (newDevices.length);
$('#devicesDown').html (downDevices.length);
$('#devicesArchived').html (archivedDevices.length);
// Initialize an empty array to store the final objects
let dataArray = [];
// Loop through each filter condition
filters.forEach(filter => {
// Calculate count dynamically based on filter condition
let count = filterDataByStatus(devicesData, filter.status).length;
console.log(getSetting('UI_hide_empty'));
// Check any condition to skip adding the object to dataArray
if (
(['', 'False'].includes(getSetting('UI_hide_empty')) || (getSetting('UI_hide_empty') == "True" && count > 0)) &&
(getSetting('UI_shown_cards') == "" || getSetting('UI_shown_cards').includes(filter.status))
) {
dataArray.push({
onclickEvent: `initializeDatatable('${filter.status}')`,
color: filter.color,
title: count,
label: filter.label,
icon: filter.icon
});
}
});
// render info boxes/tile cards
renderInfoboxes(
dataArray
)
// save to cache
setCache("getDevicesTotals", resultJSON);
@@ -381,12 +337,28 @@ function getDevicesTotals(devicesData) {
// console.log(resultJSON);
}
//------------------------------------------------------------------------------
function renderInfoboxes(customData) {
$.ajax({
url: 'php/components/tile_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) {
$('#TileCards').html(response); // Replace container content with fetched HTML
},
error: function(xhr, status, error) {
console.error('Error fetching infoboxes:', error);
}
});
}
// -----------------------------------------------------------------------------
// Define a function to filter data based on deviceStatus
function filterDataByStatus(data, status) {
return data.filter(function(item) {
switch (status) {
case 'my':
case 'my_devices':
to_display = getSetting('UI_MY_DEVICES');
let result = true;
@@ -403,7 +375,7 @@ function filterDataByStatus(data, status) {
result = false;
}
return result; // Include all items for 'my' status
return result; // Include all items for 'my_devices' status
case 'connected':
return item.dev_PresentLastScan === 1;
case 'favorites':
@@ -455,7 +427,7 @@ function initializeDatatable (status) {
if(!status)
{
status = 'my'
status = 'my_devices'
}
// Save status selected
@@ -463,12 +435,14 @@ function initializeDatatable (status) {
// Define color & title for the status selected
switch (deviceStatus) {
case 'my': tableTitle = getString('Device_Shortcut_AllDevices'); color = 'aqua'; break;
case 'my_devices': tableTitle = getString('Device_Shortcut_AllDevices'); color = 'aqua'; break;
case 'connected': tableTitle = getString('Device_Shortcut_Connected'); color = 'green'; break;
case 'all': tableTitle = getString('Gen_All_Devices'); color = 'aqua'; break;
case 'favorites': tableTitle = getString('Device_Shortcut_Favorites'); color = 'yellow'; break;
case 'new': tableTitle = getString('Device_Shortcut_NewDevices'); color = 'yellow'; break;
case 'down': tableTitle = getString('Device_Shortcut_DownOnly'); color = 'red'; break;
case 'archived': tableTitle = getString('Device_Shortcut_Archived'); color = 'gray'; break;
case 'offline': tableTitle = getString('Gen_Offline'); color = 'gray'; break;
default: tableTitle = getString('Device_Shortcut_Devices'); color = 'gray'; break;
}

View File

@@ -0,0 +1,43 @@
<?php
function renderInfobox($params) {
$onclickEvent = isset($params['onclickEvent']) ? $params['onclickEvent'] : '';
$color = isset($params['color']) ? $params['color'] : '';
$title = isset($params['title']) ? $params['title'] : '';
$label = isset($params['label']) ? $params['label'] : '';
$icon = isset($params['icon']) ? $params['icon'] : '';
return '
<div class="tile col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: ' . htmlspecialchars($onclickEvent) . ';">
<div class="small-box ' . htmlspecialchars($color) . '">
<div class="inner">
<h3>' . htmlspecialchars($title) . '</h3>
<p class="infobox_label">' . htmlspecialchars($label) . '</p>
</div>
<div class="icon">
<i class="fa ' . htmlspecialchars($icon) . ' text-aqua-40"></i>
</div>
</div>
</a>
</div>';
}
// Load default data from JSON file
$defaultDataFile = 'tile_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) : [];
// Use default data if 'items' is not provided or cannot be decoded
if (empty($items)) {
$items = $defaultData;
}
$html = '';
foreach ($items as $item) {
$html .= renderInfobox($item);
}
echo $html;
exit();
?>

View File

@@ -0,0 +1,17 @@
[
{
"onclickEvent": "handleClick(1)",
"color": "bg-primary",
"title": "Default Infobox 1",
"label": "Default label for Infobox 1",
"icon": "fa-bell"
},
{
"onclickEvent": "handleClick(2)",
"color": "bg-success",
"title": "Default Infobox 2",
"label": "Default label for Infobox 2",
"icon": "fa-envelope"
}
]

View File

@@ -17,7 +17,7 @@
<!-- Default to the left -->
<!-- NetAlertX footer with url -->
<a href="https://github.com/jokob-sk/NetAlertX" target="_blank">Net <b>Alert</b><sup>x</sup></a>
<a href="https://github.com/jokob-sk/NetAlertX" target="_blank">Net<b>Alert</b><sup>x</sup></a>
<!-- To the right -->

View File

@@ -282,6 +282,7 @@
"Gen_Action": "Action",
"Gen_Add": "",
"Gen_Add_All": "",
"Gen_All_Devices": "",
"Gen_AreYouSure": "Sind Sie sich sicher?",
"Gen_Backup": "Sichern",
"Gen_Cancel": "Abbrechen",

View File

@@ -270,6 +270,7 @@
"Gen_Action": "Action",
"Gen_Add": "Add",
"Gen_Add_All": "Add all",
"Gen_All_Devices": "All Devices",
"Gen_AreYouSure": "Are you sure?",
"Gen_Backup": "Run Backup",
"Gen_Cancel": "Cancel",

View File

@@ -280,6 +280,7 @@
"Gen_Action": "Acción",
"Gen_Add": "Añadir",
"Gen_Add_All": "Añadir todo",
"Gen_All_Devices": "",
"Gen_AreYouSure": "¿Estás seguro?",
"Gen_Backup": "Ejecutar copia de seguridad",
"Gen_Cancel": "Cancelar",

View File

@@ -270,6 +270,7 @@
"Gen_Action": "Action",
"Gen_Add": "",
"Gen_Add_All": "",
"Gen_All_Devices": "",
"Gen_AreYouSure": "",
"Gen_Backup": "",
"Gen_Cancel": "Annuler",

View File

@@ -270,6 +270,7 @@
"Gen_Action": "Azione",
"Gen_Add": "Aggiungi",
"Gen_Add_All": "Aggiungi tutti",
"Gen_All_Devices": "",
"Gen_AreYouSure": "Sei sicuro?",
"Gen_Backup": "Esegui backup",
"Gen_Cancel": "Annulla",

View File

@@ -270,6 +270,7 @@
"Gen_Action": "Handling",
"Gen_Add": "Legg til",
"Gen_Add_All": "Legg til alle",
"Gen_All_Devices": "",
"Gen_AreYouSure": "Er du sikker?",
"Gen_Backup": "Kjør sikkerhetskopiering",
"Gen_Cancel": "Avbryt",

View File

@@ -270,6 +270,7 @@
"Gen_Action": "Akcja",
"Gen_Add": "Dodaj",
"Gen_Add_All": "Dodaj wszystko",
"Gen_All_Devices": "",
"Gen_AreYouSure": "Jesteś pewien?",
"Gen_Backup": "Wykonaj Kopie Zapasową",
"Gen_Cancel": "Anuluj",

View File

@@ -270,6 +270,7 @@
"Gen_Action": "",
"Gen_Add": "",
"Gen_Add_All": "",
"Gen_All_Devices": "",
"Gen_AreYouSure": "",
"Gen_Backup": "",
"Gen_Cancel": "",

View File

@@ -270,6 +270,7 @@
"Gen_Action": "Действия",
"Gen_Add": "Добавить",
"Gen_Add_All": "Добавить все",
"Gen_All_Devices": "",
"Gen_AreYouSure": "Вы уверены?",
"Gen_Backup": "Запустить резервное копирование",
"Gen_Cancel": "Отмена",

View File

@@ -270,6 +270,7 @@
"Gen_Action": "",
"Gen_Add": "",
"Gen_Add_All": "",
"Gen_All_Devices": "",
"Gen_AreYouSure": "",
"Gen_Backup": "",
"Gen_Cancel": "",

View File

@@ -270,6 +270,7 @@
"Gen_Action": "",
"Gen_Add": "",
"Gen_Add_All": "",
"Gen_All_Devices": "",
"Gen_AreYouSure": "",
"Gen_Backup": "",
"Gen_Cancel": "",

View File

@@ -0,0 +1,23 @@
## Overview
PLugin functionality overview and links to external resources if relevant. Include use cases if available.
> [!TIP]
> Some tip.
### Quick setup guide
To set up the plugin correctly, make sure...
#### Required Settings
- When to run `PREF_RUN`
-
### Usage
- Head to **Settings** > **Plugin name** to adjust the default values.
### Notes
- Additional notes, limitations, Author info.

View File

@@ -0,0 +1,85 @@
{
"code_name": "ui_settings",
"unique_prefix": "UI",
"plugin_type": "core",
"enabled": true,
"data_source": "template",
"show_ui": false,
"localized": ["display_name", "description", "icon"],
"display_name": [
{
"language_code": "en_us",
"string": "UI settings"
}
],
"description": [
{
"language_code": "en_us",
"string": "Plugin to adjust UI settings."
}
],
"icon": [
{
"language_code": "en_us",
"string": "<i class=\"fa fa-paint-roller\"></i>"
}
],
"params": [],
"settings": [
{
"function": "shown_cards",
"type": "text.multiselect",
"maxLength": 50,
"default_value": [
"my_devices",
"connected",
"favorites",
"new",
"down",
"archived"
],
"options": [
"my_devices",
"connected",
"favorites",
"new",
"down",
"archived",
"offline",
"all"
],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Tiles to Show"
}
],
"description": [
{
"language_code": "en_us",
"string": "Which tiles to show on teh top of the Devices page."
}
]
},
{
"function": "hide_empty",
"type": "boolean",
"default_value": false,
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Hide empty tiles"
}
],
"description": [
{
"language_code": "en_us",
"string": "Hide Device tiles with zero results."
}
]
}
]
}

View File

@@ -132,24 +132,24 @@ def importConfigs (db, all_plugins):
# ccd(key, default, config_dir, name, inputtype, options, group, events=[], desc = "", regex = "", setJsonMetadata = {}, overrideTemplate = {})
conf.LOADED_PLUGINS = ccd('LOADED_PLUGINS', [] , c_d, 'Loaded plugins', 'text.multiselect', '', 'General')
# conf.TEST_TEST2 = ccd('TEST_TEST2', [] , c_d, 'TEST_TEST2', 'text.multiselect', '', 'General')
conf.SCAN_SUBNETS = ccd('SCAN_SUBNETS', ['192.168.1.0/24 --interface=eth1', '192.168.1.0/24 --interface=eth0'] , c_d, 'Subnets to scan', 'subnets', '', 'General')
conf.LOG_LEVEL = ccd('LOG_LEVEL', 'verbose' , c_d, 'Log verboseness', 'text.select', "['none', 'minimal', 'verbose', 'debug']", 'General')
conf.TIMEZONE = ccd('TIMEZONE', 'Europe/Berlin' , c_d, 'Time zone', 'text', '', 'General')
conf.PLUGINS_KEEP_HIST = ccd('PLUGINS_KEEP_HIST', 250 , c_d, 'Keep history entries', 'integer', '', 'General')
conf.REPORT_DASHBOARD_URL = ccd('REPORT_DASHBOARD_URL', 'http://netalertx/' , c_d, 'NetAlertX URL', 'text', '', 'General')
conf.UI_LANG = ccd('UI_LANG', 'English' , c_d, 'Language Interface', 'text.select', "['English', 'French', 'German', 'Norwegian', 'Russian', 'Spanish', 'Italian (it_it)', 'Portuguese (pt_br)', 'Polish (pl_pl)', 'Turkish (tr_tr)', 'Chinese (zh_cn)' ]", 'General')
conf.UI_PRESENCE = ccd('UI_PRESENCE', ['online', 'offline', 'archived'] , c_d, 'Include in presence', 'text.multiselect', "['online', 'offline', 'archived']", 'General')
conf.UI_DEV_SECTIONS = ccd('UI_DEV_SECTIONS', [] , c_d, 'Show sections', 'text.multiselect', "['Tile Cards', 'Device Presence']", 'General')
conf.UI_MY_DEVICES = ccd('UI_MY_DEVICES', ['online', 'offline', 'archived', 'new', 'down'] , c_d, 'Include in My Devices', 'text.multiselect', "['online', 'offline', 'archived', 'new', 'down']", 'General')
conf.UI_NOT_RANDOM_MAC = ccd('UI_NOT_RANDOM_MAC', [] , c_d, 'Exlude from Random Prefix', 'list', "", 'General')
conf.UI_ICONS = ccd('UI_ICONS', ['PGkgY2xhc3M9ImZhIGZhLWNvbXB1dGVyIj48L2k+', 'PGkgY2xhc3M9ImZhIGZhLWV0aGVybmV0Ij48L2k+', 'PGkgY2xhc3M9ImZhIGZhLWdhbWVwYWQiPjwvaT4', 'PGkgY2xhc3M9ImZhIGZhLWdsb2JlIj48L2k+', 'PGkgY2xhc3M9ImZhIGZhLWxhcHRvcCI+PC9pPg==', 'PGkgY2xhc3M9ImZhIGZhLWxpZ2h0YnVsYiI+PC9pPg==', 'PGkgY2xhc3M9ImZhIGZhLXNoaWVsZCI+PC9pPg==', 'PGkgY2xhc3M9ImZhIGZhLXdpZmkiPjwvaT4'] , c_d, 'Icons', 'list', "", 'General')
conf.UI_REFRESH = ccd('UI_REFRESH', 0 , c_d, 'Refresh interval', 'integer', "", 'General')
conf.DAYS_TO_KEEP_EVENTS = ccd('DAYS_TO_KEEP_EVENTS', 90 , c_d, 'Delete events days', 'integer', '', 'General')
conf.HRS_TO_KEEP_NEWDEV = ccd('HRS_TO_KEEP_NEWDEV', 0 , c_d, 'Keep new devices for', 'integer', "0", 'General')
conf.API_CUSTOM_SQL = ccd('API_CUSTOM_SQL', 'SELECT * FROM Devices WHERE dev_PresentLastScan = 0' , c_d, 'Custom endpoint', 'text', '', 'General')
conf.NETWORK_DEVICE_TYPES = ccd('NETWORK_DEVICE_TYPES', ['AP', 'Gateway', 'Firewall', 'Hypervisor', 'Powerline', 'Switch', 'WLAN', 'PLC', 'Router','USB LAN Adapter', 'USB WIFI Adapter', 'Internet'] , c_d, 'Network device types', 'list', '', 'General')
# UI
conf.UI_LANG = ccd('UI_LANG', 'English' , c_d, 'Language Interface', 'text.select', "['English', 'French', 'German', 'Norwegian', 'Russian', 'Spanish', 'Italian (it_it)', 'Portuguese (pt_br)', 'Polish (pl_pl)', 'Turkish (tr_tr)', 'Chinese (zh_cn)' ]", 'UI')
conf.UI_NOT_RANDOM_MAC = ccd('UI_NOT_RANDOM_MAC', [] , c_d, 'Exlude from Random Prefix', 'list', "", 'UI')
conf.UI_ICONS = ccd('UI_ICONS', ['PGkgY2xhc3M9ImZhIGZhLWNvbXB1dGVyIj48L2k+', 'PGkgY2xhc3M9ImZhIGZhLWV0aGVybmV0Ij48L2k+', 'PGkgY2xhc3M9ImZhIGZhLWdhbWVwYWQiPjwvaT4', 'PGkgY2xhc3M9ImZhIGZhLWdsb2JlIj48L2k+', 'PGkgY2xhc3M9ImZhIGZhLWxhcHRvcCI+PC9pPg==', 'PGkgY2xhc3M9ImZhIGZhLWxpZ2h0YnVsYiI+PC9pPg==', 'PGkgY2xhc3M9ImZhIGZhLXNoaWVsZCI+PC9pPg==', 'PGkgY2xhc3M9ImZhIGZhLXdpZmkiPjwvaT4'] , c_d, 'Icons', 'list', "", 'UI')
conf.UI_REFRESH = ccd('UI_REFRESH', 0 , c_d, 'Refresh interval', 'integer', "", 'UI')
conf.UI_DEV_SECTIONS = ccd('UI_DEV_SECTIONS', [] , c_d, 'Show sections', 'text.multiselect', "['Tile Cards', 'Device Presence']", 'UI')
conf.UI_PRESENCE = ccd('UI_PRESENCE', ['online', 'offline', 'archived'] , c_d, 'Include in presence', 'text.multiselect', "['online', 'offline', 'archived']", 'UI')
conf.UI_MY_DEVICES = ccd('UI_MY_DEVICES', ['online', 'offline', 'archived', 'new', 'down'] , c_d, 'Include in My Devices', 'text.multiselect', "['online', 'offline', 'archived', 'new', 'down']", 'UI')
# Init timezone in case it changed
conf.tz = timezone(conf.TIMEZONE)