36092-vm/assets/js/main.js
Flatlogic Bot 301e490568 op
2025-11-23 07:47:10 +00:00

422 lines
17 KiB
JavaScript

document.addEventListener('DOMContentLoaded', () => {
const darkModeToggle = document.getElementById('darkModeToggle');
const body = document.body;
const celsiusRadio = document.getElementById('celsius');
const fahrenheitRadio = document.getElementById('fahrenheit');
// Apply the cached theme on page load
if (localStorage.getItem('darkMode') === 'enabled') {
body.classList.add('dark-mode');
darkModeToggle.checked = true;
}
darkModeToggle.addEventListener('change', () => {
if (darkModeToggle.checked) {
body.classList.add('dark-mode');
localStorage.setItem('darkMode', 'enabled');
} else {
body.classList.remove('dark-mode');
localStorage.setItem('darkMode', 'disabled');
}
});
const weatherDisplay = document.getElementById('weather-display');
const citySearchForm = document.getElementById('city-search-form');
const cityInput = document.getElementById('city-input');
const favoriteLocationsList = document.getElementById('favorite-locations-list');
const searchHistoryContainer = document.getElementById('search-history');
const getUnit = () => localStorage.getItem('unit') || 'metric';
const getSearchHistory = () => JSON.parse(localStorage.getItem('searchHistory')) || [];
const saveSearchHistory = (city) => {
let history = getSearchHistory();
history = history.filter(item => item.toLowerCase() !== city.toLowerCase());
history.unshift(city);
if (history.length > 5) {
history.pop();
}
localStorage.setItem('searchHistory', JSON.stringify(history));
displaySearchHistory();
};
const displaySearchHistory = () => {
const history = getSearchHistory();
if (history.length > 0) {
let historyHtml = '<h6>Recent Searches:</h6><div class="d-flex flex-wrap">';
history.forEach(city => {
historyHtml += `<a href="#" class="btn btn-sm btn-outline-secondary me-2 mb-2 history-link" data-city="${city}">${city}</a>`;
});
historyHtml += '</div>';
searchHistoryContainer.innerHTML = historyHtml;
} else {
searchHistoryContainer.innerHTML = '';
}
};
const showLoading = () => {
weatherDisplay.innerHTML = '<div class="card-body text-center"><div class="spinner-border text-primary" role="status"><span class="visually-hidden">Loading...</span></div><p class="mt-2">Loading weather...</p></div>';
};
const showError = (message) => {
weatherDisplay.innerHTML = `<div class="card-body text-center"><div class="alert alert-danger">${message}</div></div>`;
};
const getAqiString = (aqi) => {
switch (aqi) {
case 1: return 'Good';
case 2: return 'Fair';
case 3: return 'Moderate';
case 4: return 'Poor';
case 5: return 'Very Poor';
default: return 'Unknown';
}
};
const showWeather = (data) => {
if (!data || !data.name || data.cod === "404") {
showError(data.message || 'Could not display weather data.');
return;
}
const unit = getUnit();
const unitSymbol = unit === 'metric' ? '°C' : '°F';
const iconUrl = `https://openweathermap.org/img/wn/${data.weather[0].icon}@2x.png`;
let favoriteStar = '';
if (isUserLoggedIn) {
favoriteStar = ` <span id="add-to-favorites" class="favorite-star" style="cursor: pointer; color: orange; font-size: 1.2em;">&#9734;</span>`; // Empty star
}
let aqiHtml = '';
if (data.aqi) {
aqiHtml = `<p>Air Quality: ${getAqiString(data.aqi)}</p>`;
}
let forecastHtml = '';
if (data.forecast && data.forecast.length > 0) {
forecastHtml += '<div class="mt-4"><h4>5-Day Forecast</h4><div class="row">';
data.forecast.forEach(day => {
const dayDate = new Date(day.dt * 1000);
const dayName = dayDate.toLocaleDateString('en-US', { weekday: 'short' });
const iconUrl = `https://openweathermap.org/img/wn/${day.icon}.png`;
forecastHtml += `
<div class="col text-center forecast-day">
<p>${dayName}</p>
<img src="${iconUrl}" alt="${day.description}" title="${day.description}">
<p>${day.temp.toFixed(1)}${unitSymbol}</p>
</div>
`;
});
forecastHtml += '</div></div>';
}
weatherDisplay.innerHTML = `
<div class="card-body text-center" data-lat="${data.coord.lat}" data-lon="${data.coord.lon}" data-city="${data.name}">
<h2 class="card-title">${data.name}${favoriteStar}</h2>
<img src="${iconUrl}" alt="Weather icon" class="weather-icon">
<h3>${data.main.temp}${unitSymbol}</h3>
<p class="text-muted">${data.weather[0].description}</p>
<p>Humidity: ${data.main.humidity}%</p>
${aqiHtml}
${forecastHtml}
</div>
<div id="weather-map" style="height: 400px; margin-top: 20px;"></div>
`;
initMap(data.coord.lat, data.coord.lon);
saveSearchHistory(data.name);
};
const initMap = (lat, lon) => {
const apiKey = '2350ecafed26b115b1557305d956630d';
const mapContainer = document.getElementById('weather-map');
if (!mapContainer || mapContainer._leaflet_id) {
// If map is already initialized, just update its view
if (mapContainer._leaflet_id) {
mapContainer._leaflet_map.setView([lat, lon], 10);
}
return;
}
const map = L.map('weather-map').setView([lat, lon], 10);
mapContainer._leaflet_map = map; // Store map instance
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
const tempLayer = L.tileLayer(`https://tile.openweathermap.org/map/temp_new/{z}/{x}/{y}.png?appid=${apiKey}`, {
attribution: 'Map data &copy; <a href="https://openweathermap.org">OpenWeatherMap</a>'
});
const cloudsLayer = L.tileLayer(`https://tile.openweathermap.org/map/clouds_new/{z}/{x}/{y}.png?appid=${apiKey}`, {
attribution: 'Map data &copy; <a href="https://openweathermap.org">OpenWeatherMap</a>'
});
const precipitationLayer = L.tileLayer(`https://tile.openweathermap.org/map/precipitation_new/{z}/{x}/{y}.png?appid=${apiKey}`, {
attribution: 'Map data &copy; <a href="https://openweathermap.org">OpenWeatherMap</a>'
});
const baseMaps = {
"OpenStreetMap": L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png')
};
const overlayMaps = {
"Temperature": tempLayer,
"Clouds": cloudsLayer,
"Precipitation": precipitationLayer
};
L.control.layers(baseMaps, overlayMaps).addTo(map);
overlayMaps.Temperature.addTo(map);
};
const fetchWeatherByCoords = (lat, lon) => {
const unit = getUnit();
showLoading();
fetch(`weather.php?lat=${lat}&lon=${lon}&units=${unit}`)
.then(response => response.json())
.then(data => {
if (data.error) {
showError(data.error);
} else {
showWeather(data);
}
})
.catch(() => showError('Failed to fetch weather data.'));
};
const fetchWeatherByCity = (city) => {
const unit = getUnit();
showLoading();
fetch(`weather.php?city=${encodeURIComponent(city)}&units=${unit}`)
.then(response => response.json())
.then(data => {
if (data.error || data.cod === "404") {
showError(data.error || data.message);
} else {
showWeather(data);
}
})
.catch(() => showError('Failed to fetch weather data.'));
};
const saveLocation = () => {
const weatherCardBody = weatherDisplay.querySelector('.card-body');
if (!weatherCardBody) return;
const city = weatherCardBody.dataset.city;
const lat = weatherCardBody.dataset.lat;
const lon = weatherCardBody.dataset.lon;
if (!city || !lat || !lon) {
alert('Could not get location data to save.');
return;
}
fetch('save_location.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ city: city, lat: lat, lon: lon }),
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Location saved!');
location.reload(); // Reload to see the updated list
} else {
alert(data.message || 'Could not save location.');
}
})
.catch(() => alert('An error occurred while saving the location.'));
};
const deleteLocation = (locationId, listItemElement) => {
if (!confirm('Are you sure you want to delete this location?')) return;
fetch('delete_location.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ location_id: locationId }),
})
.then(response => response.json())
.then(data => {
if (data.success) {
listItemElement.remove();
alert('Location deleted!');
} else {
alert(data.message || 'Could not delete location.');
}
})
.catch(() => alert('An error occurred.'));
};
const handleSubscription = (locationId, time, subscriptionId, listItem) => {
const isSubscribing = !!locationId;
const url = isSubscribing ? 'subscribe_weather.php' : 'unsubscribe_weather.php';
const body = isSubscribing ? { location_id: locationId, time: time } : { subscription_id: subscriptionId };
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert(data.message);
// Dynamically update the UI
const controlsDiv = listItem.querySelector('.subscription-controls');
if (isSubscribing) {
const newTime = new Date(`1970-01-01T${time}Z`);
const formattedTime = newTime.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', timeZone: 'UTC' });
controlsDiv.innerHTML = `
<small>Subscribed for daily alerts at ${formattedTime}.</small>
<button class="btn btn-sm btn-warning unsubscribe-btn" data-id="${data.subscription_id}">Unsubscribe</button>
`;
} else {
controlsDiv.innerHTML = `
<div class="input-group input-group-sm">
<input type="time" class="form-control alert-time-input" value="08:00">
<button class="btn btn-sm btn-primary subscribe-btn">Subscribe</button>
</div>
`;
}
} else {
alert(data.message || 'An error occurred.');
}
})
.catch(() => alert('A network error occurred.'));
};
citySearchForm.addEventListener('submit', (e) => {
e.preventDefault();
const city = cityInput.value.trim();
if (city) {
fetchWeatherByCity(city);
}
});
weatherDisplay.addEventListener('click', (e) => {
if (e.target.id === 'add-to-favorites') {
saveLocation();
}
});
if (favoriteLocationsList) {
favoriteLocationsList.addEventListener('click', (e) => {
e.preventDefault();
const listItem = e.target.closest('li');
if (e.target.classList.contains('subscribe-btn')) {
const locationId = listItem.dataset.locationId;
const timeInput = listItem.querySelector('.alert-time-input');
handleSubscription(locationId, timeInput.value, null, listItem);
} else if (e.target.classList.contains('unsubscribe-btn')) {
const subscriptionId = e.target.dataset.id;
handleSubscription(null, null, subscriptionId, listItem);
}
});
}
if (searchHistoryContainer) {
searchHistoryContainer.addEventListener('click', (e) => {
e.preventDefault();
if (e.target.classList.contains('history-link')) {
const city = e.target.dataset.city;
fetchWeatherByCity(city);
}
});
}
const getHistoryBtn = document.getElementById('get-history-btn');
if (getHistoryBtn) {
getHistoryBtn.addEventListener('click', () => {
const city = document.querySelector('#weather-display .card-title')?.textContent;
const date = document.getElementById('history-date').value;
if (!city) {
alert('Please search for a city first.');
return;
}
if (!date) {
alert('Please select a date.');
return;
}
fetch(`get_history.php?city=${encodeURIComponent(city)}&date=${date}`)
.then(response => response.json())
.then(data => {
const historyDisplay = document.getElementById('history-display');
if (data.error) {
historyDisplay.innerHTML = `<div class="alert alert-danger">${data.error}</div>`;
} else if (data.length === 0) {
historyDisplay.innerHTML = `<div class="alert alert-info">No historical data found for this date.</div>`;
} else {
let table = '<table class="table table-striped"><thead><tr><th>Time</th><th>Temp</th><th>Humidity</th><th>Description</th></tr></thead><tbody>';
data.forEach(record => {
const recordDate = new Date(record.timestamp + ' UTC');
table += `<tr>
<td>${recordDate.toLocaleTimeString()}</td>
<td>${record.temperature}°C</td>
<td>${record.humidity}%</td>
<td>${record.description}</td>
</tr>`;
});
table += '</tbody></table>';
historyDisplay.innerHTML = table;
}
})
.catch(() => {
const historyDisplay = document.getElementById('history-display');
historyDisplay.innerHTML = `<div class="alert alert-danger">Failed to fetch historical data.</div>`;
});
});
}
// Set initial unit toggle state
if (getUnit() === 'imperial') {
fahrenheitRadio.checked = true;
} else {
celsiusRadio.checked = true;
}
// Add event listeners for unit switching
[celsiusRadio, fahrenheitRadio].forEach(radio => {
radio.addEventListener('change', () => {
const selectedUnit = fahrenheitRadio.checked ? 'imperial' : 'metric';
localStorage.setItem('unit', selectedUnit);
const currentCity = document.querySelector('#weather-display .card-title')?.textContent;
if (currentCity) {
fetchWeatherByCity(currentCity);
}
});
});
// Initial weather fetch
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
position => {
fetchWeatherByCoords(position.coords.latitude, position.coords.longitude);
},
() => {
displaySearchHistory();
// Don't show an error, just let the user search.
}
);
} else {
displaySearchHistory();
}
// Initial display of search history
displaySearchHistory();
});