Files
Homepage/js/widgets.js

316 lines
8.8 KiB
JavaScript

/**
* 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 = `
<div class="widget-error">
<p>🌤️ Weather widget requires an API key</p>
<small>Get a free key from <a href="https://openweathermap.org/api" target="_blank">OpenWeatherMap</a></small>
</div>
`;
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 = '<div class="widget-error">Unable to get location</div>';
}
);
} else {
url += `&q=${settings.location}`;
fetchWeather(url, widget);
}
return widget;
}
function fetchWeather(url, widget) {
fetch(url)
.then(res => res.json())
.then(data => {
widget.innerHTML = `
<div class="weather-info">
<div class="weather-location">${data.name}</div>
<div class="weather-temp">${Math.round(data.main.temp)}°C</div>
<div class="weather-desc">${data.weather[0].description}</div>
<div class="weather-details">
<span>💨 ${data.wind.speed} m/s</span>
<span>💧 ${data.main.humidity}%</span>
</div>
</div>
`;
})
.catch(error => {
console.error('Weather fetch error:', error);
widget.innerHTML = '<div class="widget-error">Failed to load weather</div>';
});
}
// 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 = `
<div class="quote-content">
<p class="quote-text">"${data.content}"</p>
<p class="quote-author">— ${data.author}</p>
</div>
`;
})
.catch(error => {
console.error('Quote fetch error:', error);
widget.innerHTML = '<div class="widget-error">Failed to load quote</div>';
});
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 = `
<div class="modal-content">
<div class="modal-header">
<h2>Widget Settings</h2>
<button class="modal-close">&times;</button>
</div>
<div class="modal-body">
<div class="widget-setting">
<label>
<input type="checkbox" id="widget-clock" ${settings.clock?.enabled ? 'checked' : ''}>
🕐 Clock
</label>
</div>
<div class="widget-setting">
<label>
<input type="checkbox" id="widget-weather" ${settings.weather?.enabled ? 'checked' : ''}>
🌤️ Weather
</label>
<div class="widget-subsetting">
<input type="text" id="weather-api-key" placeholder="OpenWeatherMap API Key" value="${settings.weather?.apiKey || ''}">
<input type="text" id="weather-location" placeholder="Location (or 'auto')" value="${settings.weather?.location || 'auto'}">
</div>
</div>
<div class="widget-setting">
<label>
<input type="checkbox" id="widget-quote" ${settings.quote?.enabled ? 'checked' : ''}>
💭 Daily Quote
</label>
</div>
</div>
<div class="modal-footer">
<button class="btn-save">Save & Reload</button>
<button class="btn-cancel">Cancel</button>
</div>
</div>
`;
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
};