-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Phoenix - Alejandra Guevara and Tram Hoang #9
base: main
Are you sure you want to change the base?
Changes from all commits
1749cba
4990a1f
56c741a
99a46bc
d22188d
1067eb1
ea95a46
a3d6929
469ecc5
7d32299
fc33d73
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,58 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
|
||
<head> | ||
<meta charset="UTF-8"> | ||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>Weather Report</title> | ||
<link rel="preconnect" href="https://fonts.gstatic.com"> | ||
<link href="https://fonts.googleapis.com/css2?family=Rubik&display=swap" rel="stylesheet"> | ||
<link rel="stylesheet" href="styles/index.css" /> | ||
<link rel="stylesheet" href="styles/index.css"> | ||
</head> | ||
|
||
<body> | ||
<header class="header__header"> | ||
<h1>Weather Report</h1> | ||
<span>For the lovely city of | ||
<span id="headerCityName" class="header__city-name">Seattle</span></span> | ||
</header> | ||
<section class="temperature__section"> | ||
<h2>Temperature</h2> | ||
<div class="temperature__content"> | ||
<div class="temperature__controls"> | ||
<span id="increaseTempControl">⬆️</span> | ||
<span id="tempValue"></span> | ||
<span id="decreaseTempControl">⬇️</span> | ||
</div> | ||
<button id="currentTempButton">Get Realtime Temperature</button> | ||
</div> | ||
</section> | ||
<section class="sky__section"> | ||
<h2>Sky</h2> | ||
<select id="skySelect"> | ||
<option value="Sunny">Sunny</option> | ||
<option value="Cloudy">Cloudy</option> | ||
<option value="Rainy">Rainy</option> | ||
<option value="Snowy">Snowy</option> | ||
<option value="Misty">Misty</option> | ||
</select> | ||
</section> | ||
|
||
<section class="city-name__section"> | ||
<h2>City Name</h2> | ||
<input type="text" id="cityNameInput"> | ||
<button id="cityNameReset" class="city-name__reset-btn">Reset</button> | ||
</section> | ||
|
||
<section class="garden__section"> | ||
<h2>Weather Garden</h2> | ||
<div id="gardenContent" class="garden__content"> | ||
<div id="sky"></div> | ||
<div id="landscape"></div> | ||
</div> | ||
</section> | ||
<script src="https://unpkg.com/axios/dist/axios.min.js"></script> | ||
<script src="./src/index.js"></script> | ||
</body> | ||
</html> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
let currentTemperature = 70; | ||
|
||
const tempValue = document.getElementById('tempValue'); | ||
const increaseTempControl = document.getElementById('increaseTempControl'); | ||
const decreaseTempControl = document.getElementById('decreaseTempControl'); | ||
const landScrape = document.getElementById('landscape'); | ||
const skySelect = document.getElementById('skySelect'); | ||
const skyElement = document.getElementById('sky'); | ||
Comment on lines
+3
to
+8
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we import this script file at the end of our HTML, these lookups should all work as expected. But we generally don't want to make this assumption, instead, preferring to perform lookups like this in response to the |
||
|
||
|
||
const updateTemperature = () => { | ||
tempValue.textContent = `${currentTemperature}°F`; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider splitting this into two functiosn, one that is able to figure out which data to use to update the screen, and another that actually applies the supplied values to the screen. |
||
|
||
if (currentTemperature >= 80) { | ||
tempValue.style.color = 'red'; | ||
tempValue.style.backgroundColor = '#FFCCCC'; | ||
Comment on lines
+15
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Setting the |
||
landScrape.textContent = "🌵__🐍_🦂_🌵🌵__🐍_🏜_🦂"; | ||
} else if (currentTemperature >= 70) { | ||
Comment on lines
+14
to
+18
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This large chained if statement is repetative. If we stored the relevant data in a way that we could loop through (direct key lookup isn't as helpful, since our temperatures have ranges) we could make our application more data driven, so that just by changing a data structure (that maybe we could load from an API call) there could be different temperature behaviors. Consider how we could use a structure like the following: // each row holds the bottom of the temperature range, a class name, and a landscape string
const TEMPERATURE_DATA = [
[80, 'very-hot-temp', '🌵__🐍_🦂_🌵🌵__🐍_🏜_🦂'],
[70, 'hot-temp', '🌸🌿🌼__🌷🌻🌿_☘️🌱_🌻🌷'],
[60, 'warm-temp', '🌾🌾_🍃_🪨__🛤_🌾🌾🌾_🍃'],
[50, 'cool-temp', '🌲🌲🍂🌲🍁🍃🌲🌾🌲🌲🍂🍁🌲'],
[40, 'cold-temp', '🌲🌲⛄️🌲⛄️🍂🌲🍁🌲🌲⛄️🍂🌲'],
]; |
||
tempValue.style.color = 'orange'; | ||
tempValue.style.backgroundColor = '#FFE4B5'; | ||
landScrape.textContent = "🌸🌿🌼__🌷🌻🌿_☘️🌱_🌻🌷"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prefer |
||
} else if (currentTemperature >= 60) { | ||
tempValue.style.color = 'yellow'; | ||
tempValue.style.backgroundColor = 'pink'; | ||
landScrape.textContent = "🌾🌾_🍃_🪨__🛤_🌾🌾🌾_🍃"; | ||
} else if (currentTemperature >= 50) { | ||
tempValue.style.color = 'green'; | ||
tempValue.style.backgroundColor = '#CCFFCC'; | ||
landScrape.textContent = "🌲🌲🍂🌲🍁🍃🌲🌾🌲🌲🍂🍁🌲"; | ||
} else if (currentTemperature >= 40) { | ||
tempValue.style.color = 'teal'; | ||
tempValue.style.backgroundColor = '#CCFFFF'; | ||
landScrape.textContent = "🌲🌲⛄️🌲⛄️🍂🌲🍁🌲🌲⛄️🍂🌲"; | ||
} | ||
}; | ||
|
||
increaseTempControl.addEventListener('click', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should generally wait to register events until afetr the |
||
currentTemperature += 1; | ||
updateTemperature(); | ||
}); | ||
|
||
decreaseTempControl.addEventListener('click', () => { | ||
currentTemperature -= 1; | ||
updateTemperature(); | ||
}); | ||
|
||
// Wave 5 | ||
const updateSky = (skyType) => { | ||
const skyOptions = { | ||
Sunny: "☁️ ☁️ ☁️ ☀️ ☁️ ☁️", | ||
Cloudy: "☁️☁️ ☁️ ☁️☁️ ☁️ 🌤 ☁️ ☁️☁️", | ||
Rainy: "🌧🌈⛈🌧🌧💧⛈🌧🌦🌧💧🌧🌧", | ||
Snowy: "🌨❄️🌨🌨❄️❄️🌨❄️🌨❄️❄️🌨🌨", | ||
Misty: "🌫️🌫️💨🌫️🌦🌫️🌫️💨🌫️🌫️🌫️💨", | ||
}; | ||
Comment on lines
+49
to
+55
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a way we could use data like this to fill in the available sky picks in the UI rather than hard coding them in the HTML? |
||
|
||
skyElement.textContent = skyOptions[skyType] || skyOptions["Sunny"]; | ||
skySelect.value = skyType; // Sync the dropdown | ||
}; | ||
|
||
skySelect.addEventListener("change", () => { | ||
const selectedSky = skySelect.value; | ||
updateSky(selectedSky); | ||
}); | ||
|
||
// Wave 3 | ||
document.addEventListener("DOMContentLoaded", () => { | ||
const cityNameInput = document.getElementById('cityNameInput'); | ||
const headerCityName = document.getElementById('headerCityName'); | ||
const currentTempButton = document.getElementById("currentTempButton"); | ||
|
||
cityNameInput.addEventListener("input", (event) => { | ||
const newCityName = event.target.value; | ||
headerCityName.textContent = newCityName; | ||
}); | ||
|
||
// Wave 4 | ||
const getRealTimeTemperature = async () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Try to avoid defining large functions here in the handler for If everything were moved here, then the variables that lookup the elements could all legitimately by There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than implementing this as a single monolithic function, consider decomposing this into a few smaller functions.
These two functions could be chained together through a third function Additional functions could use this overall function to update the UI using the weather data results. While writing one long function can be helpful when trying to get everything working, it's worth goin back, identifying key pieces, and extracting them into helper functions with descriptive names, as this helps our code be more self-documenting. |
||
const cityName = headerCityName.textContent; | ||
|
||
try { | ||
const locationResponse = await axios.get( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice use of async/await to simplify the logic flow. |
||
`http://127.0.0.1:5000/location?q=${cityName}` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than hard-coding the URL, it can be helpful to store the base url in a variable that we also interpolate into the string here. This can make deployment easier (since we'll need to change any dev-routed endpoints to point to their production locations). const BASE_URL = 'http://127.0.0.1:5000'; then here `${BASE_URL}/location?q=${cityName}` There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prefer to pass query params using a data structure if supported by the HTTP library (axios does), as this can prevent unexpected values being injected into the query string. Notice that here, the user's city name input is directly injected into the URL. They could include their own query params in the input (e.g., axios.get(url, {
params: {
q: cityName,
},
}); |
||
); | ||
|
||
const { lat, lon } = locationResponse.data[0]; | ||
|
||
const weatherResponse = await axios.get( | ||
`http://127.0.0.1:5000/weather?lat=${lat}&lon=${lon}` | ||
); | ||
|
||
const tempKelvin = weatherResponse.data.main.temp; | ||
const tempFahrenheit = ((tempKelvin - 273.15) * 1.8) + 32; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider making a conversion helper function. |
||
|
||
currentTemperature = Math.round(tempFahrenheit); | ||
updateTemperature(); | ||
|
||
// Update the sky based on weather condition | ||
const weatherCondition = weatherResponse.data.weather[0].main; | ||
let skyType = "Sunny"; // Default sky | ||
|
||
if (weatherCondition.includes("Rain")) { | ||
skyType = "Rainy"; | ||
} else if (weatherCondition.includes("Snow")) { | ||
skyType = "Snowy"; | ||
} else if (weatherCondition.includes("Cloud")) { | ||
skyType = "Cloudy"; | ||
} else if (weatherCondition.includes("Misty")) { | ||
skyType = "Misty"; | ||
} | ||
|
||
updateSky(skyType); | ||
} catch (error) { | ||
console.error("Error fetching weather data:", error); | ||
} | ||
}; | ||
|
||
currentTempButton.addEventListener("click", getRealTimeTemperature); | ||
|
||
// Wave 6 | ||
const resetButton = document.getElementById('cityNameReset'); | ||
resetButton.addEventListener("click", () => { | ||
cityNameInput.value = ""; | ||
headerCityName.textContent = "Seattle"; | ||
currentTemperature = 70; | ||
updateTemperature(); | ||
updateSky("Sunny"); | ||
// skySelect.value = "Sunny"; | ||
}); | ||
|
||
updateTemperature(); | ||
updateSky("Sunny"); | ||
}); | ||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
h2 { | ||
margin: 0 auto 2rem auto; | ||
} | ||
|
||
body { | ||
display: grid; | ||
grid-template-columns: 1fr 2fr; | ||
grid-template-rows: auto auto auto auto; | ||
grid-gap: 1rem; | ||
|
||
font-family: "Rubik", sans-serif; | ||
font-size: 18px; | ||
background-color: #1b69f9; | ||
margin: 2rem; | ||
} | ||
|
||
.header__header { | ||
color: white; | ||
grid-column: span 3; | ||
display: flex; | ||
align-items: center; | ||
margin: 2rem auto 3rem 0; | ||
} | ||
|
||
.header__header > h1 { | ||
margin-right: 2rem; | ||
font-size: 3em; | ||
} | ||
|
||
.header__city-name { | ||
font-style: oblique; | ||
font-size: 2rem; | ||
} | ||
|
||
.header__city-name::before, | ||
.header__city-name::after { | ||
content: "✨"; | ||
} | ||
|
||
.temperature__section, | ||
.sky__section, | ||
.city-name__section { | ||
border-radius: 8px; | ||
padding: 2rem; | ||
background-color: white; | ||
} | ||
|
||
.temperature__section { | ||
grid-row: 2; | ||
} | ||
|
||
.temperature__section button { | ||
background-color: #1b69f9; | ||
border: none; | ||
color: white; | ||
padding: 15px 32px; | ||
text-align: center; | ||
text-decoration: none; | ||
display: inline-block; | ||
font-size: 16px; | ||
border-radius: 10px | ||
} | ||
|
||
.sky__section { | ||
grid-row: 3; | ||
} | ||
|
||
.city-name__section { | ||
grid-row: 4; | ||
} | ||
|
||
.garden__section { | ||
grid-row: 2 / span 3; | ||
grid-column: 2; | ||
text-align: center; | ||
align-self: center; | ||
} | ||
|
||
.temperature__content { | ||
display: flex; | ||
flex-direction: row; | ||
justify-content: space-around; | ||
/* justify-content: center; */ | ||
} | ||
|
||
#tempValue { | ||
font-size: 3rem; | ||
margin-left: 1.5rem; | ||
/* padding-right: 1rem; */ | ||
/* margin-right: 1.5rem; */ | ||
} | ||
|
||
.temperature__controls { | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
} | ||
|
||
.garden__section > h2 { | ||
color: white; | ||
} | ||
|
||
.garden__content { | ||
min-height: 200px; | ||
max-width: fit-content; | ||
margin: auto; | ||
padding: 2rem; | ||
|
||
display: flex; | ||
flex-direction: column; | ||
justify-content: space-between; | ||
|
||
border-radius: 8px; | ||
font-size: 2em; | ||
} | ||
|
||
.city-name__reset-btn { | ||
border: 0; | ||
background-color: #1655cc; | ||
color: white; | ||
border-radius: 8px; | ||
padding: 1rem; | ||
font-family: "Rubik", sans-serif; | ||
} | ||
|
||
/* .red { | ||
color: red; | ||
} | ||
|
||
.orange { | ||
color: orange; | ||
} | ||
|
||
.yellow { | ||
color: gold; | ||
} | ||
|
||
.yellow-green { | ||
color: yellowgreen; | ||
} | ||
|
||
.green { | ||
color: green; | ||
} | ||
|
||
.teal { | ||
color: teal; | ||
} | ||
|
||
.cloudy { | ||
background-color: lightgrey; | ||
} | ||
|
||
.sunny { | ||
background-color: rgb(221, 255, 255); | ||
} | ||
|
||
.rainy { | ||
background-color: lightblue; | ||
} | ||
|
||
.snowy { | ||
background-color: lightsteelblue; | ||
} */ | ||
|
||
#skyDisplay { | ||
font-size: 2rem; | ||
text-align: center; | ||
margin-top: 10px; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: typo