Skip to content
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

C22 - Sarah K Charday N #16

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
af31a9e
Added basic templates for index.html and index.css.
skoch-neat Dec 2, 2024
a742dcc
Implemented temperature control with dynamic color updates on page lo…
skoch-neat Dec 2, 2024
68a08fa
Refactored temperature control logic and landscape updates. Merged te…
skoch-neat Dec 3, 2024
f7056da
Added functionality to update city name header when user enters text …
skoch-neat Dec 3, 2024
62a4404
Added event listener for button click to get realtime temperature.
skoch-neat Dec 3, 2024
ffbfa2a
Created wave4 branch. Added placeholders (currently commented out) fo…
skoch-neat Dec 3, 2024
0ce9168
Removed placeholder in HTML file for tempValue. Added axios for proxy…
skoch-neat Dec 4, 2024
0188cfd
Meets Wave 4 Requirements. Refactored temperature handling, error log…
skoch-neat Dec 4, 2024
fdc638b
Added sky options to Index HTML file.
skoch-neat Dec 4, 2024
7a88bd9
Meets Wave 5 requirements. Added SKIES constant for sky visualization…
skoch-neat Dec 4, 2024
b4f09f6
Added reset button functionality to set city name and city name text …
skoch-neat Dec 4, 2024
65f5540
Refactored access on all DOM elements through the 'elements' object f…
skoch-neat Dec 4, 2024
778d76d
Extracted city name validation into a separate function isValidCityNa…
skoch-neat Dec 4, 2024
214f010
Removed unused variable headerCityName from event handler registration.
skoch-neat Dec 4, 2024
50bc70a
Removed axios installation, replaced local Axios script reference wi…
skoch-neat Dec 4, 2024
d8eb43e
Refactored reset button to reset UI, not just city name. Created defa…
skoch-neat Dec 4, 2024
3ff70d1
Merge pull request #1 from chardayneal/wave6
skoch-neat Dec 4, 2024
94cd506
Removed duplicate call to updateSky on page load. Reorganized functio…
skoch-neat Dec 4, 2024
56b9c2b
Merge pull request #2 from chardayneal/wave6
skoch-neat Dec 5, 2024
b530f1f
Refactored the weather application to manage state more efficiently b…
skoch-neat Dec 5, 2024
e997fe1
Update URL to make calls from rendered server instead of local server
chardayneal Dec 6, 2024
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
49 changes: 47 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,60 @@
<!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"></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>
<div class="button-and-error">
<button id="currentTempButton">Get Realtime Temperature</button>
<div id="errorMessage"></div>
</div>
</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>
</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>
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
246 changes: 246 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
const DEFAULTS = {
CITY: 'Seattle',
SKY: 'sunny',
LANDSCAPE: 'hot',
};

const WEATHER_CONFIG = {
TEMP_SCHEME: [
{
category: 'cold',
threshold: 50,
color: 'teal',
landscape: '🌲🌲⛄️🌲⛄️🍂🌲🍁🌲🌲⛄️🍂🌲',
},
{
category: 'cool',
threshold: 60,
color: 'green',
landscape: '🌾🌾_🍃_🪨__🛤_🌾🌾🌾_🍃',
},
{
category: 'warm',
threshold: 70,
color: 'yellow',
landscape: '🌸🌿🌼__🌷🌻🌿_☘️🌱_🌻🌷',
},
{
category: 'hot',
threshold: 80,
color: 'orange',
landscape: '🌵__🐍_🦂_🌵🌵__🐍_🏜_🦂',
},
{
category: 'veryHot',
threshold: Infinity,
color: 'red',
landscape: null,
},
],
SKY_VIEWS: {
sunny: "☁️ ☁️ ☁️ ☀️ ☁️ ☁️",
cloudy: "☁️☁️ ☁️ ☁️☁️ ☁️ 🌤 ☁️ ☁️☁️",
rainy: "🌧🌈⛈🌧🌧💧⛈🌧🌦🌧💧🌧🌧",
snowy: "🌨❄️🌨🌨❄️❄️🌨❄️🌨❄️❄️🌨🌨"
},
};

const getElement = (id) => document.getElementById(id);
const elements = {
tempValue: getElement('tempValue'),
incrTempControl: getElement('increaseTempControl'),
decrTempControl: getElement('decreaseTempControl'),
landscape: getElement('landscape'),
cityNameInput: getElement('cityNameInput'),
headerCityName: getElement('headerCityName'),
currentTempButton: getElement('currentTempButton'),
skySelect: getElement('skySelect'),
sky: getElement('sky'),
errorMessage: getElement('errorMessage'),
cityNameReset: getElement('cityNameReset'),
};

const state = {
city: DEFAULTS.CITY,
sky: DEFAULTS.SKY,
temperature: 0,
landscape: DEFAULTS.LANDSCAPE,
};

