From 1c264c100e644fd50f611516f45c3ea8467b1dd2 Mon Sep 17 00:00:00 2001 From: Audrey Clark Date: Tue, 14 May 2024 23:47:09 -0500 Subject: [PATCH 1/7] Add page layout, start of weather search and api functionality --- index.html | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ main.js | 58 +++++++++++++++++++++++++++++++++++++++++ style.css | 30 +++++++++++++++++++++ 3 files changed, 164 insertions(+) create mode 100644 index.html create mode 100644 main.js create mode 100644 style.css diff --git a/index.html b/index.html new file mode 100644 index 00000000..19495e15 --- /dev/null +++ b/index.html @@ -0,0 +1,76 @@ + + + + + + + + Weather App Eval + + + +
+
+
+ + + +
+ + +
+ +
+ +
+
+ + +
+
Temp, City, Weather Description Placeholder
+
Weather Icon
+
+ + +
+
+ +
+

Sunday

+
+
+

Monday

+
+
+

Tuesday

+
+
+

Wednesday

+
+
+

Thursday

+
+
+

Friday

+
+
+

Saturday

+
+
+
+
+
+
+ + + + + diff --git a/main.js b/main.js new file mode 100644 index 00000000..c5eebd30 --- /dev/null +++ b/main.js @@ -0,0 +1,58 @@ +const currentWeather = document.querySelector(".current-weather"); +const forecast = document.querySelector(".forecast"); +const cityInput = document.querySelector("#city-input"); +const searchBtn = document.querySelector(".search"); +const card = document.querySelector(".card"); +const apiKey = "3a16b1625fdf1f83be7f01c3a15bd5f0"; +const units = '&units=imperial'; + +searchBtn.addEventListener("click", (event) => { + event.preventDefault(); + getWeather(cityInput.value); +}); + +async function getWeatherData(city) { + const apiUrl = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}`; + + const response = await fetch(apiUrl); + + if(!response.ok){ + throw new Error("Could not fetch weather data"); + } + return await response.json(); +}; + + +// Determines the weather icon based on the weather ID from the API +function getWeatherIcon(weatherId) { + + switch(true){ + case (weatherId >= 200 && weatherId <= 300): + return "https://openweathermap.org/img/wn/11d@2x.png"; + case (weatherId >= 300 && weatherId <= 400): + return "https://openweathermap.org/img/wn/10d@2x.png"; + case (weatherId >= 500 && weatherId <= 600): + return "https://openweathermap.org/img/wn/09d@2x.png"; + case (weatherId >= 600 && weatherId <= 700): + return "https://openweathermap.org/img/wn/13d@2x.png"; + case (weatherId >= 700 && weatherId <= 780): + return "https://openweathermap.org/img/wn/50d@2x.png"; + case (weatherId === 800): + return "https://openweathermap.org/img/wn/01d@2x.png"; + case (weatherId >= 801 && weatherId <= 810): + return "https://openweathermap.org/img/wn/03d@2x.png"; + default: + return "❓"; + } +}; + + +function displayError(message) { + const errorDisplay = document.createElement("p"); + errorDisplay.textContent = message; + errorDisplay.classList.add("errorDisplay"); + + card.textContent = ""; + card.style.display = "flex"; + card.appendChild(errorDisplay); +}; \ No newline at end of file diff --git a/style.css b/style.css new file mode 100644 index 00000000..04bb2365 --- /dev/null +++ b/style.css @@ -0,0 +1,30 @@ +body { + display: flex; + min-height: 100vh; + margin: 0; + align-items: center; + justify-content: center; + background-color: #bfdfec; + font-family: system-ui, calibri, serif; + background-repeat: repeat-x; + background-image: url("https://i.stack.imgur.com/b7z29.png"); + animation: movement 10s linear infinite; +} + +@keyframes movement { + 0% { + background-position: 0px 0px; + } + 100% { + background-position: 560px 0px; + } +} + +.page-header { + margin-top: 20px; +} + +.search-form { + margin-top: 30px; + margin-bottom: 30px; +} From 9aeece957bd79187cf59821cb89415785558f4b1 Mon Sep 17 00:00:00 2001 From: Audrey Clark Date: Sat, 18 May 2024 17:14:20 -0500 Subject: [PATCH 2/7] Update fetch response, update html layout --- index.html | 73 ++++++++++++++++++++++++++++++++++-------------------- main.js | 59 ++++++++++++++++++++++++++----------------- 2 files changed, 82 insertions(+), 50 deletions(-) diff --git a/index.html b/index.html index 19495e15..17533340 100644 --- a/index.html +++ b/index.html @@ -13,55 +13,75 @@ Weather App Eval - -
+
- - -
- - -
-
- + + +
+ +
+
-
+
-
-
Temp, City, Weather Description Placeholder
-
Weather Icon
+
+
+

Cedar Park

+

Cloudy

+

69°F

+
+
+ weather icon +
- -
-
- -
+ +
+
+

Sunday

-
+

Monday

-
+

Tuesday

-
+

Wednesday

-
+

Thursday

-
+

Friday

-
+

Saturday

@@ -70,7 +90,6 @@

Weather App Project

- diff --git a/main.js b/main.js index c5eebd30..af81fde4 100644 --- a/main.js +++ b/main.js @@ -1,25 +1,38 @@ const currentWeather = document.querySelector(".current-weather"); -const forecast = document.querySelector(".forecast"); -const cityInput = document.querySelector("#city-input"); +const currentWeatherIcon = document.querySelector(".current-weather-icon"); +const forecast = document.querySelector(".week-forecast"); +const city = document.querySelector("#city-input"); const searchBtn = document.querySelector(".search"); const card = document.querySelector(".card"); -const apiKey = "3a16b1625fdf1f83be7f01c3a15bd5f0"; -const units = '&units=imperial'; -searchBtn.addEventListener("click", (event) => { + + +document.getElementById('weather-form').addEventListener('submit', function(event) { event.preventDefault(); - getWeather(cityInput.value); + getWeather(city.value); }); -async function getWeatherData(city) { - const apiUrl = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}`; +function getWeather(city) { + const apiKey = "3a16b1625fdf1f83be7f01c3a15bd5f0"; + const apiUrl = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=imperial`; - const response = await fetch(apiUrl); - - if(!response.ok){ - throw new Error("Could not fetch weather data"); - } - return await response.json(); + fetch(apiUrl) + .then(response => response.json()) + .then(data => { + if (data.cod === 200) { + document.querySelector('.weather-city').textContent = `Weather in ${data.name}`; + document.querySelector('.weather-description').textContent = `${data.weather[0].description}`; + document.querySelector('.weather-temperature').textContent = `${Math.round(data.main.temp)}°F`; + currentWeatherIcon.setAttribute('src', getWeatherIcon(data.weather[0].id)); + currentWeatherIcon.setAttribute('alt', `${data.weather[0].description}`); + displayWeather(data); + } else { + alert('City not found. Please enter a valid city name.'); + } + }) + .catch(error => { + console.error('Error fetching weather data:', error); + }); }; @@ -28,25 +41,25 @@ function getWeatherIcon(weatherId) { switch(true){ case (weatherId >= 200 && weatherId <= 300): - return "https://openweathermap.org/img/wn/11d@2x.png"; + return "https://openweathermap.org/img/wn/11n@2x.png"; case (weatherId >= 300 && weatherId <= 400): - return "https://openweathermap.org/img/wn/10d@2x.png"; + return "https://openweathermap.org/img/wn/10n@2x.png"; case (weatherId >= 500 && weatherId <= 600): - return "https://openweathermap.org/img/wn/09d@2x.png"; + return "https://openweathermap.org/img/wn/09n@2x.png"; case (weatherId >= 600 && weatherId <= 700): - return "https://openweathermap.org/img/wn/13d@2x.png"; + return "https://openweathermap.org/img/wn/13n@2x.png"; case (weatherId >= 700 && weatherId <= 780): - return "https://openweathermap.org/img/wn/50d@2x.png"; + return "https://openweathermap.org/img/wn/50n@2x.png"; case (weatherId === 800): - return "https://openweathermap.org/img/wn/01d@2x.png"; + return "https://openweathermap.org/img/wn/01n@2x.png"; case (weatherId >= 801 && weatherId <= 810): - return "https://openweathermap.org/img/wn/03d@2x.png"; + return "https://openweathermap.org/img/wn/03n@2x.png"; default: return "❓"; - } + } }; - +// Error messaging function displayError(message) { const errorDisplay = document.createElement("p"); errorDisplay.textContent = message; From 0ca9ec72b29d6d2e03bdaf1a5b928e4f4e5164c0 Mon Sep 17 00:00:00 2001 From: Audrey Clark Date: Thu, 30 May 2024 23:57:51 -0500 Subject: [PATCH 3/7] Add forecast functionality, update styling, clean and make it work --- index.html | 63 ++++------------ main.js | 216 +++++++++++++++++++++++++++++++++++++++-------------- style.css | 25 ++----- 3 files changed, 177 insertions(+), 127 deletions(-) diff --git a/index.html b/index.html index 17533340..276da20a 100644 --- a/index.html +++ b/index.html @@ -3,19 +3,17 @@ - + Weather App Eval -
+
-
+
Weather App Project placeholder="Enter City Here" />
- - + +
+ +
+

-
-

Cedar Park

-

Cloudy

-

69°F

-
-
- weather icon -
-
+ class="current-weather row align-items-center justify-content-center bg-primary mt-5 gy-5" + >
-
-
-

Sunday

-
-
-

Monday

-
-
-

Tuesday

-
-
-

Wednesday

-
-
-

Thursday

-
-
-

Friday

-
-
-

Saturday

-
-
+
diff --git a/main.js b/main.js index af81fde4..adaaa681 100644 --- a/main.js +++ b/main.js @@ -1,71 +1,173 @@ -const currentWeather = document.querySelector(".current-weather"); -const currentWeatherIcon = document.querySelector(".current-weather-icon"); -const forecast = document.querySelector(".week-forecast"); -const city = document.querySelector("#city-input"); -const searchBtn = document.querySelector(".search"); -const card = document.querySelector(".card"); +// imperial units +const units = 'imperial'; +let temperatureSymbol = units == 'imperial' ? '°F' : '°C'; +// create a data structure to hold current weather and 5-day forecast values +let currentWeather = []; +let forecastFive = []; - -document.getElementById('weather-form').addEventListener('submit', function(event) { +// click event listener for city input and the search button +document.querySelector('.search').addEventListener('click', function(event) { event.preventDefault(); - getWeather(city.value); + + const cityInput = document.querySelector('#city-input'); + const city = cityInput.value; + + if (city !== '') { + getWeather(city); + + cityInput.value = ''; + + } else { + alert(('Please enter a city.')); + }; }); -function getWeather(city) { - const apiKey = "3a16b1625fdf1f83be7f01c3a15bd5f0"; - const apiUrl = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=imperial`; +/** + * Fetches data from the specified URL using the GET method and returns the response as JSON. + * If the response is not OK, throws an error with a message from the response or a default message. + * If an error occurs during the fetch, displays an error message and returns an empty object. + * + * @param {string} url - The URL to fetch data from. + * @return {Promise} A promise that resolves to the JSON response data. + * @throws {Error} If the response is not OK. + */ +async function fetchApiData(url){ + try { + const response = + await fetch(url, + { + method: 'GET', dataType: 'json' + }); + const data = await response.json(); - fetch(apiUrl) - .then(response => response.json()) - .then(data => { - if (data.cod === 200) { - document.querySelector('.weather-city').textContent = `Weather in ${data.name}`; - document.querySelector('.weather-description').textContent = `${data.weather[0].description}`; - document.querySelector('.weather-temperature').textContent = `${Math.round(data.main.temp)}°F`; - currentWeatherIcon.setAttribute('src', getWeatherIcon(data.weather[0].id)); - currentWeatherIcon.setAttribute('alt', `${data.weather[0].description}`); - displayWeather(data); - } else { - alert('City not found. Please enter a valid city name.'); - } - }) - .catch(error => { + if (response.ok) { + return data; + } else { + throw new Error(data.message || 'Error fetching data'); + } + } + catch(error) { + alert('Not a valid city. Please try again.'); console.error('Error fetching weather data:', error); - }); + return {}; + } }; +async function getWeather(city) { + const apiKey = '3a16b1625fdf1f83be7f01c3a15bd5f0'; + const apiUrl = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=imperial`; + + try { + const weatherData = await fetchApiData(apiUrl); + + // Get coordinates for the forecast API + const { lon, lat } = weatherData.coord; + const forecastUrl = `https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&appid=${apiKey}&units=imperial`; + const forecastData = await fetchApiData(forecastUrl); + + console.log(weatherData); + console.log(forecastData); -// Determines the weather icon based on the weather ID from the API -function getWeatherIcon(weatherId) { - - switch(true){ - case (weatherId >= 200 && weatherId <= 300): - return "https://openweathermap.org/img/wn/11n@2x.png"; - case (weatherId >= 300 && weatherId <= 400): - return "https://openweathermap.org/img/wn/10n@2x.png"; - case (weatherId >= 500 && weatherId <= 600): - return "https://openweathermap.org/img/wn/09n@2x.png"; - case (weatherId >= 600 && weatherId <= 700): - return "https://openweathermap.org/img/wn/13n@2x.png"; - case (weatherId >= 700 && weatherId <= 780): - return "https://openweathermap.org/img/wn/50n@2x.png"; - case (weatherId === 800): - return "https://openweathermap.org/img/wn/01n@2x.png"; - case (weatherId >= 801 && weatherId <= 810): - return "https://openweathermap.org/img/wn/03n@2x.png"; - default: - return "❓"; + displayCurrentWeather(weatherData); + displayForecast(forecastData); } + catch(error) { + console.error('Error fetching weather data:', error); + } +}; + +// Function to display current weather information +function displayCurrentWeather(data) { + document.querySelector('.current-weather').replaceChildren(); + document.querySelector('.forecast').replaceChildren(); + + // Destructure the necessary data + const { name: city, main: {temp}, weather: [{description, icon}] } = data; + + // Create a template string for the current weather + const template = ` +
+

${city}

+

${Math.round(temp)}°F

+

${description}

+
+
+ weather icon +
+ ` + // Insert the template into the DOM + document.querySelector('.current-weather').insertAdjacentHTML('beforeend', template); }; -// Error messaging -function displayError(message) { - const errorDisplay = document.createElement("p"); - errorDisplay.textContent = message; - errorDisplay.classList.add("errorDisplay"); +/** + * Displays the forecast information for the next 5 days. + * @param {Object} data - The forecast data containing the list of forecast items. + * @return {void} This function does not return anything. + */ +function displayForecast(data) { + // Iterate over the forecast data and create HTML for each day + const forecastHtml = data.list.slice(0, 5).map(item => { + const date = new Date(item.dt_txt).toLocaleDateString('en-US', { weekday: 'long' }); + const temp = item.main.temp; + const description = item.weather[0].description; + const icon = item.weather[0].icon; + + return ` +
+

${date}

+ weather icon +

${Math.round(temp)}°F

+

${description}

+
+ `; + }).join(''); + + // Insert the forecast HTML into the DOM + document.querySelector('.forecast').innerHTML = forecastHtml; +}; + + +/** + * Converts a Unix timestamp to the local time zone and returns a formatted string. + * @param {number} dt - The Unix timestamp to convert. + * @return {string} The formatted date and time string in the format: YYYY-MM-DD hh:mm:ss AM/PM. + */ +function convertToLocalTime(dt) { + const date = new Date(dt * 1000); + + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); // Months are 0-based so add 1 + const day = String(date.getDate()).padStart(2, '0'); + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + const seconds = String(date.getMinutes()).padStart(2, '0'); + const period = date.getHours() < 12 ? 'AM' : 'PM'; + + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds} ${period}`; +}; + +/** + * Creates a weather description element based on the provided weather data. + * @param {Object} weatherData - The weather data object containing main weather information and timestamp. + * @param {Object} weatherData.main - The main weather information object. + * @param {number} weatherData.main.temp - The temperature in Kelvin. + * @param {string} weatherData.dt - The Unix timestamp of the weather data. + * @return {HTMLElement} The created weather description element. +*/ +// function createWeatherDescription(weatherData) { +// const { main, dt } = weatherData; + +// const description = document.createElement('div'); +// const convertedDateAndTime = convertToLocalTime(dt); + +// // '2023-11-07 07:00:00 PM' +// description.innerHTML = ` +//
${main.temp} ${temperatureSymbol} - ${convertedDateAndTime.substring(10)} - ${convertedDateAndTime.substring(5, 10)}
+// `; - card.textContent = ""; - card.style.display = "flex"; - card.appendChild(errorDisplay); -}; \ No newline at end of file +// return description; +// }; diff --git a/style.css b/style.css index 04bb2365..75b0e9d1 100644 --- a/style.css +++ b/style.css @@ -1,30 +1,15 @@ body { - display: flex; min-height: 100vh; - margin: 0; - align-items: center; - justify-content: center; background-color: #bfdfec; - font-family: system-ui, calibri, serif; - background-repeat: repeat-x; - background-image: url("https://i.stack.imgur.com/b7z29.png"); - animation: movement 10s linear infinite; -} - -@keyframes movement { - 0% { - background-position: 0px 0px; - } - 100% { - background-position: 560px 0px; - } } .page-header { - margin-top: 20px; + margin-top: 50px; } .search-form { - margin-top: 30px; - margin-bottom: 30px; + display: block; + margin-top: 20px; + margin-bottom: 20px; + align-items: center; } From 21a9457db08270deb9a5fcb3083e4187c5224b6e Mon Sep 17 00:00:00 2001 From: Audrey Clark Date: Sat, 1 Jun 2024 19:32:40 -0500 Subject: [PATCH 4/7] Update bootstrap styling --- index.html | 6 ++++-- main.js | 43 +++++++------------------------------------ 2 files changed, 11 insertions(+), 38 deletions(-) diff --git a/index.html b/index.html index 276da20a..ba2660e8 100644 --- a/index.html +++ b/index.html @@ -42,11 +42,13 @@

Weather App Project

-
+
diff --git a/main.js b/main.js index adaaa681..95fc6633 100644 --- a/main.js +++ b/main.js @@ -1,10 +1,3 @@ -// imperial units -const units = 'imperial'; -let temperatureSymbol = units == 'imperial' ? '°F' : '°C'; - -// create a data structure to hold current weather and 5-day forecast values -let currentWeather = []; -let forecastFive = []; // click event listener for city input and the search button document.querySelector('.search').addEventListener('click', function(event) { @@ -87,12 +80,12 @@ function displayCurrentWeather(data) { // Create a template string for the current weather const template = ` -
+

${city}

${Math.round(temp)}°F

${description}

-
+
weather icon -

${date}

+
+

${date}

weather icon -

${Math.round(temp)}°F

-

${description}

-
+

${Math.round(temp)}°F

+

${description}

+
`; }).join(''); @@ -149,25 +142,3 @@ function convertToLocalTime(dt) { return `${year}-${month}-${day} ${hours}:${minutes}:${seconds} ${period}`; }; - -/** - * Creates a weather description element based on the provided weather data. - * @param {Object} weatherData - The weather data object containing main weather information and timestamp. - * @param {Object} weatherData.main - The main weather information object. - * @param {number} weatherData.main.temp - The temperature in Kelvin. - * @param {string} weatherData.dt - The Unix timestamp of the weather data. - * @return {HTMLElement} The created weather description element. -*/ -// function createWeatherDescription(weatherData) { -// const { main, dt } = weatherData; - -// const description = document.createElement('div'); -// const convertedDateAndTime = convertToLocalTime(dt); - -// // '2023-11-07 07:00:00 PM' -// description.innerHTML = ` -//
${main.temp} ${temperatureSymbol} - ${convertedDateAndTime.substring(10)} - ${convertedDateAndTime.substring(5, 10)}
-// `; - -// return description; -// }; From e3ee94634640a7ed5dddbb30108f365646445bec Mon Sep 17 00:00:00 2001 From: Audrey Clark Date: Sun, 2 Jun 2024 01:04:18 -0500 Subject: [PATCH 5/7] More updates to styles --- index.html | 36 +++++++++++++++++------------------- main.js | 37 +++++++++++++++++++++++++------------ style.css | 28 ++++++++++++++++++++-------- 3 files changed, 62 insertions(+), 39 deletions(-) diff --git a/index.html b/index.html index ba2660e8..de14e9c2 100644 --- a/index.html +++ b/index.html @@ -16,39 +16,37 @@
-