/**
* 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 = `
`;
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
};