refactoring
This commit is contained in:
1 parent
69c67976c7
commit
4b5c254e41
10 files changed
+779
-750
No files matched your search
+61
-52
@@ -27,19 +27,44 @@ function getWeatherPresentation(code) {
|
||||
return { key: 'weather_partly_cloudy', icon: 'fa-cloud-sun', color: 'text-amber-500' };
|
||||
}
|
||||
|
||||
function hasLanguage(lang) {
|
||||
return Object.hasOwn(translations, lang);
|
||||
}
|
||||
|
||||
function translateWeather(key) {
|
||||
return translations[currentLang][key] || translations.en[key];
|
||||
}
|
||||
|
||||
function renderWeatherCondition() {
|
||||
const condition = document.getElementById('weather-condition');
|
||||
if (condition) condition.textContent = translations[currentLang][currentWeatherCondition] || translations.en[currentWeatherCondition];
|
||||
if (condition) condition.textContent = translateWeather(currentWeatherCondition);
|
||||
|
||||
const currentForecastCondition = document.getElementById('weather-current-condition');
|
||||
if (currentForecastCondition) {
|
||||
currentForecastCondition.textContent = translations[currentLang][currentWeatherCondition] || translations.en[currentWeatherCondition];
|
||||
}
|
||||
if (currentForecastCondition) currentForecastCondition.textContent = translateWeather(currentWeatherCondition);
|
||||
|
||||
const tomorrowForecastCondition = document.getElementById('weather-tomorrow-condition');
|
||||
if (tomorrowForecastCondition) {
|
||||
tomorrowForecastCondition.textContent = translations[currentLang][tomorrowWeatherCondition] || translations.en[tomorrowWeatherCondition];
|
||||
}
|
||||
if (tomorrowForecastCondition) tomorrowForecastCondition.textContent = translateWeather(tomorrowWeatherCondition);
|
||||
}
|
||||
|
||||
function formatForecastDate(isoDate, lang) {
|
||||
const [year, month, day] = isoDate.split('-').map(Number);
|
||||
const date = new Date(year, month - 1, day, 12);
|
||||
return new Intl.DateTimeFormat(languageLocales[lang].replace('_', '-'), {
|
||||
weekday: 'short',
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
}).format(date);
|
||||
}
|
||||
|
||||
function isValidForecast(current, daily) {
|
||||
return Number.isFinite(current?.temperature_2m)
|
||||
&& Number.isFinite(current?.weather_code)
|
||||
&& Array.isArray(daily?.time)
|
||||
&& daily.time.length >= 2
|
||||
&& /^\d{4}-\d{2}-\d{2}$/.test(daily.time[1])
|
||||
&& [daily.weather_code, daily.temperature_2m_max, daily.temperature_2m_min].every(values => Array.isArray(values) && values.length >= 2)
|
||||
&& Number.isFinite(daily.weather_code[1])
|
||||
&& [daily.temperature_2m_max[0], daily.temperature_2m_min[0], daily.temperature_2m_max[1], daily.temperature_2m_min[1]].every(Number.isFinite);
|
||||
}
|
||||
|
||||
function setWeatherRange(id, value) {
|
||||
@@ -62,29 +87,8 @@ async function loadWeatherForecast() {
|
||||
const response = await fetch('https://api.open-meteo.com/v1/forecast?latitude=12.2388&longitude=109.1967¤t=temperature_2m,weather_code&daily=weather_code,temperature_2m_max,temperature_2m_min&timezone=Asia%2FHo_Chi_Minh&forecast_days=2');
|
||||
if (!response.ok) throw new Error('Weather request failed');
|
||||
|
||||
const data = await response.json();
|
||||
const current = data.current;
|
||||
const daily = data.daily;
|
||||
if (
|
||||
!Number.isFinite(current?.temperature_2m)
|
||||
|| !Number.isFinite(current?.weather_code)
|
||||
|| !Array.isArray(daily?.time)
|
||||
|| !Array.isArray(daily?.weather_code)
|
||||
|| !Array.isArray(daily?.temperature_2m_max)
|
||||
|| !Array.isArray(daily?.temperature_2m_min)
|
||||
|| daily.time.length < 2
|
||||
|| daily.weather_code.length < 2
|
||||
|| daily.temperature_2m_max.length < 2
|
||||
|| daily.temperature_2m_min.length < 2
|
||||
|| !Number.isFinite(daily.weather_code[1])
|
||||
|| !Number.isFinite(daily.temperature_2m_max[0])
|
||||
|| !Number.isFinite(daily.temperature_2m_min[0])
|
||||
|| !Number.isFinite(daily.temperature_2m_max[1])
|
||||
|| !Number.isFinite(daily.temperature_2m_min[1])
|
||||
|| !/^\d{4}-\d{2}-\d{2}$/.test(daily.time[1])
|
||||
) {
|
||||
throw new Error('Weather response is incomplete');
|
||||
}
|
||||
const { current, daily } = await response.json();
|
||||
if (!isValidForecast(current, daily)) throw new Error('Weather response is incomplete');
|
||||
|
||||
const currentPresentation = getWeatherPresentation(current.weather_code);
|
||||
const tomorrowPresentation = getWeatherPresentation(daily.weather_code[1]);
|
||||
@@ -100,14 +104,8 @@ async function loadWeatherForecast() {
|
||||
setWeatherRange('weather-tomorrow-low', daily.temperature_2m_min[1]);
|
||||
|
||||
if (tomorrowDate) {
|
||||
const [year, month, day] = daily.time[1].split('-').map(Number);
|
||||
const date = new Date(year, month - 1, day, 12);
|
||||
tomorrowDate.dateTime = daily.time[1];
|
||||
tomorrowDate.textContent = new Intl.DateTimeFormat(languageLocales[currentLang].replace('_', '-'), {
|
||||
weekday: 'short',
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
}).format(date);
|
||||
tomorrowDate.textContent = formatForecastDate(daily.time[1], currentLang);
|
||||
}
|
||||
|
||||
currentWeatherCondition = currentPresentation.key;
|
||||
@@ -149,11 +147,11 @@ function getPreferredLanguage() {
|
||||
|
||||
function getRequestedLanguage() {
|
||||
const requested = new URLSearchParams(window.location.search).get('lang');
|
||||
return translations[requested] ? requested : null;
|
||||
return hasLanguage(requested) ? requested : null;
|
||||
}
|
||||
|
||||
function setLanguage(lang) {
|
||||
if (!translations[lang]) lang = 'vi';
|
||||
if (!hasLanguage(lang)) lang = 'vi';
|
||||
currentLang = lang;
|
||||
document.querySelectorAll('[data-i18n]').forEach(el => {
|
||||
const key = el.getAttribute('data-i18n');
|
||||
@@ -163,8 +161,7 @@ function setLanguage(lang) {
|
||||
});
|
||||
|
||||
// Reset language button styles
|
||||
const langs = ['vi', 'ru', 'en', 'zh', 'de', 'fr'];
|
||||
langs.forEach(l => {
|
||||
Object.keys(translations).forEach(l => {
|
||||
const btn = document.getElementById(`btn-${l}`);
|
||||
if (btn) {
|
||||
if (l === lang) {
|
||||
@@ -190,27 +187,20 @@ function setLanguage(lang) {
|
||||
renderWeatherCondition();
|
||||
|
||||
const tomorrowDate = document.getElementById('weather-tomorrow-date');
|
||||
if (tomorrowDate.dateTime) {
|
||||
const [year, month, day] = tomorrowDate.dateTime.split('-').map(Number);
|
||||
tomorrowDate.textContent = new Intl.DateTimeFormat(languageLocales[lang].replace('_', '-'), {
|
||||
weekday: 'short',
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
}).format(new Date(year, month - 1, day, 12));
|
||||
}
|
||||
if (tomorrowDate.dateTime) tomorrowDate.textContent = formatForecastDate(tomorrowDate.dateTime, lang);
|
||||
}
|
||||
|
||||
function toggleMobileMenu() {
|
||||
const menu = document.getElementById('mobile-menu');
|
||||
menu.classList.toggle('hidden');
|
||||
document.querySelector('[aria-controls="mobile-menu"]').setAttribute('aria-expanded', !menu.classList.contains('hidden'));
|
||||
document.getElementById('mobile-menu-toggle').setAttribute('aria-expanded', !menu.classList.contains('hidden'));
|
||||
}
|
||||
|
||||
function filterAttractions(category, event) {
|
||||
function filterAttractions(category, activeButton) {
|
||||
document.querySelectorAll('.filter-btn').forEach(btn => {
|
||||
btn.className = "filter-btn px-5 py-2 rounded-xl text-sm font-bold bg-white text-gray-600 hover:bg-gray-100 shadow-sm";
|
||||
});
|
||||
event.currentTarget.className = "filter-btn active px-5 py-2 rounded-xl text-sm font-bold bg-brand-500 text-white shadow";
|
||||
activeButton.className = "filter-btn active px-5 py-2 rounded-xl text-sm font-bold bg-brand-500 text-white shadow";
|
||||
|
||||
document.querySelectorAll('.attraction-card').forEach(card => {
|
||||
if (category === 'all' || card.getAttribute('data-category') === category) {
|
||||
@@ -250,6 +240,25 @@ function closeModal() {
|
||||
document.getElementById('success-modal').classList.add('hidden');
|
||||
}
|
||||
|
||||
function bindEvents() {
|
||||
document.querySelectorAll('[data-lang]').forEach(btn => {
|
||||
btn.addEventListener('click', () => setLanguage(btn.dataset.lang));
|
||||
});
|
||||
|
||||
document.getElementById('mobile-menu-toggle').addEventListener('click', toggleMobileMenu);
|
||||
document.querySelectorAll('.mobile-nav-link').forEach(link => {
|
||||
link.addEventListener('click', toggleMobileMenu);
|
||||
});
|
||||
|
||||
document.querySelectorAll('.filter-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => filterAttractions(btn.dataset.filter, btn));
|
||||
});
|
||||
|
||||
document.getElementById('booking-form').addEventListener('submit', handleBooking);
|
||||
document.getElementById('modal-close-btn').addEventListener('click', closeModal);
|
||||
}
|
||||
|
||||
bindEvents();
|
||||
setLanguage(getRequestedLanguage() || getPreferredLanguage());
|
||||
loadWeatherForecast();
|
||||
setInterval(loadWeatherForecast, 30 * 60 * 1000);
|
||||
Reference in new issue
Block a user