mirror of
https://github.com/StuffAnThings/qbit_manage.git
synced 2025-10-29 23:46:28 +08:00
refactor(ui): unify spinner system and improve logging in config validation
- Consolidate spinner styles into unified system in main.css - Remove duplicate spinner definitions from component CSS - Update spinner class usage across JavaScript components - Add logging separators for configuration validation steps - Remove unnecessary logging separator in torrent list retrieval
This commit is contained in:
parent
52cb4f1c50
commit
b2ee109dfe
8 changed files with 126 additions and 43 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
4.5.6-develop4
|
||||
4.5.6-develop5
|
||||
|
|
|
|||
|
|
@ -85,7 +85,6 @@ class Qbt:
|
|||
except Exception as exc:
|
||||
self.config.notify(exc, "Qbittorrent")
|
||||
raise Failed(exc)
|
||||
logger.separator("Getting Torrent List", space=False, border=False)
|
||||
self.torrent_list = self.get_torrents({"sort": "added_on"})
|
||||
self.torrentfiles = {} # a map of torrent files to track cross-seeds
|
||||
|
||||
|
|
|
|||
|
|
@ -781,6 +781,7 @@ class WebAPI:
|
|||
# Write temporary config file for validation
|
||||
temp_config_path = self.config_path / f".temp_{filename}"
|
||||
try:
|
||||
logger.separator("Configuration Validation Check", space=False, border=False)
|
||||
self._write_yaml_config(temp_config_path, request.data)
|
||||
|
||||
# Try to load config using existing validation logic
|
||||
|
|
@ -788,13 +789,17 @@ class WebAPI:
|
|||
Config(self.default_dir, temp_args)
|
||||
except Exception as e:
|
||||
errors.append(str(e))
|
||||
logger.separator("Configuration Validation Failed", space=False, border=False)
|
||||
valid = len(errors) == 0
|
||||
if valid:
|
||||
logger.separator("Configuration Valid", space=False, border=False)
|
||||
|
||||
finally:
|
||||
# Clean up temporary file
|
||||
if temp_config_path.exists():
|
||||
temp_config_path.unlink()
|
||||
|
||||
return ValidationResponse(valid=len(errors) == 0, errors=errors, warnings=warnings)
|
||||
return ValidationResponse(valid=valid, errors=errors, warnings=warnings)
|
||||
except Exception as e:
|
||||
logger.error(f"Error validating config '{filename}': {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
|
|
|||
|
|
@ -337,18 +337,7 @@
|
|||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border: 2px solid transparent;
|
||||
border-top: 2px solid currentColor;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
/* Loading spinner now uses unified system from main.css */
|
||||
|
||||
/* Preset Buttons */
|
||||
.preset-buttons {
|
||||
|
|
|
|||
|
|
@ -252,7 +252,114 @@ body {
|
|||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
/* Animation for loading states */
|
||||
/* Unified Spinner System */
|
||||
@keyframes modern-spin {
|
||||
0% {
|
||||
transform: rotate(0deg) scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
transform: rotate(180deg) scale(1.1);
|
||||
opacity: 0.7;
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg) scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Unified Spinner System */
|
||||
@keyframes smooth-rotate {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Base spinner class - can be used anywhere */
|
||||
.spinner {
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
animation: smooth-rotate 1s linear infinite;
|
||||
flex-shrink: 0;
|
||||
border: 2px solid transparent;
|
||||
border-top: 2px solid var(--primary-color);
|
||||
}
|
||||
|
||||
/* Size variants */
|
||||
.spinner-sm {
|
||||
width: 16px !important;
|
||||
height: 16px !important;
|
||||
}
|
||||
|
||||
.spinner-md {
|
||||
width: 24px !important;
|
||||
height: 24px !important;
|
||||
}
|
||||
|
||||
.spinner-lg {
|
||||
width: 48px !important;
|
||||
height: 48px !important;
|
||||
}
|
||||
|
||||
/* Context-specific spinner styles */
|
||||
.spinner.spinner-inline {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-right: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.spinner.spinner-button {
|
||||
border-top-color: currentColor;
|
||||
}
|
||||
|
||||
/* Loading overlay specific styles */
|
||||
.loading-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: var(--z-modal-backdrop);
|
||||
backdrop-filter: blur(2px);
|
||||
-webkit-backdrop-filter: blur(2px);
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
color: var(--text-inverse);
|
||||
padding: var(--spacing-xl);
|
||||
border-radius: var(--border-radius-lg);
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
/* Dark mode: lighter text for better readability */
|
||||
[data-theme="dark"] .loading-spinner {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* Auto dark mode support */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-theme]) .loading-spinner {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.loading-spinner .spinner {
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
|
||||
/* Legacy support - keep for backward compatibility */
|
||||
@keyframes rotate {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
|
|
@ -693,37 +800,20 @@ body {
|
|||
border: 0;
|
||||
}\n
|
||||
.loading-overlay {
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: var(--z-modal-backdrop); /* Use a high z-index to ensure it's on top */
|
||||
z-index: var(--z-modal-backdrop);
|
||||
backdrop-filter: blur(2px);
|
||||
-webkit-backdrop-filter: blur(2px);
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
text-align: center;
|
||||
color: var(--text-inverse); /* Use text-inverse for better contrast on dark overlay */
|
||||
}
|
||||
|
||||
.spinner {
|
||||
border: 4px solid rgba(255, 255, 255, 0.3);
|
||||
border-top: 4px solid var(--primary-color);
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 0 auto 10px auto;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Password Input Group */
|
||||
.password-input-group {
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ class SchedulerControl {
|
|||
<button type="submit" class="btn btn-primary" id="update-schedule-btn" disabled aria-describedby="update-help">
|
||||
<span class="btn-text">Save Schedule</span>
|
||||
<span class="btn-loading" style="display: none;" aria-hidden="true">
|
||||
<span class="loading-spinner" aria-hidden="true"></span>
|
||||
<span class="spinner spinner-sm spinner-button" aria-hidden="true"></span>
|
||||
Saving...
|
||||
</span>
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ export class ShareLimitsComponent {
|
|||
addButton.disabled = loading;
|
||||
const uiTexts = shareLimitsSchema.ui?.texts || {};
|
||||
addButton.innerHTML = loading
|
||||
? `<span class="loading-spinner"></span> ${uiTexts.addGroupLoadingText || 'Creating...'}`
|
||||
? `<span class="spinner spinner-sm spinner-button"></span> ${uiTexts.addGroupLoadingText || 'Creating...'}`
|
||||
: (uiTexts.addGroupButtonText || 'Add New Group');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ export function showLoading(container, message = 'Loading...') {
|
|||
loadingOverlay.className = 'loading-overlay'; // Add a class for styling
|
||||
loadingOverlay.innerHTML = `
|
||||
<div class="loading-spinner">
|
||||
<div class="spinner"></div>
|
||||
<div class="spinner spinner-lg"></div>
|
||||
<p>${message}</p>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue