-
Notifications
You must be signed in to change notification settings - Fork 85
Eval submission #82
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
base: master
Are you sure you want to change the base?
Eval submission #82
Changes from all commits
3902b29
b75c9b7
49c9c04
1763762
1e00a62
0a8fc1f
142b782
9fc58d6
531751f
d918bc1
912a354
cabac05
a6634a6
8a11c59
25decf2
5c84728
60809af
3415db5
7fd4a0c
3a4a3b6
12eb437
d043090
7c55c87
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
This file was deleted.
This file was deleted.
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| import axios from 'axios'; | ||
|
|
||
| export const FETCH_LOCATION = 'FETCH_LOCATION'; | ||
|
|
||
| const API_KEY = process.env.REACT_APP_API_KEY; | ||
| const ROOT_URL = 'https://api.openweathermap.org'; | ||
|
|
||
| export function fetchLocation(query) { | ||
|
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. Would recommend using function expressions over declarations |
||
| const forecast = fetchCoordinates(query) | ||
| .then((response) => | ||
| fetchForecast(response.data[0].lat, response.data[0].lon) | ||
| ) | ||
| .catch((error) => null); | ||
|
|
||
| return { | ||
| type: FETCH_LOCATION, | ||
| payload: forecast | ||
| }; | ||
| } | ||
|
|
||
| const fetchCoordinates = (query) => | ||
| axios.get(`${ROOT_URL}/geo/1.0/direct?q=${query}&appid=${API_KEY}`); | ||
|
|
||
| const fetchForecast = (lat, lon) => | ||
| axios.get( | ||
| `${ROOT_URL}/data/2.5/forecast?lat=${lat}&lon=${lon}&units=imperial&appid=${API_KEY}` | ||
|
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 both of these functions are doing the same kind of call, would be better to make a generic fetch function and send in all the rest of the needed data as parameters |
||
| ); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| import PropTypes from 'prop-types'; | ||
| import { | ||
| Sparklines, | ||
| SparklinesLine, | ||
| SparklinesReferenceLine | ||
| } from 'react-sparklines'; | ||
| import { useSelector } from 'react-redux'; | ||
|
|
||
| function Chart({ id, type, color }) { | ||
| const location = useSelector((state) => state.locations.entries[id]); | ||
|
|
||
| const getAverage = (array) => | ||
| Math.round(array.reduce((a, b) => a + b) / array.length); | ||
|
|
||
| const data = location.list.map((item) => item.main[type]); | ||
|
|
||
| const average = getAverage(data); | ||
|
|
||
| return ( | ||
| <> | ||
| <Sparklines data={data}> | ||
| <SparklinesLine color={color} /> | ||
| <SparklinesReferenceLine type="avg" /> | ||
| </Sparklines> | ||
| <p className="mb-0">{`Average: ${average}`}</p> | ||
| </> | ||
| ); | ||
| } | ||
|
|
||
| Chart.propTypes = { | ||
| color: PropTypes.string, | ||
| id: PropTypes.number, | ||
| type: PropTypes.string | ||
| }; | ||
|
|
||
| export default Chart; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| import Container from 'react-bootstrap/Container'; | ||
| import Table from 'react-bootstrap/Table'; | ||
| import { useSelector } from 'react-redux'; | ||
| import Chart from './Chart'; | ||
|
|
||
| function Main() { | ||
| const locations = useSelector((state) => state.locations); | ||
|
|
||
| return ( | ||
| <Container className="col-8 p-4"> | ||
| <Table borderless hover> | ||
| <thead> | ||
| <tr className="border-top border-bottom text-center"> | ||
| <th>City</th> | ||
| <th>Temperature (F)</th> | ||
| <th>Pressure (hPa)</th> | ||
| <th>Humidity (%)</th> | ||
| </tr> | ||
| </thead> | ||
| <tbody> | ||
| {locations.order.map((location) => ( | ||
| <tr | ||
| className="border-bottom align-middle text-center" | ||
| key={location} | ||
| > | ||
| <td>{locations.entries[location].city.name}</td> | ||
| <td> | ||
| <Chart id={location} type="temp" color="blue" /> | ||
| </td> | ||
| <td> | ||
| <Chart id={location} type="pressure" color="green" /> | ||
| </td> | ||
| <td> | ||
| <Chart id={location} type="humidity" color="orange" /> | ||
| </td> | ||
| </tr> | ||
| ))} | ||
| </tbody> | ||
| </Table> | ||
| </Container> | ||
| ); | ||
| } | ||
|
|
||
| export default Main; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| import Container from 'react-bootstrap/Container'; | ||
| import Button from 'react-bootstrap/Button'; | ||
| import Form from 'react-bootstrap/Form'; | ||
| import { useDispatch } from 'react-redux'; | ||
| import { fetchLocation } from '../actions'; | ||
|
|
||
| function SearchBar() { | ||
| const dispatch = useDispatch(); | ||
|
|
||
| const handleSubmit = (event, query) => { | ||
| event.preventDefault(); | ||
| event.target.search.value = ''; | ||
| dispatch(fetchLocation(query)); | ||
| }; | ||
|
|
||
| return ( | ||
| <Container className="col-4 pt-4"> | ||
| <Form | ||
| className="d-flex justify-content-between" | ||
| onSubmit={(event) => handleSubmit(event, event.target.search.value)} | ||
| > | ||
| <Form.Group className="flex-grow-1 me-3 mb-3" controlId="formSearch"> | ||
| <Form.Control | ||
| type="text" | ||
| name="search" | ||
| placeholder="Enter city here" | ||
| required | ||
| /> | ||
| </Form.Group> | ||
| <Button className="mb-3" variant="primary" type="submit"> | ||
| Submit | ||
| </Button> | ||
| </Form> | ||
| </Container> | ||
| ); | ||
| } | ||
|
|
||
| export default SearchBar; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import Container from 'react-bootstrap/Container'; | ||
| import Navbar from 'react-bootstrap/Navbar'; | ||
|
|
||
| function Header() { | ||
| return ( | ||
| <Navbar className="bg-body-tertiary"> | ||
| <Container> | ||
| <Navbar.Brand>Redux Weather</Navbar.Brand> | ||
| </Container> | ||
| </Navbar> | ||
| ); | ||
| } | ||
|
|
||
| export default Header; |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,22 @@ | ||
| import 'bootstrap/dist/css/bootstrap.min.css'; | ||
| import React from 'react'; | ||
| import ReactDOM from 'react-dom'; | ||
| import './index.css'; | ||
| import App from './App'; | ||
| import reportWebVitals from './reportWebVitals'; | ||
| import { legacy_createStore as createStore, applyMiddleware } from 'redux'; | ||
| import { Provider } from 'react-redux'; | ||
| import promise from 'redux-promise'; | ||
|
|
||
| import Header from './containers/Header'; | ||
| import SearchBar from './components/SearchBar'; | ||
| import Main from './components/Main'; | ||
| import reducers from './reducers'; | ||
|
|
||
| const createStoreWithMiddleware = applyMiddleware(promise)(createStore); | ||
|
|
||
| ReactDOM.render( | ||
| <React.StrictMode> | ||
| <App /> | ||
| </React.StrictMode>, | ||
| <Provider store={createStoreWithMiddleware(reducers)}> | ||
| <Header /> | ||
| <SearchBar /> | ||
| <Main /> | ||
| </Provider>, | ||
| document.getElementById('root') | ||
| ); | ||
|
|
||
| // If you want to start measuring performance in your app, pass a function | ||
| // to log results (for example: reportWebVitals(console.log)) | ||
| // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals | ||
| reportWebVitals(); |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| import { combineReducers } from 'redux'; | ||
| import LocationsReducer from './reducer-locations'; | ||
|
|
||
| const rootReducer = combineReducers({ | ||
| locations: LocationsReducer | ||
| }); | ||
|
|
||
| export default rootReducer; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| /* eslint-disable default-param-last */ | ||
| import { normalize, schema } from 'normalizr'; | ||
| import _ from 'lodash'; | ||
| import { FETCH_LOCATION } from '../actions'; | ||
|
|
||
| const locationSchema = new schema.Entity('locations', undefined, { | ||
| idAttribute: (value) => value.city.id | ||
| }); | ||
|
|
||
| const DEFAULT_STATE = { | ||
| entries: {}, | ||
| order: [] | ||
| }; | ||
|
|
||
| const locationsReducer = function (state = DEFAULT_STATE, action) { | ||
| switch (action.type) { | ||
| case FETCH_LOCATION: { | ||
| if (!action.payload) { | ||
| alert( | ||
| 'Sorry, there was a problem getting data for that city. Please try again.' | ||
| ); | ||
| return state; | ||
| } | ||
|
|
||
| const normalizedLocation = normalize(action.payload.data, locationSchema); | ||
|
|
||
| return { | ||
| entries: { | ||
| ...normalizedLocation.entities.locations, | ||
| ...state.entries | ||
| }, | ||
| order: _.union([normalizedLocation.result], [...state.order]) | ||
| }; | ||
| } | ||
| default: | ||
| return state; | ||
| } | ||
| }; | ||
|
|
||
| export default locationsReducer; |
This file was deleted.
This file was deleted.
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.
You are missing a .env file at your root for this to work correctly