Merge claude/1
This commit is contained in:
commit
5414b3d407
@ -21,33 +21,27 @@ class ConfigCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async loadConfig(category) {
|
async loadConfig(category) {
|
||||||
//console.log(`ConfigCore: Loading ${category} config...`);
|
|
||||||
|
|
||||||
if (this.cache.has('unified')) {
|
if (this.cache.has('unified')) {
|
||||||
//console.log(`ConfigCore: Using cached unified config`);
|
|
||||||
const cachedData = this.cache.get('unified');
|
const cachedData = this.cache.get('unified');
|
||||||
window.configData = cachedData; // Make available globally
|
window.configData = cachedData; // Make available globally
|
||||||
return cachedData.subcategories || {};
|
return cachedData.subcategories || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//console.log(`ConfigCore: Fetching from /data/config/generated/configs.json`);
|
|
||||||
const response = await fetch('/data/config/generated/configs.json');
|
const response = await fetch('/data/config/generated/configs.json');
|
||||||
//console.log(`ConfigCore: Response status: ${response.status}`);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const configData = await response.json();
|
const configData = await response.json();
|
||||||
//console.log(`ConfigCore: Loaded config data:`, configData);
|
|
||||||
|
|
||||||
this.cache.set('unified', configData);
|
this.cache.set('unified', configData);
|
||||||
window.configData = configData; // Make available globally
|
window.configData = configData; // Make available globally
|
||||||
|
|
||||||
// Return the actual subcategories, not just the category metadata
|
// Return the actual subcategories, not just the category metadata
|
||||||
const categoryData = configData.subcategories || {};
|
const categoryData = configData.subcategories || {};
|
||||||
//console.log(`ConfigCore: Available subcategories:`, Object.keys(categoryData));
|
|
||||||
|
|
||||||
return categoryData;
|
return categoryData;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
// Config Manager - Main orchestrator for modular config system
|
// Config Manager - Main orchestrator for modular config system
|
||||||
if (typeof window.ConfigManager === 'undefined') {
|
if (typeof window.ConfigManager === 'undefined') {
|
||||||
//console.log('ConfigManager: Defining new ConfigManager class...');
|
|
||||||
|
|
||||||
class ConfigManager {
|
class ConfigManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -17,7 +16,6 @@ if (typeof window.ConfigManager === 'undefined') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async renderConfig(category) {
|
async renderConfig(category) {
|
||||||
//console.log('ConfigManager: Rendering ' + category + ' config...');
|
|
||||||
|
|
||||||
const configSection = document.getElementById('config-section');
|
const configSection = document.getElementById('config-section');
|
||||||
if (!configSection) {
|
if (!configSection) {
|
||||||
@ -183,7 +181,6 @@ if (typeof window.ConfigManager === 'undefined') {
|
|||||||
var formHTML = '';
|
var formHTML = '';
|
||||||
var self = this; // Preserve 'this' context
|
var self = this; // Preserve 'this' context
|
||||||
|
|
||||||
//console.log('ConfigManager: About to process configData entries:', Object.keys(configData));
|
|
||||||
|
|
||||||
// Filter subcategories by type
|
// Filter subcategories by type
|
||||||
const subcategoryTypes = this.utils.filterSubcategoriesByType(configData, category);
|
const subcategoryTypes = this.utils.filterSubcategoriesByType(configData, category);
|
||||||
@ -191,10 +188,8 @@ if (typeof window.ConfigManager === 'undefined') {
|
|||||||
// Render regular subcategories
|
// Render regular subcategories
|
||||||
for (const subcategoryName of subcategoryTypes.regular) {
|
for (const subcategoryName of subcategoryTypes.regular) {
|
||||||
const subcategoryData = configData[subcategoryName];
|
const subcategoryData = configData[subcategoryName];
|
||||||
//console.log('ConfigManager: Processing regular subcategory:', subcategoryName, 'data:', subcategoryData);
|
|
||||||
|
|
||||||
if (typeof subcategoryData === 'object' && subcategoryData !== null) {
|
if (typeof subcategoryData === 'object' && subcategoryData !== null) {
|
||||||
//console.log('ConfigManager: Calling renderSubcategory for:', subcategoryName);
|
|
||||||
formHTML += await self.renderSubcategory.call(self, category, subcategoryName, subcategoryData);
|
formHTML += await self.renderSubcategory.call(self, category, subcategoryName, subcategoryData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -202,7 +197,6 @@ if (typeof window.ConfigManager === 'undefined') {
|
|||||||
// Render advanced and unused sections
|
// Render advanced and unused sections
|
||||||
formHTML = await this.utils.renderSectionedContent(formHTML, subcategoryTypes.advanced, subcategoryTypes.unused, self, category, configData);
|
formHTML = await this.utils.renderSectionedContent(formHTML, subcategoryTypes.advanced, subcategoryTypes.unused, self, category, configData);
|
||||||
|
|
||||||
//console.log('ConfigManager: Final formHTML length:', formHTML.length);
|
|
||||||
|
|
||||||
if (formHTML) {
|
if (formHTML) {
|
||||||
// Page-level header for the config section. Mirrors the
|
// Page-level header for the config section. Mirrors the
|
||||||
@ -249,7 +243,6 @@ if (typeof window.ConfigManager === 'undefined') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log('ConfigManager: Successfully rendered ' + category + ' config');
|
|
||||||
|
|
||||||
// Force rediscover toggles to handle timing issues
|
// Force rediscover toggles to handle timing issues
|
||||||
if (window.toggleManager && window.toggleManager.forceRediscover) {
|
if (window.toggleManager && window.toggleManager.forceRediscover) {
|
||||||
@ -265,7 +258,6 @@ if (typeof window.ConfigManager === 'undefined') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async renderSubcategory(category, subcategoryName, subcategoryData) {
|
async renderSubcategory(category, subcategoryName, subcategoryData) {
|
||||||
//console.log('ConfigManager: renderSubcategory() called - category: ' + category + ', subcategory: ' + subcategoryName);
|
|
||||||
|
|
||||||
var displaySubcategory = this.utils.formatSubcategoryName(subcategoryName);
|
var displaySubcategory = this.utils.formatSubcategoryName(subcategoryName);
|
||||||
// Strip the parent-category prefix from the display title so the user
|
// Strip the parent-category prefix from the display title so the user
|
||||||
@ -295,65 +287,44 @@ if (typeof window.ConfigManager === 'undefined') {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log('ConfigManager: Processing subcategory:', subcategoryName, 'data:', subcategoryData);
|
|
||||||
//console.log('ConfigManager: configItems count: ' + configItems.length);
|
|
||||||
//console.log('ConfigManager: All config items keys:', configItems.map(item => item.key));
|
|
||||||
|
|
||||||
if (configItems.length === 0) {
|
if (configItems.length === 0) {
|
||||||
//console.log('ConfigManager: No config items, returning empty string');
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log('ConfigManager: renderSubcategory called with:', {
|
|
||||||
//category,
|
|
||||||
//subcategoryName,
|
|
||||||
//displaySubcategory,
|
|
||||||
//hasData: !!subcategoryData
|
|
||||||
//});
|
|
||||||
|
|
||||||
// Check for master toggle in this subcategory
|
// Check for master toggle in this subcategory
|
||||||
var masterKey = configItems.find(function(item) { return item.master === true; });
|
var masterKey = configItems.find(function(item) { return item.master === true; });
|
||||||
//console.log('ConfigManager: masterKey found: ' + !!masterKey, masterKey ? masterKey.key : null);
|
|
||||||
|
|
||||||
// Look for any ENABLED options and use universal toggle renderer
|
// Look for any ENABLED options and use universal toggle renderer
|
||||||
var enabledKey = configItems.find(function(item) {
|
var enabledKey = configItems.find(function(item) {
|
||||||
//console.log('Checking item for ENABLED:', item.key, item.key.includes('ENABLED'));
|
|
||||||
return item.key.includes('ENABLED') || item.key === 'CFG_INSTALL_MODE';
|
return item.key.includes('ENABLED') || item.key === 'CFG_INSTALL_MODE';
|
||||||
});
|
});
|
||||||
//console.log('ConfigManager: enabledKey found: ' + !!enabledKey, enabledKey ? enabledKey.key : null);
|
|
||||||
|
|
||||||
// Special handling for domains section
|
// Special handling for domains section
|
||||||
var isDomains = subcategoryName.includes('domains') || subcategoryName.includes('network_domains');
|
var isDomains = subcategoryName.includes('domains') || subcategoryName.includes('network_domains');
|
||||||
//console.log('ConfigManager: isDomains:', isDomains);
|
|
||||||
|
|
||||||
// Special handling for IP whitelist section
|
// Special handling for IP whitelist section
|
||||||
var isWhitelist = subcategoryName === 'network_whitelist' || subcategoryName.includes('whitelist');
|
var isWhitelist = subcategoryName === 'network_whitelist' || subcategoryName.includes('whitelist');
|
||||||
//console.log('ConfigManager: subcategoryName:', subcategoryName, 'isWhitelist:', isWhitelist);
|
|
||||||
|
|
||||||
var resultHTML = '';
|
var resultHTML = '';
|
||||||
|
|
||||||
if (isDomains) {
|
if (isDomains) {
|
||||||
//console.log('ConfigManager: Using domains renderer');
|
|
||||||
// Render domains section with special handling
|
// Render domains section with special handling
|
||||||
resultHTML = await this.domainManager.renderDomainsSection(configItems, displaySubcategory, subcategoryDescription);
|
resultHTML = await this.domainManager.renderDomainsSection(configItems, displaySubcategory, subcategoryDescription);
|
||||||
} else if (isWhitelist) {
|
} else if (isWhitelist) {
|
||||||
//console.log('ConfigManager: Using whitelist renderer');
|
|
||||||
resultHTML = await this.whitelistManager.renderWhitelistSection(configItems, displaySubcategory, subcategoryDescription);
|
resultHTML = await this.whitelistManager.renderWhitelistSection(configItems, displaySubcategory, subcategoryDescription);
|
||||||
} else if (enabledKey) {
|
} else if (enabledKey) {
|
||||||
//console.log('ConfigManager: Using universal toggle renderer');
|
|
||||||
// Use universal toggle renderer for any ENABLED option or CFG_INSTALL_MODE
|
// Use universal toggle renderer for any ENABLED option or CFG_INSTALL_MODE
|
||||||
resultHTML = window.toggleManager ? window.toggleManager.renderToggleSection(enabledKey, configItems, displaySubcategory, subcategoryDescription) : '';
|
resultHTML = window.toggleManager ? window.toggleManager.renderToggleSection(enabledKey, configItems, displaySubcategory, subcategoryDescription) : '';
|
||||||
} else if (masterKey) {
|
} else if (masterKey) {
|
||||||
//console.log('ConfigManager: Using master toggle renderer');
|
|
||||||
// Render with master toggle
|
// Render with master toggle
|
||||||
resultHTML = this.renderer.renderSubcategoryWithMaster(masterKey, configItems, displaySubcategory, subcategoryDescription);
|
resultHTML = this.renderer.renderSubcategoryWithMaster(masterKey, configItems, displaySubcategory, subcategoryDescription);
|
||||||
} else {
|
} else {
|
||||||
//console.log('ConfigManager: Using regular renderer');
|
|
||||||
// Render regular subcategory
|
// Render regular subcategory
|
||||||
resultHTML = this.renderer.renderSubcategorySection(configItems, displaySubcategory, subcategoryDescription);
|
resultHTML = this.renderer.renderSubcategorySection(configItems, displaySubcategory, subcategoryDescription);
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log('ConfigManager: resultHTML length:', resultHTML.length);
|
|
||||||
return resultHTML;
|
return resultHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -383,5 +354,4 @@ if (typeof window.ConfigManager === 'undefined') {
|
|||||||
// Export to global scope
|
// Export to global scope
|
||||||
window.ConfigManager = ConfigManager;
|
window.ConfigManager = ConfigManager;
|
||||||
} else {
|
} else {
|
||||||
//console.log('ConfigManager: Already exists, using existing instance');
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,6 @@ class ConfigSidebar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
populateSidebar() {
|
populateSidebar() {
|
||||||
//console.log('ConfigSidebar: Populating sidebar with categories...');
|
|
||||||
|
|
||||||
this.categoriesList = document.getElementById('config-categories-list');
|
this.categoriesList = document.getElementById('config-categories-list');
|
||||||
if (!this.categoriesList) {
|
if (!this.categoriesList) {
|
||||||
@ -157,7 +156,6 @@ class ConfigSidebar {
|
|||||||
// Set initial active category
|
// Set initial active category
|
||||||
this.setActiveCategory(window.configCategory || 'overview');
|
this.setActiveCategory(window.configCategory || 'overview');
|
||||||
|
|
||||||
//console.log('ConfigSidebar: Sidebar populated with ' + categoriesArray.length + ' categories');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setActiveCategory(categoryId) {
|
setActiveCategory(categoryId) {
|
||||||
|
|||||||
@ -94,10 +94,8 @@ class ConfigUtils {
|
|||||||
|
|
||||||
for (const subcategoryName of unusedSubcategories) {
|
for (const subcategoryName of unusedSubcategories) {
|
||||||
const subcategoryData = configData[subcategoryName];
|
const subcategoryData = configData[subcategoryName];
|
||||||
//console.log('ConfigUtils: Processing unused subcategory:', subcategoryName, 'data:', subcategoryData);
|
|
||||||
|
|
||||||
if (typeof subcategoryData === 'object' && subcategoryData !== null) {
|
if (typeof subcategoryData === 'object' && subcategoryData !== null) {
|
||||||
//console.log('ConfigUtils: Calling renderSubcategory for:', subcategoryName);
|
|
||||||
formHTML += await self.renderSubcategory.call(self, category, subcategoryName, subcategoryData);
|
formHTML += await self.renderSubcategory.call(self, category, subcategoryName, subcategoryData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,7 +35,6 @@ window.ConfigValidator = function() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log(`Config file exists: ${config.name}`);
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
results.valid = false;
|
results.valid = false;
|
||||||
|
|||||||
@ -13,7 +13,6 @@ class DomainManager {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
//console.log('DomainManager: Traefik status check failed, assuming not installed:', error.message);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -192,7 +191,6 @@ class DomainManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addNewDomain() {
|
addNewDomain() {
|
||||||
//console.log('Add Domain button clicked!');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Find the highest existing domain number
|
// Find the highest existing domain number
|
||||||
@ -208,7 +206,6 @@ class DomainManager {
|
|||||||
|
|
||||||
// Check if we've reached the maximum of 9 domains (count only existing domains, not empty slots)
|
// Check if we've reached the maximum of 9 domains (count only existing domains, not empty slots)
|
||||||
if (domainNumbers.length >= 9) {
|
if (domainNumbers.length >= 9) {
|
||||||
//console.log('Maximum of 9 domains reached');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,7 +255,6 @@ class DomainManager {
|
|||||||
console.error('DomainManager not available for deleteDomain');
|
console.error('DomainManager not available for deleteDomain');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//console.log(`Delete domain button clicked for: ${domainKey}`);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Find the domain-building-block and remove it
|
// Find the domain-building-block and remove it
|
||||||
@ -324,7 +320,6 @@ class DomainManager {
|
|||||||
|
|
||||||
// Standalone domain management functions - immediately available
|
// Standalone domain management functions - immediately available
|
||||||
window.addDomain = function() {
|
window.addDomain = function() {
|
||||||
//console.log('Add Domain button clicked!');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Before adding new domain, validate that all existing domains have valid format
|
// Before adding new domain, validate that all existing domains have valid format
|
||||||
@ -357,7 +352,6 @@ window.addDomain = function() {
|
|||||||
|
|
||||||
// Check if we've reached the maximum of 9 domains
|
// Check if we've reached the maximum of 9 domains
|
||||||
if (domainNumbers.length >= 9) {
|
if (domainNumbers.length >= 9) {
|
||||||
//console.log('Maximum of 9 domains reached');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,7 +402,6 @@ window.addDomain = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
window.deleteDomain = function(domainKey, buttonElement) {
|
window.deleteDomain = function(domainKey, buttonElement) {
|
||||||
//console.log(`Delete domain button clicked for: ${domainKey}`);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Find the domain-building-block and remove it
|
// Find the domain-building-block and remove it
|
||||||
@ -506,15 +499,12 @@ function updateAddDomainButton() {
|
|||||||
// Validate domain format when user tries to add a new domain
|
// Validate domain format when user tries to add a new domain
|
||||||
function validateDomainFormat(input, showNotifications = true) {
|
function validateDomainFormat(input, showNotifications = true) {
|
||||||
const value = input.value.trim();
|
const value = input.value.trim();
|
||||||
//console.log('validateDomainFormat called with:', value, 'showNotifications:', showNotifications);
|
|
||||||
//console.log('window.notificationSystem available:', !!window.notificationSystem);
|
|
||||||
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
// Clear styling for empty fields
|
// Clear styling for empty fields
|
||||||
input.style.borderColor = '#dc3545';
|
input.style.borderColor = '#dc3545';
|
||||||
input.title = 'Domain cannot be empty';
|
input.title = 'Domain cannot be empty';
|
||||||
if (showNotifications && window.notificationSystem) {
|
if (showNotifications && window.notificationSystem) {
|
||||||
//console.log('Attempting to show empty domain notification');
|
|
||||||
window.notificationSystem.error('Domain cannot be empty');
|
window.notificationSystem.error('Domain cannot be empty');
|
||||||
} else {
|
} else {
|
||||||
console.error('Domain cannot be empty - notification system not available');
|
console.error('Domain cannot be empty - notification system not available');
|
||||||
@ -537,7 +527,6 @@ function validateDomainFormat(input, showNotifications = true) {
|
|||||||
input.style.borderColor = '#dc3545';
|
input.style.borderColor = '#dc3545';
|
||||||
input.title = 'Invalid domain format (e.g., example.com)';
|
input.title = 'Invalid domain format (e.g., example.com)';
|
||||||
if (showNotifications && window.notificationSystem) {
|
if (showNotifications && window.notificationSystem) {
|
||||||
//console.log('Attempting to show invalid format notification');
|
|
||||||
window.notificationSystem.error('Invalid domain format: "' + value + '". Please use a valid domain like example.com');
|
window.notificationSystem.error('Invalid domain format: "' + value + '". Please use a valid domain like example.com');
|
||||||
} else {
|
} else {
|
||||||
console.error('Invalid domain format - notification system not available');
|
console.error('Invalid domain format - notification system not available');
|
||||||
@ -547,7 +536,6 @@ function validateDomainFormat(input, showNotifications = true) {
|
|||||||
input.style.borderColor = '#dc3545';
|
input.style.borderColor = '#dc3545';
|
||||||
input.title = 'Domain already exists';
|
input.title = 'Domain already exists';
|
||||||
if (showNotifications && window.notificationSystem) {
|
if (showNotifications && window.notificationSystem) {
|
||||||
//console.log('Attempting to show duplicate notification');
|
|
||||||
window.notificationSystem.error('Domain "' + value + '" already exists');
|
window.notificationSystem.error('Domain "' + value + '" already exists');
|
||||||
} else {
|
} else {
|
||||||
console.error('Domain already exists - notification system not available');
|
console.error('Domain already exists - notification system not available');
|
||||||
@ -609,7 +597,6 @@ function checkForInvalidDomainFormat(input, domainValue) {
|
|||||||
input.style.borderColor = '#dc3545';
|
input.style.borderColor = '#dc3545';
|
||||||
input.title = 'Invalid domain format (e.g., example.com)';
|
input.title = 'Invalid domain format (e.g., example.com)';
|
||||||
if (window.notificationSystem) {
|
if (window.notificationSystem) {
|
||||||
//console.log('Showing invalid domain format notification');
|
|
||||||
window.notificationSystem.error('Invalid domain format: "' + domainValue + '". Please use a valid domain like example.com');
|
window.notificationSystem.error('Invalid domain format: "' + domainValue + '". Please use a valid domain like example.com');
|
||||||
} else {
|
} else {
|
||||||
console.error('Invalid domain format - notification system not available');
|
console.error('Invalid domain format - notification system not available');
|
||||||
@ -634,7 +621,6 @@ function checkForDuplicateDomain(input, domainValue) {
|
|||||||
input.style.borderColor = '#dc3545';
|
input.style.borderColor = '#dc3545';
|
||||||
input.title = 'Domain already exists';
|
input.title = 'Domain already exists';
|
||||||
if (window.notificationSystem) {
|
if (window.notificationSystem) {
|
||||||
//console.log('Showing duplicate domain notification');
|
|
||||||
window.notificationSystem.error('Domain "' + domainValue + '" already exists');
|
window.notificationSystem.error('Domain "' + domainValue + '" already exists');
|
||||||
} else {
|
} else {
|
||||||
console.error('Domain already exists - notification system not available');
|
console.error('Domain already exists - notification system not available');
|
||||||
|
|||||||
@ -140,22 +140,18 @@ class IPWhitelistManager {
|
|||||||
try {
|
try {
|
||||||
// Get only the visible whitelist input values (exclude hidden input)
|
// Get only the visible whitelist input values (exclude hidden input)
|
||||||
const whitelistInputs = document.querySelectorAll('input[name="CFG_IPS_WHITELIST"]:not([type="hidden"])');
|
const whitelistInputs = document.querySelectorAll('input[name="CFG_IPS_WHITELIST"]:not([type="hidden"])');
|
||||||
//console.log('saveWhitelistEntries: Found inputs:', whitelistInputs.length);
|
|
||||||
whitelistInputs.forEach((input, index) => {
|
whitelistInputs.forEach((input, index) => {
|
||||||
//console.log(` Input ${index}: id="${input.id}", value="${input.value}"`);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const values = Array.from(whitelistInputs)
|
const values = Array.from(whitelistInputs)
|
||||||
.map(input => input.value.trim())
|
.map(input => input.value.trim())
|
||||||
.filter(value => value.length > 0); // Filter out empty values
|
.filter(value => value.length > 0); // Filter out empty values
|
||||||
|
|
||||||
//console.log('saveWhitelistEntries: Filtered values:', values);
|
|
||||||
|
|
||||||
// Find the hidden CFG_IPS_WHITELIST input and update it
|
// Find the hidden CFG_IPS_WHITELIST input and update it
|
||||||
const hiddenInput = document.querySelector('input[name="CFG_IPS_WHITELIST"][type="hidden"]');
|
const hiddenInput = document.querySelector('input[name="CFG_IPS_WHITELIST"][type="hidden"]');
|
||||||
if (hiddenInput) {
|
if (hiddenInput) {
|
||||||
hiddenInput.value = values.join(', ');
|
hiddenInput.value = values.join(', ');
|
||||||
//console.log('Saved whitelist values:', values.join(', '));
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error saving whitelist entries:', error);
|
console.error('Error saving whitelist entries:', error);
|
||||||
@ -164,7 +160,6 @@ class IPWhitelistManager {
|
|||||||
|
|
||||||
// Validate individual whitelist entry
|
// Validate individual whitelist entry
|
||||||
validateWhitelistEntry(input, showNotifications = true) {
|
validateWhitelistEntry(input, showNotifications = true) {
|
||||||
//console.log('validateWhitelistEntry called with:', input.value, 'showNotifications:', showNotifications);
|
|
||||||
|
|
||||||
const value = input.value.trim();
|
const value = input.value.trim();
|
||||||
|
|
||||||
@ -174,14 +169,12 @@ class IPWhitelistManager {
|
|||||||
input.title = '';
|
input.title = '';
|
||||||
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
//console.log('Empty value - allowing for now');
|
|
||||||
// Empty is allowed for now (validation happens on add)
|
// Empty is allowed for now (validation happens on add)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special case for localhost
|
// Special case for localhost
|
||||||
if (value.toLowerCase() === 'localhost') {
|
if (value.toLowerCase() === 'localhost') {
|
||||||
//console.log('localhost detected - valid');
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,15 +188,8 @@ class IPWhitelistManager {
|
|||||||
const isValidDomain = domainPattern.test(value);
|
const isValidDomain = domainPattern.test(value);
|
||||||
const isValidFormat = isValidIP || isValidDomain || value.toLowerCase() === 'localhost';
|
const isValidFormat = isValidIP || isValidDomain || value.toLowerCase() === 'localhost';
|
||||||
|
|
||||||
//console.log('Format validation:', {
|
|
||||||
//value: value,
|
|
||||||
//isValidIP: isValidIP,
|
|
||||||
//isValidDomain: isValidDomain,
|
|
||||||
//isValidFormat: isValidFormat
|
|
||||||
//});
|
|
||||||
|
|
||||||
if (!isValidFormat) {
|
if (!isValidFormat) {
|
||||||
//console.log('Invalid format detected - showing error');
|
|
||||||
// Flash the input field
|
// Flash the input field
|
||||||
input.style.animation = 'whitelist-flash 0.5s ease-in-out 2';
|
input.style.animation = 'whitelist-flash 0.5s ease-in-out 2';
|
||||||
input.focus();
|
input.focus();
|
||||||
@ -213,10 +199,8 @@ class IPWhitelistManager {
|
|||||||
|
|
||||||
// Show notification
|
// Show notification
|
||||||
if (showNotifications && window.notificationSystem) {
|
if (showNotifications && window.notificationSystem) {
|
||||||
//console.log('Showing notification for invalid format');
|
|
||||||
window.notificationSystem.error(`Invalid whitelist format: "${value}". Please use a valid IP address, domain (e.g., example.com), or localhost`);
|
window.notificationSystem.error(`Invalid whitelist format: "${value}". Please use a valid IP address, domain (e.g., example.com), or localhost`);
|
||||||
} else {
|
} else {
|
||||||
//console.log('Notification system not available or showNotifications is false');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input.title = 'Invalid IP address, domain, or localhost format (e.g., 192.168.1.100, example.com, or localhost)';
|
input.title = 'Invalid IP address, domain, or localhost format (e.g., 192.168.1.100, example.com, or localhost)';
|
||||||
@ -225,7 +209,6 @@ class IPWhitelistManager {
|
|||||||
|
|
||||||
// Check for duplicates
|
// Check for duplicates
|
||||||
const allWhitelistInputs = document.querySelectorAll('input[name="CFG_IPS_WHITELIST"]:not([type="hidden"])');
|
const allWhitelistInputs = document.querySelectorAll('input[name="CFG_IPS_WHITELIST"]:not([type="hidden"])');
|
||||||
//console.log('Checking for duplicates among', allWhitelistInputs.length, 'inputs');
|
|
||||||
|
|
||||||
const duplicates = [];
|
const duplicates = [];
|
||||||
|
|
||||||
@ -238,10 +221,8 @@ class IPWhitelistManager {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//console.log('Found duplicates:', duplicates);
|
|
||||||
|
|
||||||
if (duplicates.length > 0) {
|
if (duplicates.length > 0) {
|
||||||
//console.log('Duplicate detected - showing error');
|
|
||||||
// Flash the input field
|
// Flash the input field
|
||||||
input.style.animation = 'whitelist-flash 0.5s ease-in-out 2';
|
input.style.animation = 'whitelist-flash 0.5s ease-in-out 2';
|
||||||
input.focus();
|
input.focus();
|
||||||
@ -251,7 +232,6 @@ class IPWhitelistManager {
|
|||||||
|
|
||||||
// Show notification
|
// Show notification
|
||||||
if (showNotifications && window.notificationSystem) {
|
if (showNotifications && window.notificationSystem) {
|
||||||
//console.log('Showing notification for duplicate');
|
|
||||||
window.notificationSystem.error(`"${value}" already exists in the whitelist`);
|
window.notificationSystem.error(`"${value}" already exists in the whitelist`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,7 +240,6 @@ class IPWhitelistManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Valid entry - save it
|
// Valid entry - save it
|
||||||
//console.log('Valid entry - saving');
|
|
||||||
this.saveWhitelistEntries();
|
this.saveWhitelistEntries();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -303,18 +282,15 @@ class IPWhitelistManager {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
//console.log('Traefik check failed:', error.message);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add new whitelist entry
|
// Add new whitelist entry
|
||||||
addWhitelistEntry() {
|
addWhitelistEntry() {
|
||||||
//console.log('Add whitelist entry button clicked!');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Before adding new entry, validate that all existing entries have valid format
|
// Before adding new entry, validate that all existing entries have valid format
|
||||||
//console.log('About to call validateBeforeAddWhitelist()');
|
|
||||||
|
|
||||||
// Test if the function exists
|
// Test if the function exists
|
||||||
if (typeof this.validateBeforeAddWhitelist !== 'function') {
|
if (typeof this.validateBeforeAddWhitelist !== 'function') {
|
||||||
@ -323,14 +299,11 @@ class IPWhitelistManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const canAdd = this.validateBeforeAddWhitelist();
|
const canAdd = this.validateBeforeAddWhitelist();
|
||||||
//console.log('validateBeforeAddWhitelist() returned:', canAdd);
|
|
||||||
|
|
||||||
if (!canAdd) {
|
if (!canAdd) {
|
||||||
//console.log('Validation failed - not adding new entry');
|
|
||||||
return; // Validation failed, don't add new entry
|
return; // Validation failed, don't add new entry
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log('Validation passed - proceeding with add');
|
|
||||||
|
|
||||||
// Find the hidden CFG_IPS_WHITELIST input
|
// Find the hidden CFG_IPS_WHITELIST input
|
||||||
const hiddenInput = document.querySelector('input[name="CFG_IPS_WHITELIST"]');
|
const hiddenInput = document.querySelector('input[name="CFG_IPS_WHITELIST"]');
|
||||||
@ -345,12 +318,9 @@ class IPWhitelistManager {
|
|||||||
.map(entry => entry.trim())
|
.map(entry => entry.trim())
|
||||||
.filter(entry => entry.length > 0); // Don't filter out empty strings for counting
|
.filter(entry => entry.length > 0); // Don't filter out empty strings for counting
|
||||||
|
|
||||||
//console.log('Current values:', currentValues);
|
|
||||||
//console.log('Current values length:', currentValues.length);
|
|
||||||
|
|
||||||
// Check if we've reached the maximum
|
// Check if we've reached the maximum
|
||||||
if (currentValues.length >= this.maxWhitelistEntries) {
|
if (currentValues.length >= this.maxWhitelistEntries) {
|
||||||
//console.log('Maximum of ' + this.maxWhitelistEntries + ' whitelist entries reached');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -372,13 +342,11 @@ class IPWhitelistManager {
|
|||||||
const highestNumber = allNumbers.length > 0 ? Math.max(...allNumbers) : 0;
|
const highestNumber = allNumbers.length > 0 ? Math.max(...allNumbers) : 0;
|
||||||
const newEntryNumber = highestNumber + 1;
|
const newEntryNumber = highestNumber + 1;
|
||||||
|
|
||||||
//console.log('Highest existing number:', highestNumber, 'New entry number:', newEntryNumber);
|
|
||||||
|
|
||||||
currentValues.push('');
|
currentValues.push('');
|
||||||
|
|
||||||
// Update the hidden input
|
// Update the hidden input
|
||||||
hiddenInput.value = currentValues.join(', ');
|
hiddenInput.value = currentValues.join(', ');
|
||||||
//console.log('Updated hidden input to:', hiddenInput.value);
|
|
||||||
|
|
||||||
// Create new entry HTML and add to DOM
|
// Create new entry HTML and add to DOM
|
||||||
const newEntryHTML = `
|
const newEntryHTML = `
|
||||||
@ -406,41 +374,31 @@ class IPWhitelistManager {
|
|||||||
|
|
||||||
// Add the new entry to the DOM
|
// Add the new entry to the DOM
|
||||||
const whitelistContainer = document.querySelector('.whitelist-building-blocks');
|
const whitelistContainer = document.querySelector('.whitelist-building-blocks');
|
||||||
//console.log('Step 1: Found whitelist container:', !!whitelistContainer);
|
|
||||||
|
|
||||||
if (whitelistContainer) {
|
if (whitelistContainer) {
|
||||||
// Remove empty state if it exists
|
// Remove empty state if it exists
|
||||||
const emptyState = whitelistContainer.querySelector('.whitelist-empty-state');
|
const emptyState = whitelistContainer.querySelector('.whitelist-empty-state');
|
||||||
//console.log('Step 2: Found empty state:', !!emptyState);
|
|
||||||
if (emptyState) {
|
if (emptyState) {
|
||||||
emptyState.remove();
|
emptyState.remove();
|
||||||
//console.log('Step 3: Removed empty state');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add new entry
|
// Add new entry
|
||||||
const tempDiv = document.createElement('div');
|
const tempDiv = document.createElement('div');
|
||||||
tempDiv.innerHTML = newEntryHTML;
|
tempDiv.innerHTML = newEntryHTML;
|
||||||
const newBlock = tempDiv.firstElementChild;
|
const newBlock = tempDiv.firstElementChild;
|
||||||
//console.log('Step 4: Created new block:', !!newBlock);
|
|
||||||
|
|
||||||
whitelistContainer.appendChild(newBlock);
|
whitelistContainer.appendChild(newBlock);
|
||||||
//console.log('Step 5: Added new block to container');
|
|
||||||
|
|
||||||
// Focus on the new input
|
// Focus on the new input
|
||||||
const newInput = newBlock.querySelector('input');
|
const newInput = newBlock.querySelector('input');
|
||||||
//console.log('Step 6: Found new input:', !!newInput);
|
|
||||||
if (newInput) {
|
if (newInput) {
|
||||||
newInput.focus();
|
newInput.focus();
|
||||||
//console.log('Step 7: Focused new input');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update add button state
|
// Update add button state
|
||||||
this.updateAddEntryButton();
|
this.updateAddEntryButton();
|
||||||
//console.log('Step 8: Updated add button state');
|
|
||||||
|
|
||||||
//console.log('Step 9: Add function completed successfully');
|
|
||||||
} else {
|
} else {
|
||||||
//console.log('Step 1: Whitelist container not found!');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -450,14 +408,11 @@ class IPWhitelistManager {
|
|||||||
|
|
||||||
// Check if all entries are valid before allowing new entry addition
|
// Check if all entries are valid before allowing new entry addition
|
||||||
validateBeforeAddWhitelist() {
|
validateBeforeAddWhitelist() {
|
||||||
//console.log('=== validateBeforeAddWhitelist START ===');
|
|
||||||
|
|
||||||
// Simple test - just check for empty entries
|
// Simple test - just check for empty entries
|
||||||
const allInputs = document.querySelectorAll('input[name="CFG_IPS_WHITELIST"]:not([type="hidden"])');
|
const allInputs = document.querySelectorAll('input[name="CFG_IPS_WHITELIST"]:not([type="hidden"])');
|
||||||
//console.log('Found', allInputs.length, 'inputs');
|
|
||||||
|
|
||||||
allInputs.forEach((input, index) => {
|
allInputs.forEach((input, index) => {
|
||||||
//console.log(`Input ${index}: value="${input.value}"`);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const input of allInputs) {
|
for (const input of allInputs) {
|
||||||
@ -465,7 +420,6 @@ class IPWhitelistManager {
|
|||||||
|
|
||||||
// Check if entry is empty
|
// Check if entry is empty
|
||||||
if (!entryValue) {
|
if (!entryValue) {
|
||||||
//console.log('Found empty entry - blocking add');
|
|
||||||
// Flash the empty input field
|
// Flash the empty input field
|
||||||
input.style.animation = 'whitelist-flash 0.5s ease-in-out 2';
|
input.style.animation = 'whitelist-flash 0.5s ease-in-out 2';
|
||||||
input.focus();
|
input.focus();
|
||||||
@ -477,26 +431,20 @@ class IPWhitelistManager {
|
|||||||
if (window.notificationSystem) {
|
if (window.notificationSystem) {
|
||||||
window.notificationSystem.error('Whitelist entry cannot be empty');
|
window.notificationSystem.error('Whitelist entry cannot be empty');
|
||||||
}
|
}
|
||||||
//console.log('=== validateBeforeAddWhitelist END (false - empty) ===');
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check entry format with notification (always show)
|
// Check entry format with notification (always show)
|
||||||
if (this.checkForInvalidWhitelistFormat(input, entryValue, false)) {
|
if (this.checkForInvalidWhitelistFormat(input, entryValue, false)) {
|
||||||
//console.log('Found invalid format - blocking add');
|
|
||||||
//console.log('=== validateBeforeAddWhitelist END (false - invalid) ===');
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for duplicates with notification (always show)
|
// Check for duplicates with notification (always show)
|
||||||
if (this.checkForDuplicateWhitelistEntry(input, entryValue)) {
|
if (this.checkForDuplicateWhitelistEntry(input, entryValue)) {
|
||||||
//console.log('Found duplicate entry - blocking add');
|
|
||||||
//console.log('=== validateBeforeAddWhitelist END (false - duplicate) ===');
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log('=== validateBeforeAddWhitelist END (true) ===');
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -550,12 +498,6 @@ class IPWhitelistManager {
|
|||||||
const isValidIP = ipPattern.test(entryValue) || ipv6Pattern.test(entryValue);
|
const isValidIP = ipPattern.test(entryValue) || ipv6Pattern.test(entryValue);
|
||||||
const isValidDomain = domainPattern.test(entryValue);
|
const isValidDomain = domainPattern.test(entryValue);
|
||||||
|
|
||||||
//console.log('checkForInvalidWhitelistFormat validation:', {
|
|
||||||
//entryValue: entryValue,
|
|
||||||
//isValidIP: isValidIP,
|
|
||||||
//isValidDomain: isValidDomain,
|
|
||||||
//isValidFormat: isValidIP || isValidDomain || entryValue.toLowerCase() === 'localhost'
|
|
||||||
//});
|
|
||||||
|
|
||||||
if (!isValidIP && !isValidDomain) {
|
if (!isValidIP && !isValidDomain) {
|
||||||
// Flash the input field
|
// Flash the input field
|
||||||
@ -579,7 +521,6 @@ class IPWhitelistManager {
|
|||||||
|
|
||||||
// Delete whitelist entry
|
// Delete whitelist entry
|
||||||
deleteWhitelistEntry(entryIndex, buttonElement) {
|
deleteWhitelistEntry(entryIndex, buttonElement) {
|
||||||
//console.log(`Delete whitelist entry button clicked for index: ${entryIndex}`);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get the actual whitelist number from the input ID
|
// Get the actual whitelist number from the input ID
|
||||||
@ -599,7 +540,6 @@ class IPWhitelistManager {
|
|||||||
const match = inputId.match(/config-CFG_IPS_WHITELIST_(\d+)/);
|
const match = inputId.match(/config-CFG_IPS_WHITELIST_(\d+)/);
|
||||||
const whitelistNumber = match ? parseInt(match[1]) : 0;
|
const whitelistNumber = match ? parseInt(match[1]) : 0;
|
||||||
|
|
||||||
//console.log(`Actual whitelist number: ${whitelistNumber}`);
|
|
||||||
|
|
||||||
// Find all whitelist numbers to determine the highest
|
// Find all whitelist numbers to determine the highest
|
||||||
const allBlocks = document.querySelectorAll('.whitelist-building-block');
|
const allBlocks = document.querySelectorAll('.whitelist-building-block');
|
||||||
@ -616,11 +556,9 @@ class IPWhitelistManager {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const highestNumber = Math.max(...allNumbers);
|
const highestNumber = Math.max(...allNumbers);
|
||||||
//console.log(`Highest whitelist number: ${highestNumber}`);
|
|
||||||
|
|
||||||
// Only allow deletion if this is the highest numbered entry AND it's not #1
|
// Only allow deletion if this is the highest numbered entry AND it's not #1
|
||||||
if (whitelistNumber === 1) {
|
if (whitelistNumber === 1) {
|
||||||
//console.log('Cannot delete entry #1');
|
|
||||||
if (window.notificationSystem) {
|
if (window.notificationSystem) {
|
||||||
window.notificationSystem.error('Entry 1 cannot be deleted');
|
window.notificationSystem.error('Entry 1 cannot be deleted');
|
||||||
}
|
}
|
||||||
@ -628,7 +566,6 @@ class IPWhitelistManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (whitelistNumber !== highestNumber) {
|
if (whitelistNumber !== highestNumber) {
|
||||||
//console.log('Can only delete the highest numbered entry');
|
|
||||||
if (window.notificationSystem) {
|
if (window.notificationSystem) {
|
||||||
window.notificationSystem.error('Can only delete the highest numbered entry');
|
window.notificationSystem.error('Can only delete the highest numbered entry');
|
||||||
}
|
}
|
||||||
@ -640,7 +577,6 @@ class IPWhitelistManager {
|
|||||||
|
|
||||||
// Remove the building block from DOM
|
// Remove the building block from DOM
|
||||||
entryBlock.remove();
|
entryBlock.remove();
|
||||||
//console.log('Removed entry block from DOM');
|
|
||||||
|
|
||||||
// Update the hidden input with remaining values
|
// Update the hidden input with remaining values
|
||||||
this.saveWhitelistEntries();
|
this.saveWhitelistEntries();
|
||||||
@ -827,7 +763,6 @@ function checkForDuplicateWhitelistEntry(input, entryValue) {
|
|||||||
input.style.borderColor = '#dc3545';
|
input.style.borderColor = '#dc3545';
|
||||||
input.title = 'Whitelist entry already exists';
|
input.title = 'Whitelist entry already exists';
|
||||||
if (window.notificationSystem) {
|
if (window.notificationSystem) {
|
||||||
//console.log('Showing duplicate whitelist entry notification');
|
|
||||||
window.notificationSystem.error('Whitelist entry "' + entryValue + '" already exists');
|
window.notificationSystem.error('Whitelist entry "' + entryValue + '" already exists');
|
||||||
} else {
|
} else {
|
||||||
console.error('Whitelist entry already exists - notification system not available');
|
console.error('Whitelist entry already exists - notification system not available');
|
||||||
@ -855,7 +790,6 @@ function checkForInvalidWhitelistFormat(input, entryValue) {
|
|||||||
input.style.borderColor = '#dc3545';
|
input.style.borderColor = '#dc3545';
|
||||||
input.title = 'Invalid IP address or domain format (e.g., 192.168.1.100 or example.com)';
|
input.title = 'Invalid IP address or domain format (e.g., 192.168.1.100 or example.com)';
|
||||||
if (window.notificationSystem) {
|
if (window.notificationSystem) {
|
||||||
//console.log('Showing invalid whitelist format notification');
|
|
||||||
window.notificationSystem.error('Invalid whitelist format: "' + entryValue + '". Please use a valid IP address or domain like 192.168.1.100 or example.com');
|
window.notificationSystem.error('Invalid whitelist format: "' + entryValue + '". Please use a valid IP address or domain like 192.168.1.100 or example.com');
|
||||||
} else {
|
} else {
|
||||||
console.error('Invalid whitelist format - notification system not available');
|
console.error('Invalid whitelist format - notification system not available');
|
||||||
@ -869,15 +803,12 @@ function checkForInvalidWhitelistFormat(input, entryValue) {
|
|||||||
// Validate whitelist entry format when user tries to add a new entry
|
// Validate whitelist entry format when user tries to add a new entry
|
||||||
function validateWhitelistEntry(input, showNotifications = true) {
|
function validateWhitelistEntry(input, showNotifications = true) {
|
||||||
const value = input.value.trim();
|
const value = input.value.trim();
|
||||||
//console.log('validateWhitelistEntry called with:', value, 'showNotifications:', showNotifications);
|
|
||||||
//console.log('window.notificationSystem available:', !!window.notificationSystem);
|
|
||||||
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
// Clear styling for empty fields
|
// Clear styling for empty fields
|
||||||
input.style.borderColor = '#dc3545';
|
input.style.borderColor = '#dc3545';
|
||||||
input.title = 'Whitelist entry cannot be empty';
|
input.title = 'Whitelist entry cannot be empty';
|
||||||
if (showNotifications && window.notificationSystem) {
|
if (showNotifications && window.notificationSystem) {
|
||||||
//console.log('Attempting to show empty whitelist notification');
|
|
||||||
window.notificationSystem.error('Whitelist entry cannot be empty');
|
window.notificationSystem.error('Whitelist entry cannot be empty');
|
||||||
} else {
|
} else {
|
||||||
console.error('Whitelist entry cannot be empty - notification system not available');
|
console.error('Whitelist entry cannot be empty - notification system not available');
|
||||||
@ -905,7 +836,6 @@ function validateWhitelistEntry(input, showNotifications = true) {
|
|||||||
input.style.borderColor = '#dc3545';
|
input.style.borderColor = '#dc3545';
|
||||||
input.title = 'Invalid IP address, domain, or localhost format (e.g., 192.168.1.100, example.com, or localhost)';
|
input.title = 'Invalid IP address, domain, or localhost format (e.g., 192.168.1.100, example.com, or localhost)';
|
||||||
if (showNotifications && window.notificationSystem) {
|
if (showNotifications && window.notificationSystem) {
|
||||||
//console.log('Attempting to show invalid whitelist format notification');
|
|
||||||
window.notificationSystem.error('Invalid whitelist format: "' + value + '". Please use a valid IP address, domain, or localhost like 192.168.1.100, example.com, or localhost');
|
window.notificationSystem.error('Invalid whitelist format: "' + value + '". Please use a valid IP address, domain, or localhost like 192.168.1.100, example.com, or localhost');
|
||||||
} else {
|
} else {
|
||||||
console.error('Invalid whitelist format - notification system not available');
|
console.error('Invalid whitelist format - notification system not available');
|
||||||
|
|||||||
@ -6,19 +6,15 @@ class ToggleManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
//console.log('ToggleManager: Initializing...');
|
|
||||||
|
|
||||||
// Auto-discover all toggle configurations
|
// Auto-discover all toggle configurations
|
||||||
this.discoverToggles();
|
this.discoverToggles();
|
||||||
//console.log('ToggleManager: Discovered', this.toggles.size, 'toggle configurations');
|
|
||||||
|
|
||||||
// Debug: Log discovered toggles
|
// Debug: Log discovered toggles
|
||||||
if (this.toggles.size > 0) {
|
if (this.toggles.size > 0) {
|
||||||
//console.log('ToggleManager: No toggles found - will retry after config loads...');
|
|
||||||
// Set up a mutation observer to detect when config content is added
|
// Set up a mutation observer to detect when config content is added
|
||||||
this.setupContentObserver();
|
this.setupContentObserver();
|
||||||
} else {
|
} else {
|
||||||
//console.log('ToggleManager: Discovered toggles:', Array.from(this.toggles.keys()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also set up observer in case more toggles are added later
|
// Also set up observer in case more toggles are added later
|
||||||
@ -26,7 +22,6 @@ class ToggleManager {
|
|||||||
|
|
||||||
// Retry discovery after a short delay to handle timing issues
|
// Retry discovery after a short delay to handle timing issues
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
//console.log('ToggleManager: Retrying toggle discovery...');
|
|
||||||
this.rediscoverToggles();
|
this.rediscoverToggles();
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
@ -40,7 +35,6 @@ class ToggleManager {
|
|||||||
const newToggles = Array.from(mutation.addedNodes).filter(node => node.nodeType === Node.ELEMENT_NODE && (node.dataset?.toggleConfig || node.querySelector('[data-toggle-config]')));
|
const newToggles = Array.from(mutation.addedNodes).filter(node => node.nodeType === Node.ELEMENT_NODE && (node.dataset?.toggleConfig || node.querySelector('[data-toggle-config]')));
|
||||||
|
|
||||||
if (newToggles.length > 0) {
|
if (newToggles.length > 0) {
|
||||||
//console.log('ToggleManager: New toggle elements detected, re-discovering...');
|
|
||||||
this.rediscoverToggles();
|
this.rediscoverToggles();
|
||||||
observer.disconnect(); // Stop observing once we find toggles
|
observer.disconnect(); // Stop observing once we find toggles
|
||||||
}
|
}
|
||||||
@ -60,35 +54,24 @@ class ToggleManager {
|
|||||||
|
|
||||||
// Re-discover toggles (called after content is loaded)
|
// Re-discover toggles (called after content is loaded)
|
||||||
rediscoverToggles() {
|
rediscoverToggles() {
|
||||||
//console.log('ToggleManager: Re-discovering toggles...');
|
|
||||||
this.toggles.clear(); // Clear existing toggles
|
this.toggles.clear(); // Clear existing toggles
|
||||||
this.discoverToggles();
|
this.discoverToggles();
|
||||||
//console.log('ToggleManager: Re-discovered', this.toggles.size, 'toggle configurations');
|
|
||||||
|
|
||||||
if (this.toggles.size > 0) {
|
if (this.toggles.size > 0) {
|
||||||
//console.log('ToggleManager: Successfully discovered toggles:', Array.from(this.toggles.keys()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-discover toggle configurations from the page
|
// Auto-discover toggle configurations from the page
|
||||||
discoverToggles() {
|
discoverToggles() {
|
||||||
//console.log('ToggleManager: Looking for elements with [data-toggle-config]...');
|
|
||||||
|
|
||||||
// Find all elements with data-toggle-config attribute
|
// Find all elements with data-toggle-config attribute
|
||||||
const toggleElements = document.querySelectorAll('[data-toggle-config]');
|
const toggleElements = document.querySelectorAll('[data-toggle-config]');
|
||||||
//console.log('ToggleManager: Found', toggleElements.length, 'elements with data-toggle-config');
|
|
||||||
|
|
||||||
toggleElements.forEach((element, index) => {
|
toggleElements.forEach((element, index) => {
|
||||||
const config = element.dataset.toggleConfig;
|
const config = element.dataset.toggleConfig;
|
||||||
const sectionId = element.dataset.sectionId;
|
const sectionId = element.dataset.sectionId;
|
||||||
const toggleType = element.dataset.toggleType || 'checkbox';
|
const toggleType = element.dataset.toggleType || 'checkbox';
|
||||||
|
|
||||||
//console.log(`ToggleManager: Processing element ${index}:`, {
|
|
||||||
//config: config,
|
|
||||||
//sectionId: sectionId,
|
|
||||||
//toggleType: toggleType,
|
|
||||||
//element: element.tagName + (element.id ? '#' + element.id : '') + (element.name ? '[name=' + element.name + ']' : '')
|
|
||||||
//});
|
|
||||||
|
|
||||||
if (config && sectionId) {
|
if (config && sectionId) {
|
||||||
this.toggles.set(config, {
|
this.toggles.set(config, {
|
||||||
@ -97,18 +80,13 @@ class ToggleManager {
|
|||||||
toggleType: toggleType,
|
toggleType: toggleType,
|
||||||
element: element
|
element: element
|
||||||
});
|
});
|
||||||
//console.log(`ToggleManager: Registered toggle for config: ${config}`);
|
|
||||||
} else {
|
} else {
|
||||||
//console.log(`ToggleManager: Skipping element - missing config or sectionId`);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Universal toggle function - works for any config option
|
// Universal toggle function - works for any config option
|
||||||
toggle(configKey, isEnabled) {
|
toggle(configKey, isEnabled) {
|
||||||
//console.log('=== TOGGLE MANAGER DEBUG ===');
|
|
||||||
//console.log('configKey:', configKey);
|
|
||||||
//console.log('isEnabled:', isEnabled);
|
|
||||||
|
|
||||||
const toggle = this.toggles.get(configKey);
|
const toggle = this.toggles.get(configKey);
|
||||||
if (!toggle) {
|
if (!toggle) {
|
||||||
@ -116,20 +94,15 @@ class ToggleManager {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log('Toggle found:', toggle);
|
|
||||||
|
|
||||||
const sectionContent = document.getElementById(toggle.sectionId);
|
const sectionContent = document.getElementById(toggle.sectionId);
|
||||||
const fields = sectionContent?.querySelectorAll('.config-fields input, .config-fields select, .config-fields textarea');
|
const fields = sectionContent?.querySelectorAll('.config-fields input, .config-fields select, .config-fields textarea');
|
||||||
|
|
||||||
//console.log('sectionContent found:', !!sectionContent);
|
|
||||||
//console.log('fields found:', fields ? fields.length : 0);
|
|
||||||
|
|
||||||
if (sectionContent && fields) {
|
if (sectionContent && fields) {
|
||||||
if (isEnabled) {
|
if (isEnabled) {
|
||||||
//console.log('Enabling section...');
|
|
||||||
sectionContent.classList.remove('hidden');
|
sectionContent.classList.remove('hidden');
|
||||||
fields.forEach((field, index) => {
|
fields.forEach((field, index) => {
|
||||||
//console.log(`Enabling field ${index}:`, field);
|
|
||||||
field.disabled = false;
|
field.disabled = false;
|
||||||
const fieldGroup = field?.closest('.field-group');
|
const fieldGroup = field?.closest('.field-group');
|
||||||
if (fieldGroup) {
|
if (fieldGroup) {
|
||||||
@ -138,10 +111,8 @@ class ToggleManager {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
//console.log('Disabling section...');
|
|
||||||
sectionContent.classList.add('hidden');
|
sectionContent.classList.add('hidden');
|
||||||
fields.forEach((field, index) => {
|
fields.forEach((field, index) => {
|
||||||
//console.log(`Disabling field ${index}:`, field);
|
|
||||||
field.disabled = true;
|
field.disabled = true;
|
||||||
const fieldGroup = field?.closest('.field-group');
|
const fieldGroup = field?.closest('.field-group');
|
||||||
if (fieldGroup) {
|
if (fieldGroup) {
|
||||||
@ -151,7 +122,6 @@ class ToggleManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log('=== TOGGLE MANAGER SUCCESS ===');
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
console.error('ToggleManager: Section content or fields not found');
|
console.error('ToggleManager: Section content or fields not found');
|
||||||
@ -300,7 +270,6 @@ window.toggleManager.renderToggleSection = ToggleManager.renderToggleSection;
|
|||||||
|
|
||||||
// Add method to manually trigger discovery when config is loaded
|
// Add method to manually trigger discovery when config is loaded
|
||||||
window.toggleManager.forceRediscover = function() {
|
window.toggleManager.forceRediscover = function() {
|
||||||
//console.log('ToggleManager: Force re-discovering toggles...');
|
|
||||||
window.toggleManager.rediscoverToggles();
|
window.toggleManager.rediscoverToggles();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -3,9 +3,6 @@
|
|||||||
|
|
||||||
class AppTabbedManager {
|
class AppTabbedManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
// console.log('🔍 AppTabbedManager constructor called');
|
|
||||||
// console.log('🔍 URL in constructor:', window.location.href);
|
|
||||||
// console.log('🔍 Search params in constructor:', window.location.search);
|
|
||||||
|
|
||||||
// Store original URL for task parameter detection
|
// Store original URL for task parameter detection
|
||||||
this.originalUrl = window.location.href;
|
this.originalUrl = window.location.href;
|
||||||
@ -13,22 +10,18 @@ class AppTabbedManager {
|
|||||||
|
|
||||||
// Check sessionStorage for task parameter (fallback)
|
// Check sessionStorage for task parameter (fallback)
|
||||||
const sessionTaskId = sessionStorage.getItem('pendingTaskId');
|
const sessionTaskId = sessionStorage.getItem('pendingTaskId');
|
||||||
// console.log('🔍 Session storage task ID:', sessionTaskId);
|
|
||||||
|
|
||||||
// Debug: Check if task parameter exists in original URL
|
// Debug: Check if task parameter exists in original URL
|
||||||
const originalParams = new URLSearchParams(this.originalSearch);
|
const originalParams = new URLSearchParams(this.originalSearch);
|
||||||
const originalTaskId = originalParams.get('task');
|
const originalTaskId = originalParams.get('task');
|
||||||
// console.log('🔍 Original task ID in constructor:', originalTaskId);
|
|
||||||
|
|
||||||
// Try to get task parameter from performance navigation if available
|
// Try to get task parameter from performance navigation if available
|
||||||
if (performance && performance.getEntriesByType) {
|
if (performance && performance.getEntriesByType) {
|
||||||
const navigationEntries = performance.getEntriesByType('navigation');
|
const navigationEntries = performance.getEntriesByType('navigation');
|
||||||
if (navigationEntries.length > 0) {
|
if (navigationEntries.length > 0) {
|
||||||
const navEntry = navigationEntries[0];
|
const navEntry = navigationEntries[0];
|
||||||
// console.log('🔍 Navigation entry URL:', navEntry.name);
|
|
||||||
const navParams = new URLSearchParams(new URL(navEntry.name).search);
|
const navParams = new URLSearchParams(new URL(navEntry.name).search);
|
||||||
const navTaskId = navParams.get('task');
|
const navTaskId = navParams.get('task');
|
||||||
// console.log('🔍 Navigation task ID:', navTaskId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +62,6 @@ class AppTabbedManager {
|
|||||||
setCurrentApp(appName) {
|
setCurrentApp(appName) {
|
||||||
if (this.currentApp === appName) return;
|
if (this.currentApp === appName) return;
|
||||||
|
|
||||||
// console.log('🔄 setCurrentApp: switching from %s to %s', this.currentApp, appName);
|
|
||||||
this.currentApp = appName;
|
this.currentApp = appName;
|
||||||
|
|
||||||
// Before clearing disabled button references, restore any static backup action buttons
|
// Before clearing disabled button references, restore any static backup action buttons
|
||||||
@ -89,7 +81,6 @@ class AppTabbedManager {
|
|||||||
this.enableTabs();
|
this.enableTabs();
|
||||||
|
|
||||||
const running = this.getRunningTaskForApp(appName);
|
const running = this.getRunningTaskForApp(appName);
|
||||||
// console.log('🔍 setCurrentApp: running task for %s = %o', appName, running);
|
|
||||||
if (running) {
|
if (running) {
|
||||||
this.activeTaskId = running.taskId;
|
this.activeTaskId = running.taskId;
|
||||||
}
|
}
|
||||||
@ -109,7 +100,6 @@ class AppTabbedManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log('🔍 Original app name from URL:', appName);
|
|
||||||
|
|
||||||
// Convert full app name to slug for task filtering
|
// Convert full app name to slug for task filtering
|
||||||
if (appName && window.apps) {
|
if (appName && window.apps) {
|
||||||
@ -124,14 +114,11 @@ class AppTabbedManager {
|
|||||||
const command = appData.command || '';
|
const command = appData.command || '';
|
||||||
const parts = command.split(' ');
|
const parts = command.split(' ');
|
||||||
const slug = parts[parts.length - 1]; // Return the slug
|
const slug = parts[parts.length - 1]; // Return the slug
|
||||||
// console.log('🔄 Converted to slug:', slug, 'from appData:', appData.name);
|
|
||||||
return slug;
|
return slug;
|
||||||
} else {
|
} else {
|
||||||
// console.log('⚠️ No app data found for:', appName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log('🔄 Returning original app name:', appName);
|
|
||||||
return appName;
|
return appName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,10 +197,6 @@ class AppTabbedManager {
|
|||||||
|
|
||||||
// Switch between tabs
|
// Switch between tabs
|
||||||
switchTab(tabId) {
|
switchTab(tabId) {
|
||||||
// console.log('🔄 switchTab called with:', tabId);
|
|
||||||
// console.log('🔍 Current currentApp before switch:', this.currentApp);
|
|
||||||
// console.log('🔍 Current URL when switching:', window.location.href);
|
|
||||||
// console.log('🔍 URL search when switching:', window.location.search);
|
|
||||||
|
|
||||||
// Remove active class from all main navigation tabs
|
// Remove active class from all main navigation tabs
|
||||||
document.querySelectorAll('.main-tab-button').forEach(btn => {
|
document.querySelectorAll('.main-tab-button').forEach(btn => {
|
||||||
@ -245,12 +228,10 @@ class AppTabbedManager {
|
|||||||
|
|
||||||
// Update URL (only tab, not app) - but only on app pages
|
// Update URL (only tab, not app) - but only on app pages
|
||||||
if (this.isAppPage()) {
|
if (this.isAppPage()) {
|
||||||
// console.log('🔄 About to updateURL with tab:', tabId);
|
|
||||||
this.updateURL(null, tabId);
|
this.updateURL(null, tabId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load tab-specific content
|
// Load tab-specific content
|
||||||
// console.log('🔄 About to load tab content for tab:', tabId, 'with currentApp:', this.currentApp);
|
|
||||||
this.loadTabContent(tabId);
|
this.loadTabContent(tabId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,8 +240,6 @@ class AppTabbedManager {
|
|||||||
const actualTabId = tabId === 'logs' ? 'tasks' : tabId;
|
const actualTabId = tabId === 'logs' ? 'tasks' : tabId;
|
||||||
|
|
||||||
const currentAppFromUrl = this.getAppFromURL();
|
const currentAppFromUrl = this.getAppFromURL();
|
||||||
// console.log('📂 loadTabContent: tabId=%s, currentApp=%s, fromUrl=%s',
|
|
||||||
// tabId, this.currentApp, currentAppFromUrl);
|
|
||||||
|
|
||||||
// Update currentApp if URL has different app name. Route through setCurrentApp
|
// Update currentApp if URL has different app name. Route through setCurrentApp
|
||||||
// so any disable state from the previous app gets cleared before we render.
|
// so any disable state from the previous app gets cleared before we render.
|
||||||
@ -297,7 +276,6 @@ class AppTabbedManager {
|
|||||||
// Make sure app detail view is visible and app is loaded
|
// Make sure app detail view is visible and app is loaded
|
||||||
if (window.appsManager) {
|
if (window.appsManager) {
|
||||||
// Use showAppDetail to ensure proper initialization (same as config tab)
|
// Use showAppDetail to ensure proper initialization (same as config tab)
|
||||||
// console.log('🔄 Ensuring app detail is loaded for:', this.currentApp);
|
|
||||||
window.appsManager.showAppDetail(this.currentApp);
|
window.appsManager.showAppDetail(this.currentApp);
|
||||||
|
|
||||||
// Wait a bit for DOM to be ready after app detail is rendered
|
// Wait a bit for DOM to be ready after app detail is rendered
|
||||||
@ -306,11 +284,9 @@ class AppTabbedManager {
|
|||||||
|
|
||||||
switch (actualTabId) {
|
switch (actualTabId) {
|
||||||
case 'tasks':
|
case 'tasks':
|
||||||
// console.log('🔄 loadTabContent: Loading tasks for app:', this.currentApp);
|
|
||||||
await this.loadAppTasks();
|
await this.loadAppTasks();
|
||||||
break;
|
break;
|
||||||
case 'backups':
|
case 'backups':
|
||||||
// console.log('🔄 loadTabContent: Loading backups for app:', this.currentApp);
|
|
||||||
await this.loadAppBackups();
|
await this.loadAppBackups();
|
||||||
// IMPORTANT: Re-apply button state if there are running tasks
|
// IMPORTANT: Re-apply button state if there are running tasks
|
||||||
this.restoreButtonState();
|
this.restoreButtonState();
|
||||||
@ -335,7 +311,6 @@ class AppTabbedManager {
|
|||||||
break;
|
break;
|
||||||
case 'config':
|
case 'config':
|
||||||
// Config is already handled by showAppDetail above
|
// Config is already handled by showAppDetail above
|
||||||
// console.log('🔧 Config content already loaded by showAppDetail');
|
|
||||||
// IMPORTANT: Re-apply button state if there are running tasks
|
// IMPORTANT: Re-apply button state if there are running tasks
|
||||||
this.restoreButtonState();
|
this.restoreButtonState();
|
||||||
break;
|
break;
|
||||||
@ -355,7 +330,6 @@ class AppTabbedManager {
|
|||||||
|
|
||||||
// Load tasks specific to current app
|
// Load tasks specific to current app
|
||||||
async loadAppTasks() {
|
async loadAppTasks() {
|
||||||
// console.log('🔄 loadAppTasks called, currentApp:', this.currentApp);
|
|
||||||
|
|
||||||
// Show loading spinner by showing the initial loading state
|
// Show loading spinner by showing the initial loading state
|
||||||
const tasksContainer = document.getElementById('app-tasks');
|
const tasksContainer = document.getElementById('app-tasks');
|
||||||
@ -398,32 +372,20 @@ class AppTabbedManager {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Load all tasks
|
// Load all tasks
|
||||||
// console.log('🔄 Loading tasks...');
|
|
||||||
// console.log('🔍 Using currentApp for filtering:', this.currentApp);
|
|
||||||
await this.tasksManager.loadTasks();
|
await this.tasksManager.loadTasks();
|
||||||
const allTasks = this.tasksManager.tasks || [];
|
const allTasks = this.tasksManager.tasks || [];
|
||||||
// console.log('📊 All tasks loaded:', allTasks.length);
|
|
||||||
// console.log('📋 All tasks data:', allTasks);
|
|
||||||
// console.log('📋 Sample task app names:', allTasks.slice(0, 3).map(t => t.app));
|
|
||||||
|
|
||||||
// Filter tasks for current app
|
// Filter tasks for current app
|
||||||
const appTasks = allTasks.filter(task => task.app === this.currentApp);
|
const appTasks = allTasks.filter(task => task.app === this.currentApp);
|
||||||
// console.log('🎯 Filtering tasks for app:', this.currentApp);
|
|
||||||
// console.log('📋 Available task.app values:', [...new Set(allTasks.map(t => t.app))]);
|
|
||||||
// console.log('🎯 Filtered tasks for', this.currentApp, ':', appTasks.length);
|
|
||||||
|
|
||||||
// Debug: Show what would match if we used different app names
|
// Debug: Show what would match if we used different app names
|
||||||
// console.log('🔍 Debug - Testing different app names:');
|
|
||||||
['libreportal', 'fail2ban', 'LibrePortal', 'Fail2Ban'].forEach(testApp => {
|
['libreportal', 'fail2ban', 'LibrePortal', 'Fail2Ban'].forEach(testApp => {
|
||||||
const testTasks = allTasks.filter(task => task.app === testApp);
|
const testTasks = allTasks.filter(task => task.app === testApp);
|
||||||
// console.log(` - "${testApp}": ${testTasks.length} tasks`);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (appTasks.length === 0) {
|
if (appTasks.length === 0) {
|
||||||
// console.log('⚠️ No tasks found for', this.currentApp, '- checking if tasks have different app names');
|
|
||||||
// Show some task details for debugging
|
// Show some task details for debugging
|
||||||
if (allTasks.length > 0) {
|
if (allTasks.length > 0) {
|
||||||
// console.log('📋 Sample tasks:', allTasks.slice(0, 3).map(t => ({ id: t.id, app: t.app, command: t.command })));
|
|
||||||
}
|
}
|
||||||
tasksContainer.innerHTML = `<p style="color: #888;">No tasks found for ${this.currentApp}.</p>`;
|
tasksContainer.innerHTML = `<p style="color: #888;">No tasks found for ${this.currentApp}.</p>`;
|
||||||
return;
|
return;
|
||||||
@ -441,10 +403,8 @@ class AppTabbedManager {
|
|||||||
|
|
||||||
// Handle pending task ID from URL parameter
|
// Handle pending task ID from URL parameter
|
||||||
if (this.pendingTaskId) {
|
if (this.pendingTaskId) {
|
||||||
// console.log('🔍 Handling pending task ID after tasks loaded:', this.pendingTaskId);
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (typeof window.toggleAppTaskDetails === 'function') {
|
if (typeof window.toggleAppTaskDetails === 'function') {
|
||||||
// console.log('🔍 Opening task details for pending task:', this.pendingTaskId);
|
|
||||||
window.toggleAppTaskDetails(this.pendingTaskId);
|
window.toggleAppTaskDetails(this.pendingTaskId);
|
||||||
|
|
||||||
// Scroll to the task element after opening details
|
// Scroll to the task element after opening details
|
||||||
@ -466,7 +426,6 @@ class AppTabbedManager {
|
|||||||
|
|
||||||
// Scroll to specific task element with smooth animation
|
// Scroll to specific task element with smooth animation
|
||||||
scrollToTask(taskId) {
|
scrollToTask(taskId) {
|
||||||
// console.log('🔍 Scrolling to task:', taskId);
|
|
||||||
|
|
||||||
// Find the task element by ID or data attribute
|
// Find the task element by ID or data attribute
|
||||||
let taskElement = document.getElementById(`task-${taskId}`);
|
let taskElement = document.getElementById(`task-${taskId}`);
|
||||||
@ -485,7 +444,6 @@ class AppTabbedManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (taskElement) {
|
if (taskElement) {
|
||||||
// console.log('🔍 Found task element, scrolling to it:', taskElement);
|
|
||||||
|
|
||||||
// Smooth scroll to the task element
|
// Smooth scroll to the task element
|
||||||
taskElement.scrollIntoView({
|
taskElement.scrollIntoView({
|
||||||
@ -511,7 +469,6 @@ class AppTabbedManager {
|
|||||||
setupAppTaskFunctions() {
|
setupAppTaskFunctions() {
|
||||||
// Create app-specific toggleTaskDetails function
|
// Create app-specific toggleTaskDetails function
|
||||||
window.toggleAppTaskDetails = (taskId) => {
|
window.toggleAppTaskDetails = (taskId) => {
|
||||||
// console.log('🔍 App-specific toggleTaskDetails called for:', taskId);
|
|
||||||
|
|
||||||
const details = document.getElementById(`details-${taskId}`);
|
const details = document.getElementById(`details-${taskId}`);
|
||||||
const toggleBtn = document.querySelector(`.task-btn.toggle-details[onclick*="toggleTaskDetails('${taskId}')"]`);
|
const toggleBtn = document.querySelector(`.task-btn.toggle-details[onclick*="toggleTaskDetails('${taskId}')"]`);
|
||||||
@ -599,11 +556,9 @@ class AppTabbedManager {
|
|||||||
async initialize() {
|
async initialize() {
|
||||||
// Prevent double initialization
|
// Prevent double initialization
|
||||||
if (this.initialized) {
|
if (this.initialized) {
|
||||||
// console.log('⚠️ AppTabbedManager already initialized, skipping');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log('🚀 AppTabbedManager initializing, currentApp:', this.currentApp);
|
|
||||||
|
|
||||||
// Initialize task system if not already done (with retry)
|
// Initialize task system if not already done (with retry)
|
||||||
if (this.tasksManager && !this.tasksManager.commands) {
|
if (this.tasksManager && !this.tasksManager.commands) {
|
||||||
@ -612,11 +567,9 @@ class AppTabbedManager {
|
|||||||
const maxAttempts = 5;
|
const maxAttempts = 5;
|
||||||
|
|
||||||
while (!initialized && attempts < maxAttempts) {
|
while (!initialized && attempts < maxAttempts) {
|
||||||
// console.log(`🔄 Attempting to initialize task system (${attempts + 1}/${maxAttempts})...`);
|
|
||||||
try {
|
try {
|
||||||
initialized = this.tasksManager.initializeTaskSystem();
|
initialized = this.tasksManager.initializeTaskSystem();
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
// console.log('✅ Task system initialized successfully');
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Task system initialization error:', error);
|
console.error('❌ Task system initialization error:', error);
|
||||||
@ -670,7 +623,6 @@ class AppTabbedManager {
|
|||||||
|
|
||||||
// Set current app from URL BEFORE setting up URL monitoring
|
// Set current app from URL BEFORE setting up URL monitoring
|
||||||
const urlAppName = this.getAppFromURL();
|
const urlAppName = this.getAppFromURL();
|
||||||
// console.log('🔍 Setting initial currentApp from URL:', urlAppName);
|
|
||||||
this.currentApp = urlAppName;
|
this.currentApp = urlAppName;
|
||||||
|
|
||||||
// Check for running tasks for this app and auto-switch to tasks tab if found
|
// Check for running tasks for this app and auto-switch to tasks tab if found
|
||||||
@ -686,22 +638,17 @@ class AppTabbedManager {
|
|||||||
// Check for task parameter and handle it AFTER tasks are loaded
|
// Check for task parameter and handle it AFTER tasks are loaded
|
||||||
// Use original URL since the current URL might have been modified
|
// Use original URL since the current URL might have been modified
|
||||||
const urlParams = new URLSearchParams(this.originalSearch);
|
const urlParams = new URLSearchParams(this.originalSearch);
|
||||||
// console.log('🔍 Original URL search during init:', this.originalSearch);
|
|
||||||
// console.log('🔍 Original URL params during init:', Object.fromEntries(urlParams.entries()));
|
|
||||||
let taskId = urlParams.get('task');
|
let taskId = urlParams.get('task');
|
||||||
// console.log('🔍 Task ID from original params:', taskId);
|
|
||||||
|
|
||||||
// Fallback: Check sessionStorage if URL doesn't have task parameter
|
// Fallback: Check sessionStorage if URL doesn't have task parameter
|
||||||
if (!taskId) {
|
if (!taskId) {
|
||||||
taskId = sessionStorage.getItem('pendingTaskId');
|
taskId = sessionStorage.getItem('pendingTaskId');
|
||||||
// console.log('🔍 Task ID from sessionStorage fallback:', taskId);
|
|
||||||
// Clear sessionStorage after using it
|
// Clear sessionStorage after using it
|
||||||
if (taskId) {
|
if (taskId) {
|
||||||
sessionStorage.removeItem('pendingTaskId');
|
sessionStorage.removeItem('pendingTaskId');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (taskId) {
|
if (taskId) {
|
||||||
// console.log('🔍 Task parameter found:', taskId);
|
|
||||||
// Store the task ID to handle after tasks are loaded
|
// Store the task ID to handle after tasks are loaded
|
||||||
this.pendingTaskId = taskId;
|
this.pendingTaskId = taskId;
|
||||||
// Force tasks tab
|
// Force tasks tab
|
||||||
@ -722,7 +669,6 @@ class AppTabbedManager {
|
|||||||
// Set initial active tab (only if no task parameter)
|
// Set initial active tab (only if no task parameter)
|
||||||
if (!taskId) {
|
if (!taskId) {
|
||||||
const initialTab = this.getTabFromURL();
|
const initialTab = this.getTabFromURL();
|
||||||
// console.log('🔄 Setting initial tab:', initialTab, 'with currentApp:', this.currentApp);
|
|
||||||
this.switchTab(initialTab);
|
this.switchTab(initialTab);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -739,7 +685,6 @@ class AppTabbedManager {
|
|||||||
const newAppName = this.getAppFromURL();
|
const newAppName = this.getAppFromURL();
|
||||||
// Only update if currentApp is already set and app actually changed
|
// Only update if currentApp is already set and app actually changed
|
||||||
if (this.currentApp && newAppName !== this.currentApp) {
|
if (this.currentApp && newAppName !== this.currentApp) {
|
||||||
// console.log('🔄 URL changed, updating app from', this.currentApp, 'to', newAppName);
|
|
||||||
this.updateApp(newAppName);
|
this.updateApp(newAppName);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -776,7 +721,6 @@ class AppTabbedManager {
|
|||||||
// Create backup (placeholder function)
|
// Create backup (placeholder function)
|
||||||
async createBackup(appName) {
|
async createBackup(appName) {
|
||||||
// Placeholder - will be implemented with actual backup logic
|
// Placeholder - will be implemented with actual backup logic
|
||||||
// console.log(`Creating backup for ${appName}...`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup task event listeners for button state management
|
// Setup task event listeners for button state management
|
||||||
@ -785,8 +729,6 @@ class AppTabbedManager {
|
|||||||
const { taskId, appName, action } = event.detail;
|
const { taskId, appName, action } = event.detail;
|
||||||
const key = this.taskKey(appName, action);
|
const key = this.taskKey(appName, action);
|
||||||
|
|
||||||
// console.log('📌 taskCreated: appName=%s, currentApp=%s, action=%s, key=%s',
|
|
||||||
// appName, this.currentApp, action, key);
|
|
||||||
|
|
||||||
if (this.runningTasks.has(key)) {
|
if (this.runningTasks.has(key)) {
|
||||||
const existing = this.runningTasks.get(key);
|
const existing = this.runningTasks.get(key);
|
||||||
@ -818,7 +760,6 @@ class AppTabbedManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.runningTasks.set(key, { taskId, appName, action });
|
this.runningTasks.set(key, { taskId, appName, action });
|
||||||
// console.log('📌 taskCreated: stored in runningTasks, will disable=%s', appName === this.currentApp);
|
|
||||||
|
|
||||||
if (appName === this.currentApp) {
|
if (appName === this.currentApp) {
|
||||||
this.disableAppButtons(appName, action);
|
this.disableAppButtons(appName, action);
|
||||||
@ -924,8 +865,6 @@ class AppTabbedManager {
|
|||||||
|
|
||||||
// Disable app buttons during task execution
|
// Disable app buttons during task execution
|
||||||
disableAppButtons(appName, action) {
|
disableAppButtons(appName, action) {
|
||||||
// console.log('🚫 disableAppButtons called: appName=%s, action=%s, currentApp=%s',
|
|
||||||
// appName, action, this.currentApp);
|
|
||||||
|
|
||||||
// Also disable config and backup tabs
|
// Also disable config and backup tabs
|
||||||
this.disableTabs();
|
this.disableTabs();
|
||||||
@ -940,7 +879,6 @@ class AppTabbedManager {
|
|||||||
|
|
||||||
// Disable ALL buttons in the app content section
|
// Disable ALL buttons in the app content section
|
||||||
const allButtons = appContent.querySelectorAll('button:not([disabled]):not(.tab-button)');
|
const allButtons = appContent.querySelectorAll('button:not([disabled]):not(.tab-button)');
|
||||||
// console.log('🚫 disableAppButtons found %d buttons to disable', allButtons.length);
|
|
||||||
|
|
||||||
allButtons.forEach(button => {
|
allButtons.forEach(button => {
|
||||||
// Skip tab buttons (config, backup, tasks tabs)
|
// Skip tab buttons (config, backup, tasks tabs)
|
||||||
@ -978,7 +916,6 @@ class AppTabbedManager {
|
|||||||
tempDiv.innerHTML = originalContent;
|
tempDiv.innerHTML = originalContent;
|
||||||
const textContent = tempDiv.textContent || tempDiv.innerText || originalContent;
|
const textContent = tempDiv.textContent || tempDiv.innerText || originalContent;
|
||||||
|
|
||||||
// console.log('🔃 Adding spinner to button:', button.textContent.trim(), 'for app:', appName);
|
|
||||||
button.innerHTML = `
|
button.innerHTML = `
|
||||||
<span class="spinner" style="
|
<span class="spinner" style="
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@ -1001,7 +938,6 @@ class AppTabbedManager {
|
|||||||
this.disabledButtons.add(button);
|
this.disabledButtons.add(button);
|
||||||
});
|
});
|
||||||
|
|
||||||
// console.log(`🔍 Disabled ${allButtons.length} buttons for ${appName} during ${action}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore button state when switching tabs. Only disable if the *current* app
|
// Restore button state when switching tabs. Only disable if the *current* app
|
||||||
@ -1092,12 +1028,10 @@ window.AppTabbedManager = AppTabbedManager;
|
|||||||
|
|
||||||
// Initialize when DOM is loaded
|
// Initialize when DOM is loaded
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
// console.log('🔍 DOMContentLoaded: Skipping automatic initialization - SPA will handle it');
|
|
||||||
// Don't initialize here - let SPA handle it
|
// Don't initialize here - let SPA handle it
|
||||||
});
|
});
|
||||||
|
|
||||||
// Also initialize when scripts are loaded (for SPA navigation)
|
// Also initialize when scripts are loaded (for SPA navigation)
|
||||||
window.addEventListener('load', async () => {
|
window.addEventListener('load', async () => {
|
||||||
// console.log('🔍 Window load: Skipping automatic initialization - SPA will handle it');
|
|
||||||
// Don't initialize here - let SPA handle it
|
// Don't initialize here - let SPA handle it
|
||||||
});
|
});
|
||||||
|
|||||||
@ -206,7 +206,6 @@ class AppsManager {
|
|||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const appsData = await response.json();
|
const appsData = await response.json();
|
||||||
window.apps = appsData.apps || [];
|
window.apps = appsData.apps || [];
|
||||||
// console.log(`✅ Reloaded ${window.apps.length} apps`);
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Failed to reload apps data:', error);
|
console.error('❌ Failed to reload apps data:', error);
|
||||||
@ -340,7 +339,6 @@ class AppsManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
showAppDetail(appName, forceConfigTab = false) {
|
showAppDetail(appName, forceConfigTab = false) {
|
||||||
// console.log('🔍 showAppDetail called with:', { appName, forceConfigTab });
|
|
||||||
//// // console.log(`AppsManager: Showing app detail: ${appName}`);
|
//// // console.log(`AppsManager: Showing app detail: ${appName}`);
|
||||||
|
|
||||||
// Don't proceed if appName is empty - redirect to apps list instead
|
// Don't proceed if appName is empty - redirect to apps list instead
|
||||||
@ -362,12 +360,10 @@ class AppsManager {
|
|||||||
if (forceConfigTab) {
|
if (forceConfigTab) {
|
||||||
// Force config tab for install/manage buttons
|
// Force config tab for install/manage buttons
|
||||||
targetTab = 'config';
|
targetTab = 'config';
|
||||||
// console.log('🔍 Forcing config tab due to forceConfigTab=true');
|
|
||||||
} else {
|
} else {
|
||||||
// Preserve existing tab or default to config for direct navigation
|
// Preserve existing tab or default to config for direct navigation
|
||||||
const currentUrl = new URL(window.location.href);
|
const currentUrl = new URL(window.location.href);
|
||||||
targetTab = currentUrl.searchParams.get('tab') || 'config';
|
targetTab = currentUrl.searchParams.get('tab') || 'config';
|
||||||
// console.log('🔍 Preserving existing tab:', targetTab);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const newUrl = window.appPath(appName, targetTab);
|
const newUrl = window.appPath(appName, targetTab);
|
||||||
@ -411,8 +407,6 @@ class AppsManager {
|
|||||||
|
|
||||||
// Show app detail with config tab (for install/manage buttons)
|
// Show app detail with config tab (for install/manage buttons)
|
||||||
showAppDetailWithConfig(appName) {
|
showAppDetailWithConfig(appName) {
|
||||||
// console.log('🔍 showAppDetailWithConfig called with:', appName);
|
|
||||||
// console.log('🔍 Forcing config tab for button click');
|
|
||||||
|
|
||||||
// Check if there's a running task for this app — switch straight to the tasks
|
// Check if there's a running task for this app — switch straight to the tasks
|
||||||
// tab if so, instead of landing on config (whose buttons would be disabled).
|
// tab if so, instead of landing on config (whose buttons would be disabled).
|
||||||
@ -440,7 +434,6 @@ class AppsManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Simulate clicking target tab functionally
|
// Simulate clicking target tab functionally
|
||||||
// console.log('🔄 Simulating target tab click:', targetTab);
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.appTabbedManager.switchTab(targetTab);
|
window.appTabbedManager.switchTab(targetTab);
|
||||||
// Highlight the running task if switching to tasks tab
|
// Highlight the running task if switching to tasks tab
|
||||||
@ -1438,7 +1431,6 @@ class AppsManager {
|
|||||||
} else if (window.librePortalSPA) {
|
} else if (window.librePortalSPA) {
|
||||||
// Fallback: navigate to app with tasks tab
|
// Fallback: navigate to app with tasks tab
|
||||||
const taskUrl = window.appPath(appName, 'tasks', null, task ? task.id : null);
|
const taskUrl = window.appPath(appName, 'tasks', null, task ? task.id : null);
|
||||||
// console.log(`🔄 Navigating to app tasks with uninstall task: ${task?.id}`);
|
|
||||||
window.librePortalSPA.navigateTo(taskUrl);
|
window.librePortalSPA.navigateTo(taskUrl);
|
||||||
} else if (window.navigateToRoute) {
|
} else if (window.navigateToRoute) {
|
||||||
window.navigateToRoute(window.appPath(appName, 'tasks', null, task ? task.id : null).replace(/^\//, ''));
|
window.navigateToRoute(window.appPath(appName, 'tasks', null, task ? task.id : null).replace(/^\//, ''));
|
||||||
@ -1513,7 +1505,6 @@ class AppsManager {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(`🔍 Install button disabled for ${appName} during ${action}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enableInstallButton(appName) {
|
enableInstallButton(appName) {
|
||||||
@ -1538,7 +1529,6 @@ class AppsManager {
|
|||||||
const spinners = button.querySelectorAll('.spinner');
|
const spinners = button.querySelectorAll('.spinner');
|
||||||
spinners.forEach(spinner => spinner.remove());
|
spinners.forEach(spinner => spinner.remove());
|
||||||
|
|
||||||
// console.log(`🔍 Install button enabled for ${appName}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
disableUninstallButton(appName, action) {
|
disableUninstallButton(appName, action) {
|
||||||
@ -1579,7 +1569,6 @@ class AppsManager {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(`🔍 Uninstall button disabled for ${appName} during ${action}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enableUninstallButton(appName) {
|
enableUninstallButton(appName) {
|
||||||
@ -1604,7 +1593,6 @@ class AppsManager {
|
|||||||
const spinners = button.querySelectorAll('.spinner');
|
const spinners = button.querySelectorAll('.spinner');
|
||||||
spinners.forEach(spinner => spinner.remove());
|
spinners.forEach(spinner => spinner.remove());
|
||||||
|
|
||||||
// console.log(`🔍 Uninstall button enabled for ${appName}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Static methods for global access
|
// Static methods for global access
|
||||||
@ -1848,9 +1836,7 @@ class ServiceButtons {
|
|||||||
// Get services for a specific app from config, then fill in real IPs/ports from apps-services.json
|
// Get services for a specific app from config, then fill in real IPs/ports from apps-services.json
|
||||||
async getServicesForApp(appName) {
|
async getServicesForApp(appName) {
|
||||||
const portConfig = this.parsePortConfig(appName);
|
const portConfig = this.parsePortConfig(appName);
|
||||||
// console.log(`📦 Port config for ${appName}:`, portConfig);
|
|
||||||
const portServices = portConfig.filter(p => p.buttonEnabled);
|
const portServices = portConfig.filter(p => p.buttonEnabled);
|
||||||
// console.log(`✅ Enabled services for ${appName}:`, portServices);
|
|
||||||
|
|
||||||
if (portServices.length === 0) return [];
|
if (portServices.length === 0) return [];
|
||||||
|
|
||||||
@ -1858,7 +1844,6 @@ class ServiceButtons {
|
|||||||
if (this.services.length === 0) {
|
if (this.services.length === 0) {
|
||||||
await this.loadServices();
|
await this.loadServices();
|
||||||
}
|
}
|
||||||
// console.log(`🌐 Loaded ${this.services.length} services from apps-services.json`);
|
|
||||||
|
|
||||||
// Merge port config with real data from apps-services.json
|
// Merge port config with real data from apps-services.json
|
||||||
return portServices.map(portService => {
|
return portServices.map(portService => {
|
||||||
@ -1868,7 +1853,6 @@ class ServiceButtons {
|
|||||||
s.name === portService.name
|
s.name === portService.name
|
||||||
);
|
);
|
||||||
|
|
||||||
// console.log(`🔗 Matching service for ${portService.name}:`, serviceData);
|
|
||||||
|
|
||||||
const merged = {
|
const merged = {
|
||||||
...portService,
|
...portService,
|
||||||
@ -1879,7 +1863,6 @@ class ServiceButtons {
|
|||||||
externalURL: serviceData?.externalURL || '',
|
externalURL: serviceData?.externalURL || '',
|
||||||
internalURL: serviceData?.internalURL || ''
|
internalURL: serviceData?.internalURL || ''
|
||||||
};
|
};
|
||||||
// console.log(`🎯 Merged service data:`, merged);
|
|
||||||
return merged;
|
return merged;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -77,22 +77,17 @@ class PortManager {
|
|||||||
|
|
||||||
// Get available services for an app
|
// Get available services for an app
|
||||||
async getAvailableServices(appName) {
|
async getAvailableServices(appName) {
|
||||||
//console.log(`🔌 PortManager: Getting services for app: ${appName}`);
|
|
||||||
try {
|
try {
|
||||||
// Load apps data to get services for this app (with cache busting)
|
// Load apps data to get services for this app (with cache busting)
|
||||||
const timestamp = Date.now();
|
const timestamp = Date.now();
|
||||||
const response = await fetch(`/data/apps/generated/apps.json?t=${timestamp}`);
|
const response = await fetch(`/data/apps/generated/apps.json?t=${timestamp}`);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
//console.log(`🔌 Failed to fetch apps.json: ${response.status}`);
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const appsData = await response.json();
|
const appsData = await response.json();
|
||||||
//console.log(`🔌 Apps data loaded:`, appsData);
|
|
||||||
//console.log(`🔌 Available app names:`, appsData.apps.map(app => app.name));
|
|
||||||
|
|
||||||
let app = appsData.apps.find(a => a.name === appName);
|
let app = appsData.apps.find(a => a.name === appName);
|
||||||
//console.log(`🔌 Found app for ${appName}:`, app);
|
|
||||||
|
|
||||||
// Try fuzzy matching if exact match fails
|
// Try fuzzy matching if exact match fails
|
||||||
if (!app) {
|
if (!app) {
|
||||||
@ -100,18 +95,15 @@ class PortManager {
|
|||||||
a.name.toLowerCase().includes(appName.toLowerCase()) ||
|
a.name.toLowerCase().includes(appName.toLowerCase()) ||
|
||||||
appName.toLowerCase().includes(a.name.toLowerCase())
|
appName.toLowerCase().includes(a.name.toLowerCase())
|
||||||
);
|
);
|
||||||
//console.log(`🔌 Fuzzy match for ${appName}:`, fuzzyApp);
|
|
||||||
if (fuzzyApp) {
|
if (fuzzyApp) {
|
||||||
app = fuzzyApp;
|
app = fuzzyApp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (app && app.services) {
|
if (app && app.services) {
|
||||||
//console.log(`🔌 Services found for ${appName}:`, app.services);
|
|
||||||
return app.services;
|
return app.services;
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log(`🔌 No services found for ${appName}`);
|
|
||||||
return [];
|
return [];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading services:', error);
|
console.error('Error loading services:', error);
|
||||||
@ -196,7 +188,6 @@ class PortManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
//console.log('Could not fetch app title, using app name');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show confirmation dialog
|
// Show confirmation dialog
|
||||||
@ -477,8 +468,6 @@ class PortManager {
|
|||||||
this.appName = appName;
|
this.appName = appName;
|
||||||
this.availableServices = await this.getAvailableServices(appName);
|
this.availableServices = await this.getAvailableServices(appName);
|
||||||
|
|
||||||
//console.log(`🔌 PortManager: Available services for ${appName}:`, this.availableServices);
|
|
||||||
//console.log(`🔌 PortManager: Number of services: ${this.availableServices.length}`);
|
|
||||||
|
|
||||||
// Force full-width layout for port manager containers
|
// Force full-width layout for port manager containers
|
||||||
this.forceFullWidthLayout();
|
this.forceFullWidthLayout();
|
||||||
@ -489,7 +478,6 @@ class PortManager {
|
|||||||
const index = parseInt(select.dataset.index);
|
const index = parseInt(select.dataset.index);
|
||||||
const currentService = this.ports[index]?.service || '';
|
const currentService = this.ports[index]?.service || '';
|
||||||
|
|
||||||
//console.log(`🔌 PortManager: Port ${index} current service: "${currentService}"`);
|
|
||||||
|
|
||||||
// Clear existing options
|
// Clear existing options
|
||||||
select.innerHTML = '<option value="">Select a service...</option>';
|
select.innerHTML = '<option value="">Select a service...</option>';
|
||||||
@ -508,22 +496,18 @@ class PortManager {
|
|||||||
// Case 1: Only one service and no current service - auto-select
|
// Case 1: Only one service and no current service - auto-select
|
||||||
shouldAutoSelect = true;
|
shouldAutoSelect = true;
|
||||||
isAutoMatched = true;
|
isAutoMatched = true;
|
||||||
//console.log(`🔌 PortManager: Auto-selecting single service "${service}" for port ${index}`);
|
|
||||||
} else if (this.availableServices.length === 1 && currentService && currentService !== service) {
|
} else if (this.availableServices.length === 1 && currentService && currentService !== service) {
|
||||||
// Case 2: Only one service but current service doesn't match - auto-match
|
// Case 2: Only one service but current service doesn't match - auto-match
|
||||||
shouldAutoSelect = true;
|
shouldAutoSelect = true;
|
||||||
isAutoMatched = true;
|
isAutoMatched = true;
|
||||||
//console.log(`🔌 PortManager: Auto-matching service "${service}" (was "${currentService}") for port ${index}`);
|
|
||||||
} else if (this.availableServices.length === 1 && currentService === service) {
|
} else if (this.availableServices.length === 1 && currentService === service) {
|
||||||
// Case 3: Single service and current service matches - still show auto-match indicator
|
// Case 3: Single service and current service matches - still show auto-match indicator
|
||||||
shouldAutoSelect = true;
|
shouldAutoSelect = true;
|
||||||
isAutoMatched = true;
|
isAutoMatched = true;
|
||||||
//console.log(`🔌 PortManager: Single service matches "${service}" for port ${index} - showing auto-match indicator`);
|
|
||||||
} else if (this.availableServices.length > 1 && currentService === service) {
|
} else if (this.availableServices.length > 1 && currentService === service) {
|
||||||
// Case 4: Multiple services and current service matches - normal selection
|
// Case 4: Multiple services and current service matches - normal selection
|
||||||
shouldAutoSelect = true;
|
shouldAutoSelect = true;
|
||||||
isAutoMatched = false;
|
isAutoMatched = false;
|
||||||
//console.log(`🔌 PortManager: Normal selection of service "${service}" for port ${index}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldAutoSelect) {
|
if (shouldAutoSelect) {
|
||||||
@ -732,7 +716,6 @@ class PortManager {
|
|||||||
const index = parseInt(select.dataset.index);
|
const index = parseInt(select.dataset.index);
|
||||||
const currentService = this.ports[index]?.service || '';
|
const currentService = this.ports[index]?.service || '';
|
||||||
|
|
||||||
//console.log(`🔌 PortManager: Port ${index} current service: "${currentService}"`);
|
|
||||||
|
|
||||||
// Clear existing options
|
// Clear existing options
|
||||||
select.innerHTML = '<option value="">Select a service...</option>';
|
select.innerHTML = '<option value="">Select a service...</option>';
|
||||||
@ -751,22 +734,18 @@ class PortManager {
|
|||||||
// Case 1: Only one service and no current service - auto-select
|
// Case 1: Only one service and no current service - auto-select
|
||||||
shouldAutoSelect = true;
|
shouldAutoSelect = true;
|
||||||
isAutoMatched = true;
|
isAutoMatched = true;
|
||||||
//console.log(`🔌 PortManager: Auto-selecting single service "${service}" for port ${index}`);
|
|
||||||
} else if (this.availableServices.length === 1 && currentService && currentService !== service) {
|
} else if (this.availableServices.length === 1 && currentService && currentService !== service) {
|
||||||
// Case 2: Only one service but current service doesn't match - auto-match
|
// Case 2: Only one service but current service doesn't match - auto-match
|
||||||
shouldAutoSelect = true;
|
shouldAutoSelect = true;
|
||||||
isAutoMatched = true;
|
isAutoMatched = true;
|
||||||
//console.log(`🔌 PortManager: Auto-matching service "${service}" (was "${currentService}") for port ${index}`);
|
|
||||||
} else if (this.availableServices.length === 1 && currentService === service) {
|
} else if (this.availableServices.length === 1 && currentService === service) {
|
||||||
// Case 3: Single service and current service matches - still show auto-match indicator
|
// Case 3: Single service and current service matches - still show auto-match indicator
|
||||||
shouldAutoSelect = true;
|
shouldAutoSelect = true;
|
||||||
isAutoMatched = true;
|
isAutoMatched = true;
|
||||||
//console.log(`🔌 PortManager: Single service matches "${service}" for port ${index} - showing auto-match indicator`);
|
|
||||||
} else if (this.availableServices.length > 1 && currentService === service) {
|
} else if (this.availableServices.length > 1 && currentService === service) {
|
||||||
// Case 4: Multiple services and current service matches - normal selection
|
// Case 4: Multiple services and current service matches - normal selection
|
||||||
shouldAutoSelect = true;
|
shouldAutoSelect = true;
|
||||||
isAutoMatched = false;
|
isAutoMatched = false;
|
||||||
//console.log(`🔌 PortManager: Normal selection of service "${service}" for port ${index}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldAutoSelect) {
|
if (shouldAutoSelect) {
|
||||||
|
|||||||
@ -114,15 +114,12 @@ function navigateToApp(appName) {
|
|||||||
|
|
||||||
// Filter apps by search term (removed - not used in dashboard)
|
// Filter apps by search term (removed - not used in dashboard)
|
||||||
function filterApps(searchTerm) {
|
function filterApps(searchTerm) {
|
||||||
//console.log('Filter apps functionality removed from dashboard');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter apps by category (removed - not used in dashboard)
|
// Filter apps by category (removed - not used in dashboard)
|
||||||
function filterAppsByCategory(category) {
|
function filterAppsByCategory(category) {
|
||||||
//console.log('Filter apps by category functionality removed from dashboard');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate category filter (removed - not used in dashboard)
|
// Populate category filter (removed - not used in dashboard)
|
||||||
function populateCategoryFilter() {
|
function populateCategoryFilter() {
|
||||||
//console.log('Category filter population removed from dashboard');
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,7 +55,6 @@ Object.assign(TasksManager.prototype, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get tasks using new system
|
// Get tasks using new system
|
||||||
// console.log('📥 Getting tasks using new queue system...');
|
|
||||||
|
|
||||||
// Get queue and current status
|
// Get queue and current status
|
||||||
let queue = [];
|
let queue = [];
|
||||||
@ -75,7 +74,6 @@ Object.assign(TasksManager.prototype, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.log('📝 Queue file not found, starting with empty queue');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -92,7 +90,6 @@ Object.assign(TasksManager.prototype, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.log('📝 Current file not found, no current task');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load individual task files
|
// Load individual task files
|
||||||
@ -120,7 +117,6 @@ Object.assign(TasksManager.prototype, {
|
|||||||
|
|
||||||
// Scan tasks folder for all task files (including completed ones) - OPTIMIZED
|
// Scan tasks folder for all task files (including completed ones) - OPTIMIZED
|
||||||
try {
|
try {
|
||||||
// console.log('🔍 Scanning tasks folder for all task files...');
|
|
||||||
const tasksResponse = await fetch('/read-directory?path=tasks');
|
const tasksResponse = await fetch('/read-directory?path=tasks');
|
||||||
if (tasksResponse.ok) {
|
if (tasksResponse.ok) {
|
||||||
const files = await tasksResponse.json();
|
const files = await tasksResponse.json();
|
||||||
@ -130,7 +126,6 @@ Object.assign(TasksManager.prototype, {
|
|||||||
file !== 'current.json'
|
file !== 'current.json'
|
||||||
);
|
);
|
||||||
|
|
||||||
// console.log(`📁 Found ${taskFiles.length} task files in folder`);
|
|
||||||
|
|
||||||
// OPTIMIZATION: Batch load tasks instead of individual calls
|
// OPTIMIZATION: Batch load tasks instead of individual calls
|
||||||
const missingTaskIds = taskFiles
|
const missingTaskIds = taskFiles
|
||||||
@ -138,7 +133,6 @@ Object.assign(TasksManager.prototype, {
|
|||||||
.filter(taskId => !allTasks.find(task => task.id === taskId));
|
.filter(taskId => !allTasks.find(task => task.id === taskId));
|
||||||
|
|
||||||
if (missingTaskIds.length > 0) {
|
if (missingTaskIds.length > 0) {
|
||||||
// console.log(`📦 Batch loading ${missingTaskIds.length} missing tasks...`);
|
|
||||||
try {
|
try {
|
||||||
const batchResponse = await fetch('/read-tasks-batch', {
|
const batchResponse = await fetch('/read-tasks-batch', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -151,12 +145,10 @@ Object.assign(TasksManager.prototype, {
|
|||||||
batchTasks.forEach(task => {
|
batchTasks.forEach(task => {
|
||||||
if (task) {
|
if (task) {
|
||||||
allTasks.push(task);
|
allTasks.push(task);
|
||||||
// console.log(`✅ Added completed task ${task.id} from batch load`);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Fallback to individual loading if batch endpoint not available
|
// Fallback to individual loading if batch endpoint not available
|
||||||
// console.log('⚠️ Batch endpoint not available, falling back to individual loading');
|
|
||||||
await this.loadTasksIndividually(missingTaskIds, allTasks);
|
await this.loadTasksIndividually(missingTaskIds, allTasks);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -181,7 +173,6 @@ Object.assign(TasksManager.prototype, {
|
|||||||
// Sort by creation time (newest first)
|
// Sort by creation time (newest first)
|
||||||
this.tasks.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
|
this.tasks.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
|
||||||
|
|
||||||
// console.log(`✅ Loaded ${this.tasks.length} tasks`);
|
|
||||||
//// // console.log('📋 All tasks:', this.tasks);
|
//// // console.log('📋 All tasks:', this.tasks);
|
||||||
|
|
||||||
this.renderTasks();
|
this.renderTasks();
|
||||||
@ -207,7 +198,6 @@ Object.assign(TasksManager.prototype, {
|
|||||||
const task = await this.taskManager.getTask(taskId);
|
const task = await this.taskManager.getTask(taskId);
|
||||||
if (task) {
|
if (task) {
|
||||||
allTasks.push(task);
|
allTasks.push(task);
|
||||||
// console.log(`✅ Added completed task ${taskId} from individual load`);
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`⚠️ Failed to load task ${taskId}:`, error);
|
console.warn(`⚠️ Failed to load task ${taskId}:`, error);
|
||||||
|
|||||||
@ -149,7 +149,6 @@ Object.assign(TasksManager.prototype, {
|
|||||||
return displayNames[category] || category.charAt(0).toUpperCase() + category.slice(1);
|
return displayNames[category] || category.charAt(0).toUpperCase() + category.slice(1);
|
||||||
},
|
},
|
||||||
renderTask(task) {
|
renderTask(task) {
|
||||||
// console.log(`🔍 renderTask called with task:`, task);
|
|
||||||
|
|
||||||
// Debug undefined status
|
// Debug undefined status
|
||||||
if (!task.status) {
|
if (!task.status) {
|
||||||
@ -164,24 +163,10 @@ Object.assign(TasksManager.prototype, {
|
|||||||
const hasError = task.error && task.error.length > 0;
|
const hasError = task.error && task.error.length > 0;
|
||||||
const hasLogs = task.log && Array.isArray(task.log) && task.log.length > 0;
|
const hasLogs = task.log && Array.isArray(task.log) && task.log.length > 0;
|
||||||
|
|
||||||
// console.log(`🔍 Task fields check:`, {
|
|
||||||
//hasOutput: hasOutput,
|
|
||||||
//hasError: hasError,
|
|
||||||
//hasLogs: hasLogs,
|
|
||||||
//isRunning: isRunning,
|
|
||||||
//outputLength: task.output ? task.output.length : 0,
|
|
||||||
//error: task.error,
|
|
||||||
//logCount: task.log ? task.log.length : 0
|
|
||||||
//});
|
|
||||||
|
|
||||||
const executionTime = task.startedAt && task.completedAt ?
|
const executionTime = task.startedAt && task.completedAt ?
|
||||||
this.calculateExecutionTime(task.startedAt, task.completedAt) : null;
|
this.calculateExecutionTime(task.startedAt, task.completedAt) : null;
|
||||||
|
|
||||||
// console.log('🔍 renderTask debug:', {
|
|
||||||
//taskStatus: task.status,
|
|
||||||
//statusClass: statusClass,
|
|
||||||
//statusDisplay: task.status ? task.status.toUpperCase() : 'UNKNOWN'
|
|
||||||
//});
|
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="task-item" data-task-id="${task.id}">
|
<div class="task-item" data-task-id="${task.id}">
|
||||||
@ -390,7 +375,6 @@ Object.assign(TasksManager.prototype, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.log('Could not load apps.json, will use fallback');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,7 +397,6 @@ Object.assign(TasksManager.prototype, {
|
|||||||
|
|
||||||
// If no installed apps found from apps data, fall back to task-based apps
|
// If no installed apps found from apps data, fall back to task-based apps
|
||||||
if (installedApps.length === 0) {
|
if (installedApps.length === 0) {
|
||||||
// console.log('No installed apps found, using task-based app list');
|
|
||||||
const taskApps = [...new Set(this.tasks.map(task => task.app).filter(Boolean))];
|
const taskApps = [...new Set(this.tasks.map(task => task.app).filter(Boolean))];
|
||||||
|
|
||||||
if (taskApps.length === 0) {
|
if (taskApps.length === 0) {
|
||||||
|
|||||||
@ -92,7 +92,6 @@ Object.assign(TasksManager.prototype, {
|
|||||||
const streamData = this.activeLogStreams.get(taskId);
|
const streamData = this.activeLogStreams.get(taskId);
|
||||||
streamData.stream.stop();
|
streamData.stream.stop();
|
||||||
this.activeLogStreams.delete(taskId);
|
this.activeLogStreams.delete(taskId);
|
||||||
// console.log(`⏹️ Stopped log streaming for task ${taskId}`);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Load task logs automatically
|
// Load task logs automatically
|
||||||
@ -201,26 +200,20 @@ Object.assign(TasksManager.prototype, {
|
|||||||
},
|
},
|
||||||
// Start global live log updater - simple 2-second updates for all running tasks
|
// Start global live log updater - simple 2-second updates for all running tasks
|
||||||
startGlobalLiveLogUpdater() {
|
startGlobalLiveLogUpdater() {
|
||||||
// console.log(`🔄 Starting global live log updater`);
|
|
||||||
|
|
||||||
// Update every 2 seconds
|
// Update every 2 seconds
|
||||||
setInterval(async () => {
|
setInterval(async () => {
|
||||||
// console.log(`🔄 Global updater running - checking tasks...`);
|
|
||||||
|
|
||||||
// Find all running tasks
|
// Find all running tasks
|
||||||
const runningTasks = this.tasks.filter(task => task.status === 'running');
|
const runningTasks = this.tasks.filter(task => task.status === 'running');
|
||||||
// console.log(`🔄 Found ${runningTasks.length} running tasks:`, runningTasks.map(t => t.id));
|
|
||||||
|
|
||||||
if (runningTasks.length > 0) {
|
if (runningTasks.length > 0) {
|
||||||
// console.log(`🔄 Updating live logs for ${runningTasks.length} running tasks`);
|
|
||||||
|
|
||||||
// Update each running task's live logs
|
// Update each running task's live logs
|
||||||
for (const task of runningTasks) {
|
for (const task of runningTasks) {
|
||||||
// console.log(`🔄 About to update live logs for task ${task.id}`);
|
|
||||||
await this.updateLiveLogsSimple(task.id);
|
await this.updateLiveLogsSimple(task.id);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// console.log(`🔄 No running tasks found, skipping live log updates`);
|
|
||||||
}
|
}
|
||||||
}, 2000); // Every 2 seconds
|
}, 2000); // Every 2 seconds
|
||||||
},
|
},
|
||||||
@ -228,26 +221,20 @@ Object.assign(TasksManager.prototype, {
|
|||||||
async updateLiveLogsSimple(taskId) {
|
async updateLiveLogsSimple(taskId) {
|
||||||
const liveLogsElement = document.getElementById(`live-logs-${taskId}`);
|
const liveLogsElement = document.getElementById(`live-logs-${taskId}`);
|
||||||
if (!liveLogsElement) {
|
if (!liveLogsElement) {
|
||||||
// console.log(`⚠️ Live logs element not found for task ${taskId}`);
|
|
||||||
return; // Silently skip if element not found
|
return; // Silently skip if element not found
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// console.log(`🔄 Reading log file for task ${taskId}`);
|
|
||||||
|
|
||||||
// Read the log file content
|
// Read the log file content
|
||||||
const response = await fetch(`/read-file?path=tasks/${taskId}.log`);
|
const response = await fetch(`/read-file?path=tasks/${taskId}.log`);
|
||||||
// console.log(`🔄 Log file response status: ${response.status} for task ${taskId}`);
|
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const logContent = await response.text();
|
const logContent = await response.text();
|
||||||
// console.log(`🔄 Log file content length: ${logContent.length} chars for task ${taskId}`);
|
|
||||||
// console.log(`🔄 First 100 chars of log content: "${logContent.substring(0, 100)}..."`);
|
|
||||||
|
|
||||||
if (logContent.trim()) {
|
if (logContent.trim()) {
|
||||||
// Split into lines and display
|
// Split into lines and display
|
||||||
const lines = logContent.split('\n').filter(line => line.trim());
|
const lines = logContent.split('\n').filter(line => line.trim());
|
||||||
// console.log(`🔄 Displaying ${lines.length} log lines for task ${taskId}`);
|
|
||||||
|
|
||||||
liveLogsElement.innerHTML = lines.map(line =>
|
liveLogsElement.innerHTML = lines.map(line =>
|
||||||
`<div class="log-entry">${this.parseAnsiColors(line)}</div>`
|
`<div class="log-entry">${this.parseAnsiColors(line)}</div>`
|
||||||
@ -255,7 +242,6 @@ Object.assign(TasksManager.prototype, {
|
|||||||
// Auto-scroll to bottom
|
// Auto-scroll to bottom
|
||||||
liveLogsElement.scrollTop = liveLogsElement.scrollHeight;
|
liveLogsElement.scrollTop = liveLogsElement.scrollHeight;
|
||||||
} else {
|
} else {
|
||||||
// console.log(`🔄 Log file is empty for task ${taskId}`);
|
|
||||||
liveLogsElement.innerHTML = '<div class="log-entry">🔄 Waiting for logs...</div>';
|
liveLogsElement.innerHTML = '<div class="log-entry">🔄 Waiting for logs...</div>';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -276,12 +262,10 @@ Object.assign(TasksManager.prototype, {
|
|||||||
const detailsElement = taskElement.querySelector('.task-details');
|
const detailsElement = taskElement.querySelector('.task-details');
|
||||||
if (!detailsElement) return;
|
if (!detailsElement) return;
|
||||||
|
|
||||||
// console.log(`🔄 Updating task structure for ${taskId} to show simplified logs`);
|
|
||||||
|
|
||||||
// Check if logs container already exists
|
// Check if logs container already exists
|
||||||
const existingLogs = detailsElement.querySelector('.task-logs .log-container');
|
const existingLogs = detailsElement.querySelector('.task-logs .log-container');
|
||||||
if (existingLogs) {
|
if (existingLogs) {
|
||||||
// console.log(`🔄 Logs container already exists for ${taskId}`);
|
|
||||||
return; // Already exists, no need to update
|
return; // Already exists, no need to update
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,7 +281,6 @@ Object.assign(TasksManager.prototype, {
|
|||||||
|
|
||||||
// Insert logs section at the bottom of details
|
// Insert logs section at the bottom of details
|
||||||
detailsElement.insertAdjacentHTML('beforeend', logsHtml);
|
detailsElement.insertAdjacentHTML('beforeend', logsHtml);
|
||||||
// console.log(`✅ Added simplified logs section for task ${taskId}`);
|
|
||||||
|
|
||||||
// Auto-load logs
|
// Auto-load logs
|
||||||
this.loadTaskLogs(taskId);
|
this.loadTaskLogs(taskId);
|
||||||
@ -312,7 +295,6 @@ Object.assign(TasksManager.prototype, {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(`🔄 Found task element:`, taskElement);
|
|
||||||
|
|
||||||
// Update status and content
|
// Update status and content
|
||||||
const statusElement = taskElement.querySelector('.task-status');
|
const statusElement = taskElement.querySelector('.task-status');
|
||||||
@ -338,7 +320,6 @@ Object.assign(TasksManager.prototype, {
|
|||||||
contentElement.textContent = task.command;
|
contentElement.textContent = task.command;
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(`🔄 Updated task ${task.id} display: ${task.status}`);
|
|
||||||
},
|
},
|
||||||
// Update highlighted task status and UI
|
// Update highlighted task status and UI
|
||||||
async updateHighlightedTaskStatus(taskId) {
|
async updateHighlightedTaskStatus(taskId) {
|
||||||
@ -347,14 +328,12 @@ Object.assign(TasksManager.prototype, {
|
|||||||
const task = await this.taskManager.getTaskSummary(taskId);
|
const task = await this.taskManager.getTaskSummary(taskId);
|
||||||
if (!task) return;
|
if (!task) return;
|
||||||
|
|
||||||
// console.log(`🔄 Updating highlighted task ${taskId} status: ${task.status}`);
|
|
||||||
|
|
||||||
// Update task display
|
// Update task display
|
||||||
this.updateTaskDisplay(task);
|
this.updateTaskDisplay(task);
|
||||||
|
|
||||||
// If task completed or failed, always load output
|
// If task completed or failed, always load output
|
||||||
if ((task.status === 'completed' || task.status === 'failed')) {
|
if ((task.status === 'completed' || task.status === 'failed')) {
|
||||||
// console.log(`🔄 Task ${taskId} is ${task.status}, loading output...`);
|
|
||||||
|
|
||||||
const details = document.getElementById(`details-${taskId}`);
|
const details = document.getElementById(`details-${taskId}`);
|
||||||
if (details && details.style.display === 'block') {
|
if (details && details.style.display === 'block') {
|
||||||
|
|||||||
@ -145,12 +145,10 @@ class TasksManager {
|
|||||||
// Only check for specific task parameter if we're not coming from an app page
|
// Only check for specific task parameter if we're not coming from an app page
|
||||||
const taskParam = searchParams.get('task');
|
const taskParam = searchParams.get('task');
|
||||||
if (taskParam) {
|
if (taskParam) {
|
||||||
// console.log(`🎯 Found task parameter in URL: ${taskParam} on main tasks page`);
|
|
||||||
this.highlightedTaskId = taskParam;
|
this.highlightedTaskId = taskParam;
|
||||||
} else {
|
} else {
|
||||||
// Clear any existing highlighted task when on main tasks page without task param
|
// Clear any existing highlighted task when on main tasks page without task param
|
||||||
this.highlightedTaskId = null;
|
this.highlightedTaskId = null;
|
||||||
// console.log(`🎯 Clearing highlighted task on main tasks page`);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Not on main tasks page, get default filter from localStorage
|
// Not on main tasks page, get default filter from localStorage
|
||||||
@ -158,7 +156,6 @@ class TasksManager {
|
|||||||
this.highlightedTaskId = null; // Always clear when not on tasks page
|
this.highlightedTaskId = null; // Always clear when not on tasks page
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(`🎯 Tasks category from URL: ${this.currentCategory}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateURL(category, taskId = null) {
|
updateURL(category, taskId = null) {
|
||||||
@ -186,7 +183,6 @@ class TasksManager {
|
|||||||
await this.loadTasks();
|
await this.loadTasks();
|
||||||
|
|
||||||
// Force a refresh to ensure latest data
|
// Force a refresh to ensure latest data
|
||||||
// console.log('🔄 Refreshing tasks data on initialization...');
|
|
||||||
await this.loadTasks();
|
await this.loadTasks();
|
||||||
|
|
||||||
// Setup auto-refresh
|
// Setup auto-refresh
|
||||||
|
|||||||
@ -99,7 +99,6 @@ Object.assign(TasksManager.prototype, {
|
|||||||
},
|
},
|
||||||
// Auto-expand a task when it's created
|
// Auto-expand a task when it's created
|
||||||
async autoExpandTask(taskId) {
|
async autoExpandTask(taskId) {
|
||||||
// console.log(`🔄 Auto-expanding task ${taskId}`);
|
|
||||||
|
|
||||||
// Wait for task to be rendered
|
// Wait for task to be rendered
|
||||||
let attempts = 0;
|
let attempts = 0;
|
||||||
@ -107,12 +106,10 @@ Object.assign(TasksManager.prototype, {
|
|||||||
|
|
||||||
const tryExpand = async () => {
|
const tryExpand = async () => {
|
||||||
attempts++;
|
attempts++;
|
||||||
// console.log(`🔄 Auto-expand attempt ${attempts}/${maxAttempts} for task ${taskId}`);
|
|
||||||
|
|
||||||
// Check if task element exists
|
// Check if task element exists
|
||||||
const taskElement = document.querySelector(`[data-task-id="${taskId}"]`);
|
const taskElement = document.querySelector(`[data-task-id="${taskId}"]`);
|
||||||
if (!taskElement) {
|
if (!taskElement) {
|
||||||
// console.log(`⚠️ Task element not found for ${taskId}, attempt ${attempts}`);
|
|
||||||
if (attempts < maxAttempts) {
|
if (attempts < maxAttempts) {
|
||||||
setTimeout(tryExpand, 500); // Try again in 500ms
|
setTimeout(tryExpand, 500); // Try again in 500ms
|
||||||
} else {
|
} else {
|
||||||
@ -121,12 +118,10 @@ Object.assign(TasksManager.prototype, {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(`✅ Found task element for ${taskId}`);
|
|
||||||
|
|
||||||
// Get the details element
|
// Get the details element
|
||||||
const details = document.getElementById(`details-${taskId}`);
|
const details = document.getElementById(`details-${taskId}`);
|
||||||
if (!details) {
|
if (!details) {
|
||||||
// console.log(`⚠️ Details element not found for ${taskId}, attempt ${attempts}`);
|
|
||||||
if (attempts < maxAttempts) {
|
if (attempts < maxAttempts) {
|
||||||
setTimeout(tryExpand, 500);
|
setTimeout(tryExpand, 500);
|
||||||
} else {
|
} else {
|
||||||
@ -135,7 +130,6 @@ Object.assign(TasksManager.prototype, {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(`✅ Found details element for ${taskId}`);
|
|
||||||
|
|
||||||
// Expand the task details
|
// Expand the task details
|
||||||
details.style.display = 'block';
|
details.style.display = 'block';
|
||||||
@ -158,7 +152,6 @@ Object.assign(TasksManager.prototype, {
|
|||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(`✅ Auto-expanded task ${taskId}`);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
tryExpand();
|
tryExpand();
|
||||||
|
|||||||
@ -18,9 +18,7 @@ class SystemLoader {
|
|||||||
|
|
||||||
this.initializeSystems();
|
this.initializeSystems();
|
||||||
this.initializeHealthChecks();
|
this.initializeHealthChecks();
|
||||||
// console.log('🚨 BREAKPOINT: About to call initializeComponents()');
|
|
||||||
this.initializeComponentRegistry();
|
this.initializeComponentRegistry();
|
||||||
// console.log('BREAKPOINT: initializeComponents() completed');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize component registry
|
// Initialize component registry
|
||||||
@ -60,7 +58,6 @@ class SystemLoader {
|
|||||||
initializer: () => {
|
initializer: () => {
|
||||||
if (typeof setupMobileMenu === 'function') {
|
if (typeof setupMobileMenu === 'function') {
|
||||||
setupMobileMenu();
|
setupMobileMenu();
|
||||||
// console.log('✅ Mobile menu initialized');
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
console.warn('⚠️ setupMobileMenu not available');
|
console.warn('⚠️ setupMobileMenu not available');
|
||||||
@ -81,7 +78,6 @@ class SystemLoader {
|
|||||||
initializer: () => {
|
initializer: () => {
|
||||||
if (typeof initConfirmationDialog === 'function') {
|
if (typeof initConfirmationDialog === 'function') {
|
||||||
initConfirmationDialog();
|
initConfirmationDialog();
|
||||||
// console.log('✅ Confirmation dialog initialized');
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
console.warn('⚠️ initConfirmationDialog not available');
|
console.warn('⚠️ initConfirmationDialog not available');
|
||||||
@ -98,7 +94,6 @@ class SystemLoader {
|
|||||||
initializer: () => {
|
initializer: () => {
|
||||||
if (typeof NotificationSystem !== 'undefined') {
|
if (typeof NotificationSystem !== 'undefined') {
|
||||||
window.notificationSystem = new NotificationSystem();
|
window.notificationSystem = new NotificationSystem();
|
||||||
// console.log('✅ Notification system initialized');
|
|
||||||
return window.notificationSystem;
|
return window.notificationSystem;
|
||||||
}
|
}
|
||||||
console.warn('⚠️ NotificationSystem not available');
|
console.warn('⚠️ NotificationSystem not available');
|
||||||
@ -116,18 +111,15 @@ class SystemLoader {
|
|||||||
// Initialize data loading system
|
// Initialize data loading system
|
||||||
if (typeof initializeData === 'function') {
|
if (typeof initializeData === 'function') {
|
||||||
initializeData();
|
initializeData();
|
||||||
// console.log('✅ Data loading system initialized');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize dashboard-specific functions
|
// Initialize dashboard-specific functions
|
||||||
if (typeof loadSystemInfo === 'function') {
|
if (typeof loadSystemInfo === 'function') {
|
||||||
loadSystemInfo();
|
loadSystemInfo();
|
||||||
// console.log('✅ System info loaded');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof setupEventListeners === 'function') {
|
if (typeof setupEventListeners === 'function') {
|
||||||
setupEventListeners();
|
setupEventListeners();
|
||||||
// console.log('✅ Dashboard event listeners set up');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -141,7 +133,6 @@ class SystemLoader {
|
|||||||
this.components.set('task-system', {
|
this.components.set('task-system', {
|
||||||
system: 'task',
|
system: 'task',
|
||||||
initializer: () => {
|
initializer: () => {
|
||||||
// console.log('🔧 Initializing Task System...');
|
|
||||||
|
|
||||||
// Open the SSE feed before anything else so we don't miss the upserts
|
// Open the SSE feed before anything else so we don't miss the upserts
|
||||||
// that fire as the page boots.
|
// that fire as the page boots.
|
||||||
@ -152,7 +143,6 @@ class SystemLoader {
|
|||||||
// Create TasksManager instance
|
// Create TasksManager instance
|
||||||
if (typeof TasksManager !== 'undefined') {
|
if (typeof TasksManager !== 'undefined') {
|
||||||
window.tasksManager = new TasksManager();
|
window.tasksManager = new TasksManager();
|
||||||
// console.log('✅ TasksManager initialized and available globally');
|
|
||||||
return window.tasksManager;
|
return window.tasksManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,15 +175,10 @@ class SystemLoader {
|
|||||||
this.components.set('apps-manager', {
|
this.components.set('apps-manager', {
|
||||||
system: 'managers',
|
system: 'managers',
|
||||||
initializer: () => {
|
initializer: () => {
|
||||||
// console.log('🔧 DEBUG: AppsManager initializer called');
|
|
||||||
// console.log('🔧 DEBUG: AppsManager class available:', typeof AppsManager !== 'undefined');
|
|
||||||
// console.log('🔧 DEBUG: Available globals:', Object.keys(window).filter(key => key.includes('Manager')));
|
|
||||||
|
|
||||||
if (typeof AppsManager !== 'undefined') {
|
if (typeof AppsManager !== 'undefined') {
|
||||||
// console.log('🔧 DEBUG: Creating AppsManager instance...');
|
|
||||||
try {
|
try {
|
||||||
window.appsManager = new AppsManager();
|
window.appsManager = new AppsManager();
|
||||||
// console.log('✅ AppsManager instance created successfully');
|
|
||||||
return window.appsManager;
|
return window.appsManager;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Failed to create AppsManager instance:', error);
|
console.error('❌ Failed to create AppsManager instance:', error);
|
||||||
@ -229,13 +214,9 @@ class SystemLoader {
|
|||||||
this.components.set('app-tabbed-manager', {
|
this.components.set('app-tabbed-manager', {
|
||||||
system: 'managers',
|
system: 'managers',
|
||||||
initializer: () => {
|
initializer: () => {
|
||||||
// console.log('🔧 DEBUG: Attempting to initialize AppTabbedManager...');
|
|
||||||
// console.log('🔧 DEBUG: AppTabbedManager class available:', typeof AppTabbedManager !== 'undefined');
|
|
||||||
// console.log('🔧 DEBUG: Available globals:', Object.keys(window).filter(key => key.includes('Tabbed') || key.includes('Manager')));
|
|
||||||
|
|
||||||
if (typeof AppTabbedManager !== 'undefined') {
|
if (typeof AppTabbedManager !== 'undefined') {
|
||||||
window.appTabbedManager = new AppTabbedManager();
|
window.appTabbedManager = new AppTabbedManager();
|
||||||
// console.log('✅ App Tabbed Manager initialized');
|
|
||||||
return window.appTabbedManager;
|
return window.appTabbedManager;
|
||||||
}
|
}
|
||||||
console.warn('⚠️ AppTabbedManager not available');
|
console.warn('⚠️ AppTabbedManager not available');
|
||||||
@ -246,8 +227,6 @@ class SystemLoader {
|
|||||||
script: '/components/apps/core/js/app-tabbed-manager.js'
|
script: '/components/apps/core/js/app-tabbed-manager.js'
|
||||||
});
|
});
|
||||||
|
|
||||||
// console.log('TEST: Components added. Total components:', this.components.size);
|
|
||||||
// console.log('TEST: Method completed');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize all system definitions
|
// Initialize all system definitions
|
||||||
@ -259,7 +238,6 @@ class SystemLoader {
|
|||||||
dependencies: [],
|
dependencies: [],
|
||||||
checks: ['dom', 'spa', 'utils']
|
checks: ['dom', 'spa', 'utils']
|
||||||
});
|
});
|
||||||
// console.log('🔍 Core System checks:', ['dom', 'spa', 'utils']);
|
|
||||||
|
|
||||||
this.systems.set('data', {
|
this.systems.set('data', {
|
||||||
name: 'Data Functions',
|
name: 'Data Functions',
|
||||||
@ -268,7 +246,6 @@ class SystemLoader {
|
|||||||
dependencies: ['core'],
|
dependencies: ['core'],
|
||||||
checks: []
|
checks: []
|
||||||
});
|
});
|
||||||
// console.log('🔍 Data Functions checks:', []);
|
|
||||||
|
|
||||||
this.systems.set('components', {
|
this.systems.set('components', {
|
||||||
name: 'UI Components',
|
name: 'UI Components',
|
||||||
@ -369,16 +346,6 @@ class SystemLoader {
|
|||||||
const hasDataLoader = typeof DataLoader !== 'undefined';
|
const hasDataLoader = typeof DataLoader !== 'undefined';
|
||||||
const hasInitializeData = typeof initializeData === 'function';
|
const hasInitializeData = typeof initializeData === 'function';
|
||||||
|
|
||||||
// console.log('🔍 Utils Check:', {
|
|
||||||
//hasDataLoader,
|
|
||||||
//hasSafeGetElement,
|
|
||||||
//hasSafeQuerySelector,
|
|
||||||
//hasGetAppIcon,
|
|
||||||
//hasSetupThemeToggle,
|
|
||||||
//hasInitializeData,
|
|
||||||
// Check what's actually available globally
|
|
||||||
//availableGlobals: Object.keys(window).filter(key =>
|
|
||||||
//key.includes('safe') ||
|
|
||||||
//key.includes('get') ||
|
//key.includes('get') ||
|
||||||
//key.includes('Data') ||
|
//key.includes('Data') ||
|
||||||
//key.includes('Theme') ||
|
//key.includes('Theme') ||
|
||||||
@ -410,19 +377,6 @@ class SystemLoader {
|
|||||||
const hasLoadConfigDetailData = typeof loadConfigDetailData === 'function';
|
const hasLoadConfigDetailData = typeof loadConfigDetailData === 'function';
|
||||||
const hasLoadMinimalData = typeof loadMinimalData === 'function';
|
const hasLoadMinimalData = typeof loadMinimalData === 'function';
|
||||||
|
|
||||||
// console.log('🔍 Data Loader Check:', {
|
|
||||||
//hasDataLoader,
|
|
||||||
//hasInitializeData,
|
|
||||||
//hasLoadDashboardData,
|
|
||||||
//hasLoadAppsPageData,
|
|
||||||
//hasLoadConfigDetailData,
|
|
||||||
//hasLoadMinimalData,
|
|
||||||
//availableDataFunctions: Object.keys(window).filter(key =>
|
|
||||||
//key.includes('load') ||
|
|
||||||
//key.includes('Data') ||
|
|
||||||
//key.includes('initialize')
|
|
||||||
//).slice(0, 10)
|
|
||||||
//});
|
|
||||||
|
|
||||||
// Check if data loader functions are available
|
// Check if data loader functions are available
|
||||||
return hasInitializeData && (hasLoadDashboardData || hasLoadAppsPageData || hasLoadConfigDetailData);
|
return hasInitializeData && (hasLoadDashboardData || hasLoadAppsPageData || hasLoadConfigDetailData);
|
||||||
@ -439,7 +393,6 @@ class SystemLoader {
|
|||||||
weight: 2,
|
weight: 2,
|
||||||
timeout: 1000, // Reduced timeout since we're not actually checking
|
timeout: 1000, // Reduced timeout since we're not actually checking
|
||||||
check: async () => {
|
check: async () => {
|
||||||
// console.log('🔍 Topbar Check: Will be loaded dynamically during component initialization');
|
|
||||||
// Always pass - TopbarComponent will be loaded dynamically by mobile-menu component
|
// Always pass - TopbarComponent will be loaded dynamically by mobile-menu component
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -452,7 +405,6 @@ class SystemLoader {
|
|||||||
weight: 1,
|
weight: 1,
|
||||||
timeout: 1000,
|
timeout: 1000,
|
||||||
check: async () => {
|
check: async () => {
|
||||||
// console.log('🔍 Confirmation Dialog Check: Will be loaded dynamically during component initialization');
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -463,7 +415,6 @@ class SystemLoader {
|
|||||||
weight: 1,
|
weight: 1,
|
||||||
timeout: 1000, // Reduced timeout since we're not actually checking
|
timeout: 1000, // Reduced timeout since we're not actually checking
|
||||||
check: async () => {
|
check: async () => {
|
||||||
// console.log('🔍 Notification System Check: Will be loaded dynamically during component initialization');
|
|
||||||
// Always pass - NotificationSystem will be loaded dynamically by notifications component
|
// Always pass - NotificationSystem will be loaded dynamically by notifications component
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -484,7 +435,6 @@ class SystemLoader {
|
|||||||
weight: 3,
|
weight: 3,
|
||||||
timeout: 1000, // Reduced timeout since we're not actually checking
|
timeout: 1000, // Reduced timeout since we're not actually checking
|
||||||
check: async () => {
|
check: async () => {
|
||||||
// console.log('🔍 Apps Manager Check: Will be loaded dynamically during component initialization');
|
|
||||||
// Always pass - AppsManager will be loaded dynamically by apps-manager component
|
// Always pass - AppsManager will be loaded dynamically by apps-manager component
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -496,7 +446,6 @@ class SystemLoader {
|
|||||||
weight: 3,
|
weight: 3,
|
||||||
timeout: 1000, // Reduced timeout since we're not actually checking
|
timeout: 1000, // Reduced timeout since we're not actually checking
|
||||||
check: async () => {
|
check: async () => {
|
||||||
// console.log('🔍 Task Manager Check: Will be loaded dynamically during component initialization');
|
|
||||||
// Always pass - TasksManager will be loaded dynamically by task-manager component
|
// Always pass - TasksManager will be loaded dynamically by task-manager component
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -508,7 +457,6 @@ class SystemLoader {
|
|||||||
weight: 2,
|
weight: 2,
|
||||||
timeout: 1000, // Reduced timeout since we're not actually checking
|
timeout: 1000, // Reduced timeout since we're not actually checking
|
||||||
check: async () => {
|
check: async () => {
|
||||||
// console.log('🔍 Config Manager Check: Will be loaded dynamically during component initialization');
|
|
||||||
// Always pass - ConfigManager will be loaded dynamically by config-manager component
|
// Always pass - ConfigManager will be loaded dynamically by config-manager component
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -520,7 +468,6 @@ class SystemLoader {
|
|||||||
weight: 3,
|
weight: 3,
|
||||||
timeout: 1000, // Reduced timeout since we're not actually checking
|
timeout: 1000, // Reduced timeout since we're not actually checking
|
||||||
check: async () => {
|
check: async () => {
|
||||||
// console.log('🔍 App Tabbed Manager Check: Will be loaded dynamically during component initialization');
|
|
||||||
// Always pass - AppTabbedManager will be loaded dynamically by app-tabbed-manager component
|
// Always pass - AppTabbedManager will be loaded dynamically by app-tabbed-manager component
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -598,38 +545,30 @@ class SystemLoader {
|
|||||||
|
|
||||||
// Icon Preloading Check
|
// Icon Preloading Check
|
||||||
async checkIconPreloading() {
|
async checkIconPreloading() {
|
||||||
// console.log('🔄 Starting icon preloading check...');
|
|
||||||
|
|
||||||
// Load data directly since window.apps isn't populated yet
|
// Load data directly since window.apps isn't populated yet
|
||||||
let appsData = [];
|
let appsData = [];
|
||||||
let categoriesData = [];
|
let categoriesData = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// console.log('🌐 Fetching data from /data/apps/generated/apps.json and /data/apps/apps-categories.json');
|
|
||||||
const [appsResponse, categoriesResponse] = await Promise.all([
|
const [appsResponse, categoriesResponse] = await Promise.all([
|
||||||
fetch('/data/apps/generated/apps.json'),
|
fetch('/data/apps/generated/apps.json'),
|
||||||
fetch('/data/apps/apps-categories.json')
|
fetch('/data/apps/apps-categories.json')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// console.log('📊 Apps response status:', appsResponse.status, appsResponse.statusText);
|
|
||||||
// console.log('📊 Categories response status:', categoriesResponse.status, categoriesResponse.statusText);
|
|
||||||
|
|
||||||
if (appsResponse.ok) {
|
if (appsResponse.ok) {
|
||||||
const appsText = await appsResponse.text();
|
const appsText = await appsResponse.text();
|
||||||
// console.log('📄 Apps response text length:', appsText.length);
|
|
||||||
const appsParsed = JSON.parse(appsText);
|
const appsParsed = JSON.parse(appsText);
|
||||||
appsData = appsParsed.apps || [];
|
appsData = appsParsed.apps || [];
|
||||||
// console.log('✅ Apps data loaded successfully');
|
|
||||||
} else {
|
} else {
|
||||||
console.warn('⚠️ Apps response not ok:', appsResponse.status);
|
console.warn('⚠️ Apps response not ok:', appsResponse.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (categoriesResponse.ok) {
|
if (categoriesResponse.ok) {
|
||||||
const categoriesText = await categoriesResponse.text();
|
const categoriesText = await categoriesResponse.text();
|
||||||
// console.log('📄 Categories response text length:', categoriesText.length);
|
|
||||||
const categoriesParsed = JSON.parse(categoriesText);
|
const categoriesParsed = JSON.parse(categoriesText);
|
||||||
categoriesData = categoriesParsed.categories || [];
|
categoriesData = categoriesParsed.categories || [];
|
||||||
// console.log('✅ Categories data loaded successfully');
|
|
||||||
} else {
|
} else {
|
||||||
console.warn('⚠️ Categories response not ok:', categoriesResponse.status);
|
console.warn('⚠️ Categories response not ok:', categoriesResponse.status);
|
||||||
}
|
}
|
||||||
@ -637,8 +576,6 @@ class SystemLoader {
|
|||||||
console.warn('⚠️ Failed to load data for icon preloading:', error);
|
console.warn('⚠️ Failed to load data for icon preloading:', error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log('📊 Apps data length:', appsData?.length || 0);
|
|
||||||
// console.log('📊 Categories data length:', categoriesData?.length || 0);
|
|
||||||
|
|
||||||
// Collect all icon URLs to preload
|
// Collect all icon URLs to preload
|
||||||
const iconUrls = new Set();
|
const iconUrls = new Set();
|
||||||
@ -669,7 +606,6 @@ class SystemLoader {
|
|||||||
iconUrls.add('/core/icons/categories/all.svg');
|
iconUrls.add('/core/icons/categories/all.svg');
|
||||||
iconUrls.add('/core/icons/categories/installed.svg');
|
iconUrls.add('/core/icons/categories/installed.svg');
|
||||||
|
|
||||||
// console.log(`📦 Found ${iconUrls.size} unique icons to preload`);
|
|
||||||
|
|
||||||
// Preload all icons using Image objects
|
// Preload all icons using Image objects
|
||||||
const preloadPromises = Array.from(iconUrls).map(iconUrl => {
|
const preloadPromises = Array.from(iconUrls).map(iconUrl => {
|
||||||
@ -682,14 +618,11 @@ class SystemLoader {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await Promise.all(preloadPromises);
|
await Promise.all(preloadPromises);
|
||||||
// console.log(`✅ Preloaded ${iconUrls.size} app center icons`);
|
|
||||||
|
|
||||||
// Pre-render app center in background
|
// Pre-render app center in background
|
||||||
if (window.appsManager && typeof window.appsManager.showAppsList === 'function') {
|
if (window.appsManager && typeof window.appsManager.showAppsList === 'function') {
|
||||||
// console.log('🔄 Pre-rendering app center content...');
|
|
||||||
window.appsManager.showAppsList('all');
|
window.appsManager.showAppsList('all');
|
||||||
await new Promise(resolve => setTimeout(resolve, 300)); // Wait for rendering
|
await new Promise(resolve => setTimeout(resolve, 300)); // Wait for rendering
|
||||||
// console.log('✅ App center pre-rendered');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -725,15 +658,12 @@ class SystemLoader {
|
|||||||
|
|
||||||
// Initialize all components in dependency order
|
// Initialize all components in dependency order
|
||||||
async initializeComponents() {
|
async initializeComponents() {
|
||||||
// console.log('🔧 BREAKPOINT: initializeComponents() called!');
|
|
||||||
// console.log('🔧 Initializing components...');
|
|
||||||
|
|
||||||
// Get systems in priority order
|
// Get systems in priority order
|
||||||
const sortedSystems = Array.from(this.systems.entries())
|
const sortedSystems = Array.from(this.systems.entries())
|
||||||
.sort(([,a], [,b]) => a.priority - b.priority);
|
.sort(([,a], [,b]) => a.priority - b.priority);
|
||||||
|
|
||||||
for (const [systemId, system] of sortedSystems) {
|
for (const [systemId, system] of sortedSystems) {
|
||||||
// console.log(`🔄 Processing system: ${systemId}`);
|
|
||||||
|
|
||||||
// Check dependencies first
|
// Check dependencies first
|
||||||
const dependenciesMet = system.dependencies.every(dep =>
|
const dependenciesMet = system.dependencies.every(dep =>
|
||||||
@ -741,7 +671,6 @@ class SystemLoader {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!dependenciesMet) {
|
if (!dependenciesMet) {
|
||||||
// console.log(`⏳ Skipping ${system.name} - dependencies not met:`, system.dependencies);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -749,19 +678,15 @@ class SystemLoader {
|
|||||||
const systemComponents = Array.from(this.components.entries())
|
const systemComponents = Array.from(this.components.entries())
|
||||||
.filter(([, component]) => component.system === systemId);
|
.filter(([, component]) => component.system === systemId);
|
||||||
|
|
||||||
// console.log(`📦 Found ${systemComponents.length} components for ${systemId}:`, systemComponents.map(([id]) => id));
|
|
||||||
|
|
||||||
// Initialize components for this system
|
// Initialize components for this system
|
||||||
for (const [componentId, component] of systemComponents) {
|
for (const [componentId, component] of systemComponents) {
|
||||||
try {
|
try {
|
||||||
// console.log(`🔧 Initializing ${componentId}...`);
|
|
||||||
|
|
||||||
// Load component script(s) if specified
|
// Load component script(s) if specified
|
||||||
if (component.script) {
|
if (component.script) {
|
||||||
// console.log(`📦 Loading script for ${componentId}: ${component.script}`);
|
|
||||||
await this.loadScript(component.script);
|
await this.loadScript(component.script);
|
||||||
} else if (component.scripts) {
|
} else if (component.scripts) {
|
||||||
// console.log(`📦 Loading scripts for ${componentId}:`, component.scripts);
|
|
||||||
await this.loadScripts(component.scripts);
|
await this.loadScripts(component.scripts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -771,7 +696,6 @@ class SystemLoader {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!componentDepsMet) {
|
if (!componentDepsMet) {
|
||||||
// console.log(`⏳ Skipping ${componentId} - component dependencies not met:`, component.dependencies);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -781,10 +705,8 @@ class SystemLoader {
|
|||||||
// Store result if available
|
// Store result if available
|
||||||
if (result && component.global) {
|
if (result && component.global) {
|
||||||
window[component.global] = result;
|
window[component.global] = result;
|
||||||
// console.log(`✅ ${componentId} stored as global: ${component.global}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(`✅ ${componentId} initialized successfully`);
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`❌ Failed to initialize ${componentId}:`, error);
|
console.error(`❌ Failed to initialize ${componentId}:`, error);
|
||||||
@ -798,10 +720,8 @@ class SystemLoader {
|
|||||||
|
|
||||||
// Mark system as initialized
|
// Mark system as initialized
|
||||||
system.initialized = true;
|
system.initialized = true;
|
||||||
// console.log(`✅ ${system.name} system initialized`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log('🎉 Component initialization complete');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load a script dynamically
|
// Load a script dynamically
|
||||||
@ -839,8 +759,6 @@ class SystemLoader {
|
|||||||
results.get(dep)?.status === 'passed'
|
results.get(dep)?.status === 'passed'
|
||||||
);
|
);
|
||||||
|
|
||||||
// console.log(`🔍 Processing system: ${system.name} (priority: ${system.priority}, critical: ${system.critical})`);
|
|
||||||
// console.log(`🔍 Dependencies met: ${dependenciesMet}`);
|
|
||||||
|
|
||||||
if (!dependenciesMet) {
|
if (!dependenciesMet) {
|
||||||
results.set(systemId, {
|
results.set(systemId, {
|
||||||
@ -856,7 +774,6 @@ class SystemLoader {
|
|||||||
|
|
||||||
// If system has no checks, it automatically passes
|
// If system has no checks, it automatically passes
|
||||||
if (system.checks.length === 0) {
|
if (system.checks.length === 0) {
|
||||||
// console.log(`✅ System "${system.name}" (${systemId}) has no checks - automatically passing`);
|
|
||||||
|
|
||||||
// Trigger status update for systems with no checks
|
// Trigger status update for systems with no checks
|
||||||
this.trigger('onSystemCheck', {
|
this.trigger('onSystemCheck', {
|
||||||
@ -880,15 +797,12 @@ class SystemLoader {
|
|||||||
status: 'checking'
|
status: 'checking'
|
||||||
});
|
});
|
||||||
|
|
||||||
// console.log(`🔍 System "${system.name}" (${systemId}) is running check "${check.name}" (${checkId})`);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await this.runSingleCheck(checkId, check);
|
const result = await this.runSingleCheck(checkId, check);
|
||||||
systemResults.push(result);
|
systemResults.push(result);
|
||||||
|
|
||||||
if (result.status === 'failed') {
|
if (result.status === 'failed') {
|
||||||
// console.log(`❌ Check failed: ${check.name} for system ${system.name}`);
|
|
||||||
// console.log(`🔍 System critical: ${system.critical}`);
|
|
||||||
|
|
||||||
this.errors.push({
|
this.errors.push({
|
||||||
system: system.name,
|
system: system.name,
|
||||||
@ -921,8 +835,6 @@ class SystemLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.log(`❌ Exception in check: ${check.name} for system ${system.name}:`, error.message);
|
|
||||||
// console.log(`🔍 System critical: ${system.critical}`);
|
|
||||||
|
|
||||||
systemResults.push({
|
systemResults.push({
|
||||||
id: checkId,
|
id: checkId,
|
||||||
@ -989,7 +901,6 @@ class SystemLoader {
|
|||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// console.log(`🔍 Running health check: ${check.name} (${checkId})`);
|
|
||||||
|
|
||||||
const result = await Promise.race([
|
const result = await Promise.race([
|
||||||
Promise.resolve(check.check()),
|
Promise.resolve(check.check()),
|
||||||
@ -1001,7 +912,6 @@ class SystemLoader {
|
|||||||
const duration = Date.now() - startTime;
|
const duration = Date.now() - startTime;
|
||||||
const passed = Boolean(result);
|
const passed = Boolean(result);
|
||||||
|
|
||||||
// console.log(`✅ Health check ${check.name}: ${passed ? 'PASSED' : 'FAILED'} (${duration}ms)`);
|
|
||||||
|
|
||||||
this.completedChecks += check.weight;
|
this.completedChecks += check.weight;
|
||||||
this.progress = Math.min((this.completedChecks / this.totalChecks) * 100, 100);
|
this.progress = Math.min((this.completedChecks / this.totalChecks) * 100, 100);
|
||||||
@ -1082,7 +992,6 @@ class SystemLoader {
|
|||||||
|
|
||||||
// Check required config files
|
// Check required config files
|
||||||
async checkConfigFiles() {
|
async checkConfigFiles() {
|
||||||
// console.log('🔍 Config Files Check: Starting validation');
|
|
||||||
|
|
||||||
// Critical files that MUST exist and not be empty
|
// Critical files that MUST exist and not be empty
|
||||||
const criticalFiles = [
|
const criticalFiles = [
|
||||||
@ -1099,7 +1008,6 @@ class SystemLoader {
|
|||||||
'/data/config/generated/configs.json'
|
'/data/config/generated/configs.json'
|
||||||
];
|
];
|
||||||
|
|
||||||
// console.log('🔍 Critical files to check:', criticalFiles);
|
|
||||||
|
|
||||||
// Optional files (can be missing but should exist if possible)
|
// Optional files (can be missing but should exist if possible)
|
||||||
const optionalFiles = [
|
const optionalFiles = [
|
||||||
@ -1117,34 +1025,26 @@ class SystemLoader {
|
|||||||
|
|
||||||
for (const file of allFiles) {
|
for (const file of allFiles) {
|
||||||
try {
|
try {
|
||||||
// console.log(`🔍 Checking file: ${file}`);
|
|
||||||
const response = await fetch(file, { method: 'HEAD' });
|
const response = await fetch(file, { method: 'HEAD' });
|
||||||
// console.log(`📊 Response status: ${response.status} ${response.statusText}`);
|
|
||||||
// console.log(`📊 Content-Length: ${response.headers.get('content-length')}`);
|
|
||||||
// console.log(`📊 Response ok: ${response.ok}`);
|
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
// Get content length to check if file is empty
|
// Get content length to check if file is empty
|
||||||
const contentLength = response.headers.get('content-length');
|
const contentLength = response.headers.get('content-length');
|
||||||
const isEmpty = contentLength === '0' || contentLength === null;
|
const isEmpty = contentLength === '0' || contentLength === null;
|
||||||
|
|
||||||
// console.log(`🔍 File ${file} - contentLength: ${contentLength}, isEmpty: ${isEmpty}`);
|
|
||||||
|
|
||||||
// For critical files, also check actual content
|
// For critical files, also check actual content
|
||||||
if (criticalFiles.includes(file) && !isEmpty) {
|
if (criticalFiles.includes(file) && !isEmpty) {
|
||||||
try {
|
try {
|
||||||
const getResponse = await fetch(file);
|
const getResponse = await fetch(file);
|
||||||
const content = await getResponse.text();
|
const content = await getResponse.text();
|
||||||
// console.log(`📄 Content preview for ${file}:`, content.substring(0, 100) + '...');
|
|
||||||
|
|
||||||
// Check if content starts with '{' or '[' (basic JSON file check)
|
// Check if content starts with '{' or '[' (basic JSON file check)
|
||||||
const startsWithBrace = content.trim().startsWith('{');
|
const startsWithBrace = content.trim().startsWith('{');
|
||||||
const startsWithBracket = content.trim().startsWith('[');
|
const startsWithBracket = content.trim().startsWith('[');
|
||||||
const isValidJSONStart = startsWithBrace || startsWithBracket;
|
const isValidJSONStart = startsWithBrace || startsWithBracket;
|
||||||
// console.log(`🔍 JSON structure check for ${file}: starts with '{' or '['? ${isValidJSONStart} (content: ${content.trim().substring(0, 10)}...)`);
|
|
||||||
|
|
||||||
if (!isValidJSONStart) {
|
if (!isValidJSONStart) {
|
||||||
// console.log(`❌ Critical file does not start with '{' or '[' (not valid JSON): ${file}`);
|
|
||||||
results.criticalEmpty.push(file);
|
results.criticalEmpty.push(file);
|
||||||
results.empty.push(file);
|
results.empty.push(file);
|
||||||
}
|
}
|
||||||
@ -1152,7 +1052,6 @@ class SystemLoader {
|
|||||||
else if (content.includes('404 Not Found') || content.includes('404') && content.includes('Not Found') ||
|
else if (content.includes('404 Not Found') || content.includes('404') && content.includes('Not Found') ||
|
||||||
(content.includes('Error') && (content.includes('<!DOCTYPE') || content.includes('<html>') || content.includes('status'))) ||
|
(content.includes('Error') && (content.includes('<!DOCTYPE') || content.includes('<html>') || content.includes('status'))) ||
|
||||||
content.includes('<!DOCTYPE') || content.includes('<html>')) {
|
content.includes('<!DOCTYPE') || content.includes('<html>')) {
|
||||||
// console.log(`❌ Critical file contains error/fallback content: ${file}`);
|
|
||||||
results.criticalEmpty.push(file);
|
results.criticalEmpty.push(file);
|
||||||
results.empty.push(file);
|
results.empty.push(file);
|
||||||
}
|
}
|
||||||
@ -1160,17 +1059,13 @@ class SystemLoader {
|
|||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
JSON.parse(content);
|
JSON.parse(content);
|
||||||
// console.log(`✅ JSON syntax valid for: ${file}`);
|
|
||||||
results.available.push(file);
|
results.available.push(file);
|
||||||
// console.log(`✅ File is available and valid: ${file}`);
|
|
||||||
} catch (jsonError) {
|
} catch (jsonError) {
|
||||||
// console.log(`❌ Critical file has invalid JSON syntax: ${file} - ${jsonError.message}`);
|
|
||||||
results.criticalEmpty.push(file);
|
results.criticalEmpty.push(file);
|
||||||
results.empty.push(file);
|
results.empty.push(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (contentError) {
|
} catch (contentError) {
|
||||||
// console.log(`❌ Failed to read content for ${file}:`, contentError.message);
|
|
||||||
results.criticalEmpty.push(file);
|
results.criticalEmpty.push(file);
|
||||||
results.empty.push(file);
|
results.empty.push(file);
|
||||||
}
|
}
|
||||||
@ -1178,39 +1073,24 @@ class SystemLoader {
|
|||||||
results.empty.push(file);
|
results.empty.push(file);
|
||||||
if (criticalFiles.includes(file)) {
|
if (criticalFiles.includes(file)) {
|
||||||
results.criticalEmpty.push(file);
|
results.criticalEmpty.push(file);
|
||||||
// console.log(`❌ Critical file is EMPTY: ${file}`);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
results.available.push(file);
|
results.available.push(file);
|
||||||
// console.log(`✅ File is available: ${file}`);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
results.missing.push(file);
|
results.missing.push(file);
|
||||||
if (criticalFiles.includes(file)) {
|
if (criticalFiles.includes(file)) {
|
||||||
results.criticalMissing.push(file);
|
results.criticalMissing.push(file);
|
||||||
// console.log(`❌ Critical file is MISSING: ${file}`);
|
|
||||||
}
|
}
|
||||||
// console.log(`❌ File not accessible: ${file} - ${response.status} ${response.statusText}`);
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.log(`❌ Exception checking file ${file}:`, error.message);
|
|
||||||
results.missing.push(file);
|
results.missing.push(file);
|
||||||
if (criticalFiles.includes(file)) {
|
if (criticalFiles.includes(file)) {
|
||||||
results.criticalMissing.push(file);
|
results.criticalMissing.push(file);
|
||||||
// console.log(`❌ Critical file exception: ${file}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log('Config Files Results:', {
|
|
||||||
//available: results.available.length,
|
|
||||||
//missing: results.missing.length,
|
|
||||||
//empty: results.empty.length,
|
|
||||||
//criticalMissing: results.criticalMissing.length,
|
|
||||||
//criticalEmpty: results.criticalEmpty.length,
|
|
||||||
//missingFiles: results.missing,
|
|
||||||
//emptyFiles: results.empty
|
|
||||||
//});
|
|
||||||
|
|
||||||
// Fail if ANY critical files are missing or empty
|
// Fail if ANY critical files are missing or empty
|
||||||
if (results.criticalMissing.length > 0 || results.criticalEmpty.length > 0) {
|
if (results.criticalMissing.length > 0 || results.criticalEmpty.length > 0) {
|
||||||
@ -1226,19 +1106,16 @@ class SystemLoader {
|
|||||||
throw new Error(errorMessage);
|
throw new Error(errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log('✅ Config Files Check PASSED');
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for update lock file to prevent concurrent updates
|
// Check for update lock file to prevent concurrent updates
|
||||||
async checkUpdateLock() {
|
async checkUpdateLock() {
|
||||||
// console.log('🔍 Update Lock Check: Checking for update lock file');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/data/updater-lock', { method: 'HEAD' });
|
const response = await fetch('/data/updater-lock', { method: 'HEAD' });
|
||||||
const lockExists = response.ok;
|
const lockExists = response.ok;
|
||||||
|
|
||||||
// console.log(`🔒 Update lock file exists: ${lockExists}`);
|
|
||||||
|
|
||||||
if (lockExists) {
|
if (lockExists) {
|
||||||
return {
|
return {
|
||||||
@ -1262,13 +1139,11 @@ class SystemLoader {
|
|||||||
|
|
||||||
// Check for pause lock file to prevent operations during pause
|
// Check for pause lock file to prevent operations during pause
|
||||||
async checkPauseLock() {
|
async checkPauseLock() {
|
||||||
// console.log('🔍 Pause Lock Check: Checking for pause lock file');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/data/pause.lock', { method: 'HEAD' });
|
const response = await fetch('/data/pause.lock', { method: 'HEAD' });
|
||||||
const lockExists = response.ok;
|
const lockExists = response.ok;
|
||||||
|
|
||||||
// console.log(`🔒 Pause lock file exists: ${lockExists}`);
|
|
||||||
|
|
||||||
if (lockExists) {
|
if (lockExists) {
|
||||||
return {
|
return {
|
||||||
@ -1320,12 +1195,10 @@ class SystemLoader {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// Check if script is already loaded
|
// Check if script is already loaded
|
||||||
if (document.querySelector(`script[src="${src}"]`)) {
|
if (document.querySelector(`script[src="${src}"]`)) {
|
||||||
// console.log(`📦 Script already loaded: ${src}`);
|
|
||||||
resolve();
|
resolve();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(`📦 Loading script: ${src}`);
|
|
||||||
const script = document.createElement('script');
|
const script = document.createElement('script');
|
||||||
script.src = src;
|
script.src = src;
|
||||||
// Dynamically-inserted scripts default to async=true and run in whatever
|
// Dynamically-inserted scripts default to async=true and run in whatever
|
||||||
@ -1335,7 +1208,6 @@ class SystemLoader {
|
|||||||
// prototype). Mirrors the ordered kernel ctx.loadScripts() path.
|
// prototype). Mirrors the ordered kernel ctx.loadScripts() path.
|
||||||
script.async = false;
|
script.async = false;
|
||||||
script.onload = () => {
|
script.onload = () => {
|
||||||
// console.log(`✅ Script loaded: ${src}`);
|
|
||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
script.onerror = () => {
|
script.onerror = () => {
|
||||||
@ -1350,9 +1222,7 @@ class SystemLoader {
|
|||||||
async loadScripts(scripts) {
|
async loadScripts(scripts) {
|
||||||
if (!scripts || scripts.length === 0) return;
|
if (!scripts || scripts.length === 0) return;
|
||||||
|
|
||||||
// console.log(`📦 Loading ${scripts.length} scripts for component...`);
|
|
||||||
await Promise.all(scripts.map(script => this.loadScript(script)));
|
await Promise.all(scripts.map(script => this.loadScript(script)));
|
||||||
// console.log(`✅ All scripts loaded for component`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
// Main System Orchestrator - Ties together loading, setup, and initialization
|
// Main System Orchestrator - Ties together loading, setup, and initialization
|
||||||
class SystemOrchestrator {
|
class SystemOrchestrator {
|
||||||
constructor() {
|
constructor() {
|
||||||
// console.log('🏗️ SystemOrchestrator: Constructor called');
|
|
||||||
this.systemLoader = null;
|
this.systemLoader = null;
|
||||||
this.loadingUI = null;
|
this.loadingUI = null;
|
||||||
this.setupDetector = null;
|
this.setupDetector = null;
|
||||||
@ -11,14 +10,11 @@ class SystemOrchestrator {
|
|||||||
|
|
||||||
// Initialize the entire system
|
// Initialize the entire system
|
||||||
async initialize() {
|
async initialize() {
|
||||||
// console.log('🚀 SystemOrchestrator: initialize() called');
|
|
||||||
|
|
||||||
if (this.isInitialized) {
|
if (this.isInitialized) {
|
||||||
// console.log('🔄 SystemOrchestrator: Already initialized, returning existing promise');
|
|
||||||
return this.loadingPromise;
|
return this.loadingPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log('🆕 SystemOrchestrator: Starting initialization...');
|
|
||||||
try {
|
try {
|
||||||
// Auth gate — must complete before anything else loads or fetches data
|
// Auth gate — must complete before anything else loads or fetches data
|
||||||
if (window.authManager) {
|
if (window.authManager) {
|
||||||
@ -26,20 +22,15 @@ class SystemOrchestrator {
|
|||||||
window.authManager.interceptFetch();
|
window.authManager.interceptFetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log('🚀 System Orchestrator: Initializing...');
|
|
||||||
|
|
||||||
// Initialize components
|
// Initialize components
|
||||||
// console.log('🔧 SystemOrchestrator: Creating SystemLoader...');
|
|
||||||
this.systemLoader = new SystemLoader();
|
this.systemLoader = new SystemLoader();
|
||||||
|
|
||||||
// console.log('🎨 SystemOrchestrator: Creating LoadingUI...');
|
|
||||||
this.loadingUI = new LoadingUI();
|
this.loadingUI = new LoadingUI();
|
||||||
|
|
||||||
// console.log('🔍 SystemOrchestrator: Creating SetupDetector...');
|
|
||||||
this.setupDetector = new SetupDetector();
|
this.setupDetector = new SetupDetector();
|
||||||
|
|
||||||
// Show loading screen
|
// Show loading screen
|
||||||
// console.log('📺 SystemOrchestrator: Showing loading screen...');
|
|
||||||
this.loadingUI.initialize();
|
this.loadingUI.initialize();
|
||||||
|
|
||||||
// Setup event listeners
|
// Setup event listeners
|
||||||
@ -54,18 +45,15 @@ class SystemOrchestrator {
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
if (setupStatus.isFirstTime && !hasActiveHandoff) {
|
if (setupStatus.isFirstTime && !hasActiveHandoff) {
|
||||||
// console.log('🔧 First-time setup detected');
|
|
||||||
await this.handleFirstTimeSetup();
|
await this.handleFirstTimeSetup();
|
||||||
} else if (setupStatus.isFirstTime && hasActiveHandoff) {
|
} else if (setupStatus.isFirstTime && hasActiveHandoff) {
|
||||||
console.log('[orchestrator] setup handoff in progress, skipping wizard');
|
console.log('[orchestrator] setup handoff in progress, skipping wizard');
|
||||||
await this.handleNormalLoading();
|
await this.handleNormalLoading();
|
||||||
} else {
|
} else {
|
||||||
// console.log('✅ Returning user detected, proceeding with normal loading');
|
|
||||||
await this.handleNormalLoading();
|
await this.handleNormalLoading();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isInitialized = true;
|
this.isInitialized = true;
|
||||||
// console.log('✅ System Orchestrator: Initialization complete');
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ System Orchestrator: Initialization failed:', error);
|
console.error('❌ System Orchestrator: Initialization failed:', error);
|
||||||
@ -97,7 +85,6 @@ class SystemOrchestrator {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.systemLoader.on('onComplete', (data) => {
|
this.systemLoader.on('onComplete', (data) => {
|
||||||
// console.log('✅ System Loader completed:', data.success ? 'SUCCESS' : 'FAILED');
|
|
||||||
|
|
||||||
// Don't show errors here - let handleNormalLoading handle all error display logic
|
// Don't show errors here - let handleNormalLoading handle all error display logic
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
@ -118,7 +105,6 @@ class SystemOrchestrator {
|
|||||||
|
|
||||||
// Handle first-time setup
|
// Handle first-time setup
|
||||||
async handleFirstTimeSetup() {
|
async handleFirstTimeSetup() {
|
||||||
// console.log('🎯 Starting first-time setup flow...');
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// Create and show setup wizard
|
// Create and show setup wizard
|
||||||
@ -139,7 +125,6 @@ class SystemOrchestrator {
|
|||||||
|
|
||||||
// Handle normal loading sequence
|
// Handle normal loading sequence
|
||||||
async handleNormalLoading() {
|
async handleNormalLoading() {
|
||||||
// console.log('📊 Starting normal loading sequence...');
|
|
||||||
|
|
||||||
// Wait longer for all scripts to fully load
|
// Wait longer for all scripts to fully load
|
||||||
await new Promise(resolve => setTimeout(resolve, 2500));
|
await new Promise(resolve => setTimeout(resolve, 2500));
|
||||||
@ -156,7 +141,6 @@ class SystemOrchestrator {
|
|||||||
|
|
||||||
if (missingDataFiles.length > 0) {
|
if (missingDataFiles.length > 0) {
|
||||||
console.warn('⚠️ Missing LibrePortal data files detected');
|
console.warn('⚠️ Missing LibrePortal data files detected');
|
||||||
// console.log('🔍 Showing enhanced error for missing data files');
|
|
||||||
|
|
||||||
// Show enhanced error message with setup instructions
|
// Show enhanced error message with setup instructions
|
||||||
const enhancedErrors = results.errors.map(error => {
|
const enhancedErrors = results.errors.map(error => {
|
||||||
@ -178,21 +162,16 @@ class SystemOrchestrator {
|
|||||||
|
|
||||||
// Listen for continue button click
|
// Listen for continue button click
|
||||||
window.addEventListener('loadingContinue', () => {
|
window.addEventListener('loadingContinue', () => {
|
||||||
// console.log('🔄 User chose to continue despite errors');
|
|
||||||
this.proceedToApplicationWithErrors();
|
this.proceedToApplicationWithErrors();
|
||||||
}, { once: true }); // Remove listener after first use
|
}, { once: true }); // Remove listener after first use
|
||||||
this.errorAlreadyHandled = true;
|
this.errorAlreadyHandled = true;
|
||||||
// console.log('🔍 Set errorAlreadyHandled = true, returning early');
|
|
||||||
return; // Prevent reaching the general error handling below
|
return; // Prevent reaching the general error handling below
|
||||||
}
|
}
|
||||||
|
|
||||||
if (results.success) {
|
if (results.success) {
|
||||||
// console.log('✅ All systems passed health checks');
|
|
||||||
|
|
||||||
// Initialize all components (even if some health checks failed)
|
// Initialize all components (even if some health checks failed)
|
||||||
// console.log('🚨 BREAKPOINT: About to call initializeComponents()');
|
|
||||||
await this.systemLoader.initializeComponents();
|
await this.systemLoader.initializeComponents();
|
||||||
// console.log('🚨 BREAKPOINT: initializeComponents() completed');
|
|
||||||
|
|
||||||
// Hide loading screen only if everything is successful
|
// Hide loading screen only if everything is successful
|
||||||
this.loadingUI.hide();
|
this.loadingUI.hide();
|
||||||
@ -201,21 +180,17 @@ class SystemOrchestrator {
|
|||||||
this.initializeOriginalSPA();
|
this.initializeOriginalSPA();
|
||||||
this.proceedToApplication();
|
this.proceedToApplication();
|
||||||
} else {
|
} else {
|
||||||
// console.log('🔍 General error handling - errorAlreadyHandled:', this.errorAlreadyHandled);
|
|
||||||
console.error('❌ Critical systems failed:', results.errors);
|
console.error('❌ Critical systems failed:', results.errors);
|
||||||
console.error('❌ Failed systems details:', JSON.stringify(results.errors, null, 2));
|
console.error('❌ Failed systems details:', JSON.stringify(results.errors, null, 2));
|
||||||
|
|
||||||
// Show errors in loading UI and keep it visible (only if not already handled)
|
// Show errors in loading UI and keep it visible (only if not already handled)
|
||||||
if (!this.errorAlreadyHandled) {
|
if (!this.errorAlreadyHandled) {
|
||||||
// console.log('🔍 Showing general error (not already handled)');
|
|
||||||
this.loadingUI.showError(results.errors);
|
this.loadingUI.showError(results.errors);
|
||||||
} else {
|
} else {
|
||||||
// console.log('🔍 Skipping general error display (already handled)');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen for continue button click
|
// Listen for continue button click
|
||||||
window.addEventListener('loadingContinue', () => {
|
window.addEventListener('loadingContinue', () => {
|
||||||
// console.log('🔄 User chose to continue despite errors');
|
|
||||||
this.proceedToApplicationWithErrors();
|
this.proceedToApplicationWithErrors();
|
||||||
}, { once: true }); // Remove listener after first use
|
}, { once: true }); // Remove listener after first use
|
||||||
}
|
}
|
||||||
@ -223,7 +198,6 @@ class SystemOrchestrator {
|
|||||||
|
|
||||||
// Proceed to main application
|
// Proceed to main application
|
||||||
proceedToApplication() {
|
proceedToApplication() {
|
||||||
// console.log('🎯 Proceeding to main application...');
|
|
||||||
|
|
||||||
// Don't initialize SPA here - wait for component initialization to complete
|
// Don't initialize SPA here - wait for component initialization to complete
|
||||||
// SPA will be initialized after all components are ready
|
// SPA will be initialized after all components are ready
|
||||||
@ -231,12 +205,9 @@ class SystemOrchestrator {
|
|||||||
|
|
||||||
// Proceed with application despite errors (user choice)
|
// Proceed with application despite errors (user choice)
|
||||||
async proceedToApplicationWithErrors() {
|
async proceedToApplicationWithErrors() {
|
||||||
// console.log('🎯 Proceeding with application despite errors...');
|
|
||||||
|
|
||||||
// Initialize components even though some checks failed
|
// Initialize components even though some checks failed
|
||||||
// console.log('🚨 BREAKPOINT: About to call initializeComponents() with errors');
|
|
||||||
await this.systemLoader.initializeComponents();
|
await this.systemLoader.initializeComponents();
|
||||||
// console.log('🚨 BREAKPOINT: initializeComponents() completed with errors');
|
|
||||||
|
|
||||||
// Hide loading screen and proceed
|
// Hide loading screen and proceed
|
||||||
this.loadingUI.hide();
|
this.loadingUI.hide();
|
||||||
@ -249,25 +220,19 @@ class SystemOrchestrator {
|
|||||||
|
|
||||||
// Initialize the original SPA system
|
// Initialize the original SPA system
|
||||||
initializeOriginalSPA() {
|
initializeOriginalSPA() {
|
||||||
// console.log('🔄 Initializing original SPA...');
|
|
||||||
|
|
||||||
if (typeof window.spaClean !== 'undefined') {
|
if (typeof window.spaClean !== 'undefined') {
|
||||||
// console.log('✅ SPA instance already initialized by SystemLoader');
|
|
||||||
|
|
||||||
// Now manually call the SPA's init method since we prevented auto-initialization
|
// Now manually call the SPA's init method since we prevented auto-initialization
|
||||||
if (typeof window.spaClean.init === 'function') {
|
if (typeof window.spaClean.init === 'function') {
|
||||||
// console.log('🔄 Manually calling SPA init()...');
|
|
||||||
window.spaClean.init().then(() => {
|
window.spaClean.init().then(() => {
|
||||||
// console.log('✅ SPA initialization completed');
|
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error('❌ SPA initialization failed:', error);
|
console.error('❌ SPA initialization failed:', error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (typeof LibrePortalSPAClean !== 'undefined') {
|
} else if (typeof LibrePortalSPAClean !== 'undefined') {
|
||||||
// console.log('🔄 Creating SPA instance manually...');
|
|
||||||
window.spaClean = new LibrePortalSPAClean();
|
window.spaClean = new LibrePortalSPAClean();
|
||||||
// console.log('✅ SPA instance created');
|
|
||||||
} else {
|
} else {
|
||||||
console.error('❌ SPA class not available');
|
console.error('❌ SPA class not available');
|
||||||
return;
|
return;
|
||||||
@ -294,7 +259,6 @@ class SystemOrchestrator {
|
|||||||
|
|
||||||
// Retry initialization
|
// Retry initialization
|
||||||
async retryInitialization() {
|
async retryInitialization() {
|
||||||
// console.log('🔄 Retrying initialization...');
|
|
||||||
|
|
||||||
// Reset UI
|
// Reset UI
|
||||||
this.loadingUI.reset();
|
this.loadingUI.reset();
|
||||||
@ -347,7 +311,6 @@ class SystemOrchestrator {
|
|||||||
|
|
||||||
// Force re-initialization (for testing)
|
// Force re-initialization (for testing)
|
||||||
async forceReinitialize() {
|
async forceReinitialize() {
|
||||||
// console.log('🔄 Force re-initializing...');
|
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
if (this.loadingUI && this.loadingUI.isVisible) {
|
if (this.loadingUI && this.loadingUI.isVisible) {
|
||||||
@ -368,15 +331,12 @@ window.SystemOrchestrator = SystemOrchestrator;
|
|||||||
|
|
||||||
// Auto-initialize when DOM is ready
|
// Auto-initialize when DOM is ready
|
||||||
if (document.readyState === 'loading') {
|
if (document.readyState === 'loading') {
|
||||||
// console.log('🔄 SystemOrchestrator: DOM not ready, adding listener');
|
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
// console.log('🚀 SystemOrchestrator: DOM ready, initializing...');
|
|
||||||
window.systemOrchestrator = new SystemOrchestrator();
|
window.systemOrchestrator = new SystemOrchestrator();
|
||||||
await window.systemOrchestrator.initialize();
|
await window.systemOrchestrator.initialize();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// DOM already ready
|
// DOM already ready
|
||||||
// console.log('🚀 SystemOrchestrator: DOM already ready, initializing immediately...');
|
|
||||||
(async () => {
|
(async () => {
|
||||||
window.systemOrchestrator = new SystemOrchestrator();
|
window.systemOrchestrator = new SystemOrchestrator();
|
||||||
await window.systemOrchestrator.initialize();
|
await window.systemOrchestrator.initialize();
|
||||||
|
|||||||
@ -93,7 +93,6 @@ class ConfigShared {
|
|||||||
|
|
||||||
// Auto-detect and create appropriate field type
|
// Auto-detect and create appropriate field type
|
||||||
static createSmartField(fieldId, key, value, title, description, options = {}) {
|
static createSmartField(fieldId, key, value, title, description, options = {}) {
|
||||||
//console.log(`createSmartField: key=${key}, value=${value}, config=${!!options.config}`);
|
|
||||||
|
|
||||||
// Check if value is boolean (true/false strings)
|
// Check if value is boolean (true/false strings)
|
||||||
const isBoolean = value === 'true' || value === 'false';
|
const isBoolean = value === 'true' || value === 'false';
|
||||||
@ -115,7 +114,6 @@ class ConfigShared {
|
|||||||
|
|
||||||
// Handle standard toggle changes
|
// Handle standard toggle changes
|
||||||
static handleToggleChange(checkbox, key, category) {
|
static handleToggleChange(checkbox, key, category) {
|
||||||
//console.log(`Toggle changed: ${key} = ${checkbox.checked} (category: ${category})`);
|
|
||||||
|
|
||||||
// Trigger custom event for other components to listen to
|
// Trigger custom event for other components to listen to
|
||||||
const event = new CustomEvent('configToggleChanged', {
|
const event = new CustomEvent('configToggleChanged', {
|
||||||
@ -131,7 +129,6 @@ class ConfigShared {
|
|||||||
|
|
||||||
// Handle master toggle changes (enables/disables multiple fields)
|
// Handle master toggle changes (enables/disables multiple fields)
|
||||||
static handleMasterToggle(checkbox, sectionId, fieldIds) {
|
static handleMasterToggle(checkbox, sectionId, fieldIds) {
|
||||||
//console.log(`Master toggle changed: ${sectionId} = ${checkbox.checked}`);
|
|
||||||
|
|
||||||
// Enable/disable related fields
|
// Enable/disable related fields
|
||||||
if (fieldIds && fieldIds.length > 0) {
|
if (fieldIds && fieldIds.length > 0) {
|
||||||
@ -161,7 +158,6 @@ class ConfigShared {
|
|||||||
|
|
||||||
// Handle section toggle changes (shows/hides sections)
|
// Handle section toggle changes (shows/hides sections)
|
||||||
static handleSectionToggle(checkbox, sectionId) {
|
static handleSectionToggle(checkbox, sectionId) {
|
||||||
//console.log(`Section toggle changed: ${sectionId} = ${checkbox.checked}`);
|
|
||||||
|
|
||||||
const content = document.getElementById(`${sectionId}-content`);
|
const content = document.getElementById(`${sectionId}-content`);
|
||||||
if (content) {
|
if (content) {
|
||||||
@ -179,7 +175,6 @@ class ConfigShared {
|
|||||||
static async saveToggleValue(key, value) {
|
static async saveToggleValue(key, value) {
|
||||||
try {
|
try {
|
||||||
// For now, just log the change - local implementation
|
// For now, just log the change - local implementation
|
||||||
//console.log('Toggle value changed:', key, value ? 'true' : 'false');
|
|
||||||
|
|
||||||
// TODO: Implement local config file update
|
// TODO: Implement local config file update
|
||||||
// This would require backend integration to write to actual config files
|
// This would require backend integration to write to actual config files
|
||||||
@ -355,7 +350,6 @@ class ConfigShared {
|
|||||||
const crontabValue = `${minute} ${cronHour} * * *`;
|
const crontabValue = `${minute} ${cronHour} * * *`;
|
||||||
hiddenInput.value = crontabValue;
|
hiddenInput.value = crontabValue;
|
||||||
|
|
||||||
//console.log(`Updated crontab for ${key}: ${crontabValue}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle password visibility
|
// Toggle password visibility
|
||||||
@ -530,7 +524,6 @@ class ConfigShared {
|
|||||||
|
|
||||||
// Generate appropriate field based on value type and key
|
// Generate appropriate field based on value type and key
|
||||||
static generateField(fieldId, key, value, title, description, options = {}, allConfig = {}) {
|
static generateField(fieldId, key, value, title, description, options = {}, allConfig = {}) {
|
||||||
//console.log(`generateField: key=${key}, value=${value}, CFG_INSTALL_MODE=${allConfig.CFG_INSTALL_MODE?.value}`);
|
|
||||||
|
|
||||||
// Note: Git fields (CFG_GIT_*) are now handled by the toggle system in renderGitSection
|
// Note: Git fields (CFG_GIT_*) are now handled by the toggle system in renderGitSection
|
||||||
// They don't need to be hidden here since the section itself is toggled
|
// They don't need to be hidden here since the section itself is toggled
|
||||||
@ -730,14 +723,11 @@ class ConfigShared {
|
|||||||
} else if (key.includes('TIMEZONE')) {
|
} else if (key.includes('TIMEZONE')) {
|
||||||
// Special handling for Timezone - create comprehensive timezone dropdown
|
// Special handling for Timezone - create comprehensive timezone dropdown
|
||||||
const timezoneOptions = ConfigOptions.getTimezoneOptions();
|
const timezoneOptions = ConfigOptions.getTimezoneOptions();
|
||||||
//console.log('Timezone key:', key, 'Current value:', value, 'Type:', typeof value);
|
|
||||||
//console.log('Available timezone options:', timezoneOptions.map(opt => ({value: opt.value, label: opt.label})));
|
|
||||||
fieldHTML += `
|
fieldHTML += `
|
||||||
<select id="${fieldId}" name="${key}" class="form-control">
|
<select id="${fieldId}" name="${key}" class="form-control">
|
||||||
${timezoneOptions.map(opt => `<option value="${opt.value}" ${opt.value === value ? 'selected' : ''}>${opt.label}</option>`).join('')}
|
${timezoneOptions.map(opt => `<option value="${opt.value}" ${opt.value === value ? 'selected' : ''}>${opt.label}</option>`).join('')}
|
||||||
</select>
|
</select>
|
||||||
`;
|
`;
|
||||||
//console.log('Generated timezone dropdown HTML for', key, 'with value', value);
|
|
||||||
} else if (key === 'CFG_INSTALL_MODE') {
|
} else if (key === 'CFG_INSTALL_MODE') {
|
||||||
const selectOptions = ConfigOptions.getSelectOptions(key);
|
const selectOptions = ConfigOptions.getSelectOptions(key);
|
||||||
fieldHTML += `<select id="${fieldId}" name="${key}" class="form-control" onchange="handleInstallModeChange(this)">${selectOptions.map(opt => `<option value="${opt.value}" ${opt.value === value ? 'selected' : ''}>${opt.label}</option>`).join('')}</select>`;
|
fieldHTML += `<select id="${fieldId}" name="${key}" class="form-control" onchange="handleInstallModeChange(this)">${selectOptions.map(opt => `<option value="${opt.value}" ${opt.value === value ? 'selected' : ''}>${opt.label}</option>`).join('')}</select>`;
|
||||||
@ -745,25 +735,17 @@ class ConfigShared {
|
|||||||
const selectOptions = ConfigOptions.getSelectOptions(key);
|
const selectOptions = ConfigOptions.getSelectOptions(key);
|
||||||
fieldHTML += `<select id="${fieldId}" name="${key}" class="form-control">${selectOptions.map(opt => `<option value="${opt.value}" ${opt.value === value ? 'selected' : ''}>${opt.label}</option>`).join('')}</select>`;
|
fieldHTML += `<select id="${fieldId}" name="${key}" class="form-control">${selectOptions.map(opt => `<option value="${opt.value}" ${opt.value === value ? 'selected' : ''}>${opt.label}</option>`).join('')}</select>`;
|
||||||
} else if (ConfigOptions.isDropdownKey(key) || (options && Object.keys(options).length > 0)) {
|
} else if (ConfigOptions.isDropdownKey(key) || (options && Object.keys(options).length > 0)) {
|
||||||
//console.log('=== GENERIC DROPDOWN BLOCK ENTERED for key:', key);
|
|
||||||
//console.log('Dropdown detected for key:', key);
|
|
||||||
//console.log('isDropdownKey result:', ConfigOptions.isDropdownKey(key));
|
|
||||||
//console.log('options available:', options);
|
|
||||||
const selectOptions = (options && typeof options === 'string') ? this.parseOptions(options) : ConfigOptions.getSelectOptions(key);
|
const selectOptions = (options && typeof options === 'string') ? this.parseOptions(options) : ConfigOptions.getSelectOptions(key);
|
||||||
//console.log('selectOptions:', selectOptions);
|
|
||||||
fieldHTML += `
|
fieldHTML += `
|
||||||
<select id="${fieldId}" name="${key}" class="form-control">
|
<select id="${fieldId}" name="${key}" class="form-control">
|
||||||
${selectOptions.map(opt => `<option value="${opt.value}" ${opt.value === value ? 'selected' : ''}>${opt.label}</option>`).join('')}
|
${selectOptions.map(opt => `<option value="${opt.value}" ${opt.value === value ? 'selected' : ''}>${opt.label}</option>`).join('')}
|
||||||
</select>
|
</select>
|
||||||
`;
|
`;
|
||||||
//console.log('Generated dropdown for', key, 'with value', value);
|
|
||||||
} else if (key.includes('DESCRIPTION') || key.includes('COMMENTS') || key.includes('NOTES')) {
|
} else if (key.includes('DESCRIPTION') || key.includes('COMMENTS') || key.includes('NOTES')) {
|
||||||
//console.log('Textarea detected for key:', key);
|
|
||||||
fieldHTML += `
|
fieldHTML += `
|
||||||
<textarea id="${fieldId}" name="${key}" class="form-control" rows="4">${value}</textarea>
|
<textarea id="${fieldId}" name="${key}" class="form-control" rows="4">${value}</textarea>
|
||||||
`;
|
`;
|
||||||
} else {
|
} else {
|
||||||
//console.log('Default text input for key:', key);
|
|
||||||
// Default text input with event handlers and options
|
// Default text input with event handlers and options
|
||||||
const inputClass = className ? `form-control ${className}` : 'form-control';
|
const inputClass = className ? `form-control ${className}` : 'form-control';
|
||||||
const inputPlaceholder = placeholder || '';
|
const inputPlaceholder = placeholder || '';
|
||||||
@ -832,7 +814,6 @@ class ConfigShared {
|
|||||||
fieldGroup.style.pointerEvents = 'auto';
|
fieldGroup.style.pointerEvents = 'auto';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
//console.log(`Section ${sectionId} enabled`);
|
|
||||||
} else {
|
} else {
|
||||||
// Disable section
|
// Disable section
|
||||||
sectionContent.classList.add('disabled');
|
sectionContent.classList.add('disabled');
|
||||||
@ -844,7 +825,6 @@ class ConfigShared {
|
|||||||
fieldGroup.style.pointerEvents = 'none';
|
fieldGroup.style.pointerEvents = 'none';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
//console.log(`Section ${sectionId} disabled`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -860,11 +840,9 @@ class ConfigShared {
|
|||||||
if (isEnabled) {
|
if (isEnabled) {
|
||||||
// Show section
|
// Show section
|
||||||
sectionContent.classList.remove('hidden');
|
sectionContent.classList.remove('hidden');
|
||||||
//console.log(`Section ${sectionId} shown`);
|
|
||||||
} else {
|
} else {
|
||||||
// Hide section
|
// Hide section
|
||||||
sectionContent.classList.add('hidden');
|
sectionContent.classList.add('hidden');
|
||||||
//console.log(`Section ${sectionId} hidden`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -931,22 +909,15 @@ class ConfigShared {
|
|||||||
|
|
||||||
// Universal toggle function for all _ENABLED options
|
// Universal toggle function for all _ENABLED options
|
||||||
static toggleSection(sectionId, isEnabled) {
|
static toggleSection(sectionId, isEnabled) {
|
||||||
//console.log('=== UNIVERSAL TOGGLE DEBUG ===');
|
|
||||||
//console.log('sectionId:', sectionId);
|
|
||||||
//console.log('isEnabled:', isEnabled);
|
|
||||||
|
|
||||||
const sectionContent = document.getElementById(sectionId);
|
const sectionContent = document.getElementById(sectionId);
|
||||||
const fields = sectionContent?.querySelectorAll('.config-fields input, .config-fields select, .config-fields textarea');
|
const fields = sectionContent?.querySelectorAll('.config-fields input, .config-fields select, .config-fields textarea');
|
||||||
|
|
||||||
//console.log('sectionContent found:', !!sectionContent);
|
|
||||||
//console.log('fields found:', fields ? fields.length : 0);
|
|
||||||
|
|
||||||
if (sectionContent && fields) {
|
if (sectionContent && fields) {
|
||||||
if (isEnabled) {
|
if (isEnabled) {
|
||||||
//console.log('Enabling section...');
|
|
||||||
sectionContent.classList.remove('hidden');
|
sectionContent.classList.remove('hidden');
|
||||||
fields.forEach((field, index) => {
|
fields.forEach((field, index) => {
|
||||||
//console.log(`Enabling field ${index}:`, field);
|
|
||||||
field.disabled = false;
|
field.disabled = false;
|
||||||
const fieldGroup = field?.closest('.field-group');
|
const fieldGroup = field?.closest('.field-group');
|
||||||
if (fieldGroup) {
|
if (fieldGroup) {
|
||||||
@ -955,10 +926,8 @@ class ConfigShared {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
//console.log('Disabling section...');
|
|
||||||
sectionContent.classList.add('hidden');
|
sectionContent.classList.add('hidden');
|
||||||
fields.forEach((field, index) => {
|
fields.forEach((field, index) => {
|
||||||
//console.log(`Disabling field ${index}:`, field);
|
|
||||||
field.disabled = true;
|
field.disabled = true;
|
||||||
const fieldGroup = field?.closest('.field-group');
|
const fieldGroup = field?.closest('.field-group');
|
||||||
if (fieldGroup) {
|
if (fieldGroup) {
|
||||||
@ -969,51 +938,34 @@ class ConfigShared {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log('=== UNIVERSAL TOGGLE DEBUG END ===');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remote backup section toggle function
|
// Remote backup section toggle function
|
||||||
static toggleMailSection(sectionId, isEnabled) {
|
static toggleMailSection(sectionId, isEnabled) {
|
||||||
//console.log('=== TOGGLE MAIL SECTION DEBUG ===');
|
|
||||||
//console.log('sectionId:', sectionId);
|
|
||||||
//console.log('isEnabled:', isEnabled);
|
|
||||||
|
|
||||||
alert('toggleMailSection called: ' + sectionId + ', enabled: ' + isEnabled);
|
alert('toggleMailSection called: ' + sectionId + ', enabled: ' + isEnabled);
|
||||||
|
|
||||||
//console.log('Looking for sectionContent...');
|
|
||||||
const sectionContent = document.getElementById(sectionId);
|
const sectionContent = document.getElementById(sectionId);
|
||||||
//console.log('sectionContent found:', !!sectionContent);
|
|
||||||
|
|
||||||
//console.log('Looking for mailFields...');
|
|
||||||
const mailFields = sectionContent?.querySelectorAll('.config-fields input, .config-fields select, .config-fields textarea');
|
const mailFields = sectionContent?.querySelectorAll('.config-fields input, .config-fields select, .config-fields textarea');
|
||||||
//console.log('mailFields found:', mailFields ? mailFields.length : 0);
|
|
||||||
|
|
||||||
if (sectionContent && mailFields) {
|
if (sectionContent && mailFields) {
|
||||||
//console.log('Enabling mail section...');
|
|
||||||
if (isEnabled) {
|
if (isEnabled) {
|
||||||
//console.log('Removing hidden class...');
|
|
||||||
sectionContent.classList.remove('hidden');
|
sectionContent.classList.remove('hidden');
|
||||||
//console.log('Enabling fields...');
|
|
||||||
mailFields.forEach((field, index) => {
|
mailFields.forEach((field, index) => {
|
||||||
//console.log(`Disabling field ${index}:`, field);
|
|
||||||
field.disabled = false;
|
field.disabled = false;
|
||||||
const fieldGroup = field?.closest('.field-group');
|
const fieldGroup = field?.closest('.field-group');
|
||||||
if (fieldGroup) {
|
if (fieldGroup) {
|
||||||
//console.log('Enabling field group...');
|
|
||||||
fieldGroup.style.opacity = '1';
|
fieldGroup.style.opacity = '1';
|
||||||
fieldGroup.style.pointerEvents = 'auto';
|
fieldGroup.style.pointerEvents = 'auto';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
//console.log('Disabling mail section...');
|
|
||||||
sectionContent.classList.add('hidden');
|
sectionContent.classList.add('hidden');
|
||||||
//console.log('Disabling fields...');
|
|
||||||
mailFields.forEach((field, index) => {
|
mailFields.forEach((field, index) => {
|
||||||
//console.log(`Enabling field ${index}:`, field);
|
|
||||||
field.disabled = true;
|
field.disabled = true;
|
||||||
const fieldGroup = field?.closest('.field-group');
|
const fieldGroup = field?.closest('.field-group');
|
||||||
if (fieldGroup) {
|
if (fieldGroup) {
|
||||||
//console.log('Disabling field group...');
|
|
||||||
fieldGroup.style.opacity = '0.5';
|
fieldGroup.style.opacity = '0.5';
|
||||||
fieldGroup.style.pointerEvents = 'none';
|
fieldGroup.style.pointerEvents = 'none';
|
||||||
}
|
}
|
||||||
@ -1021,7 +973,6 @@ class ConfigShared {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log('=== TOGGLE MAIL SECTION DEBUG END ===');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static toggleRemoteBackupSection(sectionId, isEnabled) {
|
static toggleRemoteBackupSection(sectionId, isEnabled) {
|
||||||
@ -1488,7 +1439,6 @@ window.ConfigShared = ConfigShared;
|
|||||||
|
|
||||||
// Global toggle change function for checkbox handling
|
// Global toggle change function for checkbox handling
|
||||||
window.handleToggleChange = function(checkbox, key) {
|
window.handleToggleChange = function(checkbox, key) {
|
||||||
//console.log(`Toggle changed: ${key} = ${checkbox.checked}`);
|
|
||||||
// This function can be extended to handle specific toggle logic
|
// This function can be extended to handle specific toggle logic
|
||||||
// For now, it just logs change
|
// For now, it just logs change
|
||||||
};
|
};
|
||||||
@ -1500,12 +1450,10 @@ window.isReloadingConfig = false;
|
|||||||
window.handleInstallModeChange = function(selectElement) {
|
window.handleInstallModeChange = function(selectElement) {
|
||||||
// Prevent multiple simultaneous reloads
|
// Prevent multiple simultaneous reloads
|
||||||
if (window.isReloadingConfig) {
|
if (window.isReloadingConfig) {
|
||||||
//console.log('Config reload already in progress, ignoring...');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const installMode = selectElement.value;
|
const installMode = selectElement.value;
|
||||||
//console.log(`Install mode changed to: ${installMode}`);
|
|
||||||
|
|
||||||
// Set flag to prevent multiple reloads
|
// Set flag to prevent multiple reloads
|
||||||
window.isReloadingConfig = true;
|
window.isReloadingConfig = true;
|
||||||
@ -1515,11 +1463,9 @@ window.handleInstallModeChange = function(selectElement) {
|
|||||||
if (window.configManager) {
|
if (window.configManager) {
|
||||||
// Clear cache to ensure we get fresh data with updated CFG_INSTALL_MODE
|
// Clear cache to ensure we get fresh data with updated CFG_INSTALL_MODE
|
||||||
window.configManager.cache.clear();
|
window.configManager.cache.clear();
|
||||||
//console.log('Cache cleared for fresh config data');
|
|
||||||
|
|
||||||
// Get the current category from the URL or default to 'general'
|
// Get the current category from the URL or default to 'general'
|
||||||
const currentCategory = window.configCategory || 'general';
|
const currentCategory = window.configCategory || 'general';
|
||||||
//console.log(`Reloading config category: ${currentCategory}`);
|
|
||||||
window.configManager.renderConfig(currentCategory).finally(() => {
|
window.configManager.renderConfig(currentCategory).finally(() => {
|
||||||
// Clear the flag after reload is complete
|
// Clear the flag after reload is complete
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -1529,7 +1475,6 @@ window.handleInstallModeChange = function(selectElement) {
|
|||||||
} else if (window.configRouter) {
|
} else if (window.configRouter) {
|
||||||
// Fallback to configRouter if configManager is not available
|
// Fallback to configRouter if configManager is not available
|
||||||
const currentCategory = window.configCategory || 'general';
|
const currentCategory = window.configCategory || 'general';
|
||||||
//console.log(`Using configRouter to reload: ${currentCategory}`);
|
|
||||||
window.configRouter.loadConfigComponentManual(currentCategory).finally(() => {
|
window.configRouter.loadConfigComponentManual(currentCategory).finally(() => {
|
||||||
// Clear the flag after reload is complete
|
// Clear the flag after reload is complete
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
@ -184,7 +184,6 @@ class ConfigOptions {
|
|||||||
|
|
||||||
// Get select options for specific config keys
|
// Get select options for specific config keys
|
||||||
static getSelectOptions(key) {
|
static getSelectOptions(key) {
|
||||||
//console.log('=== getSelectOptions ENTRY === called with:', key);
|
|
||||||
const optionMaps = {
|
const optionMaps = {
|
||||||
'CFG_DOCKER_INSTALL_TYPE': [
|
'CFG_DOCKER_INSTALL_TYPE': [
|
||||||
{ value: 'rooted', label: 'Rooted' },
|
{ value: 'rooted', label: 'Rooted' },
|
||||||
@ -274,7 +273,6 @@ class ConfigOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const result = optionMaps[key] || [];
|
const result = optionMaps[key] || [];
|
||||||
//console.log('Final result for', key, ':', result);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,7 +328,6 @@ class ConfigOptions {
|
|||||||
|
|
||||||
// Check if a config key should use dropdown
|
// Check if a config key should use dropdown
|
||||||
static isDropdownKey(key) {
|
static isDropdownKey(key) {
|
||||||
//console.log('ConfigOptions.isDropdownKey called with:', key);
|
|
||||||
const result = key === 'CFG_DOCKER_INSTALL_TYPE' ||
|
const result = key === 'CFG_DOCKER_INSTALL_TYPE' ||
|
||||||
key === 'CFG_UFW_LOGGING' ||
|
key === 'CFG_UFW_LOGGING' ||
|
||||||
key === 'CFG_TEXT_EDITOR' ||
|
key === 'CFG_TEXT_EDITOR' ||
|
||||||
@ -345,7 +342,6 @@ class ConfigOptions {
|
|||||||
key === 'CFG_GLUETUN_VPN_SERVICE_PROVIDER' ||
|
key === 'CFG_GLUETUN_VPN_SERVICE_PROVIDER' ||
|
||||||
key === 'CFG_GLUETUN_VPN_TYPE' ||
|
key === 'CFG_GLUETUN_VPN_TYPE' ||
|
||||||
key === 'CFG_TRAEFIK_DASHBOARD_ACCESS';
|
key === 'CFG_TRAEFIK_DASHBOARD_ACCESS';
|
||||||
//console.log('ConfigOptions.isDropdownKey result:', result);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,11 +353,9 @@ class ConfigOptions {
|
|||||||
// Fetch available domains from system configuration
|
// Fetch available domains from system configuration
|
||||||
static async getAvailableDomains() {
|
static async getAvailableDomains() {
|
||||||
try {
|
try {
|
||||||
//console.log('🔍 Starting domain fetch...');
|
|
||||||
|
|
||||||
// Try to load system config to get domain information
|
// Try to load system config to get domain information
|
||||||
const response = await fetch('/data/config/generated/configs.json');
|
const response = await fetch('/data/config/generated/configs.json');
|
||||||
//console.log('📡 Config response status:', response.status);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
console.warn('Could not load system config for domains, returning empty list');
|
console.warn('Could not load system config for domains, returning empty list');
|
||||||
@ -369,12 +363,8 @@ class ConfigOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const configData = await response.json();
|
const configData = await response.json();
|
||||||
//console.log('📄 Full config data:', configData);
|
|
||||||
//console.log('🔧 Config keys available:', Object.keys(configData));
|
|
||||||
|
|
||||||
const config = configData.config || {};
|
const config = configData.config || {};
|
||||||
//console.log('⚙️ Config object:', config);
|
|
||||||
//console.log('🔑 Config keys:', Object.keys(config));
|
|
||||||
|
|
||||||
const domains = [];
|
const domains = [];
|
||||||
|
|
||||||
@ -382,7 +372,6 @@ class ConfigOptions {
|
|||||||
for (let i = 1; i <= 9; i++) {
|
for (let i = 1; i <= 9; i++) {
|
||||||
const domainKey = `CFG_DOMAIN_${i}`;
|
const domainKey = `CFG_DOMAIN_${i}`;
|
||||||
const domainValue = config[domainKey]?.value || config[domainKey] || '';
|
const domainValue = config[domainKey]?.value || config[domainKey] || '';
|
||||||
//console.log(`🌐 Checking ${domainKey}:`, domainValue);
|
|
||||||
|
|
||||||
if (domainValue.trim() !== '') {
|
if (domainValue.trim() !== '') {
|
||||||
domains.push({
|
domains.push({
|
||||||
@ -393,7 +382,6 @@ class ConfigOptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log('✅ Found configured domains:', domains);
|
|
||||||
return domains;
|
return domains;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -404,12 +392,9 @@ class ConfigOptions {
|
|||||||
|
|
||||||
// Get domain options for DOMAIN field
|
// Get domain options for DOMAIN field
|
||||||
static async getDomainOptions() {
|
static async getDomainOptions() {
|
||||||
//console.log('🎯 Getting domain options...');
|
|
||||||
const domains = await this.getAvailableDomains();
|
const domains = await this.getAvailableDomains();
|
||||||
//console.log('📊 Domains returned:', domains);
|
|
||||||
|
|
||||||
if (domains.length === 0) {
|
if (domains.length === 0) {
|
||||||
//console.log('⚠️ No domains found, returning fallback option');
|
|
||||||
// No domains configured - return empty option
|
// No domains configured - return empty option
|
||||||
return [
|
return [
|
||||||
{ value: '1', label: 'No domains configured - Configure domains in Network settings first' }
|
{ value: '1', label: 'No domains configured - Configure domains in Network settings first' }
|
||||||
@ -422,7 +407,6 @@ class ConfigOptions {
|
|||||||
label: domain.domain
|
label: domain.domain
|
||||||
}));
|
}));
|
||||||
|
|
||||||
//console.log('✅ Generated domain options:', options);
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -56,7 +56,6 @@ class DataLoader {
|
|||||||
static async getContainers() {
|
static async getContainers() {
|
||||||
try {
|
try {
|
||||||
// Local implementation - return empty array for now
|
// Local implementation - return empty array for now
|
||||||
//console.log('Loading containers locally...');
|
|
||||||
|
|
||||||
// TODO: Implement local container detection
|
// TODO: Implement local container detection
|
||||||
// This would require backend integration to query Docker
|
// This would require backend integration to query Docker
|
||||||
@ -72,14 +71,12 @@ class DataLoader {
|
|||||||
|
|
||||||
static async loadSystemConfigs() {
|
static async loadSystemConfigs() {
|
||||||
try {
|
try {
|
||||||
//console.log('Loading system configs');
|
|
||||||
|
|
||||||
// Load unified config
|
// Load unified config
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/data/config/generated/configs.json');
|
const response = await fetch('/data/config/generated/configs.json');
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
//console.log('✅ Loaded unified system config');
|
|
||||||
return data;
|
return data;
|
||||||
} else {
|
} else {
|
||||||
console.warn('Failed to load unified config:', response.status);
|
console.warn('Failed to load unified config:', response.status);
|
||||||
@ -97,7 +94,6 @@ class DataLoader {
|
|||||||
|
|
||||||
static async loadConfig(configType) {
|
static async loadConfig(configType) {
|
||||||
try {
|
try {
|
||||||
//console.log(`Loading ${configType} config`);
|
|
||||||
|
|
||||||
const response = await fetch(`/data/config/generated/config_${configType}.json`);
|
const response = await fetch(`/data/config/generated/config_${configType}.json`);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@ -170,7 +166,6 @@ let lastSystemDataHash = null;
|
|||||||
|
|
||||||
// Dashboard-specific data loading
|
// Dashboard-specific data loading
|
||||||
async function loadDashboardData() {
|
async function loadDashboardData() {
|
||||||
// console.log('🔄 Loading dashboard data...');
|
|
||||||
|
|
||||||
// Load all apps with installation status
|
// Load all apps with installation status
|
||||||
const allApps = await DataLoader.loadApps();
|
const allApps = await DataLoader.loadApps();
|
||||||
@ -215,7 +210,6 @@ async function loadDashboardData() {
|
|||||||
// Show/refresh the "out of date" banner on the dashboard.
|
// Show/refresh the "out of date" banner on the dashboard.
|
||||||
window.updateNotifier?.renderDashboardBanner();
|
window.updateNotifier?.renderDashboardBanner();
|
||||||
|
|
||||||
// console.log('✅ Dashboard data loaded successfully');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Countdown timer for next automatic update
|
// Countdown timer for next automatic update
|
||||||
@ -297,7 +291,6 @@ function getTimeAgo(date) {
|
|||||||
|
|
||||||
// Apps page data loading
|
// Apps page data loading
|
||||||
async function loadAppsPageData() {
|
async function loadAppsPageData() {
|
||||||
//console.log('Loading apps page data (all apps + categories)...');
|
|
||||||
|
|
||||||
// Load apps and categories in parallel
|
// Load apps and categories in parallel
|
||||||
try {
|
try {
|
||||||
@ -307,18 +300,11 @@ async function loadAppsPageData() {
|
|||||||
DataLoader.loadConfigCategories()
|
DataLoader.loadConfigCategories()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
//console.log('🔍 DataLoader Results:');
|
|
||||||
//console.log(' - appsData length:', appsData?.length || 0);
|
|
||||||
//console.log(' - sidebarCategoriesData type:', typeof sidebarCategoriesData);
|
|
||||||
//console.log(' - sidebarCategoriesData keys:', Object.keys(sidebarCategoriesData || {}));
|
|
||||||
//console.log(' - configCategoriesData type:', typeof configCategoriesData);
|
|
||||||
//console.log(' - configCategoriesData keys:', Object.keys(configCategoriesData || {}));
|
|
||||||
|
|
||||||
apps = appsData;
|
apps = appsData;
|
||||||
categories = sidebarCategoriesData; // For sidebar
|
categories = sidebarCategoriesData; // For sidebar
|
||||||
systemConfigs = []; // Apps page doesn't need system configs
|
systemConfigs = []; // Apps page doesn't need system configs
|
||||||
|
|
||||||
//console.log(`Apps page loaded: ${apps.length} apps, ${categories.length} categories`);
|
|
||||||
////console.log('Apps array:', apps);
|
////console.log('Apps array:', apps);
|
||||||
////console.log('Categories array:', categories);
|
////console.log('Categories array:', categories);
|
||||||
|
|
||||||
@ -381,7 +367,6 @@ async function loadConfigDetailData() {
|
|||||||
// Load system information for dashboard
|
// Load system information for dashboard
|
||||||
async function loadSystemInfo() {
|
async function loadSystemInfo() {
|
||||||
try {
|
try {
|
||||||
//console.log('Loading system data');
|
|
||||||
|
|
||||||
// Add timestamp to prevent browser caching
|
// Add timestamp to prevent browser caching
|
||||||
const timestamp = Date.now();
|
const timestamp = Date.now();
|
||||||
@ -399,9 +384,6 @@ async function loadSystemInfo() {
|
|||||||
const diskData = await diskResponse.json();
|
const diskData = await diskResponse.json();
|
||||||
const memoryData = await memoryResponse.json();
|
const memoryData = await memoryResponse.json();
|
||||||
|
|
||||||
//console.log('System data:', systemData);
|
|
||||||
//console.log('Disk data:', diskData);
|
|
||||||
//console.log('Memory data:', memoryData);
|
|
||||||
|
|
||||||
// Prepare system data
|
// Prepare system data
|
||||||
const osInfo = systemData.os || 'Unknown';
|
const osInfo = systemData.os || 'Unknown';
|
||||||
@ -411,7 +393,6 @@ async function loadSystemInfo() {
|
|||||||
osInfo.split(' ')[0]; // Take first part like "Linux"
|
osInfo.split(' ')[0]; // Take first part like "Linux"
|
||||||
|
|
||||||
const diskChartData = diskData.root || diskData;
|
const diskChartData = diskData.root || diskData;
|
||||||
//console.log('Disk chart data:', diskChartData);
|
|
||||||
|
|
||||||
// Wait for all dashboard elements to be available
|
// Wait for all dashboard elements to be available
|
||||||
await waitForDashboardElements();
|
await waitForDashboardElements();
|
||||||
@ -423,14 +404,12 @@ async function loadSystemInfo() {
|
|||||||
|
|
||||||
if (systemInfoEl) {
|
if (systemInfoEl) {
|
||||||
systemInfoEl.textContent = cleanOs;
|
systemInfoEl.textContent = cleanOs;
|
||||||
// console.log('✅ Updated OS:', cleanOs);
|
|
||||||
} else {
|
} else {
|
||||||
console.warn('⚠️ system-info element not found');
|
console.warn('⚠️ system-info element not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uptimeEl) {
|
if (uptimeEl) {
|
||||||
uptimeEl.textContent = systemData.uptime || 'Unknown';
|
uptimeEl.textContent = systemData.uptime || 'Unknown';
|
||||||
// console.log('✅ Updated uptime:', systemData.uptime);
|
|
||||||
} else {
|
} else {
|
||||||
console.warn('⚠️ uptime-info element not found');
|
console.warn('⚠️ uptime-info element not found');
|
||||||
}
|
}
|
||||||
@ -438,7 +417,6 @@ async function loadSystemInfo() {
|
|||||||
// Update memory info - use the text field which should be properly formatted
|
// Update memory info - use the text field which should be properly formatted
|
||||||
if (memoryEl) {
|
if (memoryEl) {
|
||||||
memoryEl.textContent = memoryData.text || 'Unknown';
|
memoryEl.textContent = memoryData.text || 'Unknown';
|
||||||
// console.log('✅ Updated memory:', memoryData.text);
|
|
||||||
} else {
|
} else {
|
||||||
console.warn('⚠️ memory-info element not found');
|
console.warn('⚠️ memory-info element not found');
|
||||||
}
|
}
|
||||||
@ -636,7 +614,6 @@ function updateDiskChart(data) {
|
|||||||
|
|
||||||
// Minimal data loading (fallback)
|
// Minimal data loading (fallback)
|
||||||
async function loadMinimalData() {
|
async function loadMinimalData() {
|
||||||
//console.log('Loading minimal data...');
|
|
||||||
|
|
||||||
apps = [];
|
apps = [];
|
||||||
categories = [];
|
categories = [];
|
||||||
@ -645,11 +622,9 @@ async function loadMinimalData() {
|
|||||||
|
|
||||||
// Preload essential data for smooth navigation
|
// Preload essential data for smooth navigation
|
||||||
async function preloadOtherPagesData(currentPage) {
|
async function preloadOtherPagesData(currentPage) {
|
||||||
//console.log('Preloading essential data for navigation...');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// No preloading needed - data loads fresh each page
|
// No preloading needed - data loads fresh each page
|
||||||
//console.log('Preloading complete (no caching)');
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error preloading navigation data:', error);
|
console.error('Error preloading navigation data:', error);
|
||||||
|
|||||||
@ -3,7 +3,6 @@
|
|||||||
function safeGetElement(id) {
|
function safeGetElement(id) {
|
||||||
const element = document.getElementById(id);
|
const element = document.getElementById(id);
|
||||||
if (!element) {
|
if (!element) {
|
||||||
//console.log(`${id} element not found`);
|
|
||||||
}
|
}
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
@ -11,7 +10,6 @@ function safeGetElement(id) {
|
|||||||
function safeQuerySelector(selector) {
|
function safeQuerySelector(selector) {
|
||||||
const element = document.querySelector(selector);
|
const element = document.querySelector(selector);
|
||||||
if (!element) {
|
if (!element) {
|
||||||
//console.log(`${selector} element not found`);
|
|
||||||
}
|
}
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
@ -19,7 +17,6 @@ function safeQuerySelector(selector) {
|
|||||||
function safeQuerySelectorAll(selector) {
|
function safeQuerySelectorAll(selector) {
|
||||||
const elements = document.querySelectorAll(selector);
|
const elements = document.querySelectorAll(selector);
|
||||||
if (!elements || elements.length === 0) {
|
if (!elements || elements.length === 0) {
|
||||||
//console.log(`${selector} elements not found`);
|
|
||||||
}
|
}
|
||||||
return elements;
|
return elements;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,6 @@ class LibrePortalSPAClean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
//console.log('🚀 Clean SPA: Initializing...');
|
|
||||||
|
|
||||||
// Setup routes from the feature manifest (falls back to the built-in table)
|
// Setup routes from the feature manifest (falls back to the built-in table)
|
||||||
await this.setupRoutesFromManifest();
|
await this.setupRoutesFromManifest();
|
||||||
@ -32,11 +31,9 @@ class LibrePortalSPAClean {
|
|||||||
// Handle initial route
|
// Handle initial route
|
||||||
this.handleInitialRoute();
|
this.handleInitialRoute();
|
||||||
|
|
||||||
//console.log('✅ Clean SPA: Initialization complete');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async waitForTopbar() {
|
async waitForTopbar() {
|
||||||
//console.log('⏳ Waiting for topbar to load...');
|
|
||||||
|
|
||||||
// Wait for topbar component to be available and loaded
|
// Wait for topbar component to be available and loaded
|
||||||
let attempts = 0;
|
let attempts = 0;
|
||||||
@ -46,7 +43,6 @@ class LibrePortalSPAClean {
|
|||||||
if (typeof TopbarComponent !== 'undefined' && TopbarComponent.loadTopbar) {
|
if (typeof TopbarComponent !== 'undefined' && TopbarComponent.loadTopbar) {
|
||||||
try {
|
try {
|
||||||
await TopbarComponent.loadTopbar();
|
await TopbarComponent.loadTopbar();
|
||||||
//console.log('✅ Topbar loaded successfully');
|
|
||||||
return;
|
return;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('⚠️ Topbar loading failed, retrying...', error);
|
console.warn('⚠️ Topbar loading failed, retrying...', error);
|
||||||
@ -78,7 +74,6 @@ class LibrePortalSPAClean {
|
|||||||
// Legacy /config, /peers, /ssh are handled by _legacyRedirect() at the top
|
// Legacy /config, /peers, /ssh are handled by _legacyRedirect() at the top
|
||||||
// of navigate() (rewrites to the canonical /admin/* path).
|
// of navigate() (rewrites to the canonical /admin/* path).
|
||||||
|
|
||||||
//console.log('📍 Routes registered:', Array.from(this.routes.keys()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the route table from the feature manifest (window.LP.features) so
|
// Build the route table from the feature manifest (window.LP.features) so
|
||||||
@ -136,7 +131,6 @@ class LibrePortalSPAClean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._routesFromManifest = true;
|
this._routesFromManifest = true;
|
||||||
//console.log('📍 Routes registered from manifest:', Array.from(this.routes.keys()));
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[spa] manifest routing failed, using built-in routes:', err);
|
console.error('[spa] manifest routing failed, using built-in routes:', err);
|
||||||
this.setupRoutes();
|
this.setupRoutes();
|
||||||
@ -176,14 +170,12 @@ class LibrePortalSPAClean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async loadCoreData() {
|
async loadCoreData() {
|
||||||
//console.log('📊 Loading core data...');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Load apps
|
// Load apps
|
||||||
if (typeof DataLoader !== 'undefined' && DataLoader.loadApps) {
|
if (typeof DataLoader !== 'undefined' && DataLoader.loadApps) {
|
||||||
this.apps = await DataLoader.loadApps();
|
this.apps = await DataLoader.loadApps();
|
||||||
window.apps = this.apps;
|
window.apps = this.apps;
|
||||||
//console.log(`📱 Loaded ${this.apps.length} apps`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load categories
|
// Load categories
|
||||||
@ -191,11 +183,9 @@ class LibrePortalSPAClean {
|
|||||||
this.categories = await DataLoader.loadCategories();
|
this.categories = await DataLoader.loadCategories();
|
||||||
window.categories = this.categories;
|
window.categories = this.categories;
|
||||||
window.sidebarCategories = this.categories; // Ensure this is always available
|
window.sidebarCategories = this.categories; // Ensure this is always available
|
||||||
//console.log(`📂 Loaded ${Object.keys(this.categories).length} categories`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dataLoaded = true;
|
this.dataLoaded = true;
|
||||||
//console.log('✅ Core data loaded successfully');
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Failed to load core data:', error);
|
console.error('❌ Failed to load core data:', error);
|
||||||
@ -205,20 +195,16 @@ class LibrePortalSPAClean {
|
|||||||
|
|
||||||
handleInitialRoute() {
|
handleInitialRoute() {
|
||||||
const path = window.location.pathname + window.location.search;
|
const path = window.location.pathname + window.location.search;
|
||||||
// console.log('🎯 SPA: Handling initial route:', path);
|
|
||||||
|
|
||||||
// Handle root path - redirect to dashboard
|
// Handle root path - redirect to dashboard
|
||||||
if (path === '/' || path === '') {
|
if (path === '/' || path === '') {
|
||||||
// console.log('🏠 SPA: Redirecting to dashboard');
|
|
||||||
this.navigate('/dashboard', false);
|
this.navigate('/dashboard', false);
|
||||||
} else {
|
} else {
|
||||||
// console.log('🔀 SPA: Navigating to:', path);
|
|
||||||
this.navigate(path, false); // Don't add to history for initial load
|
this.navigate(path, false); // Don't add to history for initial load
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async navigate(path, addToHistory = true) {
|
async navigate(path, addToHistory = true) {
|
||||||
// console.log('🚀 SPA: navigate called with:', path, 'addToHistory:', addToHistory);
|
|
||||||
|
|
||||||
// Legacy URL redirects (the old /ssh, /peers, /config short URLs → the Admin
|
// Legacy URL redirects (the old /ssh, /peers, /config short URLs → the Admin
|
||||||
// area). Rewritten here, at the top of navigate() and BEFORE the isLoading
|
// area). Rewritten here, at the top of navigate() and BEFORE the isLoading
|
||||||
@ -233,12 +219,10 @@ class LibrePortalSPAClean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.isLoading) {
|
if (this.isLoading) {
|
||||||
// console.log('⏳ Navigation already in progress, ignoring:', path);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.currentRoute === path && addToHistory) {
|
if (this.currentRoute === path && addToHistory) {
|
||||||
//console.log('🔄 Same route, skipping navigation:', path);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,7 +243,6 @@ class LibrePortalSPAClean {
|
|||||||
// navigation. See components/dashboard/index.js.)
|
// navigation. See components/dashboard/index.js.)
|
||||||
|
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
//console.log('🧭 Navigating to:', path);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Update browser history
|
// Update browser history
|
||||||
@ -303,11 +286,9 @@ class LibrePortalSPAClean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
findRouteHandler(path) {
|
findRouteHandler(path) {
|
||||||
//console.log('🔍 Finding handler for path:', path);
|
|
||||||
|
|
||||||
// Exact match first
|
// Exact match first
|
||||||
if (this.routes.has(path)) {
|
if (this.routes.has(path)) {
|
||||||
//console.log('✅ Exact match found:', path);
|
|
||||||
return this.routes.get(path);
|
return this.routes.get(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,11 +298,9 @@ class LibrePortalSPAClean {
|
|||||||
basePath = path.split('?')[0];
|
basePath = path.split('?')[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log('🔍 Checking base path:', basePath, 'for full path:', path);
|
|
||||||
|
|
||||||
// Check base path against routes
|
// Check base path against routes
|
||||||
if (this.routes.has(basePath)) {
|
if (this.routes.has(basePath)) {
|
||||||
//console.log('✅ Base path match found:', basePath);
|
|
||||||
return this.routes.get(basePath);
|
return this.routes.get(basePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,31 +309,22 @@ class LibrePortalSPAClean {
|
|||||||
if (route.includes('*')) {
|
if (route.includes('*')) {
|
||||||
const pattern = route.replace('*', '');
|
const pattern = route.replace('*', '');
|
||||||
if (basePath.startsWith(pattern)) {
|
if (basePath.startsWith(pattern)) {
|
||||||
//console.log('✅ Wildcard match:', pattern, 'for base path:', basePath);
|
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log('❌ No handler found for:', path);
|
|
||||||
//console.log('❌ Base path:', basePath);
|
|
||||||
//console.log('❌ Available routes:', Array.from(this.routes.keys()));
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleDashboard() {
|
async handleDashboard() {
|
||||||
// console.log('🏠 SPA: Loading dashboard...');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// console.log('📄 SPA: Fetching dashboard content...');
|
|
||||||
const html = await this.fetchContent('/components/dashboard/html/dashboard-content.html');
|
const html = await this.fetchContent('/components/dashboard/html/dashboard-content.html');
|
||||||
// console.log('📄 SPA: Dashboard content fetched, loading...');
|
|
||||||
this.loadContent(html, 'Dashboard');
|
this.loadContent(html, 'Dashboard');
|
||||||
// console.log('📄 SPA: Dashboard content loaded');
|
|
||||||
|
|
||||||
// Dashboard should already be initialized by SystemLoader
|
// Dashboard should already be initialized by SystemLoader
|
||||||
if (typeof loadInstalledApps === 'function') {
|
if (typeof loadInstalledApps === 'function') {
|
||||||
// console.log('📱 SPA: Loading installed apps...');
|
|
||||||
loadInstalledApps();
|
loadInstalledApps();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -404,7 +374,6 @@ class LibrePortalSPAClean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async handleApps() {
|
async handleApps() {
|
||||||
//console.log('📱 Loading apps...');
|
|
||||||
|
|
||||||
// Category from the path (/apps/<category>), else legacy ?=<cat> / ?apps=.
|
// Category from the path (/apps/<category>), else legacy ?=<cat> / ?apps=.
|
||||||
const seg = window.location.pathname.replace(/^\/apps\/?/, '').split('/')[0];
|
const seg = window.location.pathname.replace(/^\/apps\/?/, '').split('/')[0];
|
||||||
@ -422,19 +391,15 @@ class LibrePortalSPAClean {
|
|||||||
try {
|
try {
|
||||||
// Ensure unified layout is loaded (like the old SPA)
|
// Ensure unified layout is loaded (like the old SPA)
|
||||||
if (!document.querySelector('.apps-layout')) {
|
if (!document.querySelector('.apps-layout')) {
|
||||||
//console.log('📄 Loading apps layout HTML...');
|
|
||||||
const html = await this.fetchContent('/components/apps/core/html/apps-unified-layout.html');
|
const html = await this.fetchContent('/components/apps/core/html/apps-unified-layout.html');
|
||||||
this.loadContent(html, 'Applications');
|
this.loadContent(html, 'Applications');
|
||||||
} else {
|
} else {
|
||||||
//console.log('📄 Apps layout already exists, skipping HTML load');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apps manager should already be initialized by SystemLoader
|
// Apps manager should already be initialized by SystemLoader
|
||||||
if (window.appsManager) {
|
if (window.appsManager) {
|
||||||
//console.log('✅ AppsManager already initialized by SystemLoader');
|
|
||||||
await window.appsManager.initialize();
|
await window.appsManager.initialize();
|
||||||
|
|
||||||
//console.log('✅ Apps loaded successfully');
|
|
||||||
} else {
|
} else {
|
||||||
console.error('AppsManager not available - SystemLoader should have initialized it');
|
console.error('AppsManager not available - SystemLoader should have initialized it');
|
||||||
throw new Error('AppsManager not initialized by SystemLoader');
|
throw new Error('AppsManager not initialized by SystemLoader');
|
||||||
@ -446,7 +411,6 @@ class LibrePortalSPAClean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async handleAppDetail() {
|
async handleAppDetail() {
|
||||||
//console.log('🔍 Loading app detail...');
|
|
||||||
|
|
||||||
// Extract app name. Path-based /app/<name> first, then legacy ?app= / ?=name.
|
// Extract app name. Path-based /app/<name> first, then legacy ?app= / ?=name.
|
||||||
const url = new URL(window.location);
|
const url = new URL(window.location);
|
||||||
@ -467,7 +431,6 @@ class LibrePortalSPAClean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log('🔍 Parsed app name:', appName, 'from URL:', url.search);
|
|
||||||
|
|
||||||
if (!appName) {
|
if (!appName) {
|
||||||
this.navigate('/apps', false);
|
this.navigate('/apps', false);
|
||||||
@ -496,14 +459,12 @@ class LibrePortalSPAClean {
|
|||||||
|
|
||||||
// AppTabbedManager should already be initialized by SystemLoader
|
// AppTabbedManager should already be initialized by SystemLoader
|
||||||
if (window.appTabbedManager) {
|
if (window.appTabbedManager) {
|
||||||
// console.log('✅ AppTabbedManager already initialized by SystemLoader');
|
|
||||||
await window.appTabbedManager.initialize();
|
await window.appTabbedManager.initialize();
|
||||||
} else {
|
} else {
|
||||||
console.error('AppTabbedManager not available - SystemLoader should have initialized it');
|
console.error('AppTabbedManager not available - SystemLoader should have initialized it');
|
||||||
throw new Error('AppTabbedManager not initialized by SystemLoader');
|
throw new Error('AppTabbedManager not initialized by SystemLoader');
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log('✅ App detail loaded:', appName);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ App detail load error:', error);
|
console.error('❌ App detail load error:', error);
|
||||||
this.showError('Failed to load application details');
|
this.showError('Failed to load application details');
|
||||||
@ -535,7 +496,6 @@ class LibrePortalSPAClean {
|
|||||||
|
|
||||||
// Legacy /config and /config?=<x> → the path-based /admin equivalent.
|
// Legacy /config and /config?=<x> → the path-based /admin equivalent.
|
||||||
async handleTasks() {
|
async handleTasks() {
|
||||||
//console.log('📋 Loading tasks...');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const html = await this.fetchContent('/components/tasks/html/tasks-content.html');
|
const html = await this.fetchContent('/components/tasks/html/tasks-content.html');
|
||||||
@ -543,7 +503,6 @@ class LibrePortalSPAClean {
|
|||||||
|
|
||||||
// Tasks manager should already be initialized by SystemLoader
|
// Tasks manager should already be initialized by SystemLoader
|
||||||
if (window.tasksManager) {
|
if (window.tasksManager) {
|
||||||
//console.log('✅ TasksManager already initialized by SystemLoader');
|
|
||||||
await window.tasksManager.init();
|
await window.tasksManager.init();
|
||||||
} else {
|
} else {
|
||||||
console.warn('⚠️ TasksManager not available yet, task functionality will be limited');
|
console.warn('⚠️ TasksManager not available yet, task functionality will be limited');
|
||||||
@ -557,7 +516,6 @@ class LibrePortalSPAClean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fetchContent(url) {
|
async fetchContent(url) {
|
||||||
//console.log('📥 Fetching:', url);
|
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||||
@ -595,7 +553,6 @@ class LibrePortalSPAClean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateNavigation() {
|
updateNavigation() {
|
||||||
//console.log('🔗 SPA: Using fallback navigation logic');
|
|
||||||
|
|
||||||
// Remove ALL active classes
|
// Remove ALL active classes
|
||||||
document.querySelectorAll('.nav-item').forEach(item => {
|
document.querySelectorAll('.nav-item').forEach(item => {
|
||||||
@ -622,7 +579,6 @@ class LibrePortalSPAClean {
|
|||||||
activeId = 'nav-dashboard';
|
activeId = 'nav-dashboard';
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log('🔗 SPA: Setting active nav:', activeId);
|
|
||||||
const activeElement = document.getElementById(activeId);
|
const activeElement = document.getElementById(activeId);
|
||||||
if (activeElement) {
|
if (activeElement) {
|
||||||
activeElement.classList.add('nav-active');
|
activeElement.classList.add('nav-active');
|
||||||
@ -693,7 +649,6 @@ window.appPartsFromPath = function (pathname) {
|
|||||||
// Global navigation function for click handlers
|
// Global navigation function for click handlers
|
||||||
window.navigateToRoute = function(href) {
|
window.navigateToRoute = function(href) {
|
||||||
if (window.spaClean) {
|
if (window.spaClean) {
|
||||||
//console.log('🔗 Converting href:', href);
|
|
||||||
|
|
||||||
// Convert href to clean path - handle various formats
|
// Convert href to clean path - handle various formats
|
||||||
let route = href.replace('.html', '').replace('./', '').replace(/^\//, '');
|
let route = href.replace('.html', '').replace('./', '').replace(/^\//, '');
|
||||||
@ -713,7 +668,6 @@ window.navigateToRoute = function(href) {
|
|||||||
route = '/' + route;
|
route = '/' + route;
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log('🎯 Final route:', route);
|
|
||||||
window.spaClean.navigate(route);
|
window.spaClean.navigate(route);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -261,9 +261,6 @@ class LoadingUI {
|
|||||||
|
|
||||||
// Show error message
|
// Show error message
|
||||||
showError(errors) {
|
showError(errors) {
|
||||||
// console.log('🔍 LoadingUI.showError called with errors:', errors.length);
|
|
||||||
// console.log('🔍 Existing error details elements:', document.querySelectorAll('.error-details').length);
|
|
||||||
// console.log('🔍 Call stack:', new Error().stack);
|
|
||||||
|
|
||||||
const actionsContainer = document.getElementById('loading-actions');
|
const actionsContainer = document.getElementById('loading-actions');
|
||||||
|
|
||||||
@ -379,7 +376,6 @@ class LoadingUI {
|
|||||||
attachEventListeners() {
|
attachEventListeners() {
|
||||||
if (this.retryButton) {
|
if (this.retryButton) {
|
||||||
this.retryButton.addEventListener('click', () => {
|
this.retryButton.addEventListener('click', () => {
|
||||||
// console.log('🔄 Retry button clicked - refreshing page');
|
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,11 +29,9 @@ class ConfirmationDialog {
|
|||||||
this.overlay.addEventListener('click', () => this.hide());
|
this.overlay.addEventListener('click', () => this.hide());
|
||||||
this.dialog.addEventListener('click', (e) => e.stopPropagation());
|
this.dialog.addEventListener('click', (e) => e.stopPropagation());
|
||||||
|
|
||||||
//console.log('Confirmation dialog initialized');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
show(title, message, onConfirm, confirmText = 'Confirm', cancelText = 'Cancel', confirmClass = 'primary', showDataLossCheckbox = false) {
|
show(title, message, onConfirm, confirmText = 'Confirm', cancelText = 'Cancel', confirmClass = 'primary', showDataLossCheckbox = false) {
|
||||||
//console.log('Showing confirmation dialog');
|
|
||||||
|
|
||||||
this.callback = onConfirm;
|
this.callback = onConfirm;
|
||||||
|
|
||||||
@ -67,8 +65,6 @@ class ConfirmationDialog {
|
|||||||
this.overlay.classList.add('active');
|
this.overlay.classList.add('active');
|
||||||
|
|
||||||
// Debug: Check if class was added
|
// Debug: Check if class was added
|
||||||
//console.log('Active class added:', this.overlay.className);
|
|
||||||
//console.log('Has active class:', this.overlay.classList.contains('active'));
|
|
||||||
|
|
||||||
// Handle checkbox if present
|
// Handle checkbox if present
|
||||||
if (showDataLossCheckbox) {
|
if (showDataLossCheckbox) {
|
||||||
@ -87,7 +83,6 @@ class ConfirmationDialog {
|
|||||||
};
|
};
|
||||||
document.addEventListener('keydown', handleEscape);
|
document.addEventListener('keydown', handleEscape);
|
||||||
|
|
||||||
//console.log('Confirmation dialog shown');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateConfirmButton(isChecked) {
|
updateConfirmButton(isChecked) {
|
||||||
@ -111,7 +106,6 @@ class ConfirmationDialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log('Confirmation dialog confirmed');
|
|
||||||
if (this.callback) {
|
if (this.callback) {
|
||||||
this.callback();
|
this.callback();
|
||||||
}
|
}
|
||||||
@ -119,13 +113,9 @@ class ConfirmationDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
//console.log('Hiding confirmation dialog');
|
|
||||||
//console.log('Before remove - classes:', this.overlay.className);
|
|
||||||
|
|
||||||
this.overlay.classList.remove('active');
|
this.overlay.classList.remove('active');
|
||||||
|
|
||||||
//console.log('After remove - classes:', this.overlay.className);
|
|
||||||
//console.log('Has active class after remove:', this.overlay.classList.contains('active'));
|
|
||||||
|
|
||||||
this.callback = null;
|
this.callback = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -161,8 +161,6 @@ class TaskCommands {
|
|||||||
const taskFileName = `${taskId}.json`;
|
const taskFileName = `${taskId}.json`;
|
||||||
const taskFilePath = `tasks/queue/${taskFileName}`;
|
const taskFilePath = `tasks/queue/${taskFileName}`;
|
||||||
|
|
||||||
// console.log('🔍 Creating task file:', taskFilePath);
|
|
||||||
// console.log('🔍 Task ID:', taskId);
|
|
||||||
|
|
||||||
const task = {
|
const task = {
|
||||||
id: taskId,
|
id: taskId,
|
||||||
@ -175,7 +173,6 @@ class TaskCommands {
|
|||||||
updatedAt: new Date().toISOString()
|
updatedAt: new Date().toISOString()
|
||||||
};
|
};
|
||||||
|
|
||||||
// console.log('🔍 Task object:', task);
|
|
||||||
|
|
||||||
const response = await fetch('/write-file', {
|
const response = await fetch('/write-file', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@ -1,18 +1,14 @@
|
|||||||
// Preserve task parameter before SPA strips it
|
// Preserve task parameter before SPA strips it
|
||||||
(function() {
|
(function() {
|
||||||
// console.log('🔍 Task parameter preservation script loaded');
|
|
||||||
|
|
||||||
// Check current URL for task parameter
|
// Check current URL for task parameter
|
||||||
const currentUrl = window.location.href;
|
const currentUrl = window.location.href;
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
const taskId = urlParams.get('task');
|
const taskId = urlParams.get('task');
|
||||||
|
|
||||||
// console.log('🔍 Current URL:', currentUrl);
|
|
||||||
// console.log('🔍 Task parameter detected:', taskId);
|
|
||||||
|
|
||||||
if (taskId) {
|
if (taskId) {
|
||||||
// Store task parameter in sessionStorage
|
// Store task parameter in sessionStorage
|
||||||
sessionStorage.setItem('pendingTaskId', taskId);
|
sessionStorage.setItem('pendingTaskId', taskId);
|
||||||
// console.log('🔍 Stored task ID in sessionStorage:', taskId);
|
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|||||||
@ -16,7 +16,6 @@ class TaskRouter {
|
|||||||
*/
|
*/
|
||||||
async routeAction(action, params = {}) {
|
async routeAction(action, params = {}) {
|
||||||
try {
|
try {
|
||||||
//console.log(`🎯 Routing action: ${action}`, params);
|
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'install':
|
case 'install':
|
||||||
@ -102,7 +101,6 @@ class TaskRouter {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.commandQueue.push(queuedTask);
|
this.commandQueue.push(queuedTask);
|
||||||
//console.log(`📋 Queued action: ${action}`, params);
|
|
||||||
|
|
||||||
// Start processing if not already running
|
// Start processing if not already running
|
||||||
if (!this.isProcessing) {
|
if (!this.isProcessing) {
|
||||||
@ -121,17 +119,14 @@ class TaskRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.isProcessing = true;
|
this.isProcessing = true;
|
||||||
//console.log('🔄 Processing command queue...');
|
|
||||||
|
|
||||||
while (this.commandQueue.length > 0) {
|
while (this.commandQueue.length > 0) {
|
||||||
const queuedTask = this.commandQueue.shift();
|
const queuedTask = this.commandQueue.shift();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//console.log(`⚡ Executing queued action: ${queuedTask.action}`);
|
|
||||||
await this.routeAction(queuedTask.action, queuedTask.params);
|
await this.routeAction(queuedTask.action, queuedTask.params);
|
||||||
|
|
||||||
queuedTask.status = 'completed';
|
queuedTask.status = 'completed';
|
||||||
//console.log(`✅ Completed queued action: ${queuedTask.action}`);
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
queuedTask.status = 'failed';
|
queuedTask.status = 'failed';
|
||||||
@ -158,7 +153,6 @@ class TaskRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.isProcessing = false;
|
this.isProcessing = false;
|
||||||
//console.log('✅ Command queue processing complete');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -304,7 +304,6 @@ class TopbarComponent {
|
|||||||
if (href) {
|
if (href) {
|
||||||
// Special handling for tasks navigation
|
// Special handling for tasks navigation
|
||||||
if (item.id === 'nav-tasks' && window.tasksManager) {
|
if (item.id === 'nav-tasks' && window.tasksManager) {
|
||||||
// console.log('🔄 Tasks button clicked - forcing clean state and reloading...');
|
|
||||||
|
|
||||||
// Force clean state
|
// Force clean state
|
||||||
window.tasksManager.highlightedTaskId = null;
|
window.tasksManager.highlightedTaskId = null;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user