-
-
- Docs ->
-
- Find in-depth information about Next.js features and API.
-
+ const testLat = 44.34;
+ const testLon = 10.99;
+
+ useEffect(() => {
+ dispatch(fetchWeather());
+ }, [dispatch]);
-
-
- Learn ->
-
- Learn about Next.js in an interactive course with quizzes!
-
-
-
- Templates ->
-
- Explore the Next.js 13 playground.
-
+ const handleClick = () => {
+ dispatch(fetchCity(input))
+ .then((response) =>{
+ console.log(response.payload);
+ const latitude = response.payload[0].lat;
+ const longitude = response.payload[0].lon;
+ console.log(latitude, longitude);
+ dispatch(fetchWeather( {latitude, longitude} ))
+ //get rid of this last chain when finished
+ .then((response) => {
+ console.log(response.payload);
+ })
+ })
+ };
-
-
- Deploy ->
-
-
- Instantly deploy your Next.js site to a shareable URL with Vercel.
-
-
+ return (
+
+
+ setInput(e.target.value)} />
+
)
-}
+
+}
\ No newline at end of file
diff --git a/app/store/configureStore.js b/app/store/configureStore.js
new file mode 100644
index 0000000..f4e8a50
--- /dev/null
+++ b/app/store/configureStore.js
@@ -0,0 +1,8 @@
+import { configureStore } from '@reduxjs/toolkit';
+import rootReducer from './rootReducer';
+
+const store = configureStore({
+ reducer: rootReducer,
+});
+
+export default store;
\ No newline at end of file
diff --git a/app/store/rootReducer.js b/app/store/rootReducer.js
new file mode 100644
index 0000000..a245913
--- /dev/null
+++ b/app/store/rootReducer.js
@@ -0,0 +1,8 @@
+import { combineReducers } from 'redux';
+import weatherReducer from './slices/weather';
+
+const rootReducer = combineReducers({
+ weather: weatherReducer,
+});
+
+export default rootReducer;
\ No newline at end of file
diff --git a/app/store/slices/weather.js b/app/store/slices/weather.js
new file mode 100644
index 0000000..601c1b7
--- /dev/null
+++ b/app/store/slices/weather.js
@@ -0,0 +1,65 @@
+import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
+import { useDispatch, useSelector } from 'react-redux';
+import axios from 'axios';
+
+export const fetchWeather = createAsyncThunk('weather/fetchData', async ({latitude, longitude}) => {
+ try {
+
+ const response = await axios.get(`http://api.openweathermap.org/data/2.5/forecast?lat=${latitude}&lon=${longitude}&appid=b68f55460cf44818aabff0456c2d1963`);
+ return response.data;
+ } catch (error) {
+ throw Error(error.response.data.message);
+ }
+});
+
+export const fetchCity = createAsyncThunk('weather/fetchCity', async (cityName) => {
+ try {
+ const response = await axios.get(`http://api.openweathermap.org/geo/1.0/direct?q=${cityName}&limit=5&appid=b68f55460cf44818aabff0456c2d1963`);
+ return response.data;
+ } catch (error) {
+ console.log(error('Error fetching city name: ', error));
+ }
+});
+
+export const weatherSlice = createSlice({
+ name: 'weather',
+ initialState: {
+ weather: null,
+ city: null,
+ status: 'idle',
+ error: null,
+ },
+ reducers: {},
+ extraReducers: (builder) => {
+ builder
+ .addCase(fetchWeather.pending, (state) => {
+ state.status = 'loading';
+ state.error = null;
+ })
+ .addCase(fetchWeather.fulfilled, (state, action) => {
+ state.status = 'succeeded';
+ state.weather = action.payload;
+ state.error = null;
+ })
+ .addCase(fetchWeather.rejected, (state, action) => {
+ state.status = 'failed';
+ state.error = action.payload;
+ });
+ builder
+ .addCase(fetchCity.pending, (state) => {
+ state.status = 'loading';
+ state.error = null;
+ })
+ .addCase(fetchCity.fulfilled, (state, action) => {
+ state.status = 'succeeded';
+ state.city = action.payload;
+ state.error = null;
+ })
+ .addCase(fetchCity.rejected, (state, action) => {
+ state.status = 'failed';
+ state.error = action.payload;
+ });
+ },
+});
+
+export default weatherSlice.reducer;
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 90f6bb1..a9c1969 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,11 +8,14 @@
"name": "parsity_rtk_weather",
"version": "0.1.0",
"dependencies": {
+ "@reduxjs/toolkit": "^2.2.3",
+ "axios": "^1.6.8",
"eslint": "8.50.0",
"eslint-config-next": "13.5.3",
"next": "13.5.3",
"react": "18.2.0",
- "react-dom": "18.2.0"
+ "react-dom": "18.2.0",
+ "react-redux": "^9.1.2"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -296,6 +299,29 @@
"node": ">= 8"
}
},
+ "node_modules/@reduxjs/toolkit": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.3.tgz",
+ "integrity": "sha512-76dll9EnJXg4EVcI5YNxZA/9hSAmZsFqzMmNRHvIlzw2WS/twfcVX3ysYrWGJMClwEmChQFC4yRq74tn6fdzRA==",
+ "dependencies": {
+ "immer": "^10.0.3",
+ "redux": "^5.0.1",
+ "redux-thunk": "^3.1.0",
+ "reselect": "^5.0.1"
+ },
+ "peerDependencies": {
+ "react": "^16.9.0 || ^17.0.0 || ^18",
+ "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "react": {
+ "optional": true
+ },
+ "react-redux": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@rushstack/eslint-patch": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.5.0.tgz",
@@ -314,6 +340,11 @@
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="
},
+ "node_modules/@types/use-sync-external-store": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
+ "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
+ },
"node_modules/@typescript-eslint/parser": {
"version": "6.7.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.3.tgz",
@@ -615,6 +646,11 @@
"has-symbols": "^1.0.3"
}
},
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ },
"node_modules/available-typed-arrays": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
@@ -634,6 +670,16 @@
"node": ">=4"
}
},
+ "node_modules/axios": {
+ "version": "1.6.8",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz",
+ "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
"node_modules/axobject-query": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz",
@@ -753,6 +799,17 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -826,6 +883,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/dequal": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
@@ -1491,6 +1556,25 @@
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz",
"integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ=="
},
+ "node_modules/follow-redirects": {
+ "version": "1.15.6",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
+ "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
"node_modules/for-each": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
@@ -1499,6 +1583,19 @@
"is-callable": "^1.1.3"
}
},
+ "node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -1759,6 +1856,15 @@
"node": ">= 4"
}
},
+ "node_modules/immer": {
+ "version": "10.1.1",
+ "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
+ "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/immer"
+ }
+ },
"node_modules/import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@@ -2270,6 +2376,25 @@
"node": ">=8.6"
}
},
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -2628,6 +2753,11 @@
"react-is": "^16.13.1"
}
},
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
"node_modules/punycode": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
@@ -2683,6 +2813,41 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
+ "node_modules/react-redux": {
+ "version": "9.1.2",
+ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz",
+ "integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==",
+ "dependencies": {
+ "@types/use-sync-external-store": "^0.0.3",
+ "use-sync-external-store": "^1.0.0"
+ },
+ "peerDependencies": {
+ "@types/react": "^18.2.25",
+ "react": "^18.0",
+ "redux": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "redux": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/redux": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
+ "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="
+ },
+ "node_modules/redux-thunk": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
+ "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
+ "peerDependencies": {
+ "redux": "^5.0.0"
+ }
+ },
"node_modules/reflect.getprototypeof": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz",
@@ -2723,6 +2888,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/reselect": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.0.tgz",
+ "integrity": "sha512-aw7jcGLDpSgNDyWBQLv2cedml85qd95/iszJjN988zX1t7AVRJi19d9kto5+W7oCfQ94gyo40dVbT6g2k4/kXg=="
+ },
"node_modules/resolve": {
"version": "1.22.6",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz",
@@ -3225,6 +3395,14 @@
"punycode": "^2.1.0"
}
},
+ "node_modules/use-sync-external-store": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz",
+ "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/watchpack": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
diff --git a/package.json b/package.json
index 6ca0f8f..44a48a0 100644
--- a/package.json
+++ b/package.json
@@ -9,10 +9,13 @@
"lint": "next lint"
},
"dependencies": {
+ "@reduxjs/toolkit": "^2.2.3",
+ "axios": "^1.6.8",
"eslint": "8.50.0",
"eslint-config-next": "13.5.3",
"next": "13.5.3",
"react": "18.2.0",
- "react-dom": "18.2.0"
+ "react-dom": "18.2.0",
+ "react-redux": "^9.1.2"
}
}
From 3b8771f39b87e0649d5459c89a1d6131880027e7 Mon Sep 17 00:00:00 2001
From: levydowell <33126924+levydowell@users.noreply.github.com>
Date: Wed, 15 May 2024 07:14:34 -0600
Subject: [PATCH 2/5] Figured out how to map temp data, now need to incorporate
sparklines
---
app/components/cityInput.js | 38 ++++++++++++++++++++++++++
app/page.js | 54 +++++++++++++++----------------------
package-lock.json | 15 ++++++++++-
package.json | 3 ++-
4 files changed, 76 insertions(+), 34 deletions(-)
diff --git a/app/components/cityInput.js b/app/components/cityInput.js
index e69de29..70c1b0b 100644
--- a/app/components/cityInput.js
+++ b/app/components/cityInput.js
@@ -0,0 +1,38 @@
+'use client';
+import styles from '../page.module.css';
+import { useEffect } from 'react';
+import { fetchWeather, fetchCity } from '../store/slices/weather';
+import { useDispatch, useSelector } from 'react-redux';
+import { useState } from 'react';
+
+
+
+export const InputField = () => {
+ const dispatch = useDispatch();
+ const weather = useSelector((state) => state.weather.weather);
+ const city = useSelector((state) => state.weather.city);
+ const [input, setInput] = useState();
+
+ const handleClick = () => {
+ dispatch(fetchCity(input))
+ .then((response) =>{
+ console.log(response.payload);
+ const latitude = response.payload[0].lat;
+ const longitude = response.payload[0].lon;
+ console.log(latitude, longitude);
+ dispatch(fetchWeather( {latitude, longitude} ))
+
+ })
+ };
+
+ return (
+
+
+ setInput(e.target.value)} />
+
+
+
+)
+}
+
diff --git a/app/page.js b/app/page.js
index eaf2c75..309b1d7 100644
--- a/app/page.js
+++ b/app/page.js
@@ -1,49 +1,39 @@
'use client';
import styles from './page.module.css';
-import Link from 'next/link';
import { useEffect } from 'react';
import { fetchWeather, fetchCity } from './store/slices/weather';
import { useDispatch, useSelector } from 'react-redux';
import { useState } from 'react';
+import { InputField } from './components/cityInput';
+import React from 'react';
+import { Sparklines } from 'react-sparklines';
export default function Home() {
- const dispatch = useDispatch();
+ const [lineData, setLineData] = useState('');
+
const weather = useSelector((state) => state.weather.weather);
- const city = useSelector((state) => state.weather.city);
- const [input, setInput] = useState();
+ const testLog = weather;
+ console.log('weather', testLog);
- const testLat = 44.34;
- const testLon = 10.99;
- useEffect(() => {
- dispatch(fetchWeather());
- }, [dispatch]);
-
- const handleClick = () => {
- dispatch(fetchCity(input))
- .then((response) =>{
- console.log(response.payload);
- const latitude = response.payload[0].lat;
- const longitude = response.payload[0].lon;
- console.log(latitude, longitude);
- dispatch(fetchWeather( {latitude, longitude} ))
- //get rid of this last chain when finished
- .then((response) => {
- console.log(response.payload);
- })
- })
- };
- return (
-
-
- setInput(e.target.value)} />
-
-
-
+
+ if (weather == null) {
+ return (
+
+
+
)
+ } else {
+ return (
+
+
+ {weather.list.map((datum) => datum.main.temp)}
+
+ )
+ }
+
}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index a9c1969..3272a7f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,7 +15,8 @@
"next": "13.5.3",
"react": "18.2.0",
"react-dom": "18.2.0",
- "react-redux": "^9.1.2"
+ "react-redux": "^9.1.2",
+ "react-sparklines": "^1.7.0"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -2835,6 +2836,18 @@
}
}
},
+ "node_modules/react-sparklines": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/react-sparklines/-/react-sparklines-1.7.0.tgz",
+ "integrity": "sha512-bJFt9K4c5Z0k44G8KtxIhbG+iyxrKjBZhdW6afP+R7EnIq+iKjbWbEFISrf3WKNFsda+C46XAfnX0StS5fbDcg==",
+ "dependencies": {
+ "prop-types": "^15.5.10"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-dom": "*"
+ }
+ },
"node_modules/redux": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
diff --git a/package.json b/package.json
index 44a48a0..16e2fda 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
"next": "13.5.3",
"react": "18.2.0",
"react-dom": "18.2.0",
- "react-redux": "^9.1.2"
+ "react-redux": "^9.1.2",
+ "react-sparklines": "^1.7.0"
}
}
From 1322d42ec2eebc13a5f97dc952b57d1f590a0601 Mon Sep 17 00:00:00 2001
From: levydowell <33126924+levydowell@users.noreply.github.com>
Date: Thu, 16 May 2024 07:21:27 -0600
Subject: [PATCH 3/5] sparklines populating for all cities entered. Now need to
add pressure and humidity
---
app/components/allData.js | 29 ++++++++++
app/globals.css | 108 +-----------------------------------
app/page.js | 5 +-
app/page.module.css | 3 +-
app/store/slices/weather.js | 4 +-
5 files changed, 38 insertions(+), 111 deletions(-)
create mode 100644 app/components/allData.js
diff --git a/app/components/allData.js b/app/components/allData.js
new file mode 100644
index 0000000..edb61bd
--- /dev/null
+++ b/app/components/allData.js
@@ -0,0 +1,29 @@
+'use client';
+import React from 'react';
+import { Sparklines, SparklinesLine, SparklinesReferenceLine } from 'react-sparklines';
+import { useDispatch, useSelector } from 'react-redux';
+
+export const AllData = () => {
+ const weather = useSelector((state) => state.weather.weather);
+
+ let tempData = [];
+ // weather.list.forEach((e) => {
+ // if (weather.list.indexOf(e) % 3 === 0) {
+ // tempData.push(e);
+ // }
+ // });
+
+ return weather.map((cityWeather) => {
+ console.log('cityweather', cityWeather);
+ return (
+
+ datum.main.temp)} width={20} height={10} margin={1}>
+
+
+
+
+ )
+ })
+
+
+}
\ No newline at end of file
diff --git a/app/globals.css b/app/globals.css
index d4f491e..360408c 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -1,107 +1,3 @@
-:root {
- --max-width: 1100px;
- --border-radius: 12px;
- --font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono',
- 'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro',
- 'Fira Mono', 'Droid Sans Mono', 'Courier New', monospace;
-
- --foreground-rgb: 0, 0, 0;
- --background-start-rgb: 214, 219, 220;
- --background-end-rgb: 255, 255, 255;
-
- --primary-glow: conic-gradient(
- from 180deg at 50% 50%,
- #16abff33 0deg,
- #0885ff33 55deg,
- #54d6ff33 120deg,
- #0071ff33 160deg,
- transparent 360deg
- );
- --secondary-glow: radial-gradient(
- rgba(255, 255, 255, 1),
- rgba(255, 255, 255, 0)
- );
-
- --tile-start-rgb: 239, 245, 249;
- --tile-end-rgb: 228, 232, 233;
- --tile-border: conic-gradient(
- #00000080,
- #00000040,
- #00000030,
- #00000020,
- #00000010,
- #00000010,
- #00000080
- );
-
- --callout-rgb: 238, 240, 241;
- --callout-border-rgb: 172, 175, 176;
- --card-rgb: 180, 185, 188;
- --card-border-rgb: 131, 134, 135;
-}
-
-@media (prefers-color-scheme: dark) {
- :root {
- --foreground-rgb: 255, 255, 255;
- --background-start-rgb: 0, 0, 0;
- --background-end-rgb: 0, 0, 0;
-
- --primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0));
- --secondary-glow: linear-gradient(
- to bottom right,
- rgba(1, 65, 255, 0),
- rgba(1, 65, 255, 0),
- rgba(1, 65, 255, 0.3)
- );
-
- --tile-start-rgb: 2, 13, 46;
- --tile-end-rgb: 2, 5, 19;
- --tile-border: conic-gradient(
- #ffffff80,
- #ffffff40,
- #ffffff30,
- #ffffff20,
- #ffffff10,
- #ffffff10,
- #ffffff80
- );
-
- --callout-rgb: 20, 20, 20;
- --callout-border-rgb: 108, 108, 108;
- --card-rgb: 100, 100, 100;
- --card-border-rgb: 200, 200, 200;
- }
-}
-
-* {
- box-sizing: border-box;
- padding: 0;
- margin: 0;
-}
-
-html,
-body {
- max-width: 100vw;
- overflow-x: hidden;
-}
-
-body {
- color: rgb(var(--foreground-rgb));
- background: linear-gradient(
- to bottom,
- transparent,
- rgb(var(--background-end-rgb))
- )
- rgb(var(--background-start-rgb));
-}
-
-a {
- color: inherit;
- text-decoration: none;
-}
-
-@media (prefers-color-scheme: dark) {
- html {
- color-scheme: dark;
- }
+.graphs {
+ width: 25%;
}
diff --git a/app/page.js b/app/page.js
index 309b1d7..c79b1b5 100644
--- a/app/page.js
+++ b/app/page.js
@@ -5,8 +5,9 @@ import { fetchWeather, fetchCity } from './store/slices/weather';
import { useDispatch, useSelector } from 'react-redux';
import { useState } from 'react';
import { InputField } from './components/cityInput';
+import { AllData } from './components/allData';
import React from 'react';
-import { Sparklines } from 'react-sparklines';
+import { Sparklines, SparklinesLine, SparklinesReferenceLine } from 'react-sparklines';
export default function Home() {
@@ -30,7 +31,7 @@ export default function Home() {
return (
- {weather.list.map((datum) => datum.main.temp)}
+
)
}
diff --git a/app/page.module.css b/app/page.module.css
index 6676d2c..79b96a3 100644
--- a/app/page.module.css
+++ b/app/page.module.css
@@ -97,7 +97,7 @@
.center::before,
.center::after {
- content: '';
+ content: "";
left: 50%;
position: absolute;
filter: blur(45px);
@@ -107,6 +107,7 @@
.logo {
position: relative;
}
+
/* Enable hover only on non-touch devices */
@media (hover: hover) and (pointer: fine) {
.card:hover {
diff --git a/app/store/slices/weather.js b/app/store/slices/weather.js
index 601c1b7..fa6a7d9 100644
--- a/app/store/slices/weather.js
+++ b/app/store/slices/weather.js
@@ -24,7 +24,7 @@ export const fetchCity = createAsyncThunk('weather/fetchCity', async (cityName)
export const weatherSlice = createSlice({
name: 'weather',
initialState: {
- weather: null,
+ weather: [],
city: null,
status: 'idle',
error: null,
@@ -38,7 +38,7 @@ export const weatherSlice = createSlice({
})
.addCase(fetchWeather.fulfilled, (state, action) => {
state.status = 'succeeded';
- state.weather = action.payload;
+ state.weather.push(action.payload);
state.error = null;
})
.addCase(fetchWeather.rejected, (state, action) => {
From 64c6c742290ebed8095cb59d95acf3ae611e4ce0 Mon Sep 17 00:00:00 2001
From: levydowell <33126924+levydowell@users.noreply.github.com>
Date: Fri, 17 May 2024 07:08:34 -0600
Subject: [PATCH 4/5] Sparklines working for all graphs, and all cities entered
are rendered
---
app/components/allData.js | 41 ++++++++++++++++++++++++++++-----------
app/globals.css | 9 +++++++++
app/page.js | 8 --------
3 files changed, 39 insertions(+), 19 deletions(-)
diff --git a/app/components/allData.js b/app/components/allData.js
index edb61bd..8217bfa 100644
--- a/app/components/allData.js
+++ b/app/components/allData.js
@@ -5,22 +5,41 @@ import { useDispatch, useSelector } from 'react-redux';
export const AllData = () => {
const weather = useSelector((state) => state.weather.weather);
+ const city = useSelector((state) => state.weather.city);
- let tempData = [];
- // weather.list.forEach((e) => {
- // if (weather.list.indexOf(e) % 3 === 0) {
- // tempData.push(e);
- // }
- // });
return weather.map((cityWeather) => {
console.log('cityweather', cityWeather);
return (
-
-
datum.main.temp)} width={20} height={10} margin={1}>
-
-
-
+
+
+
+
{cityWeather.city.name}
+
+
+
+
Temperature
+ datum.main.temp)} width={20} height={10} margin={1}>
+
+
+
+
+
+
+
Pressure
+ datum.main.pressure)} width={20} height={10} margin={1}>
+
+
+
+
+
+
+
Humidity
+ datum.main.humidity)} width={20} height={10} margin={1}>
+
+
+
+
)
})
diff --git a/app/globals.css b/app/globals.css
index 360408c..3e985c4 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -1,3 +1,12 @@
.graphs {
width: 25%;
}
+
+.graph-container {
+ display: flex;
+ align-items: center;
+}
+
+.city-name {
+ text-align: center;
+}
diff --git a/app/page.js b/app/page.js
index c79b1b5..d1f98b1 100644
--- a/app/page.js
+++ b/app/page.js
@@ -7,19 +7,11 @@ import { useState } from 'react';
import { InputField } from './components/cityInput';
import { AllData } from './components/allData';
import React from 'react';
-import { Sparklines, SparklinesLine, SparklinesReferenceLine } from 'react-sparklines';
export default function Home() {
- const [lineData, setLineData] = useState('');
const weather = useSelector((state) => state.weather.weather);
- const testLog = weather;
- console.log('weather', testLog);
-
-
-
-
if (weather == null) {
return (
From 57da5361b19b868ac19fe3940d9bb56f69202034 Mon Sep 17 00:00:00 2001
From: levydowell <33126924+levydowell@users.noreply.github.com>
Date: Tue, 21 May 2024 06:42:14 -0600
Subject: [PATCH 5/5] sumbission commit
---
app/components/allData.js | 36 +++++++++++++++++++++++++++++-------
app/components/cityInput.js | 25 +++++++++++--------------
app/globals.css | 4 ++++
app/page.js | 17 +++++++----------
app/store/slices/weather.js | 25 +++++++++++++++++--------
package-lock.json | 29 +++++++++++++++++++++++++++++
package.json | 1 +
7 files changed, 98 insertions(+), 39 deletions(-)
diff --git a/app/components/allData.js b/app/components/allData.js
index 8217bfa..c959e4a 100644
--- a/app/components/allData.js
+++ b/app/components/allData.js
@@ -1,44 +1,66 @@
'use client';
import React from 'react';
import { Sparklines, SparklinesLine, SparklinesReferenceLine } from 'react-sparklines';
-import { useDispatch, useSelector } from 'react-redux';
+import { useSelector } from 'react-redux';
+import 'bootstrap/dist/css/bootstrap.min.css';
+/**
+ *
+ * @returns react component containing all of the weather data for each city entered.
+ */
export const AllData = () => {
const weather = useSelector((state) => state.weather.weather);
- const city = useSelector((state) => state.weather.city);
+ /**
+ *
+ * @param {*} data The data list returned from openweather api.
+ * @param {*} avgType The weather distinction nested within the data list. Naming convention must be
+ * exactly the same as json layout, i.e `temp`, `pressure`, or `humidity`.
+ * @returns The average of the sum of all data points for avgType.
+ */
+ const findAvg = (data, avgType) => {
+ const typeData = data.map((datum) => datum.main[avgType]);
+ console.log('here', typeData, typeData.length);
+ const sum = typeData.reduce(
+ (acc, value) => acc + value, 0,
+ );
+
+ return Math.round(sum/typeData.length);
+ }
return weather.map((cityWeather) => {
- console.log('cityweather', cityWeather);
return (
-
+
{cityWeather.city.name}
-
Temperature
+
Temperature
datum.main.temp)} width={20} height={10} margin={1}>
+
{findAvg(cityWeather.list, 'temp')} F
-
Pressure
+
Pressure
datum.main.pressure)} width={20} height={10} margin={1}>
+
{findAvg(cityWeather.list, 'pressure')} hPa
-
Humidity
+
Humidity
datum.main.humidity)} width={20} height={10} margin={1}>
+
{findAvg(cityWeather.list, 'humidity')} %
)
diff --git a/app/components/cityInput.js b/app/components/cityInput.js
index 70c1b0b..7a6c0e8 100644
--- a/app/components/cityInput.js
+++ b/app/components/cityInput.js
@@ -1,38 +1,35 @@
'use client';
-import styles from '../page.module.css';
-import { useEffect } from 'react';
+import 'bootstrap/dist/css/bootstrap.css';
import { fetchWeather, fetchCity } from '../store/slices/weather';
-import { useDispatch, useSelector } from 'react-redux';
+import { useDispatch } from 'react-redux';
import { useState } from 'react';
-
-
export const InputField = () => {
const dispatch = useDispatch();
- const weather = useSelector((state) => state.weather.weather);
- const city = useSelector((state) => state.weather.city);
const [input, setInput] = useState();
+ /**
+ * dispatch `fetchCity` for async api call to retrieve city coords.
+ * dispatch `fetchWeather` to get all weather information.
+ */
const handleClick = () => {
dispatch(fetchCity(input))
.then((response) =>{
- console.log(response.payload);
const latitude = response.payload[0].lat;
const longitude = response.payload[0].lon;
- console.log(latitude, longitude);
dispatch(fetchWeather( {latitude, longitude} ))
})
};
+ // returns component with searchbar with event handler
return (
-
-
-
+ setInput(e.target.value)} />
-
+
-
+
)
}
diff --git a/app/globals.css b/app/globals.css
index 3e985c4..32639be 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -10,3 +10,7 @@
.city-name {
text-align: center;
}
+
+.set-margin {
+ margin: 5% 10%;
+}
diff --git a/app/page.js b/app/page.js
index d1f98b1..a91c654 100644
--- a/app/page.js
+++ b/app/page.js
@@ -1,19 +1,15 @@
'use client';
-import styles from './page.module.css';
-import { useEffect } from 'react';
-import { fetchWeather, fetchCity } from './store/slices/weather';
-import { useDispatch, useSelector } from 'react-redux';
-import { useState } from 'react';
+import 'bootstrap/dist/css/bootstrap.min.css';
+import { useSelector } from 'react-redux';
import { InputField } from './components/cityInput';
import { AllData } from './components/allData';
import React from 'react';
-
export default function Home() {
const weather = useSelector((state) => state.weather.weather);
- if (weather == null) {
+ if (weather === null) {
return (
@@ -22,11 +18,12 @@ export default function Home() {
} else {
return (
-
-
+
)
}
-
}
\ No newline at end of file
diff --git a/app/store/slices/weather.js b/app/store/slices/weather.js
index fa6a7d9..46a336c 100644
--- a/app/store/slices/weather.js
+++ b/app/store/slices/weather.js
@@ -1,26 +1,34 @@
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
-import { useDispatch, useSelector } from 'react-redux';
import axios from 'axios';
-export const fetchWeather = createAsyncThunk('weather/fetchData', async ({latitude, longitude}) => {
+/**
+ * createAsyncThunk that fetches the latitude and longitude of entered city.
+ */
+export const fetchCity = createAsyncThunk('weather/fetchCity', async (cityName) => {
try {
-
- const response = await axios.get(`http://api.openweathermap.org/data/2.5/forecast?lat=${latitude}&lon=${longitude}&appid=b68f55460cf44818aabff0456c2d1963`);
+ const response = await axios.get(`http://api.openweathermap.org/geo/1.0/direct?q=${cityName}&limit=5&appid=b68f55460cf44818aabff0456c2d1963`);
return response.data;
} catch (error) {
- throw Error(error.response.data.message);
+ console.log(error('Error fetching city name: ', error));
}
});
-export const fetchCity = createAsyncThunk('weather/fetchCity', async (cityName) => {
+/**
+ * createAsyncThunk that fetches weather data from the lat/lon coordinates of selected city.
+ */
+export const fetchWeather = createAsyncThunk('weather/fetchData', async ({latitude, longitude}) => {
try {
- const response = await axios.get(`http://api.openweathermap.org/geo/1.0/direct?q=${cityName}&limit=5&appid=b68f55460cf44818aabff0456c2d1963`);
+
+ const response = await axios.get(`http://api.openweathermap.org/data/2.5/forecast?lat=${latitude}&lon=${longitude}&units=imperial&appid=b68f55460cf44818aabff0456c2d1963`);
return response.data;
} catch (error) {
- console.log(error('Error fetching city name: ', error));
+ throw Error(error.response.data.message);
}
});
+/**
+ * Redux slice to store the state of the weather and selected city.
+ */
export const weatherSlice = createSlice({
name: 'weather',
initialState: {
@@ -30,6 +38,7 @@ export const weatherSlice = createSlice({
error: null,
},
reducers: {},
+ //reducers for asyncThunks
extraReducers: (builder) => {
builder
.addCase(fetchWeather.pending, (state) => {
diff --git a/package-lock.json b/package-lock.json
index 3272a7f..09e04a3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,6 +10,7 @@
"dependencies": {
"@reduxjs/toolkit": "^2.2.3",
"axios": "^1.6.8",
+ "bootstrap": "^5.3.3",
"eslint": "8.50.0",
"eslint-config-next": "13.5.3",
"next": "13.5.3",
@@ -300,6 +301,16 @@
"node": ">= 8"
}
},
+ "node_modules/@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+ "peer": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/popperjs"
+ }
+ },
"node_modules/@reduxjs/toolkit": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.3.tgz",
@@ -694,6 +705,24 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
+ "node_modules/bootstrap": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
+ "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/twbs"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/bootstrap"
+ }
+ ],
+ "peerDependencies": {
+ "@popperjs/core": "^2.11.8"
+ }
+ },
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
diff --git a/package.json b/package.json
index 16e2fda..3fd5a3d 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
"dependencies": {
"@reduxjs/toolkit": "^2.2.3",
"axios": "^1.6.8",
+ "bootstrap": "^5.3.3",
"eslint": "8.50.0",
"eslint-config-next": "13.5.3",
"next": "13.5.3",