diff --git a/index.html b/index.html
new file mode 100644
index 0000000..ca2f9db
--- /dev/null
+++ b/index.html
@@ -0,0 +1,60 @@
+
+
+
+
+
+ Wilsons Weather
+
+
+
+
+
+
+
+
+
+
+
+

+
Wilson's Weather
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/weather-both/cloudy-both.png b/public/weather-both/cloudy-both.png
new file mode 100644
index 0000000..e9f9d0a
Binary files /dev/null and b/public/weather-both/cloudy-both.png differ
diff --git a/public/weather-both/cold-both.png b/public/weather-both/cold-both.png
new file mode 100644
index 0000000..b60b024
Binary files /dev/null and b/public/weather-both/cold-both.png differ
diff --git a/public/weather-both/dreary-both.png b/public/weather-both/dreary-both.png
new file mode 100644
index 0000000..4f20ce1
Binary files /dev/null and b/public/weather-both/dreary-both.png differ
diff --git a/public/weather-both/flurries-both.png b/public/weather-both/flurries-both.png
new file mode 100644
index 0000000..5b53ae6
Binary files /dev/null and b/public/weather-both/flurries-both.png differ
diff --git a/public/weather-both/fog-both.png b/public/weather-both/fog-both.png
new file mode 100644
index 0000000..593b09a
Binary files /dev/null and b/public/weather-both/fog-both.png differ
diff --git a/public/weather-both/freezing-rain-both.png b/public/weather-both/freezing-rain-both.png
new file mode 100644
index 0000000..af35ff0
Binary files /dev/null and b/public/weather-both/freezing-rain-both.png differ
diff --git a/public/weather-both/hot-both.png b/public/weather-both/hot-both.png
new file mode 100644
index 0000000..0f6365e
Binary files /dev/null and b/public/weather-both/hot-both.png differ
diff --git a/public/weather-both/ice-both.png b/public/weather-both/ice-both.png
new file mode 100644
index 0000000..f6c6f2c
Binary files /dev/null and b/public/weather-both/ice-both.png differ
diff --git a/public/weather-both/rain-and-snow-both.png b/public/weather-both/rain-and-snow-both.png
new file mode 100644
index 0000000..a1a05e1
Binary files /dev/null and b/public/weather-both/rain-and-snow-both.png differ
diff --git a/public/weather-both/rain-both.png b/public/weather-both/rain-both.png
new file mode 100644
index 0000000..3e5b47d
Binary files /dev/null and b/public/weather-both/rain-both.png differ
diff --git a/public/weather-both/showers-both.png b/public/weather-both/showers-both.png
new file mode 100644
index 0000000..e78e2ce
Binary files /dev/null and b/public/weather-both/showers-both.png differ
diff --git a/public/weather-both/sleet-both.png b/public/weather-both/sleet-both.png
new file mode 100644
index 0000000..9b360bd
Binary files /dev/null and b/public/weather-both/sleet-both.png differ
diff --git a/public/weather-both/snow-both.png b/public/weather-both/snow-both.png
new file mode 100644
index 0000000..05f7605
Binary files /dev/null and b/public/weather-both/snow-both.png differ
diff --git a/public/weather-both/thunder-storm-both.png b/public/weather-both/thunder-storm-both.png
new file mode 100644
index 0000000..3520832
Binary files /dev/null and b/public/weather-both/thunder-storm-both.png differ
diff --git a/public/weather-both/windy-both.png b/public/weather-both/windy-both.png
new file mode 100644
index 0000000..d22b1e2
Binary files /dev/null and b/public/weather-both/windy-both.png differ
diff --git a/public/weather-day/hazy-sunshine-day.png b/public/weather-day/hazy-sunshine-day.png
new file mode 100644
index 0000000..09e7448
Binary files /dev/null and b/public/weather-day/hazy-sunshine-day.png differ
diff --git a/public/weather-day/intermitten-clouds-day.png b/public/weather-day/intermitten-clouds-day.png
new file mode 100644
index 0000000..fb0a1ba
Binary files /dev/null and b/public/weather-day/intermitten-clouds-day.png differ
diff --git a/public/weather-day/mostly-cloudy-day.png b/public/weather-day/mostly-cloudy-day.png
new file mode 100644
index 0000000..a80555c
Binary files /dev/null and b/public/weather-day/mostly-cloudy-day.png differ
diff --git a/public/weather-day/mostly-cloudy-with-flurries-day.png b/public/weather-day/mostly-cloudy-with-flurries-day.png
new file mode 100644
index 0000000..5610b39
Binary files /dev/null and b/public/weather-day/mostly-cloudy-with-flurries-day.png differ
diff --git a/public/weather-day/mostly-cloudy-with-showers-day.png b/public/weather-day/mostly-cloudy-with-showers-day.png
new file mode 100644
index 0000000..96c79e5
Binary files /dev/null and b/public/weather-day/mostly-cloudy-with-showers-day.png differ
diff --git a/public/weather-day/mostly-cloudy-with-snow-day.png b/public/weather-day/mostly-cloudy-with-snow-day.png
new file mode 100644
index 0000000..0fa0dcf
Binary files /dev/null and b/public/weather-day/mostly-cloudy-with-snow-day.png differ
diff --git a/public/weather-day/mostly-cloudy-with-thunder-storms-day.png b/public/weather-day/mostly-cloudy-with-thunder-storms-day.png
new file mode 100644
index 0000000..b5be8d3
Binary files /dev/null and b/public/weather-day/mostly-cloudy-with-thunder-storms-day.png differ
diff --git a/public/weather-day/mostly-sunny-day.png b/public/weather-day/mostly-sunny-day.png
new file mode 100644
index 0000000..b323c8b
Binary files /dev/null and b/public/weather-day/mostly-sunny-day.png differ
diff --git a/public/weather-day/partly-sunny-day.png b/public/weather-day/partly-sunny-day.png
new file mode 100644
index 0000000..b9bfb83
Binary files /dev/null and b/public/weather-day/partly-sunny-day.png differ
diff --git a/public/weather-day/partly-sunny-with flurries-day.png b/public/weather-day/partly-sunny-with flurries-day.png
new file mode 100644
index 0000000..2fe3017
Binary files /dev/null and b/public/weather-day/partly-sunny-with flurries-day.png differ
diff --git a/public/weather-day/partly-sunny-with-showers-day.png b/public/weather-day/partly-sunny-with-showers-day.png
new file mode 100644
index 0000000..259278f
Binary files /dev/null and b/public/weather-day/partly-sunny-with-showers-day.png differ
diff --git a/public/weather-day/partly-sunny-with-thunder-storms-day.png b/public/weather-day/partly-sunny-with-thunder-storms-day.png
new file mode 100644
index 0000000..0ce0911
Binary files /dev/null and b/public/weather-day/partly-sunny-with-thunder-storms-day.png differ
diff --git a/public/weather-day/sun-day.png b/public/weather-day/sun-day.png
new file mode 100644
index 0000000..5127a9d
Binary files /dev/null and b/public/weather-day/sun-day.png differ
diff --git a/public/weather-night/clear-night.png b/public/weather-night/clear-night.png
new file mode 100644
index 0000000..aec8e71
Binary files /dev/null and b/public/weather-night/clear-night.png differ
diff --git a/public/weather-night/hazy-moonlight-night.png b/public/weather-night/hazy-moonlight-night.png
new file mode 100644
index 0000000..9888912
Binary files /dev/null and b/public/weather-night/hazy-moonlight-night.png differ
diff --git a/public/weather-night/intermitten-clouds-night.png b/public/weather-night/intermitten-clouds-night.png
new file mode 100644
index 0000000..3844317
Binary files /dev/null and b/public/weather-night/intermitten-clouds-night.png differ
diff --git a/public/weather-night/mostly-clear-night.png b/public/weather-night/mostly-clear-night.png
new file mode 100644
index 0000000..7279392
Binary files /dev/null and b/public/weather-night/mostly-clear-night.png differ
diff --git a/public/weather-night/mostly-cloudy-night.png b/public/weather-night/mostly-cloudy-night.png
new file mode 100644
index 0000000..572bfec
Binary files /dev/null and b/public/weather-night/mostly-cloudy-night.png differ
diff --git a/public/weather-night/mostly-cloudy-with-flurries-night.png b/public/weather-night/mostly-cloudy-with-flurries-night.png
new file mode 100644
index 0000000..da96f77
Binary files /dev/null and b/public/weather-night/mostly-cloudy-with-flurries-night.png differ
diff --git a/public/weather-night/mostly-cloudy-with-showers-night.png b/public/weather-night/mostly-cloudy-with-showers-night.png
new file mode 100644
index 0000000..7c71a5f
Binary files /dev/null and b/public/weather-night/mostly-cloudy-with-showers-night.png differ
diff --git a/public/weather-night/mostly-cloudy-with-snow-night.png b/public/weather-night/mostly-cloudy-with-snow-night.png
new file mode 100644
index 0000000..47a85e7
Binary files /dev/null and b/public/weather-night/mostly-cloudy-with-snow-night.png differ
diff --git a/public/weather-night/mostly-cloudy-with-thunderstorms-night.png b/public/weather-night/mostly-cloudy-with-thunderstorms-night.png
new file mode 100644
index 0000000..e0dea63
Binary files /dev/null and b/public/weather-night/mostly-cloudy-with-thunderstorms-night.png differ
diff --git a/public/weather-night/partly-cloudy-night.png b/public/weather-night/partly-cloudy-night.png
new file mode 100644
index 0000000..587a4d0
Binary files /dev/null and b/public/weather-night/partly-cloudy-night.png differ
diff --git a/public/weather-night/partly-cloudy-with-showers-night.png b/public/weather-night/partly-cloudy-with-showers-night.png
new file mode 100644
index 0000000..5accec9
Binary files /dev/null and b/public/weather-night/partly-cloudy-with-showers-night.png differ
diff --git a/public/weather-night/partly-cloudy-with-thunderstorms-night.png b/public/weather-night/partly-cloudy-with-thunderstorms-night.png
new file mode 100644
index 0000000..5e678c2
Binary files /dev/null and b/public/weather-night/partly-cloudy-with-thunderstorms-night.png differ
diff --git a/scripts/getIp.js b/scripts/getIp.js
new file mode 100644
index 0000000..388c4ad
--- /dev/null
+++ b/scripts/getIp.js
@@ -0,0 +1,6 @@
+export async function getIp() {
+ return await fetch('https://api.ipify.org?format=json')
+ .then(response => response.json())
+ .then(data => console.log(data.ip))
+ .catch(error => console.log('Unable to get IP address', error));
+}
\ No newline at end of file
diff --git a/scripts/mobile.js b/scripts/mobile.js
new file mode 100644
index 0000000..e69de29
diff --git a/scripts/script.js b/scripts/script.js
new file mode 100644
index 0000000..4e282ec
--- /dev/null
+++ b/scripts/script.js
@@ -0,0 +1,131 @@
+import { updateWeather } from './weather.js';
+import { getIp } from './getIp.js';
+import { updateTime } from './timeDate.js';
+import { updateCurrent } from './updateCurrent.js';
+
+// This is unsecure fix at some point!!!
+const API_BASE_URL = 'https://weather-app-server-d5459d7e5648.herokuapp.com'
+
+async function startWeather(latitude, longitude) {
+ const response = await fetch(`${API_BASE_URL}/?coords=${latitude},${longitude}`);
+ const weatherData = await response.json()
+ console.log(weatherData);
+ return weatherData;
+}
+
+// Create get ip function
+async function getIP() {
+ return await fetch(`${API_BASE_URL}/get-ip`);
+}
+
+async function ipWeather(ip) {
+ const response = await fetch(`${API_BASE_URL}/ip-weather-data?ip=${ip}`);
+ const weatherData = await response.json();
+ return weatherData;
+}
+
+// Ask for location, if not get ip
+if (navigator.geolocation) {
+ navigator.geolocation.getCurrentPosition(
+ async function(position) {
+ const latitude = position.coords.latitude;
+ const longitude = position.coords.longitude;
+ const weatherData = await startWeather(latitude, longitude);
+ updateWeather(weatherData, 'Your Location');
+ updateTime(weatherData);
+ },
+ async function(error) {
+ // This function is called when an error occurs, such as when the user denies the location permission
+ console.log("Geolocation permission denied.");
+ try {
+ const ip = await getIp();
+ const weatherData = await ipWeather(ip);
+ updateWeather(weatherData, 'Your Location');
+ updateTime(weatherData);
+ } catch (error) {
+ console.error('Error:', error);
+ }
+ }
+ );
+} else {
+ console.log("Geolocation is not supported by this browser.");
+ (async () => {
+ try {
+ const ip = await getIp();
+ const weatherData = await ipWeather(ip);
+ updateWeather(weatherData, 'Your Location');
+ updateTime(weatherData);
+ } catch (error) {
+ console.error('Error:', error);
+ }
+ })();
+}
+
+// Get the form
+const searchForm = document.getElementById('search-form');
+
+// Get the search bar
+const searchBar = document.getElementById('location-search');
+
+// Create a dropdown for suggestions
+const dropdown = document.createElement('div');
+dropdown.setAttribute('id', 'dropdown');
+searchBar.parentNode.appendChild(dropdown);
+
+// Stop user from enter invalid address in search bar
+searchBar.addEventListener('keydown', function(event) {
+ if (event.key === 'Enter') {
+ event.preventDefault();
+ }
+});
+
+// Add event listener
+searchBar.addEventListener('input', async function(event) {
+ const userInput = event.target.value;
+
+ if (userInput.length > 2) { // Trigger autocomplete after 2 char
+ try {
+ const response = await fetch(`${API_BASE_URL}/search-locations?input=${encodeURIComponent(userInput)}`);
+ const data = await response.json();
+ dropdown.innerHTML = ''; // Clear previous suggestions
+
+ data.predictions.forEach(item => {
+ const div = document.createElement('div');
+ div.innerHTML = item.description;
+ div.onclick = function() {
+ searchBar.value = item.description;
+ dropdown.innerHTML = ''; // Clear suggestions after selection
+
+ // Trigger form submission event manually
+ const event = new Event('submit');
+ searchForm.dispatchEvent(event);
+ };
+ dropdown.appendChild(div);
+ });
+ } catch (error) {
+ console.error('Error:', error);
+ }
+ }
+});
+
+searchForm.addEventListener('submit', async function(event) {
+ event.preventDefault();
+ // Get the user input from the search bar
+ const userSubmission = searchBar.value;
+ console.log('Form was submitted')
+ try {
+ // Clear the search bar
+ searchBar.value = '';
+
+ // Send a request to the server with the user input
+ const response = await fetch(`${API_BASE_URL}/weather-data?input=${encodeURIComponent(userSubmission)}`);
+ const weatherData = await response.json();
+
+ // Handle the response data here
+ updateWeather(weatherData, userSubmission);
+ updateTime(weatherData);
+ } catch (error) {
+ console.error('Error:', error);
+ }
+
+});
diff --git a/scripts/timeDate.js b/scripts/timeDate.js
new file mode 100644
index 0000000..c100a3b
--- /dev/null
+++ b/scripts/timeDate.js
@@ -0,0 +1,46 @@
+export function updateTime(weatherData) {
+ // Extract timezone and timestamp from weatherData
+ const timezone = weatherData.timezone;
+ const time = weatherData.current.dt;
+
+ // Get first day of the week
+ const firstDay = weatherData.daily[0].dt;
+ const lastDay = weatherData.daily[weatherData.daily.length - 1].dt;
+
+ const weekDateOptions = {
+ month: 'numeric',
+ day: 'numeric',
+ timeZone: timezone,
+ }
+
+ const firstDayDate = new Date(firstDay * 1000).toLocaleDateString('en-US', weekDateOptions);
+ const lastDayDate = new Date(lastDay * 1000).toLocaleDateString('en-US', weekDateOptions);
+
+ // Convert Unix timestamp to Date object
+ const currentDateTime = new Date(time * 1000);
+
+
+ // Date formatting options
+ const dateOptions = {
+ weekday: 'long',
+ month: 'long',
+ day: 'numeric',
+ timeZone: timezone // Set the timezone for formatting
+ };
+ // Format date
+ const formattedDate = currentDateTime.toLocaleDateString('en-US', dateOptions);
+
+ // Time formatting options
+ const timeOptions = {
+ hour: 'numeric',
+ minute: '2-digit',
+ timeZone: timezone // Set the timezone for formatting
+ };
+ // Format time
+ const formattedTime = currentDateTime.toLocaleTimeString('en-US', timeOptions);
+
+ // Example: Update date and time in HTML
+ document.getElementById('currentTime').textContent = formattedTime;
+ document.getElementById('currentDate').textContent = formattedDate;
+ document.getElementById('currentWeek').textContent = `${firstDayDate} - ${lastDayDate}`;
+}
\ No newline at end of file
diff --git a/scripts/unitConversions.js b/scripts/unitConversions.js
new file mode 100644
index 0000000..257e56e
--- /dev/null
+++ b/scripts/unitConversions.js
@@ -0,0 +1,24 @@
+export function kelvinToFahrenheit(kelvin) {
+ const fahrenheit = Math.round((kelvin - 273.15) * 9/5 + 32);
+ return fahrenheit;
+}
+
+export function kelvinToCelsius(kelvin) {
+ const celsius = Math.round(kelvin - 273.15);
+ return celsius;
+}
+
+export function capitalizeFirstLetterOfEachWord(str) {
+ // Split the string into words
+ const words = str.split(' ');
+
+ // Capitalize the first letter of each word
+ const capitalizedWords = words.map(word => {
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
+ });
+
+ // Join the words back into a single string
+ const capitalizedStr = capitalizedWords.join(' ');
+
+ return capitalizedStr;
+}
\ No newline at end of file
diff --git a/scripts/updateCurrent.js b/scripts/updateCurrent.js
new file mode 100644
index 0000000..a36b516
--- /dev/null
+++ b/scripts/updateCurrent.js
@@ -0,0 +1,34 @@
+import { kelvinToFahrenheit, kelvinToCelsius, capitalizeFirstLetterOfEachWord } from './unitConversions.js';
+
+export function updateCurrent(weatherData, weatherCodeToImageMap, userInput) {
+ const current = weatherData.current;
+ const temperature = kelvinToFahrenheit(current.temp);
+ const description = capitalizeFirstLetterOfEachWord(current.weather[0].description);
+ const time = current.dt;
+ const sunrise = current.sunrise;
+ const sunset = current.sunset;
+ const id = current.weather[0].id;
+ let image = weatherCodeToImageMap[id];
+ if (typeof image !== 'string' && time > sunrise && time < sunset) {
+ image = image[0];
+ } else {
+ image = image[1];
+ }
+ const location = userInput.split(',');
+
+ // Get the town from userInput
+ // There's two possible scenarios for the userInput
+ // 1. A string from a verified google address
+ // 2. Your location
+ // For your location set userInput to false
+ let currentHTML = `
+
+
${location[0]}
+
${temperature}°
+
${description}
+
+ `;
+
+ const currentForecastDiv = document.querySelector('.current-body');
+ currentForecastDiv.innerHTML = currentHTML
+}
\ No newline at end of file
diff --git a/scripts/updateHourly.js b/scripts/updateHourly.js
new file mode 100644
index 0000000..cc4c704
--- /dev/null
+++ b/scripts/updateHourly.js
@@ -0,0 +1,90 @@
+import { kelvinToFahrenheit, kelvinToCelsius, capitalizeFirstLetterOfEachWord } from './unitConversions.js';
+
+export function updateHourly(weatherData, weatherCodeToImageMap) {
+ const hourly = weatherData.hourly;
+ const daily = weatherData.daily;
+ const current = weatherData.current;
+ // Get the time of sunset and sun rise for both the current and next day
+ const todayDaily = daily[0];
+ const tomorrowDaily = daily[1];
+ const firstSunrise = todayDaily.sunrise;
+ const firstSunset = todayDaily.sunset;
+ const secondSunrise = tomorrowDaily.sunrise;
+ const secondSunset = tomorrowDaily.sunset;
+ const timezone = weatherData.timezone;
+
+ let riseSet = [secondSunset, secondSunrise, firstSunset, firstSunrise];
+
+ let hourlyHTML = '';
+
+ hourly.forEach((forecast, index) => {
+ // Check to see if the current time is before for after first sunrise
+ if (index === 0 && forecast.dt >= firstSunrise) {
+ riseSet.pop();
+ }
+
+ // Convert temperatures
+ const temperature = kelvinToFahrenheit(forecast.temp);
+ const feelsLike = kelvinToFahrenheit(forecast.feels_like);
+
+ // Get weather array
+ const weatherArray = forecast.weather[0]
+
+ // Get the weather description and code
+ const weatherCode = weatherArray.id;
+ let image = weatherCodeToImageMap[weatherCode];
+ if (typeof image !== "string" && (riseSet.length%2 === 0)) {
+ image = image[1];
+ } else if (typeof image !== "string" && (riseSet.length%2 !== 0)) {
+ image = image[0];
+ }
+
+ const description = capitalizeFirstLetterOfEachWord(weatherArray.description);
+
+ // Get other weather data
+ const windSpeed = forecast.wind_speed;
+ const windDegrees = forecast.wind_deg;
+ const uvi = forecast.uvi;
+
+ const hourOptions = {
+ hour: 'numeric',
+ timeZone: timezone,
+ }
+
+ // Append to hourlyHTML string
+ hourlyHTML += `
+
+
${new Date(forecast.dt * 1000).toLocaleTimeString('en-US', hourOptions)}
+

+
${kelvinToFahrenheit(forecast.temp)}°F
+
`;
+
+
+ const currentSetRiseUnix = riseSet[riseSet.length - 1];
+ const currentSetRiseHour = new Date(currentSetRiseUnix * 1000)
+ const currentForecastHour = new Date(forecast.dt * 1000);
+
+ const riseSetOptions = {
+ hour: 'numeric',
+ minute: '2-digit',
+ timeZone: timezone,
+ }
+
+ // Check for sunrise or sunset
+ if (currentSetRiseHour.getUTCHours() === currentForecastHour.getUTCHours()) {
+ // Insert if true
+ hourlyHTML += `
+
+
${new Date(currentSetRiseUnix * 1000).toLocaleTimeString('en-US', riseSetOptions)}
+
 ? )
+
${(riseSet.length%2 === 0) ? "Sunrise" : "Sunset"}
+
+ `;
+ riseSet.pop()
+ }
+ });
+
+ // Update the DOM
+ const hourlyForecastDiv = document.querySelector('.hourly-body');
+ hourlyForecastDiv.innerHTML = hourlyHTML;
+}
\ No newline at end of file
diff --git a/scripts/updateWeekly.js b/scripts/updateWeekly.js
new file mode 100644
index 0000000..6784663
--- /dev/null
+++ b/scripts/updateWeekly.js
@@ -0,0 +1,80 @@
+import { kelvinToFahrenheit, kelvinToCelsius, capitalizeFirstLetterOfEachWord } from './unitConversions.js';
+
+export function updateWeekly(weatherData, weatherCodeToImageMap) {
+ const daily = weatherData.daily;
+
+ let weeklyHTML = '';
+ const timezone = weatherData.timezone;
+ // Date formatting options
+ const dateOptions = {
+ month: 'numeric',
+ day: 'numeric',
+ timeZone: timezone
+ };
+
+ for (let i = 0; i < daily.length; i++) {
+ // Get the the day object
+ let currentDay = daily[i];
+
+ let date = new Date(currentDay.dt * 1000);
+
+ let day = date.toLocaleDateString('en-US', {weekday: 'long', timezone})
+ let dayMonth = date.toLocaleDateString('en-US', dateOptions);
+ let weatherCode = currentDay.weather[0].id;
+ let image = weatherCodeToImageMap[weatherCode];
+
+ if (typeof image !== "string") {
+ image = image[0];
+ }
+
+ let high = kelvinToFahrenheit(currentDay.temp.max);
+ let low = kelvinToFahrenheit(currentDay.temp.min);
+ let description = capitalizeFirstLetterOfEachWord(currentDay.weather[0].description);
+ let sunrise = new Date(currentDay.sunrise * 1000).toLocaleTimeString('en-US', {hour: 'numeric', minute: '2-digit', timeZone: timezone});
+ let sunset = new Date(currentDay.sunset * 1000).toLocaleTimeString('en-US', {hour: 'numeric', minute: '2-digit', timeZone: timezone});
+ let moonPhase = currentDay.moon_phase;
+ let dayTemp = kelvinToFahrenheit(currentDay.temp.day);
+ let nightTemp = kelvinToFahrenheit(currentDay.temp.night);
+ let eveTemp = kelvinToFahrenheit(currentDay.temp.eve);
+ let mornTemp = kelvinToFahrenheit(currentDay.temp.morn);
+
+ weeklyHTML += `
+
+
+
${(i===0) ? 'Today' : day}
+
${dayMonth}
+
+
+
H: ${high}
+
L: ${low}
+
+
+

+ ${description}
+
+
+
Day Avg: ${dayTemp}
+
Night Avg: ${nightTemp}
+
+
+
+
+ `;
+
+ }
+ const weeklyForecastDiv = document.querySelector('.weekly-body');
+ weeklyForecastDiv.innerHTML = weeklyHTML;
+}
diff --git a/scripts/weather.js b/scripts/weather.js
new file mode 100644
index 0000000..152b225
--- /dev/null
+++ b/scripts/weather.js
@@ -0,0 +1,88 @@
+import { kelvinToFahrenheit, kelvinToCelsius, capitalizeFirstLetterOfEachWord } from './unitConversions.js';
+import { updateHourly } from './updateHourly.js';
+import { updateWeekly } from './updateWeekly.js';
+import { updateCurrent } from './updateCurrent.js';
+
+const weatherCodeToImageMap = {
+ // Thunderstorms - typically use both day and night images
+ 200: "/client/public/weather-both/thunder-storm-both.png",
+ 201: "/client/public/weather-both/thunder-storm-both.png",
+ 202: "/client/public/weather-both/thunder-storm-both.png",
+ 210: "/client/public/weather-both/thunder-storm-both.png",
+ 211: "/client/public/weather-both/thunder-storm-both.png",
+ 212: "/client/public/weather-both/thunder-storm-both.png",
+ 221: "/client/public/weather-both/thunder-storm-both.png",
+ 230: "/client/public/weather-both/thunder-storm-both.png",
+ 231: "/client/public/weather-both/thunder-storm-both.png",
+ 232: "/client/public/weather-both/thunder-storm-both.png",
+
+ // Drizzle - typically use both day and night images
+ 300: "/client/public/weather-both/showers-both.png",
+ 301: "/client/public/weather-both/showers-both.png",
+ 302: "/client/public/weather-both/showers-both.png",
+ 310: "/client/public/weather-both/showers-both.png",
+ 311: "/client/public/weather-both/showers-both.png",
+ 312: "/client/public/weather-both/showers-both.png",
+ 313: "/client/public/weather-both/showers-both.png",
+ 314: "/client/public/weather-both/showers-both.png",
+ 321: "/client/public/weather-both/showers-both.png",
+
+ // Rain - typically use both day and night images
+ 500: "/client/public/weather-both/rain-both.png",
+ 501: "/client/public/weather-both/rain-both.png",
+ 502: "/client/public/weather-both/rain-both.png",
+ 503: "/client/public/weather-both/rain-both.png",
+ 504: "/client/public/weather-both/rain-both.png",
+ 511: "/client/public/weather-both/freezing-rain-both.png",
+ 520: "/client/public/weather-both/showers-both.png",
+ 521: "/client/public/weather-both/showers-both.png",
+ 522: "/client/public/weather-both/showers-both.png",
+ 531: "/client/public/weather-both/showers-both.png",
+
+ // Snow - typically use both day and night images
+ 600: "/client/public/weather-both/snow-both.png",
+ 601: "/client/public/weather-both/snow-both.png",
+ 602: "/client/public/weather-both/snow-both.png",
+ 611: "/client/public/weather-both/sleet-both.png",
+ 612: "/client/public/weather-both/sleet-both.png",
+ 613: "/client/public/weather-both/sleet-both.png",
+ 615: "/client/public/weather-both/rain-and-snow-both.png",
+ 616: "/client/public/weather-both/rain-and-snow-both.png",
+ 620: "/client/public/weather-both/snow-both.png",
+ 621: "/client/public/weather-both/snow-both.png",
+ 622: "/client/public/weather-both/snow-both.png",
+
+ // Atmosphere - typically use both day and night images
+ 701: "/client/public/weather-both/fog-both.png",
+ 711: "/client/public/weather-both/smoke-both.png",
+ 721: "/client/public/weather-both/haze-both.png",
+ 731: "/client/public/weather-both/dust-both.png",
+ 741: "/client/public/weather-both/fog-both.png",
+ 751: "/client/public/weather-both/sand-both.png",
+ 761: "/client/public/weather-both/dust-both.png",
+ 762: "/client/public/weather-both/ash-both.png",
+ 771: "/client/public/weather-both/windy-both.png",
+
+ // Clear - use night or day images
+ 800: ["/client/public/weather-day/sun-day.png", "/client/public/weather-night/clear-night.png"],
+
+ // Clouds
+ 801: ["/client/public/weather-day/mostly-sunny-day.png", "/client/public/weather-night/mostly-clear-night.png"],
+ 802: ["/client/public/weather-day/partly-sunny-day.png", "/client/public/weather-night/partly-cloudy-night.png"],
+ 803: ["/client/public/weather-day/intermitten-clouds-day.png", "/client/public/weather-night/intermitten-clouds-night.png"],
+ 804: ["/client/public/weather-day/mostly-cloudy-day.png", "/client/public/weather-night/mostly-cloudy-night.png"],
+ };
+
+// TODO: create a boolean function for weather it's day, night or in between
+
+export function updateWeather(weatherData, userInput) {
+
+ // Current
+ updateCurrent(weatherData, weatherCodeToImageMap, userInput);
+
+ // Hourly
+ updateHourly(weatherData, weatherCodeToImageMap);
+
+ // Weekly
+ updateWeekly(weatherData, weatherCodeToImageMap);
+};
\ No newline at end of file
diff --git a/styles.css b/styles.css
new file mode 100644
index 0000000..113b388
--- /dev/null
+++ b/styles.css
@@ -0,0 +1,382 @@
+body {
+ margin: 0;
+ padding: 0;
+ /* width: auto;
+ height: auto; */
+ background-color: #D0E4F5;
+ font-family: 'Roboto', sans-serif;
+}
+
+/* Main section */
+.grid-main {
+ display: grid;
+ grid-template-columns: [one] 1fr [two] minmax(200px, 60vw) [three] 1fr [four];
+ grid-template-rows: [one] 100px [two] auto [three] auto [four] auto [five] 100px [six];
+ grid-template-areas: '. nav .'
+ '. current .'
+ '. hourly .'
+ '. weekly .'
+ 'footer footer footer';
+ grid-row-gap: 20px;
+}
+
+.title-text {
+ color: #ffffff;
+ text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.75), 0px 2px 4px rgba(0, 0, 0, 0.198);
+}
+
+.body-text {
+ color: #ffffff;
+ text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.75), 0px 2px 4px rgba(0, 0, 0, 0.507);
+ font-weight: bold;
+}
+
+.title-text-setrise {
+ color: #ffffff;
+ text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.488), 0px 2px 4px rgba(0, 0, 0, 0.427);
+ white-space: nowrap; /* Prevents the text from wrapping */
+ overflow: hidden; /* Hides the text that overflows the container */
+ text-overflow: ellipsis; /* Adds an ellipsis when the text overflows */
+ min-width: 100px;
+}
+
+.header {
+ z-index: 1;
+ display: flex;
+ justify-content: space-between;
+ font-size: 18px;
+ padding: 10px;
+ border-bottom: 1px solid #65656526;
+ color: #ffffff;
+ text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.75), 0px 2px 4px rgba(0, 0, 0, 0.198);
+ font-weight: bold;
+}
+
+
+
+/* Navbar Section */
+.navbar {
+ grid-area: 1/1/2/4;
+ width: 100%;
+ background-color: rgba(255, 255, 255, 0.5);
+}
+
+.navbar-content {
+ z-index: 1;
+ display: flex;
+ grid-area: nav;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.btn {
+ display: block;
+}
+
+.btn {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding-left: 60px;
+}
+
+.btn .fas {
+ color: #ffffff;
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
+ font-size: 30px;
+}
+
+.logo {
+ display: flex;
+ align-items: center;
+}
+
+#search-form {
+ width: 400px;
+ padding-left: 25px;
+}
+
+#location-search {
+ width: 100%;
+ padding: 8px 10px;
+ box-sizing: border-box;
+ border: 2px solid #ccc;
+ border-radius: 4px;
+ font-size: 16px;
+}
+
+#dropdown {
+ list-style-type: none;
+ padding: 0;
+ margin: 0;
+ background: rgba(255, 255, 255, 0.738);
+ position: absolute;
+ width: 400px;
+ box-shadow: 0 4px 8px rgba(0,0,0,0.1);
+ border-radius: 0 0 4px 4px;
+}
+
+#dropdown div {
+ padding: 8px 10px;
+ cursor: pointer;
+ border-bottom: 1px solid #ccc;
+}
+
+#dropdown div:last-child{
+ border-bottom: none;
+}
+
+#dropdown div:hover {
+ background-color: #d3d3d35c;
+}
+
+.navbar-content img {
+ padding: 0;
+ margin-left: -25px;
+ width: auto;
+ height: 60px;
+}
+
+.navbar-content h1 {
+ padding: 0;
+ margin-left: -5px;
+}
+
+/* Current Section */
+.current {
+ grid-area: current;
+ background-color: rgba(255, 255, 255, 0.5);
+ border-radius: 10px;
+}
+
+.current-body {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding-top: 10px;
+ padding-bottom: 20px;
+}
+
+.current-left {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+}
+
+.current-temp {
+ font-size: 50px;
+}
+
+.current-place {
+ font-size: 20px;
+}
+
+.current-description {
+ font-size: 17px;
+}
+
+
+/* Hourly Section */
+.hourly {
+ grid-area: hourly;
+ background-color: rgba(255, 255, 255, 0.5);
+ border-radius: 10px;
+}
+
+.hourly-body {
+ display: flex;
+ overflow-x: auto;
+
+}
+
+.scroll-hour {
+ margin-bottom: 20px;
+ padding: 10px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ color: #ffffff; /* Pure white text */
+ text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.75), 0px 2px 4px rgba(0, 0, 0, 0.198);
+ font-weight: bold;
+ height: 100%;
+}
+
+/* Week forecast */
+
+.weekly {
+ grid-area: weekly;
+ background-color: rgba(255, 255, 255, 0.5);
+ border-radius: 10px;
+}
+
+.weekly-body {
+
+}
+
+.day {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-around;
+ border-bottom: 1px solid #65656568;
+ padding: 10px;
+}
+
+.row {
+ display: flex;
+ float: row;
+ justify-content: space-around;
+ align-items: center;
+ row-gap: 20px;
+}
+
+.day p {
+ margin: 0;
+ padding: 0;
+}
+
+.day img {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: auto;
+ flex-shrink: 0;
+ width: 75px;
+}
+
+.day-date {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ flex-shrink: 0;
+ width: 75px;
+}
+
+.day-high-low {
+ display: flex;
+ flex-direction: column;
+ flex-shrink: 0;
+ width: 50px;
+}
+
+.day-description {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ flex-shrink: 0;
+ width: 175px;
+ margin-left: -20px;
+}
+
+.day-sun-set-rise {
+ display: flex;
+ flex-direction: column;
+ flex-shrink: 0;
+}
+
+.day-night {
+ display: flex;
+ flex-direction: column;
+ flex-shrink: 0;
+ width: 100px;
+}
+
+.shadow {
+ filter: drop-shadow(2px 2px 2px gray);
+}
+
+.sunrise-color {
+ color: #FFA500;
+}
+
+.sunset-color {
+ color: #272056;
+}
+
+.footer {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+/* Effects */
+.glass-effect {
+ border-radius: 10px; /* Rounded corners */
+ border: 1px solid rgba(255, 255, 255, 0.702); /* Subtle white border */
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* Soft shadow for depth */
+}
+
+
+/* Phones */
+@media (max-width: 600px) {
+ .grid-main {
+ grid-template-rows: [one] auto [two] auto [three] auto [four] auto [five] 100px [six];
+ grid-template-columns: [one] 1fr [two] minmax(200px, auto) [three] 1fr [four];
+ }
+
+ .day-description {
+ flex-shrink: 1;
+ }
+
+ .current, .hourly, .weekly {
+ margin-left: 10px;
+ margin-right: 10px;
+ }
+
+ .navbar-content {
+ flex-direction: column;
+ justify-content: center;
+ padding: 20px;
+ }
+
+ .day-night {
+ display: none;
+ }
+ #search-form, #dropdown {
+ width: 100%;
+ }
+}
+
+/* Tablets */
+@media (max-width: 768px) {
+ .grid-main {
+ grid-template-columns: [one] 1fr [two] minmax(6fr, 80vw) [three] 1fr [four];
+ }
+ #search-form, #dropdown {
+ width: 300px;
+ }
+}
+
+/* desktops */
+@media (max-width: 992px) {
+ .grid-main {
+ grid-template-columns: [one] 1fr [two] minmax(500px, 80vw) [three] 1fr [four];
+ }
+
+ #search-form, #dropdown {
+ width: 250px;
+ }
+
+ .day-night {
+ display: none;
+ }
+
+}
+
+/* desktops */
+@media (min-width: 992px) {
+ .grid-main {
+ grid-template-columns: [one] 1fr [two] minmax(700px, 6fr) [three] 1fr [four];
+ }
+}
+
+/* Extra large devices */
+@media (min-width: 1201px) {
+ .grid-main {
+ grid-template-columns: [one] 1fr [two] 1000px [three] 1fr [four];
+ }
+}
\ No newline at end of file