Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
124e32d
init
dnlbui Sep 12, 2022
b80c960
attempt to style w/bootstrap
dnlbui Sep 12, 2022
0b2f172
change variables names to easier read
dnlbui Sep 12, 2022
e4964be
Delete to start again
dnlbui Nov 28, 2022
feb36c9
Delete everything and restart
dnlbui Dec 7, 2022
d44c0e2
App and index boiler plate
dnlbui Dec 7, 2022
67fce72
set up header and store skeleton
dnlbui Dec 8, 2022
89b4abd
set up fetchweather action & add skeleton for app
dnlbui Dec 8, 2022
85e7bce
search bar skeleton
dnlbui Dec 8, 2022
5724183
search bar set up
dnlbui Dec 8, 2022
5ca5754
searchbar event and state
dnlbui Dec 8, 2022
6e562a4
bootstrap app styling
dnlbui Dec 8, 2022
c8b4635
change single quotes to double quotes
dnlbui Dec 10, 2022
da7dbe8
added middleware but need to check if needed
dnlbui Dec 12, 2022
f8f3085
Got axios to work. Needed to add http
dnlbui Dec 12, 2022
de5b6ea
trying to get weather index to work
dnlbui Dec 12, 2022
75985fe
weather-list to works. Hiccup bc not return map
dnlbui Dec 13, 2022
6c7fdd9
removed unused imports and change some spacing
dnlbui Dec 13, 2022
6bfcc4a
Added if/else on searchbar submit fxn. Adding comments
dnlbui Dec 14, 2022
bd3c30e
made a renderchart function and est repeat searech
dnlbui Dec 14, 2022
b8b8b0c
Check for city in state before search is done
dnlbui Dec 14, 2022
1c15360
added a catch
dnlbui Dec 14, 2022
dfaa446
change console error code message
dnlbui Dec 14, 2022
5060738
searches work as expected. Need to tune error
dnlbui Dec 14, 2022
0743fc5
uppercase city to have search bar validation work
dnlbui Dec 14, 2022
ae3ef3c
function for json to state object
dnlbui Dec 14, 2022
4ad9762
Added comments
dnlbui Dec 14, 2022
dbfac49
added comments
dnlbui Dec 14, 2022
e6698ce
changed spacing
dnlbui Dec 14, 2022
ac9abae
added redux toolkit
dnlbui Dec 14, 2022
ea12862
all 40 data points in array. added ave and rounded
dnlbui Dec 15, 2022
8d01677
color argument in renderChart+render averages
dnlbui Dec 15, 2022
8bdaca1
added imperial units to url
dnlbui Dec 15, 2022
ee3de92
added alert for error
dnlbui Dec 15, 2022
2968298
clear state with click. changed arguments to state
dnlbui Dec 15, 2022
ca496cf
added if else statement in reducer for errors
dnlbui Dec 15, 2022
abb5f94
moved api data manipulationa + lodash to actions
dnlbui Dec 15, 2022
4509764
removed extra else if statement and changed space
dnlbui Dec 15, 2022
b7a7ffc
trying out redux tool and spacing
dnlbui Dec 15, 2022
fcd410e
added an api variable. Tried to do local..
dnlbui Dec 15, 2022
ddbf64c
removed some comments
dnlbui Dec 15, 2022
b1e472d
condensed code for rendering
dnlbui Dec 15, 2022
457ae00
removed some comments
dnlbui Dec 15, 2022
7db84e4
changed if/then to ternary operator
dnlbui Dec 15, 2022
882b479
spacing/comments
dnlbui Jan 16, 2023
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
src/dummy.json
20,707 changes: 10,555 additions & 10,152 deletions package-lock.json

Large diffs are not rendered by default.

