316 lines
8.8 KiB
JavaScript
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">×</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
|
|
};
|