const updateState = (newState) => {
const { sky, temperature, city, landscape } = newState;

if (sky) updateSky(sky);
if (temperature !== undefined) updateUI(temperature, landscape, city);
if (city) state.city = city;
if (landscape) state.landscape = landscape;

Object.assign(state, newState);
};

const resetUI = () => {
elements.cityNameInput.value = DEFAULTS.CITY;
elements.headerCityName.textContent = DEFAULTS.CITY;
elements.skySelect.value = DEFAULTS.SKY;
updateSky(DEFAULTS.SKY);
updateWeatherForCity(DEFAULTS.CITY);
};

const updateSky = (selectedSky) => {
const skyText = WEATHER_CONFIG.SKY_VIEWS[selectedSky];
elements.sky.textContent = skyText;
};

const updateWeatherForCity = async (cityName = DEFAULTS.CITY) => {
if (!isValidCityName(cityName)) return;

try {
const location = await getCoordinates(cityName);
if (location) {
const weather = await getWeather(location);
if (weather) {
applyWeatherData(weather, cityName);
}
}
} catch (error) {
logError('Error during weather retrieval:', { message: error.message });
}
};

const isValidCityName = (cityName) => {
if (!cityName) {
displayErrorMessage('Please enter a city name.');
return false;
}
return true;
};

const getCoordinates = async (cityName) => {
try {
const response = await axios.get(`https://ada-weather-report-proxy-server.onrender.com//location?q=${cityName}`);
const firstMatch = response.data.length > 0 ? response.data[0] : null;

if (firstMatch) {
const { lat, lon } = firstMatch;
return { lat, lon };
} else {
logError('No location found for:', { cityName, });
return null;
}
} catch (error) {
logError('Error fetching location for city:', { cityName, message: error.message, });
return null;
}
};

const getWeather = async ({ lat, lon }) => {
try {
const response = await axios.get(`https://ada-weather-report-proxy-server.onrender.com/weather?lat=${lat}&lon=${lon}`);
return response.data;
} catch (error) {
logError('Error fetching weather for coordinates:', { coordinates: { lat, lon }, message: error.message, });
return null;
}
};

const applyWeatherData = (weather, city) => {
if (weather.main?.temp) {
const temperature = convertTemp(weather.main.temp);
const landscape = getLandscapeForTemperature(temperature);
updateState({
city,
temperature,
landscape,
});
} else {
logError('Invalid weather data received', weather);
}
};

const getLandscapeForTemperature = (tempInF) => {
const tempScheme = findTempScheme(tempInF);
const defaultScheme = findDefaultScheme();

return tempScheme?.landscape || defaultScheme?.landscape;
};

const findTempScheme = (tempInF) => {
return WEATHER_CONFIG.TEMP_SCHEME.find(({ threshold }) => tempInF < threshold);
};

const findDefaultScheme = () => {
return WEATHER_CONFIG.TEMP_SCHEME.find(scheme => scheme.category === DEFAULTS.LANDSCAPE);
};

const convertTemp = (tempInK) => {
const tempInF = (tempInK - 273.15) * (9 / 5) + 32;
return Math.round(tempInF);
};

const updateUI = (tempInF, landscape, city) => {
const tempScheme = findTempScheme(tempInF);

elements.tempValue.style.color = tempScheme.color || '';
elements.tempValue.textContent = tempInF;
elements.landscape.textContent = landscape || getLandscapeForTemperature(tempInF);

elements.headerCityName.textContent = city || state.city;
};

const updateTemp = (incrementValue) => {
const tempInF = state.temperature;
const temperature = tempInF + incrementValue;
updateState({ temperature });
};

const logError = (message, errorDetails) => {
console.error(message, errorDetails);
displayErrorMessage(message);
};

const displayErrorMessage = (message) => {
const errorMessageElement = elements.errorMessage;
errorMessageElement.textContent = message;
errorMessageElement.style.display = 'block';
setTimeout(() => {
errorMessageElement.style.display = 'none';
}, 5000);
};

const registerHandlers = () => {
const {
incrTempControl,
decrTempControl,
cityNameInput,
currentTempButton,
skySelect,
cityNameReset,
} = elements;

incrTempControl.addEventListener('click', () => updateTemp(1));
decrTempControl.addEventListener('click', () => updateTemp(-1));

cityNameInput.addEventListener('input', () => {
elements.headerCityName.textContent = elements.cityNameInput.value.trim()
});

currentTempButton.addEventListener('click', () => {
const cityName = elements.cityNameInput.value.trim();
updateWeatherForCity(cityName);
});

skySelect.addEventListener('change', (event) => {
const selectedSky = event.target.value;
updateSky(selectedSky);
});

cityNameReset.addEventListener('click', () => {
resetUI();
});
};

document.addEventListener('DOMContentLoaded', () => {
registerHandlers();
resetUI();
updateWeatherForCity();
});
Loading