diff --git a/front/css/app.css b/front/css/app.css index ef8f2ddf..e5ef9890 100755 --- a/front/css/app.css +++ b/front/css/app.css @@ -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); -} \ No newline at end of file +} + +/* ===== 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 ===== */ \ No newline at end of file diff --git a/front/css/dark-patch.css b/front/css/dark-patch.css index dbda3801..9dcda157 100755 --- a/front/css/dark-patch.css +++ b/front/css/dark-patch.css @@ -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; } \ No newline at end of file diff --git a/front/css/system-dark-patch.css b/front/css/system-dark-patch.css index 06121b02..74171a1e 100755 --- a/front/css/system-dark-patch.css +++ b/front/css/system-dark-patch.css @@ -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; -} \ No newline at end of file +} + + /* Settings skeleton - dark theme */ + :root { + --skel-base: #2e3540; + --skel-shine: #3d4555; + --skel-section: #252c38; + --skel-panel-bg: #1e242e; + --skel-border: #2a323e; + --skel-bg: #353c42; + } \ No newline at end of file diff --git a/front/js/common.js b/front/js/common.js index 3ae3803b..7c0762d3 100755 --- a/front/js/common.js +++ b/front/js/common.js @@ -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 }); } diff --git a/front/js/settings_utils.js b/front/js/settings_utils.js index 6cb01e62..11b1a12b 100755 --- a/front/js/settings_utils.js +++ b/front/js/settings_utils.js @@ -99,7 +99,7 @@ function pluginCards(prefixesOfEnabledPlugins, includeSettings) { }); html += ` -
+
diff --git a/front/php/templates/header.php b/front/php/templates/header.php index 8060abe9..4faff4c8 100755 --- a/front/php/templates/header.php +++ b/front/php/templates/header.php @@ -151,7 +151,7 @@
- +
Loading...
diff --git a/front/php/templates/settings_skeleton.php b/front/php/templates/settings_skeleton.php new file mode 100644 index 00000000..6114a638 --- /dev/null +++ b/front/php/templates/settings_skeleton.php @@ -0,0 +1,106 @@ + +
+ + +
+
+ + +
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+ + +
+
+ + +
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+ + +
+
+ + +
+
+
+ + + +
+
+
+
+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + + +
+
+
+
+
+
+
+
+ + +
+
+ + +
+
+ +
+ diff --git a/front/settings.php b/front/settings.php index 5780de78..8efb3515 100755 --- a/front/settings.php +++ b/front/settings.php @@ -69,20 +69,17 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
- - - - - + +
-
+
-
+
">
@@ -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 += `
+ const sectionHtml = overviewSectionsHtml[index]; + + if (sectionHtml.trim()) { + overviewSections_html += `
` + } 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 = ; @@ -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)