// 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} ${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;