diff --git a/README.md b/README.md index 4638c655..4df26944 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ -## Weather Project +# Eval 3 -This project has been created by a student at Parsity, an online software engineering course. The work in this repository is wholly of the student based on a sample starter project that can be accessed by looking at the repository that this project forks. +## Weather App -If you have any questions about this project or the program in general, visit [parsity.io](https://parsity.io/) or email hello@parsity.io. +### Mecca Conway + +#### Cohort 14 + +##### Part Time diff --git a/index.html b/index.html new file mode 100644 index 00000000..6e2a64f1 --- /dev/null +++ b/index.html @@ -0,0 +1,43 @@ + + + + + + Weatherific + + + + +
+

Weather-ific!

+
+ + +
+
+
+
+

City Name

+ +

0°F

+

Description

+
+
+
+

5-Day Forecast

+
+
+ + + + diff --git a/main.js b/main.js new file mode 100644 index 00000000..d43c35c1 --- /dev/null +++ b/main.js @@ -0,0 +1,138 @@ +const apiKey = "3f88d5f8bbfa8eb651ea5f78d37f3578"; +const currentWeatherDiv = document.getElementById("currentWeather"); +const forecastDiv = document.getElementById("forecast"); +const searchBtn = document.getElementById("searchBtn"); +const locationInput = document.getElementById("locationInput"); + +async function fetchWeatherData(lat, lon) { + try { + const response = await fetch( + `https://api.openweathermap.org/data/3.0/onecall?lat=${lat}&lon=${lon}&exclude=minutely,hourly&appid=${apiKey}&units=imperial` + ); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = await response.json(); + return data; + } catch (error) { + console.error("Error fetching weather data:", error); + } +} + +async function getApproximateLocation() { + const response = await fetch("http://ip-api.com/json"); + const data = await response.json(); + return { + lat: data.lat, + lon: data.lon, + name: `${data.city}, ${data.regionName}, ${data.country}`, + zip: data.zip, + timezone: data.timezone, + }; +} + +function displayCurrentWeather(data, cityName) { + const { temp, weather } = data.current; + + document.getElementById("cityName").innerText = cityName; + document.getElementById("temperature").innerText = `${temp.toFixed(1)}°F`; + document.getElementById("weatherDescription").innerText = + weather[0].description; + document.getElementById( + "weatherIcon" + ).src = `http://openweathermap.org/img/wn/${weather[0].icon}.png`; +} + +function displayForecast(daily) { + forecastDiv.innerHTML = ""; + daily.slice(1, 6).forEach((day) => { + const date = new Date(day.dt * 1000); + const options = { weekday: "long" }; + const dayName = date.toLocaleDateString("en-US", options); + const { temp, weather } = day; + forecastDiv.innerHTML += ` +
+
+
+

${dayName}

+

${temp.day.toFixed(1)}°F

+

${weather[0].description}

+ ${weather[0].description} +
+
+
`; + }); +} + +function isValidZipCode(zip) { + if (zip.length === 5) { + return zip.split("").every((char) => char >= "0" && char <= "9"); + } else if (zip.length === 10) { + return ( + zip[5] === "-" && + zip + .slice(0, 5) + .split("") + .every((char) => char >= "0" && char <= "9") && + zip + .slice(6) + .split("") + .every((char) => char >= "0" && char <= "9") + ); + } + return false; +} + +async function handleWeatherSearch() { + const location = locationInput.value.trim(); + if (!location) return; + + let geoUrl; + if (isValidZipCode(location)) { + geoUrl = `https://api.openweathermap.org/geo/1.0/zip?zip=${location}&appid=${apiKey}`; + } else { + geoUrl = `https://api.openweathermap.org/geo/1.0/direct?q=${location}&limit=1&appid=${apiKey}`; + } + + try { + const geoResponse = await fetch(geoUrl); + const geoData = await geoResponse.json(); + + let lat, lon, name; + if (Array.isArray(geoData) && geoData.length > 0) { + ({ lat, lon, name } = geoData[0]); + } else if (geoData.lat && geoData.lon) { + ({ lat, lon, name } = geoData); + } else { + throw new Error("Location not found"); + } + + const weatherData = await fetchWeatherData(lat, lon); + if (weatherData) { + displayCurrentWeather(weatherData, name); + displayForecast(weatherData.daily); + } + } catch (error) { + console.error("Error:", error); + alert(error.message); + } +} + +searchBtn.addEventListener("click", handleWeatherSearch); + +locationInput.addEventListener("keydown", (event) => { + if (event.key === "Enter") { + event.preventDefault(); + handleWeatherSearch(); + } +}); + +getApproximateLocation().then(async (location) => { + const weatherData = await fetchWeatherData(location.lat, location.lon); + if (weatherData) { + displayCurrentWeather(weatherData, location.name); + displayForecast(weatherData.daily); + } +}); diff --git a/styles.css b/styles.css new file mode 100644 index 00000000..5b4f639b --- /dev/null +++ b/styles.css @@ -0,0 +1,100 @@ +body { + background: #253248; + color: white; + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + margin: 0; +} + +h1 { + text-align: center; +} + +.input-group { + display: flex; + justify-content: center; + align-items: center; +} + +#locationInput { + font-size: 20px; + padding: 5px; +} + +#searchBtn { + margin-left: 10px; + font-size: 20px; + padding: 5px; +} + +.card { + border-radius: 15px; +} + +.card-body img { + width: 50px; +} + +#currentWeather { + display: flex; + justify-content: center; + align-items: center; + margin-top: 50px; + margin-bottom: 50px; +} + +#cityWeather { + border: 1px white solid; + padding: 15px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +#temperature { + margin-left: 10px; +} + +#forecast { + display: flex; + justify-content: space-evenly; + align-items: stretch; + flex-wrap: nowrap; + margin-top: 15px; +} + +.forecast-card { + border: 1px solid white; + padding: 10px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + flex-basis: calc(20% - 30px); + margin-right: 15px; + min-width: 150px; +} + +.card-body { + display: flex; + flex-direction: column; + justify-content: center; + align-item: center; + align-items: center; +} + +@media (max-width: 768px) { + #forecast { + flex-direction: column; + align-items: center; + } + + .forecast-card { + width: 100%; + margin-right: 0; + margin-bottom: 15px; + } +}