/** * widgets.js - Dashboard widgets (time, weather, etc.) */ const STORAGE_KEY_WIDGETS = 'enabled-widgets'; // Widget configurations const widgetConfigs = { clock: { name: 'Clock', icon: '🕐', enabled: true }, weather: { name: 'Weather', icon: '🌤️', enabled: false, apiKey: '', // User needs to set this location: 'auto' }, quote: { name: 'Daily Quote', icon: '💭', enabled: false } }; // Load widget settings function loadWidgetSettings() { try { const saved = localStorage.getItem(STORAGE_KEY_WIDGETS); return saved ? JSON.parse(saved) : widgetConfigs; } catch (e) { console.error('Error loading widget settings:', e); return widgetConfigs; } } // Save widget settings function saveWidgetSettings(settings) { try { localStorage.setItem(STORAGE_KEY_WIDGETS, JSON.stringify(settings)); } catch (e) { console.error('Error saving widget settings:', e); } } // Clock Widget function createClockWidget() { const widget = document.createElement('div'); widget.className = 'widget widget-clock'; const timeDisplay = document.createElement('div'); timeDisplay.className = 'widget-time'; const dateDisplay = document.createElement('div'); dateDisplay.className = 'widget-date'; function updateTime() { const now = new Date(); // Time const hours = now.getHours().toString().padStart(2, '0'); const minutes = now.getMinutes().toString().padStart(2, '0'); const seconds = now.getSeconds().toString().padStart(2, '0'); timeDisplay.textContent = `${hours}:${minutes}:${seconds}`; // Date const options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }; dateDisplay.textContent = now.toLocaleDateString('en-US', options); } updateTime(); setInterval(updateTime, 1000); widget.appendChild(timeDisplay); widget.appendChild(dateDisplay); return widget; } // Weather Widget function createWeatherWidget(settings) { const widget = document.createElement('div'); widget.className = 'widget widget-weather'; const loading = document.createElement('div'); loading.className = 'widget-loading'; loading.textContent = '🌤️ Loading weather...'; widget.appendChild(loading); // Check if API key is set if (!settings.apiKey) { widget.innerHTML = `

🌤️ Weather widget requires an API key

Get a free key from OpenWeatherMap
`; return widget; } // Fetch weather data let url = `https://api.openweathermap.org/data/2.5/weather?appid=${settings.apiKey}&units=metric`; if (settings.location === 'auto') { // Use geolocation navigator.geolocation.getCurrentPosition( (position) => { url += `&lat=${position.coords.latitude}&lon=${position.coords.longitude}`; fetchWeather(url, widget); }, () => { widget.innerHTML = '
Unable to get location
'; } ); } else { url += `&q=${settings.location}`; fetchWeather(url, widget); } return widget; } function fetchWeather(url, widget) { fetch(url) .then(res => res.json()) .then(data => { widget.innerHTML = `
${data.name}
${Math.round(data.main.temp)}°C
${data.weather[0].description}
💨 ${data.wind.speed} m/s 💧 ${data.main.humidity}%
`; }) .catch(error => { console.error('Weather fetch error:', error); widget.innerHTML = '
Failed to load weather
'; }); } // Quote Widget function createQuoteWidget() { const widget = document.createElement('div'); widget.className = 'widget widget-quote'; const loading = document.createElement('div'); loading.className = 'widget-loading'; loading.textContent = '💭 Loading quote...'; widget.appendChild(loading); fetch('https://api.quotable.io/random') .then(res => res.json()) .then(data => { widget.innerHTML = `

"${data.content}"

— ${data.author}

`; }) .catch(error => { console.error('Quote fetch error:', error); widget.innerHTML = '
Failed to load quote
'; }); return widget; } // Create widget container function createWidgetContainer() { const container = document.createElement('div'); container.id = 'widgets-container'; container.className = 'widgets-container'; return container; } // Initialize widgets function initWidgets() { const settings = loadWidgetSettings(); const container = createWidgetContainer(); // Add enabled widgets if (settings.clock?.enabled) { container.appendChild(createClockWidget()); } if (settings.weather?.enabled) { container.appendChild(createWeatherWidget(settings.weather)); } if (settings.quote?.enabled) { container.appendChild(createQuoteWidget()); } // Add to header if any widgets are enabled if (container.children.length > 0) { const header = document.querySelector('header'); if (header) { header.appendChild(container); } } // Create widget settings button createWidgetSettings(); } // Create widget settings UI function createWidgetSettings() { const button = document.createElement('button'); button.className = 'widget-settings-btn control-btn'; button.innerHTML = '⚙️ Widgets'; button.title = 'Widget Settings'; button.addEventListener('click', showWidgetSettingsModal); const header = document.querySelector('header'); if (header) { let controls = header.querySelector('.import-export-controls'); if (!controls) { controls = document.createElement('div'); controls.className = 'import-export-controls'; header.appendChild(controls); } controls.appendChild(button); } } // Show widget settings modal function showWidgetSettingsModal() { const settings = loadWidgetSettings(); const modal = document.createElement('div'); modal.className = 'modal'; modal.innerHTML = ` `; document.body.appendChild(modal); // Event listeners modal.querySelector('.modal-close').addEventListener('click', () => modal.remove()); modal.querySelector('.btn-cancel').addEventListener('click', () => modal.remove()); modal.querySelector('.btn-save').addEventListener('click', () => { const newSettings = { clock: { ...widgetConfigs.clock, enabled: modal.querySelector('#widget-clock').checked }, weather: { ...widgetConfigs.weather, enabled: modal.querySelector('#widget-weather').checked, apiKey: modal.querySelector('#weather-api-key').value, location: modal.querySelector('#weather-location').value || 'auto' }, quote: { ...widgetConfigs.quote, enabled: modal.querySelector('#widget-quote').checked } }; saveWidgetSettings(newSettings); modal.remove(); location.reload(); }); // Close on background click modal.addEventListener('click', (e) => { if (e.target === modal) modal.remove(); }); } // Export for use in other modules window.widgetsModule = { init: initWidgets };