diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..01625912d Binary files /dev/null and b/.DS_Store differ diff --git a/README.md b/README.md index f8b15f4cb..66d3e7853 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,7 @@ # Weather App -Replace this readme with your own information about your project. - -Start by briefly describing the assignment in a sentence or two. Keep it short and to the point. - -## The problem - -Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next? +The project was to build a simple weather dashboard that uses a weather API to tell today's weather and temperature and a 4-day forecast. A couple of different designers had a take on this project and it was a chance to practice to implement someone else's design. ## View it live -Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about. +https://whats-the-weather-jenny.netlify.app/ diff --git a/assets/design-1/Group16.png b/assets/design-1/Group16.png deleted file mode 100644 index c2d0e3f5a..000000000 Binary files a/assets/design-1/Group16.png and /dev/null differ diff --git a/assets/design-1/Group34.png b/assets/design-1/Group34.png deleted file mode 100644 index f7e90e552..000000000 Binary files a/assets/design-1/Group34.png and /dev/null differ diff --git a/assets/design-1/Group36.png b/assets/design-1/Group36.png deleted file mode 100644 index ce9c147cf..000000000 Binary files a/assets/design-1/Group36.png and /dev/null differ diff --git a/assets/design-1/Group37.png b/assets/design-1/Group37.png deleted file mode 100644 index ce9c147cf..000000000 Binary files a/assets/design-1/Group37.png and /dev/null differ diff --git a/assets/design-1/Group38.png b/assets/design-1/Group38.png deleted file mode 100644 index ce9c147cf..000000000 Binary files a/assets/design-1/Group38.png and /dev/null differ diff --git a/assets/design-2/noun_Cloud_1188486.svg b/assets/design-2/noun_Cloud_1188486.svg deleted file mode 100644 index c2375e901..000000000 --- a/assets/design-2/noun_Cloud_1188486.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/assets/design-2/noun_Sunglasses_2055147.svg b/assets/design-2/noun_Sunglasses_2055147.svg deleted file mode 100644 index a1fcd7e8b..000000000 --- a/assets/design-2/noun_Sunglasses_2055147.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/design-2/noun_Umbrella_2030530.svg b/assets/design-2/noun_Umbrella_2030530.svg deleted file mode 100644 index 8a414b15f..000000000 --- a/assets/design-2/noun_Umbrella_2030530.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/code/.DS_Store b/code/.DS_Store new file mode 100644 index 000000000..fb1384a3b Binary files /dev/null and b/code/.DS_Store differ diff --git a/code/assets/.DS_Store b/code/assets/.DS_Store new file mode 100644 index 000000000..a7ed4617f Binary files /dev/null and b/code/assets/.DS_Store differ diff --git a/code/assets/clear_sky_condition.jpg b/code/assets/clear_sky_condition.jpg new file mode 100644 index 000000000..c516a74ed Binary files /dev/null and b/code/assets/clear_sky_condition.jpg differ diff --git a/code/assets/cloudy_condition.jpg b/code/assets/cloudy_condition.jpg new file mode 100644 index 000000000..63e1d7908 Binary files /dev/null and b/code/assets/cloudy_condition.jpg differ diff --git a/code/assets/default_condition.jpg b/code/assets/default_condition.jpg new file mode 100644 index 000000000..595f95e9f Binary files /dev/null and b/code/assets/default_condition.jpg differ diff --git a/code/assets/misty_condition.jpg b/code/assets/misty_condition.jpg new file mode 100644 index 000000000..1ec093478 Binary files /dev/null and b/code/assets/misty_condition.jpg differ diff --git a/code/assets/rainy_condition.jpg b/code/assets/rainy_condition.jpg new file mode 100644 index 000000000..718fcf9f7 Binary files /dev/null and b/code/assets/rainy_condition.jpg differ diff --git a/code/assets/snowy_condition.jpg b/code/assets/snowy_condition.jpg new file mode 100644 index 000000000..c1ce0a252 Binary files /dev/null and b/code/assets/snowy_condition.jpg differ diff --git a/code/assets/thunderstorm_condition.jpg b/code/assets/thunderstorm_condition.jpg new file mode 100644 index 000000000..c40908599 Binary files /dev/null and b/code/assets/thunderstorm_condition.jpg differ diff --git a/code/index.html b/code/index.html new file mode 100644 index 000000000..9f4ffdcda --- /dev/null +++ b/code/index.html @@ -0,0 +1,38 @@ + + + + + + Weather App + + + + + + +
+
+
+
+

+

°c

+
+

+

+
+

