Files
dashcaddy/status/js/onboarding.js
Krystie f60a3370db fix: route all console.error through ErrorHandler for consistent tracking
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.
2026-05-14 01:31:55 -07:00

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');
})();