Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
43 changes: 43 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Weatherific</title>
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/5.1.3/css/bootstrap.min.css"
/>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div
class="container vh-100 d-flex flex-row justify-content-center align-items-center"
>
<h1 class="text-center mb-4">Weather-ific!</h1>
<div class="input-group mb-3 justify-content-center">
<input
type="text"
id="locationInput"
class="form-control"
placeholder="Enter city or zip code"
/>
<button class="btn btn-primary" id="searchBtn">Get Weather</button>
</div>
<div id="currentWeather" class="row mb-4 justify-content-center w-100">
<div class="col-md-6 d-flex align-items-center">
<div class="text-center me-3" id="cityWeather">
<h2 id="cityName">City Name</h2>
<img id="weatherIcon" src="" alt="" />
<h3 id="temperature">0°F</h3>
<p id="weatherDescription">Description</p>
</div>
</div>
</div>
<h2 class="text-center mb-3">5-Day Forecast</h2>
<div id="forecast"></div>
</div>

<script src="main.js"></script>
</body>
</html>
138 changes: 138 additions & 0 deletions main.js
Original file line number Diff line number Diff line change
@@ -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 += `
<div class="col forecast-card">
<div class="card">
<div class="card-body text-center">
<h3>${dayName}</h3>
<p>${temp.day.toFixed(1)}°F</p>
<p>${weather[0].description}</p>
<img src="http://openweathermap.org/img/wn/${
weather[0].icon
}.png" alt="${weather[0].description}">
</div>
</div>
</div>`;
});
}

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}`;
}
Comment on lines +92 to +97

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can use a ternary operator here

Suggested change
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}`;
}
const geoUrl = isValidZipCode(location) ? `https://api.openweathermap.org/geo/1.0/zip?zip=${location}&appid=${apiKey}` : `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);
}
});
100 changes: 100 additions & 0 deletions styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
body {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

most of the style here can be achieved with bootstrap
example: p-, border-, d-flex

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;
}
}