diff --git a/.gitignore b/.gitignore
index 8f322f0..339af6c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,5 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
-
+.env
# dependencies
/node_modules
/.pnp
diff --git a/README.md b/README.md
deleted file mode 100644
index 99d321a..0000000
--- a/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-## Weather Project
-
-This project has been created by a student at Parsity, an online software engineering course. The work in this repository is wholly of the student based on a sample starter project that can be accessed by looking at the repository that this project forks.
-
-If you have any questions about this project or the program in general, visit [parsity.io](https://parsity.io/) or email hello@parsity.io.
-
diff --git a/app/favicon.ico b/app/favicon.ico
deleted file mode 100644
index 718d6fe..0000000
Binary files a/app/favicon.ico and /dev/null differ
diff --git a/app/globals.css b/app/globals.css
deleted file mode 100644
index d4f491e..0000000
--- a/app/globals.css
+++ /dev/null
@@ -1,107 +0,0 @@
-: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;
- }
-}
diff --git a/app/layout.js b/app/layout.js
deleted file mode 100644
index c93f806..0000000
--- a/app/layout.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import './globals.css'
-import { Inter } from 'next/font/google'
-
-const inter = Inter({ subsets: ['latin'] })
-
-export const metadata = {
- title: 'Create Next App',
- description: 'Generated by create next app',
-}
-
-export default function RootLayout({ children }) {
- return (
-
-
{children}
-
- )
-}
diff --git a/app/page.js b/app/page.js
deleted file mode 100644
index f049c39..0000000
--- a/app/page.js
+++ /dev/null
@@ -1,95 +0,0 @@
-import Image from 'next/image'
-import styles from './page.module.css'
-
-export default function Home() {
- return (
-
-
-
- Get started by editing
- app/page.js
-
-
-
-
-
-
-
-
-
-
- )
-}
diff --git a/components/searchForm.js b/components/searchForm.js
new file mode 100644
index 0000000..c62d042
--- /dev/null
+++ b/components/searchForm.js
@@ -0,0 +1,30 @@
+import React, { useState } from 'react';
+import { useDispatch } from 'react-redux';
+import { fetchWeather } from '../reducers/weatherReducer';
+
+const SearchForm = () => {
+ const [city, setCity] = useState('');
+ const dispatch = useDispatch();
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ if (city.trim() !== '') {
+ dispatch(fetchWeather(city.trim()));
+ setCity(''); // Clear input after dispatch
+ }
+ };
+
+ return (
+
+ );
+};
+
+export default SearchForm;
diff --git a/components/weatherComponent.js b/components/weatherComponent.js
new file mode 100644
index 0000000..53acc6a
--- /dev/null
+++ b/components/weatherComponent.js
@@ -0,0 +1,102 @@
+import React from 'react';
+import { useSelector } from 'react-redux';
+import { Sparklines, SparklinesLine, SparklinesReferenceLine } from 'react-sparklines';
+
+// Convert Kelvin to Fahrenheit
+const kelvinToFahrenheit = (kelvin) => {
+ return ((kelvin - 273.15) * 9 / 5) + 32;
+};
+
+// Calculate mean of an array of numbers
+const calculateMean = (data) => {
+ if (data.length === 0) return 0;
+ const sum = data.reduce((a, b) => a + b, 0);
+ return sum / data.length;
+};
+
+const WeatherComponent = () => {
+ const { entries, loading, error } = useSelector(state => state.weather);
+
+ // Group entries by city
+ const groupedEntries = entries.reduce((acc, entry) => {
+ if (!acc[entry.city]) {
+ acc[entry.city] = [];
+ }
+ acc[entry.city].push(entry);
+ return acc;
+ }, {});
+
+ return (
+
+
+ {loading &&
Loading...
}
+ {error &&
Error: {error.message}
}
+
+
+
+
+ | City |
+ Temperature (°F) |
+ Pressure (hPa) |
+ Humidity (%) |
+
+
+
+ {Object.keys(groupedEntries).map(city => {
+ const cityEntries = groupedEntries[city];
+ const temperatureData = cityEntries.map(entry => kelvinToFahrenheit(entry.main.temp));
+ const pressureData = cityEntries.map(entry => entry.main.pressure);
+ const humidityData = cityEntries.map(entry => entry.main.humidity);
+
+ const meanTemperature = calculateMean(temperatureData).toFixed(2);
+ const meanPressure = calculateMean(pressureData).toFixed(2);
+ const meanHumidity = calculateMean(humidityData).toFixed(2);
+
+ return (
+
+ {cityEntries.map((entry, index) => (
+
+ {index === 0 && (
+ | {city} |
+ )}
+ {kelvinToFahrenheit(entry.main.temp).toFixed(2).hide} |
+ {entry.main.pressure.hide} |
+ {entry.main.humidity.hide} |
+
+ ))}
+
+ |
+
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+ | {meanTemperature} |
+ {meanPressure} |
+ {meanHumidity} |
+
+
+ );
+ })}
+
+
+
+
+ );
+};
+
+export default WeatherComponent;
diff --git a/jsconfig.json b/jsconfig.json
index 2a2e4b3..f3dc8b0 100644
--- a/jsconfig.json
+++ b/jsconfig.json
@@ -1,7 +1,12 @@
{
"compilerOptions": {
+ "baseUrl": ".",
"paths": {
- "@/*": ["./*"]
+ "@/components/*": ["components/*"],
+ "@/pages/*": ["pages/*"],
+ "@/reducers/*": ["reducers/*"],
+ "@/store/*": ["store/*"]
}
- }
+ },
+ "exclude": ["node_modules", ".next"]
}
diff --git a/layout.js b/layout.js
new file mode 100644
index 0000000..2001c0a
--- /dev/null
+++ b/layout.js
@@ -0,0 +1,16 @@
+'use client';
+import './globals.css';
+import { Inter } from 'next/font/google';
+import { Provider } from 'react-redux';
+import store from './store/configureStore';
+const inter = Inter({ subsets: ['latin'] });
+
+export default function RootLayout({ children }) {
+ return (
+
+
+ {children}
+
+
+ );
+}
\ No newline at end of file
diff --git a/next.config.js b/next.config.js
index 767719f..0566366 100644
--- a/next.config.js
+++ b/next.config.js
@@ -1,4 +1,10 @@
/** @type {import('next').NextConfig} */
const nextConfig = {}
-module.exports = nextConfig
+module.exports = {
+ env: {
+ NEXT_PUBLIC_API_KEY: process.env.NEXT_PUBLIC_API_KEY,
+ },
+};
+
+
diff --git a/package-lock.json b/package-lock.json
index 90f6bb1..871aa9c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,11 +8,22 @@
"name": "parsity_rtk_weather",
"version": "0.1.0",
"dependencies": {
+ "@reduxjs/toolkit": "^2.2.3",
+ "axios": "^1.6.8",
+ "chart.js": "^4.4.2",
"eslint": "8.50.0",
"eslint-config-next": "13.5.3",
- "next": "13.5.3",
+ "next": "^14.2.3",
"react": "18.2.0",
- "react-dom": "18.2.0"
+ "react-chartjs-2": "^5.2.0",
+ "react-dom": "18.2.0",
+ "react-redux": "^9.1.0",
+ "react-sparklines": "^1.7.0",
+ "redux": "^5.0.1",
+ "redux-thunk": "^3.1.0"
+ },
+ "devDependencies": {
+ "@types/redux": "^3.6.0"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -116,10 +127,15 @@
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
},
+ "node_modules/@kurkle/color": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz",
+ "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
+ },
"node_modules/@next/env": {
- "version": "13.5.3",
- "resolved": "https://registry.npmjs.org/@next/env/-/env-13.5.3.tgz",
- "integrity": "sha512-X4te86vsbjsB7iO4usY9jLPtZ827Mbx+WcwNBGUOIuswuTAKQtzsuoxc/6KLxCMvogKG795MhrR1LDhYgDvasg=="
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.3.tgz",
+ "integrity": "sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA=="
},
"node_modules/@next/eslint-plugin-next": {
"version": "13.5.3",
@@ -130,9 +146,9 @@
}
},
"node_modules/@next/swc-darwin-arm64": {
- "version": "13.5.3",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.5.3.tgz",
- "integrity": "sha512-6hiYNJxJmyYvvKGrVThzo4nTcqvqUTA/JvKim7Auaj33NexDqSNwN5YrrQu+QhZJCIpv2tULSHt+lf+rUflLSw==",
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.3.tgz",
+ "integrity": "sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==",
"cpu": [
"arm64"
],
@@ -145,9 +161,9 @@
}
},
"node_modules/@next/swc-darwin-x64": {
- "version": "13.5.3",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.5.3.tgz",
- "integrity": "sha512-UpBKxu2ob9scbpJyEq/xPgpdrgBgN3aLYlxyGqlYX5/KnwpJpFuIHU2lx8upQQ7L+MEmz+fA1XSgesoK92ppwQ==",
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.3.tgz",
+ "integrity": "sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==",
"cpu": [
"x64"
],
@@ -160,9 +176,9 @@
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
- "version": "13.5.3",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.5.3.tgz",
- "integrity": "sha512-5AzM7Yx1Ky+oLY6pHs7tjONTF22JirDPd5Jw/3/NazJ73uGB05NqhGhB4SbeCchg7SlVYVBeRMrMSZwJwq/xoA==",
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.3.tgz",
+ "integrity": "sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==",
"cpu": [
"arm64"
],
@@ -175,9 +191,9 @@
}
},
"node_modules/@next/swc-linux-arm64-musl": {
- "version": "13.5.3",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.5.3.tgz",
- "integrity": "sha512-A/C1shbyUhj7wRtokmn73eBksjTM7fFQoY2v/0rTM5wehpkjQRLOXI8WJsag2uLhnZ4ii5OzR1rFPwoD9cvOgA==",
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.3.tgz",
+ "integrity": "sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==",
"cpu": [
"arm64"
],
@@ -190,9 +206,9 @@
}
},
"node_modules/@next/swc-linux-x64-gnu": {
- "version": "13.5.3",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.5.3.tgz",
- "integrity": "sha512-FubPuw/Boz8tKkk+5eOuDHOpk36F80rbgxlx4+xty/U71e3wZZxVYHfZXmf0IRToBn1Crb8WvLM9OYj/Ur815g==",
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.3.tgz",
+ "integrity": "sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==",
"cpu": [
"x64"
],
@@ -205,9 +221,9 @@
}
},
"node_modules/@next/swc-linux-x64-musl": {
- "version": "13.5.3",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.5.3.tgz",
- "integrity": "sha512-DPw8nFuM1uEpbX47tM3wiXIR0Qa+atSzs9Q3peY1urkhofx44o7E1svnq+a5Q0r8lAcssLrwiM+OyJJgV/oj7g==",
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.3.tgz",
+ "integrity": "sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==",
"cpu": [
"x64"
],
@@ -220,9 +236,9 @@
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
- "version": "13.5.3",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.5.3.tgz",
- "integrity": "sha512-zBPSP8cHL51Gub/YV8UUePW7AVGukp2D8JU93IHbVDu2qmhFAn9LWXiOOLKplZQKxnIPUkJTQAJDCWBWU4UWUA==",
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.3.tgz",
+ "integrity": "sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==",
"cpu": [
"arm64"
],
@@ -235,9 +251,9 @@
}
},
"node_modules/@next/swc-win32-ia32-msvc": {
- "version": "13.5.3",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.5.3.tgz",
- "integrity": "sha512-ONcL/lYyGUj4W37D4I2I450SZtSenmFAvapkJQNIJhrPMhzDU/AdfLkW98NvH1D2+7FXwe7yclf3+B7v28uzBQ==",
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.3.tgz",
+ "integrity": "sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==",
"cpu": [
"ia32"
],
@@ -250,9 +266,9 @@
}
},
"node_modules/@next/swc-win32-x64-msvc": {
- "version": "13.5.3",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.5.3.tgz",
- "integrity": "sha512-2Vz2tYWaLqJvLcWbbTlJ5k9AN6JD7a5CN2pAeIzpbecK8ZF/yobA39cXtv6e+Z8c5UJuVOmaTldEAIxvsIux/Q==",
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.3.tgz",
+ "integrity": "sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==",
"cpu": [
"x64"
],
@@ -296,16 +312,45 @@
"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",
"integrity": "sha512-EF3948ckf3f5uPgYbQ6GhyA56Dmv8yg0+ir+BroRjwdxyZJsekhZzawOecC2rOTPCz173t7ZcR1HHZu0dZgOCw=="
},
+ "node_modules/@swc/counter": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
+ "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="
+ },
"node_modules/@swc/helpers": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
- "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==",
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz",
+ "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==",
"dependencies": {
+ "@swc/counter": "^0.1.3",
"tslib": "^2.4.0"
}
},
@@ -314,6 +359,21 @@
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="
},
+ "node_modules/@types/redux": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/@types/redux/-/redux-3.6.0.tgz",
+ "integrity": "sha512-ic+60DXHW5seNyqFvfr7Sk5cnXs+HsF9tIeIaxjOuSP5kzgDXC+AzKTYmjAfuLx4Sccm/0vjwBQj3OOkUkwOqg==",
+ "deprecated": "This is a stub types definition for Redux (https://github.com/reactjs/redux). Redux provides its own type definitions, so you don't need @types/redux installed!",
+ "dev": true,
+ "dependencies": {
+ "redux": "*"
+ }
+ },
+ "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 +675,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 +699,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",
@@ -699,9 +774,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001541",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001541.tgz",
- "integrity": "sha512-bLOsqxDgTqUBkzxbNlSBt8annkDpQB9NdzdTbO2ooJ+eC/IQcvDspDc058g84ejCelF7vHUx57KIOjEecOHXaw==",
+ "version": "1.0.30001621",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001621.tgz",
+ "integrity": "sha512-+NLXZiviFFKX0fk8Piwv3PfLPGtRqJeq2TiNoUff/qB5KJgwecJTvCXDpmlyP/eCI/GUEmp/h/y5j0yckiiZrA==",
"funding": [
{
"type": "opencollective",
@@ -732,6 +807,17 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
+ "node_modules/chart.js": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.2.tgz",
+ "integrity": "sha512-6GD7iKwFpP5kbSD4MeRRRlTnQvxfQREy36uEtm1hzHzcOqwWx0YEHuspuoNlslu+nciLIB7fjjsHkUv/FzFcOg==",
+ "dependencies": {
+ "@kurkle/color": "^0.3.0"
+ },
+ "engines": {
+ "pnpm": ">=8"
+ }
+ },
"node_modules/client-only": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
@@ -753,6 +839,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 +923,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 +1596,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 +1623,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",
@@ -1604,11 +1741,6 @@
"node": ">=10.13.0"
}
},
- "node_modules/glob-to-regexp": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
- "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="
- },
"node_modules/globals": {
"version": "13.22.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz",
@@ -1759,6 +1891,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 +2411,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",
@@ -2295,9 +2455,9 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/nanoid": {
- "version": "3.3.6",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
- "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
+ "version": "3.3.7",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
+ "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
"funding": [
{
"type": "github",
@@ -2317,38 +2477,38 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
},
"node_modules/next": {
- "version": "13.5.3",
- "resolved": "https://registry.npmjs.org/next/-/next-13.5.3.tgz",
- "integrity": "sha512-4Nt4HRLYDW/yRpJ/QR2t1v63UOMS55A38dnWv3UDOWGezuY0ZyFO1ABNbD7mulVzs9qVhgy2+ppjdsANpKP1mg==",
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/next/-/next-14.2.3.tgz",
+ "integrity": "sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==",
"dependencies": {
- "@next/env": "13.5.3",
- "@swc/helpers": "0.5.2",
+ "@next/env": "14.2.3",
+ "@swc/helpers": "0.5.5",
"busboy": "1.6.0",
- "caniuse-lite": "^1.0.30001406",
- "postcss": "8.4.14",
- "styled-jsx": "5.1.1",
- "watchpack": "2.4.0",
- "zod": "3.21.4"
+ "caniuse-lite": "^1.0.30001579",
+ "graceful-fs": "^4.2.11",
+ "postcss": "8.4.31",
+ "styled-jsx": "5.1.1"
},
"bin": {
"next": "dist/bin/next"
},
"engines": {
- "node": ">=16.14.0"
+ "node": ">=18.17.0"
},
"optionalDependencies": {
- "@next/swc-darwin-arm64": "13.5.3",
- "@next/swc-darwin-x64": "13.5.3",
- "@next/swc-linux-arm64-gnu": "13.5.3",
- "@next/swc-linux-arm64-musl": "13.5.3",
- "@next/swc-linux-x64-gnu": "13.5.3",
- "@next/swc-linux-x64-musl": "13.5.3",
- "@next/swc-win32-arm64-msvc": "13.5.3",
- "@next/swc-win32-ia32-msvc": "13.5.3",
- "@next/swc-win32-x64-msvc": "13.5.3"
+ "@next/swc-darwin-arm64": "14.2.3",
+ "@next/swc-darwin-x64": "14.2.3",
+ "@next/swc-linux-arm64-gnu": "14.2.3",
+ "@next/swc-linux-arm64-musl": "14.2.3",
+ "@next/swc-linux-x64-gnu": "14.2.3",
+ "@next/swc-linux-x64-musl": "14.2.3",
+ "@next/swc-win32-arm64-msvc": "14.2.3",
+ "@next/swc-win32-ia32-msvc": "14.2.3",
+ "@next/swc-win32-x64-msvc": "14.2.3"
},
"peerDependencies": {
"@opentelemetry/api": "^1.1.0",
+ "@playwright/test": "^1.41.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"sass": "^1.3.0"
@@ -2357,6 +2517,9 @@
"@opentelemetry/api": {
"optional": true
},
+ "@playwright/test": {
+ "optional": true
+ },
"sass": {
"optional": true
}
@@ -2588,9 +2751,9 @@
}
},
"node_modules/postcss": {
- "version": "8.4.14",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
- "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
+ "version": "8.4.31",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
"funding": [
{
"type": "opencollective",
@@ -2599,10 +2762,14 @@
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
}
],
"dependencies": {
- "nanoid": "^3.3.4",
+ "nanoid": "^3.3.6",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},
@@ -2628,6 +2795,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",
@@ -2666,6 +2838,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-chartjs-2": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz",
+ "integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==",
+ "peerDependencies": {
+ "chart.js": "^4.1.1",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/react-dom": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
@@ -2683,6 +2864,57 @@
"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.0",
+ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.0.tgz",
+ "integrity": "sha512-6qoDzIO+gbrza8h3hjMA9aq4nwVFCKFtY2iLxCtVT38Swyy2C/dJCGBXHeHLtx6qlg/8qzc2MrhOeduf5K32wQ==",
+ "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",
+ "react-native": ">=0.69",
+ "redux": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "react-native": {
+ "optional": true
+ },
+ "redux": {
+ "optional": true
+ }
+ }
+ },
+ "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",
+ "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 +2955,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",
@@ -2906,9 +3143,9 @@
}
},
"node_modules/source-map-js": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
- "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
+ "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
"engines": {
"node": ">=0.10.0"
}
@@ -3225,16 +3462,12 @@
"punycode": "^2.1.0"
}
},
- "node_modules/watchpack": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
- "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
- "dependencies": {
- "glob-to-regexp": "^0.4.1",
- "graceful-fs": "^4.1.2"
- },
- "engines": {
- "node": ">=10.13.0"
+ "node_modules/use-sync-external-store": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
+ "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/which": {
@@ -3343,14 +3576,6 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
- },
- "node_modules/zod": {
- "version": "3.21.4",
- "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz",
- "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==",
- "funding": {
- "url": "https://github.com/sponsors/colinhacks"
- }
}
}
}
diff --git a/package.json b/package.json
index 6ca0f8f..0afe1d0 100644
--- a/package.json
+++ b/package.json
@@ -9,10 +9,21 @@
"lint": "next lint"
},
"dependencies": {
+ "@reduxjs/toolkit": "^2.2.3",
+ "axios": "^1.6.8",
+ "chart.js": "^4.4.2",
"eslint": "8.50.0",
"eslint-config-next": "13.5.3",
- "next": "13.5.3",
+ "next": "^14.2.3",
"react": "18.2.0",
- "react-dom": "18.2.0"
+ "react-chartjs-2": "^5.2.0",
+ "react-dom": "18.2.0",
+ "react-redux": "^9.1.0",
+ "react-sparklines": "^1.7.0",
+ "redux": "^5.0.1",
+ "redux-thunk": "^3.1.0"
+ },
+ "devDependencies": {
+ "@types/redux": "^3.6.0"
}
}
diff --git a/app/page.module.css b/page.module.css
similarity index 92%
rename from app/page.module.css
rename to page.module.css
index 6676d2c..13460df 100644
--- a/app/page.module.css
+++ b/page.module.css
@@ -7,6 +7,32 @@
min-height: 100vh;
}
+.form {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.inputRow,
+.buttonRow {
+ display: flex;
+ justify-content: center;
+}
+
+.inputRow {
+ margin-bottom: 10px;
+}
+
+.labels {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+}
+
+.label {
+ margin-right: 10px;
+}
+
.description {
display: inherit;
justify-content: inherit;
@@ -97,7 +123,7 @@
.center::before,
.center::after {
- content: '';
+ content: "";
left: 50%;
position: absolute;
filter: blur(45px);
diff --git a/pages/_app.js b/pages/_app.js
new file mode 100644
index 0000000..fbd9944
--- /dev/null
+++ b/pages/_app.js
@@ -0,0 +1,18 @@
+import React from 'react';
+import { Provider } from 'react-redux';
+import store from '../store/configureStore';
+import WeatherComponent from '../components/weatherComponent';
+import SearchForm from '../components/searchForm';
+
+function App() {
+ return (
+
+
+
+
+
+
+ );
+}
+
+export default App;
diff --git a/pages/index.js b/pages/index.js
new file mode 100644
index 0000000..714f96c
--- /dev/null
+++ b/pages/index.js
@@ -0,0 +1,19 @@
+import React from 'react';
+import { Provider } from 'react-redux';
+import store from '../store/configureStore';
+import WeatherComponent from '../components/weatherComponent';
+import SearchForm from '../components/searchForm';
+
+const Index = () => {
+ return (
+
+
+
+
+
+
+ );
+};
+
+export default Index;
+
diff --git a/reducers/weatherReducer.js b/reducers/weatherReducer.js
new file mode 100644
index 0000000..42c3599
--- /dev/null
+++ b/reducers/weatherReducer.js
@@ -0,0 +1,48 @@
+import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
+import axios from 'axios';
+
+const initialState = {
+ city: '',
+ entries: [],
+ loading: false,
+ error: null,
+};
+
+export const fetchWeather = createAsyncThunk(
+ 'weather/fetchWeather',
+ async (city, { rejectWithValue }) => {
+ try {
+ const apiKey = process.env.NEXT_PUBLIC_API_KEY;
+ const response = await axios.get(`https://api.openweathermap.org/data/2.5/forecast?q=${city}&appid=${apiKey}`);
+ return response.data;
+ } catch (error) {
+ return rejectWithValue(error.response.data);
+ }
+ }
+);
+
+const weatherSlice = createSlice({
+ name: 'weather',
+ initialState,
+ reducers: {},
+ extraReducers: (builder) => {
+ builder
+ .addCase(fetchWeather.pending, (state) => {
+ state.loading = true;
+ state.error = null;
+ })
+ .addCase(fetchWeather.fulfilled, (state, action) => {
+ state.loading = false;
+ const newEntries = action.payload.list
+ .filter((_, index) => index % 8 === 0)
+ .map(entry => ({ ...entry, city: action.payload.city.name }));
+ state.entries = [...state.entries, ...newEntries];
+ })
+ .addCase(fetchWeather.rejected, (state, action) => {
+ state.loading = false;
+ state.error = action.payload;
+ });
+ },
+});
+
+export default weatherSlice.reducer;
diff --git a/store/configureStore.js b/store/configureStore.js
new file mode 100644
index 0000000..91150ff
--- /dev/null
+++ b/store/configureStore.js
@@ -0,0 +1,10 @@
+import { configureStore } from '@reduxjs/toolkit';
+import weatherReducer from '../reducers/weatherReducer';
+
+const store = configureStore({
+ reducer: {
+ weather: weatherReducer,
+ },
+});
+
+export default store;