+
+
+
+
+
+
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/code/script.js b/code/script.js new file mode 100644 index 000000000..5c76b833b --- /dev/null +++ b/code/script.js @@ -0,0 +1,183 @@ +// Base URL +const BASE_URL = 'https://api.openweathermap.org/data/2.5/weather'; + +// API key +const API_KEY = '958c2b8d78ace5950d5c51dc2972950f'; + +// City and country for the weather forecast +let city = 'Stockholm'; +let country = 'Sweden'; +const units = 'metric'; // Use metric units (Celsius) + +// Construct the URL for current weather data +const url = `${BASE_URL}?q=${city},${country}&units=${units}&APPID=${API_KEY}`; + +// Construct the URL for forecast data +const FORECAST_URL = `https://api.openweathermap.org/data/2.5/forecast?q=${city},${country}&units=${units}&APPID=${API_KEY}`; + +// DOMs +const temperatureElement = document.getElementById("temperature"); +const locationElement = document.getElementById("location"); +const conditionElement = document.getElementById("condition"); +const sunriseElement = document.getElementById("sunriseTime"); +const sunsetElement = document.getElementById("sunsetTime"); +const timeElement = document.getElementById("time"); +const forecastElement = document.getElementById("forecast"); + +// Function to fetch and display current weather data +const getWeatherData = () => { + fetch(url) + .then(response => response.json()) + .then(json => { + // Get data from the API response + const temperature = Math.round(json.main.temp); + const location = json.name; + const condition = json.weather[0].description; + const iconCode = json.weather[0].icon; + const sunriseTime = new Date(json.sys.sunrise * 1000).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + const sunsetTime = new Date(json.sys.sunset * 1000).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + + // Get the current local time + const localTime = new Date(); + const formattedTime = localTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + + // Update HTML elements with the weather data + temperatureElement.textContent = `${temperature}`; + temperatureElement.setAttribute('aria-label', `Current temperature is ${temperature} degrees Celsius`); + locationElement.textContent = `${location}`; + locationElement.setAttribute('aria-label', `Current location is ${location}`); + conditionElement.textContent = `${condition}`; + conditionElement.setAttribute('aria-label', `Current weather condition is ${condition}`); + sunriseElement.textContent = `Sunrise: ${sunriseTime}`; + sunriseElement.setAttribute('aria-label', `Sunrise time is ${sunriseTime}`); + sunsetElement.textContent = `Sunset: ${sunsetTime}`; + sunsetElement.setAttribute('aria-label', `Sunset time is ${sunsetTime}`); + timeElement.textContent = `Time: ${formattedTime}`; + timeElement.setAttribute('aria-label', `Current local time is ${formattedTime}`); + + // Set the background based on the weather condition + setWeatherBackground(json.weather[0].main); + }); + } + +// Function to set the background image based on weather condition +const setWeatherBackground = (weatherCondition) => { + const imageBackground = document.getElementById('imageBackground'); + + switch(weatherCondition.toLowerCase()) { + case 'clear': + imageBackground.style.backgroundImage = "url('assets/clear_sky_condition.jpg')"; + break; + case 'clouds': + imageBackground.style.backgroundImage = "url('assets/cloudy_condition.jpg')"; + break; + case 'rain': + case 'drizzle': + imageBackground.style.backgroundImage = "url('assets/rainy_condition.jpg')"; + break; + case 'thunderstorm': + imageBackground.style.backgroundImage = "url('assets/thunderstorm_condition.jpg')"; + break; + case 'snow': + imageBackground.style.backgroundImage = "url('assets/snowy_condition.jpg')"; + break; + case 'mist': + case 'smoke': + case 'haze': + case 'fog': + imageBackground.style.backgroundImage = "url('assets/misty_condition.jpg')"; + break; + default: + imageBackground.style.backgroundImage = "url('assets/default_condition.jpg')"; + } +}; + +// Function to fetch and display forecast data +const getForecastData = () => { + fetch(FORECAST_URL) + .then(response => response.json()) + .then(json => { + // Process the forecast data + let forecastData = processForecastData(json.list); + // If we don't have 5 days of forecast, fill in with additional data + if (forecastData.length < 5) { + forecastData = fillForecastData(forecastData, json.list); + } + // Display the processed forecast data + displayForecast(forecastData); + }); +}; + +// Function to process the raw forecast data +const processForecastData = (list) => { + const today = new Date().getDate(); + + return list + // Transforming each item into an object with the data we need + .map(item => ({ + date: new Date(item.dt * 1000), + icon: item.weather[0].icon, + temp: Math.round(item.main.temp) + })) + // Filter out today's weather and nighttime forecasts + .filter(item => { + const hour = item.date.getHours(); + return item.date.getDate() !== today && hour >= 6 && hour <= 18; + }) + // Reduce to max 5 items - one per day + .reduce((acc, item) => { + if (acc.length < 5 && (acc.length === 0 || item.date.getDate() !== acc[acc.length - 1].date.getDate())) { + acc.push(item); + } + return acc; + }, []); +}; + +// Function to fill in missing forecast days if there isn't 5 +const fillForecastData = (forecast, list) => { + const today = new Date().getDate(); + let i = 0; + + while (forecast.length < 5 && i < list.length) { + const date = new Date(list[i].dt * 1000); + if (date.getDate() !== today && (forecast.length === 0 || date.getDate() !== forecast[forecast.length - 1].date.getDate())) { + forecast.push({ + date: date, + icon: list[i].weather[0].icon, + temp: Math.round(list[i].main.temp) + }); + } + i++; + } + + return forecast; +}; + +// Function to display the forecast data in the HTML +const displayForecast = (forecastData) => { + // Clear any existing forecast + forecastElement.innerHTML = ''; + const daysOfWeek = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + + // Create and append a forecast item for each day + forecastData.forEach(day => { + const dayName = daysOfWeek[day.date.getDay()]; + const forecastItem = document.createElement('div'); + forecastItem.classList.add('forecast-item'); + forecastItem.tabIndex = 0; // Make the item focusable + forecastItem.setAttribute('role', 'region'); // ARIA role for screen readers + forecastItem.setAttribute('aria-label', `Forecast for ${dayName}`); // Label for screen readers + forecastItem.innerHTML = ` +
+ ${dayName} + Weather icon for ${dayName} + ${day.temp}°C +
+ `; + forecastElement.appendChild(forecastItem); + }); +}; + +// Call both functions to initialize the weather app +getWeatherData(); +getForecastData(); \ No newline at end of file diff --git a/code/styles.css b/code/styles.css new file mode 100644 index 000000000..0f72337a8 --- /dev/null +++ b/code/styles.css @@ -0,0 +1,107 @@ +body { + margin: 0; + padding: 0; + background-color: #ffffff; + font-family: "Questrial", sans-serif; +} + +.questrial-regular { + font-family: "Questrial", sans-serif; + font-weight: 400; + font-style: normal; +} + + +*:focus { + outline: 2px solid #135dab; +} + +h5 { + font-size: 18px; +} + +.weather-app { + margin: 0 auto; + position: relative; + overflow: hidden; + max-width: 320px; +} + +.sun-times { + display: flex; + justify-content: space-evenly; + max-width: 320px; + margin: 2rem 0 2rem 0; +} + +.image-background { + background-size: cover; + background-position: center; + background-color: rgb(88, 159, 218); + background-image: url('assets/default_condition.jpg'); + max-width: 100%; + margin: 0 auto; + border-bottom-left-radius: 50% 60px; + border-bottom-right-radius: 50% 60px; + overflow: hidden; +} + +.app-header, .current-weather { + position: relative; + z-index: 1; + color: rgb(255, 255, 255); +} + +.app-header { + padding: 20px; +} + +.current-weather { + padding: 10px; + text-align: center; +} + +.temp-section { + display: flex; +} + +.temperature { + font-size: 100px; + text-align: left; + padding: 0 0 0 1.5rem; + margin-bottom: 0; +} + +.celcius { + font-size: 3.5em; + margin: 4rem 0 1rem 0.5rem; +} + +.location { + font-size: 34px; + text-align: left; + padding: 0 0 0 1.5rem; +} + +.time, .condition { + padding: 0 1.5rem 0 1.5rem; + text-align: left; + font-size: 1.3rem; +} + +.forecast-section { + display: flex; + justify-content: space-between; + padding: 0 1.5rem 0 1.5rem; +} + +span { + font-size: 20px; + margin-top: 1rem; +} + +.forecast { + max-width: 500px; + margin: 0 auto; + padding: 30px 30px 0 30px; +} \ No newline at end of file diff --git a/instructions.md b/instructions.md index 42fc411ce..050e14fd1 100644 --- a/instructions.md +++ b/instructions.md @@ -115,3 +115,4 @@ Explore the API and use another endpoint of the Weather API to include supplemen **Feature: CSS Animations** Add some CSS animations to your app, e.g. pulsating sun/raindrops. +okoi \ No newline at end of file