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
30,308 changes: 7,398 additions & 22,910 deletions package-lock.json

Large diffs are not rendered by default.

17 changes: 13 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,30 @@
"@testing-library/jest-dom": "^5.11.10",
"@testing-library/react": "^11.2.6",
"@testing-library/user-event": "^12.8.3",
"axios": "^1.4.0",
"bootstrap": "^5.3.1",
"lodash": "^4.17.21",
"normalizr": "^3.6.2",
"prop-types": "^15.8.1",
"react": "^17.0.2",
"react-bootstrap": "^2.8.0",
"react-dom": "^17.0.2",
"react-redux": "^8.1.2",
"react-scripts": "4.0.3",
"react-sparklines": "^1.7.0",
"redux": "^4.2.1",
"redux-promise": "^0.6.0",
"web-vitals": "^1.1.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"start": "react-scripts --openssl-legacy-provider start",
"build": "react-scripts --openssl-legacy-provider build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
"react-app"
]
},
"browserslist": {
Expand Down
38 changes: 0 additions & 38 deletions src/App.css

This file was deleted.

25 changes: 0 additions & 25 deletions src/App.js

This file was deleted.

8 changes: 0 additions & 8 deletions src/App.test.js

This file was deleted.

27 changes: 27 additions & 0 deletions src/actions/index.js
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;
Copy link
Copy Markdown

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

const ROOT_URL = 'https://api.openweathermap.org';

export function fetchLocation(query) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The 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}`
Copy link
Copy Markdown

Choose a reason for hiding this comment

The 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

);
36 changes: 36 additions & 0 deletions src/components/Chart.js
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;
44 changes: 44 additions & 0 deletions src/components/Main.js
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;
38 changes: 38 additions & 0 deletions src/components/SearchBar.js
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;
14 changes: 14 additions & 0 deletions src/containers/Header.js
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;
13 changes: 0 additions & 13 deletions src/index.css

This file was deleted.

27 changes: 16 additions & 11 deletions src/index.js
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();
1 change: 0 additions & 1 deletion src/logo.svg

This file was deleted.

8 changes: 8 additions & 0 deletions src/reducers/index.js
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;
40 changes: 40 additions & 0 deletions src/reducers/reducer-locations.js
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;
13 changes: 0 additions & 13 deletions src/reportWebVitals.js

This file was deleted.

5 changes: 0 additions & 5 deletions src/setupTests.js

This file was deleted.