mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-06-20 22:30:11 -04:00
feat: Implement settings loading skeleton and enhance UI elements
This commit is contained in:
@@ -1083,6 +1083,7 @@ height: 50px;
|
||||
.settingswrap
|
||||
{
|
||||
margin-bottom: 100px;
|
||||
padding-top: 0px;
|
||||
}
|
||||
|
||||
.settingswrap .metadata
|
||||
@@ -1178,8 +1179,10 @@ height: 50px;
|
||||
/* Settings */
|
||||
|
||||
#settingsPage .overview-setting-value{
|
||||
display:unset;
|
||||
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
.overview-setting-value-wrap
|
||||
@@ -1228,7 +1231,7 @@ height: 50px;
|
||||
|
||||
}
|
||||
#settingsPage .panel-heading:hover{
|
||||
background-color: #272c30;
|
||||
background-color: #e8e8e8;
|
||||
}
|
||||
|
||||
.settings-expand-icon {
|
||||
@@ -2256,7 +2259,7 @@ textarea[readonly],
|
||||
----------------------------------------------------------------------------- */
|
||||
#loadingSpinner {
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
z-index: 9999;
|
||||
/* top: 0; */
|
||||
/* left: 0; */
|
||||
/* width: 100%; */
|
||||
@@ -2265,7 +2268,6 @@ textarea[readonly],
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
pointer-events: none;
|
||||
display: block;
|
||||
z-index: 800;
|
||||
}
|
||||
|
||||
.fa-spinner
|
||||
@@ -2587,4 +2589,170 @@ table.dataTable tbody > tr.selected
|
||||
.input-group-addon.text-muted {
|
||||
color: #8c8c8c;
|
||||
background-color: rgba(140, 140, 140, 0.05);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== Settings Page Loading Skeleton ===== */
|
||||
|
||||
:root {
|
||||
--skel-base: #e2e2e2;
|
||||
--skel-shine: #f0f0f0;
|
||||
--skel-section: #d4d4d4;
|
||||
--skel-panel-bg: #f5f5f5;
|
||||
--skel-border: #ddd;
|
||||
--skel-bg: #ecf0f5;
|
||||
}
|
||||
|
||||
@keyframes settingsShimmer {
|
||||
0% { background-position: -600px 0; }
|
||||
100% { background-position: 600px 0; }
|
||||
}
|
||||
|
||||
#settingsPage {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#settings-skeleton {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 50;
|
||||
background-color: var(--skel-bg);
|
||||
padding-top: 50px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
min-height: 500px;
|
||||
}
|
||||
|
||||
.skel-shimmer {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
var(--skel-base) 25%,
|
||||
var(--skel-shine) 50%,
|
||||
var(--skel-base) 75%
|
||||
);
|
||||
background-size: 600px 100%;
|
||||
animation: settingsShimmer 1.5s infinite linear;
|
||||
border-radius: 3px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Overview panel skeleton */
|
||||
.skel-overview-panel {
|
||||
margin-bottom: 10px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--skel-border);
|
||||
}
|
||||
|
||||
.skel-overview-heading {
|
||||
height: 44px;
|
||||
background: var(--skel-section);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 15px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.skel-overview-body {
|
||||
padding: 12px;
|
||||
background: var(--skel-panel-bg);
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.skel-overview-card {
|
||||
min-width: 100px;
|
||||
height: 76px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* Section accordion skeleton */
|
||||
.skel-section {
|
||||
margin-bottom: 8px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--skel-border);
|
||||
}
|
||||
|
||||
.skel-overview-header {
|
||||
height: 44px;
|
||||
padding: 0 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
background: var(--skel-panel-bg);
|
||||
}
|
||||
|
||||
.skel-section-header {
|
||||
height: 44px;
|
||||
padding: 0 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
background: var(--skel-section);
|
||||
}
|
||||
|
||||
/* Plugin block inside an open section */
|
||||
.skel-plugin-block {
|
||||
margin: 8px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--skel-border);
|
||||
}
|
||||
|
||||
.skel-plugin-header {
|
||||
height: 48px;
|
||||
padding: 0 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
background: var(--skel-section);
|
||||
}
|
||||
|
||||
.skel-plugin-body {
|
||||
background: var(--skel-panel-bg);
|
||||
}
|
||||
|
||||
.skel-setting-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 11px 15px;
|
||||
border-bottom: 1px solid var(--skel-border);
|
||||
}
|
||||
|
||||
.skel-setting-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* Skeleton line / cell primitives */
|
||||
.skel-line {
|
||||
height: 14px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.skel-icon-block {
|
||||
width: 22px;
|
||||
height: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.skel-cell-name {
|
||||
width: 20%;
|
||||
height: 14px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.skel-cell-desc {
|
||||
width: 36%;
|
||||
height: 14px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.skel-cell-input {
|
||||
flex: 1;
|
||||
height: 32px;
|
||||
}
|
||||
/* ===== /Settings Page Loading Skeleton ===== */
|
||||
@@ -516,6 +516,9 @@ textarea[readonly],
|
||||
border: 1px solid #353c42;
|
||||
color: #bec5cb;
|
||||
}
|
||||
#settingsPage .panel-heading:hover {
|
||||
background-color: #272c30;
|
||||
}
|
||||
.box.box-solid.box-info,
|
||||
.box.box-solid.box-info > .box-header {
|
||||
color: #bec5cb;
|
||||
@@ -754,4 +757,14 @@ table.dataTable tbody tr.selected, table.dataTable tbody tr .selected
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: .85em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Settings skeleton - dark theme */
|
||||
:root {
|
||||
--skel-base: #2e3540;
|
||||
--skel-shine: #3d4555;
|
||||
--skel-section: #252c38;
|
||||
--skel-panel-bg: #1e242e;
|
||||
--skel-border: #2a323e;
|
||||
--skel-bg: #353c42;
|
||||
}
|
||||
@@ -512,6 +512,9 @@
|
||||
border: 1px solid #353c42;
|
||||
color: #bec5cb;
|
||||
}
|
||||
#settingsPage .panel-heading:hover {
|
||||
background-color: #272c30;
|
||||
}
|
||||
.box.box-solid.box-info,
|
||||
.box.box-solid.box-info > .box-header {
|
||||
color: #bec5cb;
|
||||
@@ -731,4 +734,14 @@
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: .85em;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
/* Settings skeleton - dark theme */
|
||||
:root {
|
||||
--skel-base: #2e3540;
|
||||
--skel-shine: #3d4555;
|
||||
--skel-section: #252c38;
|
||||
--skel-panel-bg: #1e242e;
|
||||
--skel-border: #2a323e;
|
||||
--skel-bg: #353c42;
|
||||
}
|
||||
@@ -957,8 +957,8 @@ let animationTime = 300
|
||||
function showSpinner(stringKey = 'Loading') {
|
||||
let text = isEmpty(stringKey) ? "Loading..." : getString(stringKey || "Loading");
|
||||
|
||||
if (text == ""){
|
||||
text = "Loading"
|
||||
if (!text || !text.trim()) {
|
||||
text = "Loading..."
|
||||
}
|
||||
|
||||
const spinner = $("#loadingSpinner");
|
||||
@@ -978,7 +978,7 @@ function showSpinner(stringKey = 'Loading') {
|
||||
left: offset.left,
|
||||
width: width,
|
||||
height: height,
|
||||
zIndex: 800
|
||||
zIndex: 9999
|
||||
});
|
||||
} else {
|
||||
// Fullscreen fallback
|
||||
@@ -988,7 +988,7 @@ function showSpinner(stringKey = 'Loading') {
|
||||
left: 0,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
zIndex: 800
|
||||
zIndex: 9999
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1018,7 +1018,7 @@ function hideSpinner() {
|
||||
left: offset.left,
|
||||
width: width,
|
||||
height: height,
|
||||
zIndex: 800
|
||||
zIndex: 9999
|
||||
});
|
||||
} else {
|
||||
// Fullscreen fallback
|
||||
@@ -1028,7 +1028,7 @@ function hideSpinner() {
|
||||
left: 0,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
zIndex: 800
|
||||
zIndex: 9999
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ function pluginCards(prefixesOfEnabledPlugins, includeSettings) {
|
||||
});
|
||||
|
||||
html += `
|
||||
<div class="col-xs-6 col-sm-4 col-md-3 col-lg-2 col-xxl-1 padding-5px">
|
||||
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-3 padding-5px">
|
||||
<div class="small-box bg-green col-sm-12 " >
|
||||
<div class="inner col-sm-12">
|
||||
<a href="#${prefix}_header" onclick="toggleAllSettings('open')">
|
||||
|
||||
@@ -151,7 +151,7 @@
|
||||
<div class="nax_semitransparent-panel"></div>
|
||||
<div class="panel panel-default nax_spinner">
|
||||
<table>
|
||||
<td id="loadingSpinnerText" width="130px" ></td>
|
||||
<td id="loadingSpinnerText" width="130px">Loading...</td>
|
||||
<td><i class="fa-solid fa-spinner fa-spin-pulse"></i></td>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
106
front/php/templates/settings_skeleton.php
Normal file
106
front/php/templates/settings_skeleton.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<!-- Settings Loading Skeleton --------------------------------------------- -->
|
||||
<div id="settings-skeleton">
|
||||
|
||||
<!-- Overview panel skeleton -->
|
||||
<div class="skel-overview-panel">
|
||||
<div class="skel-overview-heading">
|
||||
<span class="skel-icon-block skel-shimmer"></span>
|
||||
<span class="skel-line skel-shimmer" style="width:180px"></span>
|
||||
</div>
|
||||
<div class="skel-overview-body">
|
||||
<div class="skel-overview-card skel-shimmer col-xs-6 col-sm-4 col-md-3 col-lg-2 col-xxl-1 padding-5px"></div>
|
||||
<div class="skel-overview-card skel-shimmer col-xs-6 col-sm-4 col-md-3 col-lg-2 col-xxl-1 padding-5px"></div>
|
||||
<div class="skel-overview-card skel-shimmer col-xs-6 col-sm-4 col-md-3 col-lg-2 col-xxl-1 padding-5px"></div>
|
||||
</div>
|
||||
<div class="skel-overview-heading">
|
||||
<span class="skel-icon-block skel-shimmer"></span>
|
||||
<span class="skel-line skel-shimmer" style="width:180px"></span>
|
||||
</div>
|
||||
<div class="skel-overview-body">
|
||||
<div class="skel-overview-card skel-shimmer col-xs-6 col-sm-4 col-md-3 col-lg-2 col-xxl-1 padding-5px"></div>
|
||||
<div class="skel-overview-card skel-shimmer col-xs-6 col-sm-4 col-md-3 col-lg-2 col-xxl-1 padding-5px"></div>
|
||||
<div class="skel-overview-card skel-shimmer col-xs-6 col-sm-4 col-md-3 col-lg-2 col-xxl-1 padding-5px"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Core section (open, with a plugin block) -->
|
||||
<div class="skel-section">
|
||||
<div class="skel-section-header">
|
||||
<span class="skel-icon-block skel-shimmer"></span>
|
||||
<span class="skel-line skel-shimmer" style="width:60px"></span>
|
||||
</div>
|
||||
<div class="skel-plugin-block">
|
||||
<div class="skel-plugin-header">
|
||||
<span class="skel-icon-block skel-shimmer"></span>
|
||||
<span class="skel-line skel-shimmer" style="width:75px"></span>
|
||||
<span class="skel-line skel-shimmer" style="width:210px"></span>
|
||||
</div>
|
||||
<div class="skel-plugin-body">
|
||||
<div class="skel-setting-row"><span class="skel-cell-name skel-shimmer"></span><span class="skel-cell-desc skel-shimmer"></span><span class="skel-cell-input skel-shimmer" style="height:75px"></span></div>
|
||||
<div class="skel-setting-row"><span class="skel-cell-name skel-shimmer" ></span><span class="skel-cell-desc skel-shimmer" ></span><span class="skel-cell-input skel-shimmer" style="max-width:35px"></span></div>
|
||||
<div class="skel-setting-row"><span class="skel-cell-name skel-shimmer" ></span><span class="skel-cell-desc skel-shimmer" ></span><span class="skel-cell-input skel-shimmer" style="max-width:35px"></span></div>
|
||||
<div class="skel-setting-row"><span class="skel-cell-name skel-shimmer"></span><span class="skel-cell-desc skel-shimmer" ></span><span class="skel-cell-input skel-shimmer" style="height:75px"></span></div>
|
||||
<div class="skel-setting-row"><span class="skel-cell-name skel-shimmer" ></span><span class="skel-cell-desc skel-shimmer" ></span><span class="skel-cell-input skel-shimmer"></span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- System section (open) -->
|
||||
<div class="skel-section">
|
||||
<div class="skel-section-header">
|
||||
<span class="skel-icon-block skel-shimmer"></span>
|
||||
<span class="skel-line skel-shimmer" style="width:80px"></span>
|
||||
</div>
|
||||
<div class="skel-plugin-block">
|
||||
<div class="skel-plugin-header">
|
||||
<span class="skel-icon-block skel-shimmer"></span>
|
||||
<span class="skel-line skel-shimmer" style="width:90px"></span>
|
||||
<span class="skel-line skel-shimmer" style="width:185px"></span>
|
||||
</div>
|
||||
<div class="skel-plugin-body">
|
||||
<div class="skel-setting-row"><span class="skel-cell-name skel-shimmer"></span><span class="skel-cell-desc skel-shimmer"></span><span class="skel-cell-input skel-shimmer"></span></div>
|
||||
<div class="skel-setting-row"><span class="skel-cell-name skel-shimmer"></span><span class="skel-cell-desc skel-shimmer"></span><span class="skel-cell-input skel-shimmer"></span></div>
|
||||
<div class="skel-setting-row"><span class="skel-cell-name skel-shimmer"></span><span class="skel-cell-desc skel-shimmer"></span><span class="skel-cell-input skel-shimmer"></span></div>
|
||||
<div class="skel-setting-row"><span class="skel-cell-name skel-shimmer"></span><span class="skel-cell-desc skel-shimmer"></span><span class="skel-cell-input skel-shimmer"></span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Device Scanners section (closed) -->
|
||||
<div class="skel-section">
|
||||
<div class="skel-section-header">
|
||||
<span class="skel-icon-block skel-shimmer"></span>
|
||||
<span class="skel-line skel-shimmer" style="width:140px"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Other Scanners section (open) -->
|
||||
<div class="skel-section">
|
||||
<div class="skel-section-header">
|
||||
<span class="skel-icon-block skel-shimmer"></span>
|
||||
<span class="skel-line skel-shimmer" style="width:120px"></span>
|
||||
</div>
|
||||
<div class="skel-plugin-block">
|
||||
<div class="skel-plugin-header">
|
||||
<span class="skel-icon-block skel-shimmer"></span>
|
||||
<span class="skel-line skel-shimmer" style="width:65px"></span>
|
||||
<span class="skel-line skel-shimmer" style="width:200px"></span>
|
||||
</div>
|
||||
<div class="skel-plugin-body">
|
||||
<div class="skel-setting-row"><span class="skel-cell-name skel-shimmer" style="width:16%"></span><span class="skel-cell-desc skel-shimmer" style="width:40%"></span><span class="skel-cell-input skel-shimmer"></span></div>
|
||||
<div class="skel-setting-row"><span class="skel-cell-name skel-shimmer"></span><span class="skel-cell-desc skel-shimmer" style="width:33%"></span><span class="skel-cell-input skel-shimmer"></span></div>
|
||||
<div class="skel-setting-row"><span class="skel-cell-name skel-shimmer" style="width:19%"></span><span class="skel-cell-desc skel-shimmer" style="width:42%"></span><span class="skel-cell-input skel-shimmer"></span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Publishers section (closed) -->
|
||||
<div class="skel-section">
|
||||
<div class="skel-section-header">
|
||||
<span class="skel-icon-block skel-shimmer"></span>
|
||||
<span class="skel-line skel-shimmer" style="width:100px"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- /Settings Loading Skeleton -------------------------------------------- -->
|
||||
@@ -69,20 +69,17 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
||||
|
||||
<div id="settingsPage" class="content-wrapper">
|
||||
|
||||
<a style="cursor:pointer">
|
||||
<span>
|
||||
<i id='toggleSettings' onclick="toggleAllSettings()" class="settings-expand-icon fa fa-angle-double-down"></i>
|
||||
</span>
|
||||
</a>
|
||||
<?php require 'php/templates/settings_skeleton.php'; ?>
|
||||
|
||||
|
||||
<!-- Content header--------------------------------------------------------- -->
|
||||
|
||||
<section class="content-header">
|
||||
|
||||
<div class ="bg-white color-palette box box-solid box-primary col-sm-12 panel panel-default panel-title" >
|
||||
<div class ="bg-white color-palette box box-solid col-sm-12 panel panel-default panel-title" >
|
||||
|
||||
<a data-toggle="collapse" href="#settingsOverview">
|
||||
<div class ="settings-group col-sm-12 panel-heading panel-title">
|
||||
<div class ="settings-group col-sm-12 ">
|
||||
<i class="<?= lang("settings_enabled_icon");?>"></i> <?= lang("settings_enabled");?>
|
||||
</div>
|
||||
</a>
|
||||
@@ -284,7 +281,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
console.error("Error:", error);
|
||||
// Handle any errors
|
||||
hideSettingsSkeleton();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -328,7 +325,10 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
||||
|
||||
overviewSections.forEach((section) => {
|
||||
|
||||
overviewSections_html += `<div class="overview-section col-sm-12" id="${section}">
|
||||
const sectionHtml = overviewSectionsHtml[index];
|
||||
|
||||
if (sectionHtml.trim()) {
|
||||
overviewSections_html += `<div class="overview-section col-sm-12" id="${section}">
|
||||
<div class="col-sm-12 " title="${getString("settings_"+section)}">
|
||||
<a href="#${section}_content_header">
|
||||
<div class="overview-group col-sm-12 col-xs-12">
|
||||
@@ -338,9 +338,10 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
${overviewSectionsHtml[index]}
|
||||
${sectionHtml}
|
||||
</div>
|
||||
</div>`
|
||||
}
|
||||
index++;
|
||||
});
|
||||
|
||||
@@ -535,9 +536,18 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
||||
initSelect2();
|
||||
initHoverNodeInfo();
|
||||
hideSpinner();
|
||||
hideSettingsSkeleton();
|
||||
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
function hideSettingsSkeleton() {
|
||||
var $skel = $('#settings-skeleton');
|
||||
if (!$skel.length) return;
|
||||
$('#settingsPage').removeClass('settings-loading');
|
||||
$skel.fadeOut(250, function() { $(this).remove(); });
|
||||
}
|
||||
|
||||
// display the name of the first person
|
||||
// echo $settingsJson[0]->name;
|
||||
var settingsNumberDB = <?php echo count($settings)?>;
|
||||
@@ -652,6 +662,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
||||
{
|
||||
showMessage (getString("settings_readonly"), 10000, "modal_red");
|
||||
console.log(`app.conf seems to be read only (canRWConfig: ${canReadAndWriteConfig})`);
|
||||
hideSettingsSkeleton();
|
||||
} else
|
||||
{
|
||||
// check if config file has been updated
|
||||
@@ -706,9 +717,10 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
showSpinner()
|
||||
handleLoadingDialog()
|
||||
// Fallback: hide skeleton after 15s in case of unexpected error
|
||||
setTimeout(hideSettingsSkeleton, 15000)
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user