13 changes: 11 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,26 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@reduxjs/toolkit": "^1.9.1",
"@testing-library/jest-dom": "^5.11.10",
"@testing-library/react": "^11.2.6",
"@testing-library/user-event": "^12.8.3",
"axios": "^0.27.2",
"bootstrap": "^5.2.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-redux": "^8.0.2",
"react-scripts": "4.0.3",
"react-sparklines": "^1.7.0",
"redux": "^4.2.0",
"redux-devtools-extension": "^2.13.9",
"redux-promise": "^0.6.0",
"save-dev": "^0.0.1-security",
"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"
},
Expand Down
Binary file removed public/logo192.png
Binary file not shown.
Binary file removed public/logo512.png
Binary file not shown.
10 changes: 0 additions & 10 deletions public/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,6 @@
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
Expand Down
Empty file added react-scripts
Empty file.
33 changes: 12 additions & 21 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
import logo from './logo.svg';
import './App.css';
//App component which acts as a container for all other components
import React from "react";
import SearchBar from "./container/search_bar";
import WeatherList from "./container/weather_list"

function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
const App = () =>{
return(
<div className="col-md-8 offset-md-2">
<SearchBar />
<br></br>
<WeatherList />
</div>
);
)
}

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

This file was deleted.

47 changes: 47 additions & 0 deletions src/actions/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import axios from "axios";
import _ from "lodash"

export const FETCH_CITYWEATHER = "FETCH_CITYWEATHER";
//const API_KEY = process.env.REDUX_APP_WEATHER_API_KEY;
const ROOT_URL = "https://api.openweathermap.org/data/2.5";
const API_KEY = "d2ebbe84afd80c9a2267505ea9c93841";

//function which returns type and payload
export function fetchCityWeather(city) {
//promise and catch
const request = axios.get(`${ROOT_URL}/forecast?q=${city}&units=imperial&appid=${API_KEY}`)
.then(request=> {
const fiveDayData = (data) => {
const temp = [];
const pressure = [];
const humidity = [];

data.data.list.forEach(element => {
temp.push(element.main.temp);
pressure.push(element.main.pressure);
humidity.push(element.main.humidity);
})

return({
city: request.data.city.name.toUpperCase(),
temp: temp,
pressure: pressure,
humidity: humidity,
tempAve: _.round(_.mean(temp)),
pressureAve: _.round(_.mean(pressure)),
humidityAve: _.round(_.mean(humidity))
})
}

return fiveDayData(request)
})
.catch(err=> {
console.log(`City not found. Input a valid city! ${err}`)
alert(`City not found. Input a valid city! \n${err}`)
})

return {
type: FETCH_CITYWEATHER,
payload: request,
}
}
17 changes: 17 additions & 0 deletions src/components/header.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//Header component function. Will return and display Redux Weather at the top.
const Header = (props) => {
return (
<div>
<div className="jumbotron text-center">
<div className="container">
<h1 className="jumbotron-heading">Redux Weather</h1>
</div>
</div>
<div className="container">
{props.children}
</div>
</div>
)
}

export default Header;
34 changes: 34 additions & 0 deletions src/container/search_bar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React, { useState } from "react"
import { useDispatch, useSelector } from "react-redux";
import _ from "lodash"
import { fetchCityWeather } from "../actions";

//funciton component that renders the searchbar
const SearchBar = () => {
//useSelector function hook allows component to extract data from Redux store state
const weatherPosts = useSelector(state => state.weatherPosts)
//usedispatch function hook returns dispatch function from redux store. Used to dispatch actions.
const dispatch = useDispatch();

const [cityState, setCity] = useState("")

const handleSearchButtonClick = (e) => {
e.preventDefault();
setCity('');
//variable which searches for input value inside of an array of objects. Returns in an arraay matched objects that contain value to component state.
const filteredCity = _.filter(weatherPosts, (obj) => cityState.toUpperCase() === obj.city)
//checking for input matches to redux state
weatherPosts.length === 0 ? dispatch(fetchCityWeather(cityState))
:filteredCity.length === 0 ? dispatch(fetchCityWeather(cityState))
:alert("City already listed!");
}

return (
<div className="input-group">
<input value={cityState} onChange={(e)=>setCity(e.target.value)} type="search" className="form-control rounded" placeholder="Enter a City" aria-label="Search" aria-describedby="search-addon" />
<button value={cityState} type="button" className="btn btn-outline-primary" onClick={handleSearchButtonClick}>search</button>
</div>
)
}

