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
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
VITE_API_SERVER="http://localhost:3001"
VITE_API_KEY=pk.9b1f47b60a4e41bb88cb29aa817c0830
2 changes: 1 addition & 1 deletion .env.sample
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VITE_API_KEY=
VITE_API_KEY=pk.9b1f47b60a4e41bb88cb29aa817c0830
90 changes: 57 additions & 33 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,83 +8,107 @@ import ErrorComponent from './components/Error';
import Weather from './components/Weather';

const API_KEY = import.meta.env.VITE_API_KEY;

// on line 37 you forgot to implement url http://localhost:3000, for these labs, you will want to create a variable
// like on line 13 that will have the value of your key from the .env file
// in the .env file you will create a variable called VITE_API_SERVER=http://localhost:3000
const VITE_API_SERVER = import.meta.env.VITE_API_SERVER;
function App() {
const [city, setCity] = useState('');
const [latitude, setLatitude] = useState(null);
const [longitude, setLongitude] = useState(null);
const [errorMessage, setErrorMessage] = useState('');
const [forecast, setForecast] = useState(null);
const [showWeather, setShowWeather] = useState(false);

// changed the way in which you are grabbing user input from the form.
// first step was to add state for your search Query (line 21)
const [searchQuery, setSearchQuery] = useState('')

// create a function that is going to handle your searchQuery whenever someone types in a new city (line 24-27)
// if your console is open, the log on line 26 will constantly add a new log whenever you type something into the
//form (every letter typed will produce a log)
const updateCity = (e) => {
console.log(e)
// you then set the state of searchQuery (which is currently an empty string) using the setSearchQuery function
// to whatever the target value will be (so if someone types in Seattle, the empty string will be replaced with
// Seattle)
setSearchQuery(e.target.value);
};


async function fetchWeatherData(lat, lon) {
try {
const response = await axios.get('http://localhost:3000/weather', {
const response = await axios.get(`${VITE_API_SERVER}/weather`, {
params: {
lat: lat,
lon: lon,
//now that you have a function that is going to handle setting the state of searchQuery,
//you now can use that state (searchQuery) as a parameter for your get request to the backend (linke 44)

searchQuery: searchQuery,
latitude: lat,
longitude: lon,

},
});

// if you open your console, and look for Weather Response, open the request tab and look for the
// responseURL.You can see the longitude, latitude as well as your searchQuery implemented
// in the url. (ie http://localhost:3001/weather?searchQuery=Seattle&latitude=47.6038321&longitude=-122.330062)
console.log("Weather Response", response)

setForecast(response.data);
setShowWeather(true);
// setShowWeather(true);
} catch (error) {
console.error('Error fetching weather data:', error);
setErrorMessage('Failed to fetch weather data. Please try again.');
setShowWeather(false);
setForecast(null);
}
}


async function getLocation(cityName) {
if (!cityName) {
return;
}

const url = `https://us1.locationiq.com/v1/search.php?key=${API_KEY}&q=${cityName}&format=json`;

// again, because searchQuery is now a part of state, when updateCity() gets called, searchQuery will be changed
// from a blank string to whatever is typed in the form. So there is no longer a need to have a parameter inside
// the parenthesis on line 66 (deleted cityname)
async function getLocation() {
// line 68 we can replace the cityname to your state, searchQuery.
const url = `https://us1.locationiq.com/v1/search.php?key=${API_KEY}&q=${searchQuery}&format=json`;
console.log(url)
try {
const response = await axios.get(url);
if (response.data && response.data.length > 0) {
// line 53 you are setting the state of city from an empty string
// to the display_name coming from locationiq.com, using setCity() function and passing in response.data[0].display_name
// to replace the empty string with that value.
setCity(response.data[0].display_name);
setLatitude(response.data[0].lat);
setLongitude(response.data[0].lon);
setErrorMessage('');


fetchWeatherData(response.data[0].lat, response.data[0].lon);
console.log('Sending request to backend...');
} else {
setErrorMessage(`No location found for '${cityName}'. Please try a different location.`);
setShowWeather(false);
setForecast(null);
setErrorMessage(`No location found for '${searchQuery}'. Please try a different location.`);
}
} catch (error) {
console.error(error.message);
setErrorMessage(`We're having trouble finding that location. Please try again.`);
setShowWeather(false);
setForecast(null);
}
}

function changeCity(newCity) {
getLocation(newCity);
setShowWeather(false);
console.log('Changing to', newCity);
}

// extra lines of code from line 69-73: do not really need it
// function changeCity(newCity) {
// getLocation(newCity);
// setShowWeather(false);
// console.log('Changing to', newCity);
// }
return (
<div className="app-container">
<Header />
{errorMessage && <ErrorComponent message={errorMessage} />}
<div className="form-container">
<CityForm city={city} handleChangeCity={changeCity} />
{/* Now that we created a new function called updateCity, we have to pass it into the form on line 103. */}
<CityForm updateCity={updateCity}city={city} handleGetLocation={getLocation} />
{latitude && longitude && <Map latitude={latitude} longitude={longitude} city={city} />}
{showWeather && forecast && <Weather forecast={forecast} />}
{/* pass in city into the Weather component so that we can use the city_name as props */}
{forecast && <Weather forecast={forecast} city={city} />}
<Footer />
</div>
</div>
);
}

export default App;

16 changes: 12 additions & 4 deletions src/components/CityForm.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { useState, useRef } from 'react';
/* eslint-disable react/prop-types */
import { useState} from 'react';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';

function CityForm(props) {
const [showHeading, setShowHeading] = useState(false);
const textInput = useRef(null);

// deleted the text on line 8
const handleSubmit = (e) => {
e.preventDefault();
setShowHeading(true);
props.handleChangeCity(textInput.current.value);
//we no longer have to pass a value into props.handleGetLocation. The getLocation() function in App.jsx has no parameter.
// so when you passed down getLocation() as handleGetLocation (line 103) and implemented it into this handleSubmit() function
// you are just calling the function once the form is submitted or when you click on the Explore button.
props.handleGetLocation();
};
return (
<Form onSubmit={handleSubmit} className="google-search-form">
Expand All @@ -21,7 +25,10 @@ function CityForm(props) {
placeholder="Oh...the places you'll go."
size="lg"
type="text"
ref={textInput}
// deleted the text and replaced with the onChange on 29. You have to use the
// updateCity() function (the one that sets searchQuery from a empty string to the e.target.value or
// what is typed into the form) as an onChange so that searchQuery gets a value (ie "Seattle")
onChange={props.updateCity}
className="search-input"
/>
<Button variant="primary" type="submit" className="search-button">
Expand All @@ -32,6 +39,7 @@ function CityForm(props) {
{showHeading && props.city && (
<h2 className="results-text">Welcome to {props.city}. <br/> Explore Below!</h2>
)}

</Form>
);
}
Expand Down
12 changes: 8 additions & 4 deletions src/components/Map.jsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import {When} from 'react-if';


const API_KEY = import.meta.env.VITE_API_KEY;
const API_KEY = 'pk.9b1f47b60a4e41bb88cb29aa817c0830'

function Map(props) {
;

function Map(props) {
console.log(API_KEY);


console.log(props.latitude)
console.log(props.longitude)
return (
<When condition={props.latitude && props.longitude}>
<figure>
<img src={`https://maps.locationiq.com/v3/staticmap?key=${API_KEY}&center=${props.latitude},${props.longitude}&size=600x600&format=png`} width="800"/>
<img src={`https://maps.locationiq.com/v3/staticmap?key=${API_KEY}&center=${props.latitude},${props.longitude}&size=600x600&format=png`} width="800" height="800"/>
</figure>

</When>
)
}

export default Map;

22 changes: 16 additions & 6 deletions src/components/Weather.jsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
/* eslint-disable react/prop-types */
import React from 'react';

const Weather = ({ forecast }) => {
const Weather = (props) => {
console.log("Forecast: ",props.forecast);
// take a look at your console and open the tabs for
// props.forecast; do you see any key that has a value display_name?
console.log("City= ", props.city);
// take a look at the console for this log; do you see the city name when you typed in Seattle?
return (

<div className="weather-container">
{forecast && Array.isArray(forecast) && forecast.length > 0 ? (
{props.forecast && (
<div>
<h2>Weather Forecast for {forecast[0].city_name}</h2>
{forecast.map((day, index) => (
{/* passed in city as props (from App.jsx) as opposed to the
previous code where you
were trying to display the city
name using forecast[0].display_nam. props.city refers
the state from App.jsx (when you set the state on line 76 in App.jsx from an empty string to the display_name from locationiq.com) */}
<h2>Weather Forecast for {props.city}</h2>
{props.forecast.map((day, index) => (
<div key={index} className="weather-day">
<p>Date: {day.date}</p>
<p>Description: {day.description}</p>
</div>
))}
</div>
) : (
<p>It's probably gonna rain. Who knows. Global Warming, Amirite?</p>
)}
</div>
);
Expand Down