Converted 35+ raw console.error calls to use ErrorHandler.logError() across 12 files. ErrorHandler provides structured logging, local error storage, and error tracking integration. Files: - app-selector.js: 4 errors (fetch templates, port check, suggested port, deploy) - globals.js: 2 errors (CSRF token get/add) - service-credentials.js: 2 errors (save/clear credentials) - totp-settings.js: 4 errors (TOTP setup, session duration, disable, AuthCard) - notification-settings.js: 2 errors (load config, load history) - setup-wizard.js: 2 errors (save config to server) - progress-tracker.js: 4 errors (storage read/write/session/fallback) - tooltip-definitions.js: 2 errors (validation, condition eval) - tour-manager.js: 2 errors (Driver.js not loaded, tooltip not found) - theme-adapter.js: 1 error (theme change callback) - weather.js: 1 error (weather update) - onboarding.js: removed duplicate console.error, fixed 2 remaining calls All console.error calls in the DashCaddy frontend now go through ErrorHandler.
189 lines
5.9 KiB
JavaScript
189 lines
5.9 KiB
JavaScript
/**
|
|
* DashCaddy User Onboarding System
|
|
* Main entry point for the tooltip-based onboarding experience
|
|
*
|
|
* This file initializes the onboarding system and coordinates between
|
|
* the various components (TourManager, ProgressTracker, ThemeAdapter, etc.)
|
|
*/
|
|
|
|
(function() {
|
|
'use strict';
|
|
|
|
const debug = (...args) => {
|
|
if (window.DASHCADDY_DEBUG) {
|
|
console.log(...args);
|
|
}
|
|
};
|
|
|
|
let progressTracker;
|
|
let themeAdapter;
|
|
let tourManager;
|
|
let dnsTemplateSelector;
|
|
let errorHandler;
|
|
|
|
/**
|
|
* Initialize the onboarding system
|
|
*/
|
|
async function initializeOnboarding() {
|
|
try {
|
|
debug('[Onboarding] Initializing system...');
|
|
|
|
if (window.__dashcaddySiteConfigLoaded) {
|
|
try {
|
|
await window.__dashcaddySiteConfigLoaded;
|
|
} catch (_) { /* ignore */ }
|
|
}
|
|
|
|
// Initialize Error Handler first
|
|
errorHandler = new ErrorHandler();
|
|
debug('[Onboarding] Error Handler initialized');
|
|
|
|
// Initialize Progress Tracker
|
|
progressTracker = new ProgressTracker('dashcaddy_onboarding');
|
|
debug('[Onboarding] Progress Tracker initialized');
|
|
|
|
// Initialize Theme Adapter
|
|
themeAdapter = new ThemeAdapter();
|
|
debug('[Onboarding] Theme Adapter initialized');
|
|
|
|
// Initialize DNS Template Selector
|
|
dnsTemplateSelector = new DnsTemplateSelector(progressTracker);
|
|
debug('[Onboarding] DNS Template Selector initialized');
|
|
|
|
// Initialize Tour Manager
|
|
tourManager = new TourManager(progressTracker, themeAdapter, dnsTemplateSelector);
|
|
debug('[Onboarding] Tour Manager initialized');
|
|
|
|
// Check if tour should auto-start
|
|
if (tourManager.shouldAutoStart()) {
|
|
debug('[Onboarding] Auto-starting tour for first-time install');
|
|
await progressTracker.markInstallOnboardingCompleted();
|
|
// Wait a bit for page to fully load
|
|
setTimeout(() => {
|
|
tourManager.startTour();
|
|
}, 1000);
|
|
} else {
|
|
const tourCompleted = progressTracker.isTourCompleted();
|
|
const currentStep = progressTracker.getCurrentStep();
|
|
debug(`[Onboarding] Tour not auto-starting (completed: ${tourCompleted}, step: ${currentStep})`);
|
|
|
|
// If tour is in progress, offer to resume
|
|
if (!tourCompleted && currentStep > 0) {
|
|
debug('[Onboarding] Tour in progress, can be resumed manually');
|
|
}
|
|
}
|
|
|
|
// Add restart tour button to tools row
|
|
addRestartTourButton();
|
|
|
|
// Expose to global scope for manual triggering
|
|
window.DashCaddyOnboarding = {
|
|
startTour: () => tourManager.startTour(),
|
|
restartTour: () => tourManager.restartTour(),
|
|
showTooltip: (id) => tourManager.showTooltip(id),
|
|
showWhatsNew: () => tourManager.showWhatsNew(),
|
|
resetProgress: () => progressTracker.resetProgress(),
|
|
getErrors: () => errorHandler.getErrors(),
|
|
getErrorStats: () => errorHandler.getStatistics()
|
|
};
|
|
|
|
debug('[Onboarding] System initialized successfully');
|
|
} catch (error) {
|
|
if (errorHandler) {
|
|
errorHandler.logError('[Onboarding] Initialization', error);
|
|
}
|
|
|
|
// Graceful degradation - don't break the dashboard
|
|
console.warn('[Onboarding] System failed to initialize, dashboard will continue without onboarding');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add restart tour button to tools row
|
|
*/
|
|
function addRestartTourButton() {
|
|
const toolsRow = document.querySelector('.tools-primary') || document.querySelector('.tools');
|
|
if (!toolsRow) return;
|
|
|
|
const clickHandler = () => {
|
|
if (tourManager) {
|
|
debug('[Onboarding] Starting tour via button click');
|
|
tourManager.restartTour();
|
|
} else {
|
|
if (errorHandler) {
|
|
errorHandler.logError('[Onboarding] Tour Manager Not Initialized', new Error('Tour manager not initialized'));
|
|
}
|
|
alert('Tour is not available. Check browser console for errors.\n\nPossible issues:\n- Driver.js library failed to load\n- JavaScript errors during initialization');
|
|
}
|
|
};
|
|
|
|
// If button already exists in the HTML, just attach the handler
|
|
const existing = document.getElementById('restart-tour-btn');
|
|
if (existing) {
|
|
existing.onclick = clickHandler;
|
|
return;
|
|
}
|
|
|
|
const button = document.createElement('button');
|
|
button.id = 'restart-tour-btn';
|
|
button.textContent = 'Help Tour';
|
|
button.title = 'Restart the onboarding tour';
|
|
button.onclick = clickHandler;
|
|
toolsRow.appendChild(button);
|
|
}
|
|
|
|
/**
|
|
* Check if Driver.js is loaded
|
|
*/
|
|
function checkDriverLoaded() {
|
|
// Driver.js v1.x IIFE: window.driver.js.driver is the factory function
|
|
const driverFactory = window.driver?.js?.driver || window.driver?.driver || window.driver;
|
|
if (typeof driverFactory !== 'function') {
|
|
console.warn('[Onboarding] Driver.js not loaded yet, will retry... window.driver:', window.driver);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Wait for Driver.js to load, then initialize
|
|
*/
|
|
function waitForDriver() {
|
|
let retries = 0;
|
|
const maxRetries = 10;
|
|
|
|
function attemptInit() {
|
|
if (checkDriverLoaded()) {
|
|
initializeOnboarding();
|
|
} else {
|
|
retries++;
|
|
if (retries < maxRetries) {
|
|
// Retry after a short delay
|
|
setTimeout(attemptInit, 500);
|
|
} else {
|
|
// Max retries reached, show fallback
|
|
if (errorHandler) {
|
|
errorHandler.handleDriverLoadFailure();
|
|
} else {
|
|
// Create temporary error handler for fallback
|
|
const tempHandler = new ErrorHandler();
|
|
tempHandler.handleDriverLoadFailure();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
attemptInit();
|
|
}
|
|
|
|
// Start initialization when DOM is ready
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', waitForDriver);
|
|
} else {
|
|
waitForDriver();
|
|
}
|
|
|
|
debug('[Onboarding] System loaded');
|
|
|
|
})();
|