diff --git a/containers/libreportal/frontend/js/components/app/app-manager.js b/containers/libreportal/frontend/js/components/app/app-manager.js
deleted file mode 100755
index 1398ecc..0000000
--- a/containers/libreportal/frontend/js/components/app/app-manager.js
+++ /dev/null
@@ -1,363 +0,0 @@
-// App Manager - Dynamic app loading with beautiful styling
-class AppManager {
- constructor() {
- this.cache = new Map();
- }
-
- getRandomLoadingMessage() {
- const messages = [
- "Preparing your application settings...",
- "Gathering application information...",
- "Loading application configuration...",
- "Setting up your app management panel...",
- "Loading the perfect app settings...",
- "Crafting your application experience...",
- "Preparing your app control panel...",
- "Loading application details...",
- "Setting up your app workspace...",
- "Configuring your application environment..."
- ];
-
- return messages[Math.floor(Math.random() * messages.length)];
- }
-
- async loadApp(appName) {
- //console.log(`AppManager: Loading ${appName} app...`);
-
- // Check cache first
- if (this.cache.has(appName)) {
- //console.log(`AppManager: Using cached ${appName} app`);
- return this.cache.get(appName);
- }
-
- try {
- // Load app data from apps.json
- const response = await fetch('/data/apps/generated/apps.json', { cache: 'no-store' });
- if (!response.ok) {
- throw new Error(`Failed to load apps.json: ${response.status}`);
- }
-
- const appsData = await response.json();
-
- // Try multiple ways to find the app
- let app = appsData.apps.find(app =>
- app.name.toLowerCase().includes(appName.toLowerCase()) ||
- app.command.toLowerCase().includes(appName.toLowerCase()) ||
- app.name === appName ||
- app.name.toLowerCase() === appName.toLowerCase()
- );
-
- if (!app) {
- // Try case-insensitive exact match
- app = appsData.apps.find(app =>
- app.name.toLowerCase() === appName.toLowerCase() ||
- app.command.toLowerCase().includes(appName.toLowerCase())
- );
- }
-
- if (!app) {
- //console.log(`Available apps:`, appsData.apps.map(a => ({ name: a.name, command: a.command })));
- throw new Error(`App ${appName} not found`);
- }
-
- //console.log(`AppManager: Loaded ${appName} app:`, app);
-
- // Cache the result
- this.cache.set(appName, app);
-
- return app;
- } catch (error) {
- console.error(`AppManager: Error loading ${appName} app:`, error);
- return null;
- }
- }
-
- async renderApp(appName) {
- //console.log(`AppManager: Rendering ${appName} app...`);
-
- const configSection = document.getElementById('config-section');
- if (!configSection) {
- console.error('AppManager: config-section element not found');
- return;
- }
-
- // Show loading with enhanced visual
- configSection.innerHTML = `
-
-
-
-
- Loading application...
-
-
- ${this.getRandomLoadingMessage()}
-
-
-
-
- `;
-
- // Update loading bar if available
- if (typeof router !== 'undefined' && router.updateProgress) {
- router.updateProgress(60);
- }
-
- try {
- // Load app data
- const app = await this.loadApp(appName);
-
- // Update loading bar
- if (typeof router !== 'undefined' && router.updateProgress) {
- router.updateProgress(70);
- }
-
- if (!app) {
- configSection.innerHTML = 'Application not found
';
- return;
- }
-
- // App config comes from apps.json (window.apps), not a separate
- // per-app JSON. Pass null — the renderer's config section is gated
- // on appConfig?.config keys so it just skips that section.
- if (typeof router !== 'undefined' && router.updateProgress) {
- router.updateProgress(80);
- }
-
- await this.renderWithOriginalStyling(appName, app, null);
-
- // Final progress update
- if (typeof router !== 'undefined' && router.updateProgress) {
- router.updateProgress(80);
- }
-
- //console.log(`AppManager: Successfully rendered ${appName} app`);
-
- } catch (error) {
- console.error(`AppManager: Error rendering ${appName} app:`, error);
- configSection.innerHTML = `Failed to load ${appName} application: ${error.message}
`;
- }
- }
-
- async renderWithOriginalStyling(appName, app, appConfig) {
- const configSection = document.getElementById('config-section');
-
- // Render using the original app-config system
- let formHTML = `
-
-
${app.displayName || app.name} Application
-
${app.description || 'Manage settings and configuration for ' + (app.displayName || app.name)}
-
-
-
-
-
-
-
-
-
-
- Save Configuration
-
-
-
-
-
-
- Reset to Defaults
-
-
-
- `;
-
- configSection.innerHTML = formHTML;
- }
-
- static async saveAppConfig(appName) {
- //console.log(`AppManager: Saving ${appName} config...`);
-
- const form = document.getElementById(`app-form-${appName}`);
- if (!form) {
- console.error('AppManager: Form not found');
- return;
- }
-
- // Show success message
- if (typeof ConfigShared !== 'undefined' && ConfigShared.showNotification) {
- ConfigShared.showNotification('Application configuration saved successfully!', 'success');
- } else {
- // Fallback message
- const message = document.createElement('div');
- message.className = 'config-message success';
- message.textContent = 'Application configuration saved successfully!';
-
- const actionsDiv = form.parentElement.querySelector('.config-actions');
- actionsDiv.insertBefore(message, actionsDiv.firstChild);
-
- // Remove message after 3 seconds
- setTimeout(() => {
- if (message.parentNode) {
- message.parentNode.removeChild(message);
- }
- }, 3000);
- }
- }
-
- static async resetAppConfig(appName) {
- //console.log(`AppManager: Resetting ${appName} config...`);
-
- if (confirm('Are you sure you want to reset all settings to their default values?')) {
- // Reload the page to reset
- window.location.reload();
- }
- }
-
- async loadScript(src) {
- // Check if script is already loaded
- const scriptId = src.replace(/[^a-zA-Z0-9]/g, '_');
- if (document.getElementById(scriptId)) {
- //console.log(`Script ${src} already loaded, skipping`);
- return;
- }
-
- //console.log(`Loading script: ${src}`);
-
- return new Promise((resolve, reject) => {
- const script = document.createElement('script');
- script.src = src;
- script.id = scriptId;
- script.onload = () => {
- //console.log(`Script loaded successfully: ${src}`);
- resolve();
- };
- script.onerror = (error) => {
- console.error(`Script failed to load: ${src}`, error);
- reject(new Error(`Failed to load script: ${src}`));
- };
- document.head.appendChild(script);
- });
- }
-}
-
-// Global instance
-window.appManager = new AppManager();
diff --git a/containers/libreportal/frontend/js/components/config/config-router.js b/containers/libreportal/frontend/js/components/config/config-router.js
deleted file mode 100755
index d465aaa..0000000
--- a/containers/libreportal/frontend/js/components/config/config-router.js
+++ /dev/null
@@ -1,291 +0,0 @@
-// Configuration Router - Loads appropriate config component based on URL
-class ConfigRouter {
- constructor() {
- this.currentComponent = null;
- }
-
- async init() {
- //console.log('ConfigRouter: Initializing...');
- // Get current category from query parameter or global variable
- const searchParams = new URLSearchParams(window.location.search);
- let currentCategory = searchParams.get('config') || window.configCategory || 'general';
-
- //console.log(`Initial parsing - searchParams.get('config'): ${searchParams.get('config')}`);
- //console.log(`Window location search: ${window.location.search}`);
- //console.log(`Initial currentCategory: ${currentCategory}`);
-
- // Handle the case where URL is config?=backup (malformed but common)
- // Check if we got the category from URL params, not from fallback
- const gotFromUrlParams = searchParams.get('config') !== null;
-
- if (!gotFromUrlParams && window.location.search.includes('?=')) {
- //console.log(`URL contains ?= and no valid config param, attempting regex match...`);
- const pathMatch = window.location.search.match(/\?=([^&]+)/);
- //console.log(`Regex match result:`, pathMatch);
- //console.log(`Regex test result:`, /\?=([^&]+)/.test(window.location.search));
- if (pathMatch && pathMatch[1]) {
- currentCategory = pathMatch[1];
- //console.log(`Updated currentCategory from regex: ${currentCategory}`);
- } else {
- //console.log(`Regex failed to match, currentCategory remains: ${currentCategory}`);
- }
- } else {
- //console.log(`Got category from URL params (${gotFromUrlParams}) or URL doesn't contain ?=, keeping: ${currentCategory}`);
- }
-
- //console.log(`Config router init: final category=${currentCategory}`);
-
- // Backup config moved to /backup — redirect old URL/bookmarks.
- if (currentCategory === 'backup') {
- if (window.librePortalSPA && typeof window.librePortalSPA.navigate === 'function') {
- window.librePortalSPA.navigate('/backup', true);
- } else {
- window.location.href = '/backup';
- }
- return;
- }
-
- // Load categories for sidebar
- await this.loadConfigCategories();
-
- // Set active category in sidebar
- this.setActiveCategory(currentCategory);
-
- // Load appropriate config component
- await this.loadConfigComponent(currentCategory);
- }
-
- async loadConfigCategories() {
- try {
- //console.log('Loading config categories from: data/config/generated/configs.json');
-
- // Start loading bar
- if (typeof router !== 'undefined' && router.updateProgress) {
- router.updateProgress(20);
- }
-
- // Load unified config file
- const response = await fetch('/data/config/generated/configs.json');
- //console.log('Response status:', response.status, response.statusText);
- //console.log('Response ok:', response.ok);
-
- if (!response.ok) {
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
- }
-
- const text = await response.text();
- //console.log('Raw response text:', text);
- //console.log('Text length:', text.length);
- //console.log('First 100 chars:', text.substring(0, 100));
-
- if (!text || text.trim() === '') {
- throw new Error('Empty response from configs.json');
- }
-
- const data = JSON.parse(text);
- const categories = data.categories;
-
- //console.log('Parsed categories:', categories);
-
- if (typeof router !== 'undefined' && router.updateProgress) {
- router.updateProgress(60);
- }
-
- const categoriesList = document.getElementById('config-categories-list');
- if (!categoriesList) {
- console.error('config-categories-list element not found');
- return;
- }
-
- categoriesList.innerHTML = '';
-
- // Convert categories object to array and sort by ORDER
- const categoriesArray = Object.entries(categories).map(([key, value]) => ({
- id: key,
- ...value
- }));
-
- // Sort by ORDER if available, otherwise by title
- categoriesArray.sort((a, b) => {
- const orderA = parseInt(a.order) || 999;
- const orderB = parseInt(b.order) || 999;
- return orderA - orderB;
- });
-
- categoriesArray.forEach(category => {
- const categoryItem = document.createElement('div');
- categoryItem.className = 'category';
- categoryItem.setAttribute('data-category', category.id);
-
- // Use correct icon from our new structure
- const iconName = category.icon || category.id;
- const iconPath = `/icons/config/${iconName}.svg`;
- //console.log(`Category: ${category.id}, Icon path: ${iconPath}`);
- categoryItem.innerHTML = `
-
- ${category.title}
- `;
-
- categoryItem.addEventListener('click', () => {
- //console.log(`Category clicked: ${category.id}`);
- this.navigateToCategory(category.id);
- });
-
- categoriesList.appendChild(categoryItem);
- });
-
- if (typeof router !== 'undefined' && router.updateProgress) {
- router.updateProgress(80);
- }
-
- } catch (error) {
- console.error('Error loading config categories:', error);
- if (typeof router !== 'undefined' && router.hideLoadingBar) {
- router.hideLoadingBar();
- }
- }
- }
-
- setActiveCategory(categoryId) {
- // Update active state
- document.querySelectorAll('.category').forEach(item => {
- item.classList.remove('active');
- });
- document.querySelector(`[data-category="${categoryId}"]`)?.classList.add('active');
- }
-
- navigateToCategory(categoryId) {
- //console.log(`Config router: navigating to ${categoryId} (SPA mode)`);
-
- // Update URL without full page reload using query parameter
- const url = `/config?=${categoryId}`;
- //console.log(`Updating URL to: ${url}`);
- window.history.pushState({}, '', url);
-
- // Set active category
- this.setActiveCategory(categoryId);
-
- // Load config content dynamically
- this.loadConfigComponent(categoryId);
- }
-
- async loadConfigComponent(categoryId) {
- try {
- //console.log(`Config router: Loading component for ${categoryId}`);
-
- // Start loading bar
- if (typeof router !== 'undefined' && router.updateProgress) {
- router.updateProgress(10);
- router.showLoadingBar();
- }
-
- // Clear current content
- const configSection = document.getElementById('config-section');
- if (configSection) {
- configSection.innerHTML = 'Loading configuration...
';
- }
-
- // Update progress
- if (typeof router !== 'undefined' && router.updateProgress) {
- router.updateProgress(30);
- }
-
- // Use the simple config manager
- if (window.configManager) {
- //console.log(`Using ConfigManager for ${categoryId}`);
- if (typeof router !== 'undefined' && router.updateProgress) {
- router.updateProgress(50);
- }
-
- await window.configManager.renderConfig(categoryId);
-
- if (typeof router !== 'undefined' && router.updateProgress) {
- router.updateProgress(90);
- }
- } else {
- // Fallback - try to load config manager
- //console.log('ConfigManager not available, loading it...');
- if (typeof router !== 'undefined' && router.updateProgress) {
- router.updateProgress(40);
- }
-
- await new Promise((resolve) => {
- const script = document.createElement('script');
- script.src = '/js/components/config/config-manager.js';
- script.onload = async () => {
- if (window.configManager) {
- if (typeof router !== 'undefined' && router.updateProgress) {
- router.updateProgress(60);
- }
- await window.configManager.renderConfig(categoryId);
- if (typeof router !== 'undefined' && router.updateProgress) {
- router.updateProgress(90);
- }
- }
- resolve();
- };
- document.head.appendChild(script);
- });
- }
-
- // Complete loading
- if (typeof router !== 'undefined' && router.updateProgress) {
- router.updateProgress(100);
- setTimeout(() => {
- router.hideLoadingBar();
- }, 500); // Small delay to show completion
- }
-
- } catch (error) {
- console.error(`Error loading config component for ${categoryId}:`, error);
- const configSection = document.getElementById('config-section');
- if (configSection) {
- configSection.innerHTML = `Failed to load ${categoryId} configuration: ${error.message}
`;
- }
- if (typeof router !== 'undefined' && router.hideLoadingBar) {
- router.hideLoadingBar();
- }
- }
- }
-
- async loadConfigComponentManual(categoryId) {
- const configSection = document.getElementById('config-section');
-
- // This method is no longer needed since we use ConfigManager for all categories
- // The individual config classes have been removed
- //console.log(`ConfigRouter: loadConfigComponentManual called for ${categoryId} - delegating to ConfigManager`);
-
- // Use ConfigManager for all categories now
- if (window.configManager) {
- await window.configManager.renderConfig(categoryId);
- } else {
- configSection.innerHTML = 'ConfigManager not available
';
- }
-
- // Hide loading bar
- if (typeof router !== 'undefined' && router.hideLoadingBar) {
- router.hideLoadingBar();
- }
- }
-
- async loadScript(src) {
- // Check if script is already loaded
- const scriptId = src.replace(/[^a-zA-Z0-9]/g, '_');
- if (document.getElementById(scriptId)) {
- //console.log(`Script ${src} already loaded, skipping`);
- return;
- }
-
- return new Promise((resolve, reject) => {
- const script = document.createElement('script');
- script.src = src;
- script.id = scriptId;
- script.onload = resolve;
- script.onerror = reject;
- document.head.appendChild(script);
- });
- }
-}
-
-// Export for global access
-window.ConfigRouter = ConfigRouter;