export default SearchBar
58 changes: 58 additions & 0 deletions src/container/weather_list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useSelector } from "react-redux";
import { Sparklines,SparklinesLine,SparklinesReferenceLine } from 'react-sparklines';

//component which returns list of weather that contains 5 day charts for temp, pressure, and humidity
const WeatherList = () =>{
//useSelector function allows component to extract data from Redux store state
const weatherPosts = useSelector(state => state.weatherPosts)

//function used to return JSX for the charts that takes the 5 day data values
const renderChart = (dataValues, color, specificCurrentWeatherMetric, symbol) => {
return(
<div>
<Sparklines limit={5} svgWidth={250} svgHeight={75} margin={5} data={dataValues}>
<SparklinesLine color={color}/>
<SparklinesReferenceLine type="mean" />
</Sparklines>
<div className="text-center">{specificCurrentWeatherMetric} {symbol}</div>
</div>
)
}

//function used return JSX row that contains all the charts and name of the city. Only render if state has objects.
const RenderRow = () => {
if(weatherPosts.length>0){
return weatherPosts.map((currentWeatherPost, index)=>{
return(
<tr key={index}>
<th>{currentWeatherPost.city}</th>
<td >{renderChart(currentWeatherPost.temp, 'blue', currentWeatherPost.tempAve, "\u2109")}</td>
<td >{renderChart(currentWeatherPost.pressure, 'green', currentWeatherPost.pressureAve, "\u3371")}</td>
<td >{renderChart(currentWeatherPost.humidity, 'red', currentWeatherPost.humidityAve, "%")}</td>
</tr>
)
})
}
else {
return (<tr><th>No Weather to Show</th></tr>)
}
}

return (
<table className="table">
<thead>
<tr>
<th scope="col">City</th>
<th className="text-center" scope="col">Temperature</th>
<th className="text-center" scope="col">Pressure</th>
<th className="text-center" scope="col">Humidity</th>
</tr>
</thead>
<tbody>
<RenderRow />
</tbody>
</table>
)
}

export default WeatherList;
36 changes: 21 additions & 15 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import "bootstrap/dist/css/bootstrap.css"
import React from "react";
import ReactDOM from "react-dom";
import { createStore,applyMiddleware } from "redux";
import promise from "redux-promise"
//provider gives application access to redux store
import { Provider } from "react-redux";

ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
import Header from "./components/header.js"
import App from "./App";
import reducers from "./reducers"

//making sure actions wait for promise to be fulfulled before we pass the data to the reducer
const createStoreWithMiddleware = applyMiddleware(promise)(createStore);

// 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();
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())}>
<Header>
<App component={App}/>
</Header>
</Provider>,
document.getElementById("root")
);
9 changes: 9 additions & 0 deletions src/reducers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {combineReducers } from "redux";
import WeatherPostsReducer from "./reducer_weather"

//combines all our reducers to have a redux state available when using hook useSelector and change state by useDispatch
const rootReducer = combineReducers({
weatherPosts: WeatherPostsReducer,
})

export default rootReducer;
18 changes: 18 additions & 0 deletions src/reducers/reducer_weather.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {FETCH_CITYWEATHER} from "../actions";

//list of reducers which return an array of objects that contain the 5 day weather for a city.
const weatherPostsReducer = (state = [], action) => {
switch (action.type) {
case FETCH_CITYWEATHER:
if(action.payload === undefined){
return state;
}
else{
return [action.payload,...state];
}
default:
return state;
}
}

export default weatherPostsReducer