secubox-openwrt/package/secubox/luci-app-crowdsec-dashboard/htdocs/luci-static/resources/crowdsec-dashboard/theme.js
CyberMind-FR b35b86684e fix(crowdsec-dashboard): Use baseclass.singleton for theme manager
Fix "not a constructor" error by using baseclass.singleton()
pattern instead of baseclass.extend() with manual instantiation.
Theme module now exports a singleton directly.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 09:30:54 +01:00

246 lines
5.6 KiB
JavaScript

'use strict';
'require baseclass';
'require uci';
/**
* CrowdSec Dashboard Theme Manager (Singleton)
* Handles loading and switching between UI themes
*
* Available themes:
* - classic: Professional SOC-style dark theme (default)
* - cards: Modern card-based UI with gradients
* - cyberpunk: Neon glow effects with orange/cyan accents
*
* Profiles can extend themes with custom configurations
*
* Usage: theme.init().then(function() { ... });
*/
return baseclass.singleton({
// Available themes
themes: {
'classic': {
name: 'Classic SOC',
description: 'Professional Security Operations Center style',
css: 'themes/classic.css'
},
'cards': {
name: 'Modern Cards',
description: 'Card-based UI with gradients and shadows',
css: 'themes/cards.css'
},
'cyberpunk': {
name: 'Cyberpunk',
description: 'Neon glow effects with terminal aesthetics',
css: 'themes/cyberpunk.css'
}
},
// Theme profiles - extend base themes with custom settings
profiles: {
'default': {
theme: 'classic',
options: {}
},
'soc': {
theme: 'classic',
options: {
fullwidth: true,
compactStats: false
}
},
'modern': {
theme: 'cards',
options: {
fullwidth: false,
animatedCards: true
}
},
'hacker': {
theme: 'cyberpunk',
options: {
fullwidth: true,
scanlines: true
}
}
},
currentTheme: null,
currentProfile: null,
/**
* Initialize theme manager and load saved preferences
*/
init: function() {
var self = this;
return uci.load('crowdsec-dashboard').then(function() {
var theme = uci.get('crowdsec-dashboard', 'main', 'theme') || 'classic';
var profile = uci.get('crowdsec-dashboard', 'main', 'profile') || 'default';
return self.loadTheme(theme, profile);
}).catch(function() {
// Default to classic if config fails
return self.loadTheme('classic', 'default');
});
},
/**
* Load a theme and apply it to the dashboard
* @param {string} themeName - Theme identifier
* @param {string} profileName - Optional profile to apply
*/
loadTheme: function(themeName, profileName) {
var theme = this.themes[themeName] || this.themes['classic'];
var profile = this.profiles[profileName] || this.profiles['default'];
// If profile specifies a different theme, use that
if (profile.theme && this.themes[profile.theme]) {
themeName = profile.theme;
theme = this.themes[themeName];
}
this.currentTheme = themeName;
this.currentProfile = profileName;
// Load base CSS first
this.loadCSS('themes/base.css');
// Load theme-specific CSS
this.loadCSS(theme.css);
// Apply theme class to body
document.body.classList.remove('theme-classic', 'theme-cards', 'theme-cyberpunk');
document.body.classList.add('theme-' + themeName);
// Apply profile options
if (profile.options) {
if (profile.options.fullwidth) {
document.body.classList.add('cs-fullwidth');
}
}
return Promise.resolve();
},
/**
* Load a CSS file
* @param {string} path - Path relative to crowdsec-dashboard resources
*/
loadCSS: function(path) {
var fullPath = L.resource('crowdsec-dashboard/' + path);
// Check if already loaded
var existing = document.querySelector('link[href="' + fullPath + '"]');
if (existing) return;
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = fullPath;
document.head.appendChild(link);
},
/**
* Switch to a different theme
* @param {string} themeName - Theme to switch to
*/
switchTheme: function(themeName) {
if (!this.themes[themeName]) {
console.warn('Unknown theme: ' + themeName);
return;
}
// Remove old theme CSS
document.querySelectorAll('link[href*="themes/"]').forEach(function(el) {
if (!el.href.includes('base.css')) {
el.remove();
}
});
return this.loadTheme(themeName, this.currentProfile);
},
/**
* Switch to a different profile
* @param {string} profileName - Profile to switch to
*/
switchProfile: function(profileName) {
if (!this.profiles[profileName]) {
console.warn('Unknown profile: ' + profileName);
return;
}
var profile = this.profiles[profileName];
return this.loadTheme(profile.theme, profileName);
},
/**
* Save current theme/profile to UCI config
*/
save: function() {
uci.set('crowdsec-dashboard', 'main', 'theme', this.currentTheme);
uci.set('crowdsec-dashboard', 'main', 'profile', this.currentProfile);
return uci.save();
},
/**
* Get list of available themes
*/
getThemes: function() {
return Object.keys(this.themes).map(function(id) {
return {
id: id,
name: this.themes[id].name,
description: this.themes[id].description
};
}, this);
},
/**
* Get list of available profiles
*/
getProfiles: function() {
return Object.keys(this.profiles).map(function(id) {
var p = this.profiles[id];
return {
id: id,
theme: p.theme,
options: p.options
};
}, this);
},
/**
* Register a custom theme
* @param {string} id - Theme identifier
* @param {object} config - Theme configuration
*/
registerTheme: function(id, config) {
if (this.themes[id]) {
console.warn('Theme already exists: ' + id);
return false;
}
this.themes[id] = config;
return true;
},
/**
* Register a custom profile
* @param {string} id - Profile identifier
* @param {object} config - Profile configuration
*/
registerProfile: function(id, config) {
if (this.profiles[id]) {
console.warn('Profile already exists: ' + id);
return false;
}
this.profiles[id] = config;
return true;
},
/**
* Get the CSS class for the dashboard container
*/
getDashboardClass: function() {
return 'cs-dashboard theme-' + (this.currentTheme || 'classic');
}
});