From fde78f74f105050a5f970523705576a183156c2f Mon Sep 17 00:00:00 2001 From: acholyn Date: Fri, 11 Oct 2024 13:02:42 +0100 Subject: [PATCH 01/20] removing call to index.css --- src/main.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main.jsx b/src/main.jsx index 89f91e5..3d9da8a 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,7 +1,6 @@ import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import App from './App.jsx' -import './index.css' createRoot(document.getElementById('root')).render( From 0e7ea5b2d7ced6ca6639d35c00fba9e6d77a5a5f Mon Sep 17 00:00:00 2001 From: acholyn Date: Tue, 15 Oct 2024 11:09:47 +0100 Subject: [PATCH 02/20] some styling --- src/styles/App.css | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/styles/App.css b/src/styles/App.css index 60dcfbd..272852b 100644 --- a/src/styles/App.css +++ b/src/styles/App.css @@ -1,5 +1,17 @@ :root { font-family: Verdana, Geneva, Tahoma, sans-serif; + + --intrinsic-grey: #16161d; +} +html, +body { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +#root { + background-color: var(--intrinsic-grey); } main { @@ -7,5 +19,15 @@ main { flex-direction: column; justify-content: center; align-items: center; - /* height: 100vh; */ + height: 100vh; + + .btn--new-task { + color: whitesmoke; + border-color: whitesmoke; + + &:hover { + background-color: whitesmoke; + color: var(--intrinsic-grey); + } + } } From d436fa8368db6ed17241417b4406e6cbd7fed646 Mon Sep 17 00:00:00 2001 From: acholyn Date: Tue, 15 Oct 2024 11:11:00 +0100 Subject: [PATCH 03/20] setting up vars with vite --- .gitignore | 1 + src/App.jsx | 40 ++++++++++++++++++++++++++-------------- src/TaskForm.jsx | 8 ++++---- src/globals.js | 17 +++++++++++++++++ vite.config.js | 9 +++++---- 5 files changed, 53 insertions(+), 22 deletions(-) create mode 100644 src/globals.js diff --git a/.gitignore b/.gitignore index 61cb0c2..d9f0678 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ dist dist-ssr *.local .vite +.env # Editor directories and files .vscode/* diff --git a/src/App.jsx b/src/App.jsx index 7faefb2..76ab6bd 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -2,23 +2,35 @@ import { Button } from "@mui/material"; import "./styles/App.css"; import TaskForm from "./TaskForm"; import { useState } from "react"; - -export const REQUEST_URL = - "https://5fpm1iy0s4.execute-api.eu-west-2.amazonaws.com"; - -export const METADATA_URL = - "https://poeddd3g4f.execute-api.eu-west-2.amazonaws.com"; +import LoginForm from "./LoginForm"; export default function App() { const [isTaskFormVisible, setTaskFormVisible] = useState(false); + const [isLoginFormVisible, setLoginFormVisible] = useState(false); + + //TODO: add a login, hide the buttons based on login + // TODO: add mapbox background thing - const showTaskForm = () => { - setTaskFormVisible(true); - }; - return( + return (
- - - -
); + + + + + + + + ); } diff --git a/src/TaskForm.jsx b/src/TaskForm.jsx index edae482..0304011 100644 --- a/src/TaskForm.jsx +++ b/src/TaskForm.jsx @@ -1,10 +1,10 @@ import { Formik, Form, Field, ErrorMessage } from "formik"; import { Button, Checkbox, TextField } from "@mui/material"; -import { REQUEST_URL } from "./App"; +import { REQUEST_URL } from "./globals"; import { generateTaskId, generateCampaignCode } from "./utils/generators"; import { useState } from "react"; import SuccessModal from "./SuccessModal"; -import "./styles/task-form.css" +import "./styles/task-form.css"; // import * as Yup from "yup"; // these will be dynamically taken from their login and generated @@ -24,8 +24,8 @@ const initialValues = { createdBy: userID, // hidden field populated dynamically organisation: "", logo: null, - private: false, - visible: false, + private: false, + visible: false, title: "", description: "", }; diff --git a/src/globals.js b/src/globals.js new file mode 100644 index 0000000..a9a711d --- /dev/null +++ b/src/globals.js @@ -0,0 +1,17 @@ +// export vars used throughout the app +export const REQUEST_URL = import.meta.env.REQUEST_URL; +export const WEB_POOL_ID = import.meta.env.WEB_POOL_ID; +export const CLIENT_ID = import.meta.env.CLIENT_ID; +export const REGION = import.meta.env.REGION; + +export let cognito = null; +export let hasCognito = false; +if (WEB_POOL_ID !== "null" && CLIENT_ID !== "null") { + // checking for null string due to amplify store + hasCognito = true; + cognito = { + userPoolId: WEB_POOL_ID, + userPoolClientId: CLIENT_ID, + region: REGION, + }; +} diff --git a/vite.config.js b/vite.config.js index 861b04b..fb84f9b 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,7 +1,8 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react-swc' +import { defineConfig, loadEnv } from "vite"; +import react from "@vitejs/plugin-react-swc"; // https://vitejs.dev/config/ export default defineConfig({ - plugins: [react()], -}) + plugins: [react()], + env: loadEnv("development", "./"), +}); From e8a6e8a9aee4ec461416aa8f23689b09d18436c8 Mon Sep 17 00:00:00 2001 From: acholyn Date: Tue, 15 Oct 2024 11:45:59 +0100 Subject: [PATCH 04/20] user context --- src/UserContext.jsx | 120 ++++++++++++++++++++++++++++++++++++++++++++ src/globals.js | 7 +++ src/main.jsx | 19 ++++--- 3 files changed, 138 insertions(+), 8 deletions(-) create mode 100644 src/UserContext.jsx diff --git a/src/UserContext.jsx b/src/UserContext.jsx new file mode 100644 index 0000000..8cd395d --- /dev/null +++ b/src/UserContext.jsx @@ -0,0 +1,120 @@ +import { useState, useCallback, createContext, useEffect } from "react"; +import { initiateAuthRefresh, signOut } from "./utils/auth"; + +// Create the User Context +export const UserContext = createContext(); + +// Custom hook to use user context is exported from globals + +export const UserProvider = ({ children }) => { + const [idToken, setIdToken] = useState(null); + const [accessToken, setAccessToken] = useState(null); + const [refreshToken, setRefreshToken] = useState(null); + const [displayName, setDisplayName] = useState(null); + const [email, setEmail] = useState(null); + const [loggedIn, setLoggedIn] = useState(false); + + useEffect(() => { + checkForDetails(); + }, [checkForDetails]); // check for details when component mounts ie app starts + + const setUserDetails = useCallback((userDetails) => { + const base64Payload = userDetails.idToken.split(".")[1]; + const decodedIdTokenPayload = JSON.parse(atob(base64Payload)); + setDisplayName(decodedIdTokenPayload["preferred_username"]); + setEmail(decodedIdTokenPayload["email"]); + + setAccessToken(userDetails.accessToken); + setIdToken(userDetails.idToken); + setRefreshToken(userDetails.refreshToken); + setLoggedIn(true); + + setLocalStorage( + userDetails.idToken, + userDetails.accessToken, + userDetails.refreshToken + ); // the states won't have been updated in time, so use the original prop + }, []); + + // Check for user tokens from localStorage and update user info + const checkForDetails = useCallback(() => { + const storedIdToken = localStorage.getItem("idToken"); + const storedAccessToken = localStorage.getItem("accessToken"); + const storedRefreshToken = localStorage.getItem("refreshToken"); + + let userDetails = { + accessToken: storedAccessToken, + idToken: storedIdToken, + refreshToken: storedRefreshToken, + }; + const userDetailsNotNull = Object.values(userDetails).every( + (value) => value !== null && value !== "null" && value !== undefined + ); + + // Only set user details if tokens exist and are not the string "null" + if (userDetailsNotNull) { + setUserDetails(userDetails); + return true; + } else return false; + }, [setUserDetails]); + + // update localStorage values + const setLocalStorage = (idToken, accessToken, refreshToken) => { + localStorage.setItem("idToken", idToken || "null"); // Save as "null" if null + localStorage.setItem("accessToken", accessToken || "null"); + localStorage.setItem("refreshToken", refreshToken || "null"); + }; + + // Function to refresh tokens + const refresh = useCallback(() => { + if (refreshToken) { + initiateAuthRefresh({ refreshToken }).then((response) => { + const authResult = response.AuthenticationResult; + setIdToken(authResult.IdToken); + setAccessToken(authResult.AccessToken); + setRefreshToken(authResult.RefreshToken); + }); + } + }, [refreshToken]); + + // Function to log out + const logout = useCallback(() => { + if (accessToken) { + signOut(accessToken).then( + (response) => { + console.info("Successfully signed out", response); + }, + (error) => { + console.error("Error signing out", error); + } + ); + } + // Clear localStorage and state + localStorage.removeItem("accessToken"); + localStorage.removeItem("idToken"); + localStorage.removeItem("refreshToken"); + setIdToken(null); + setAccessToken(null); + setRefreshToken(null); + setLoggedIn(false); + }, [accessToken]); + + return ( + + {children} + + ); +}; diff --git a/src/globals.js b/src/globals.js index a9a711d..c787442 100644 --- a/src/globals.js +++ b/src/globals.js @@ -1,3 +1,6 @@ +import { useContext } from "react"; +import { UserContext } from "./UserContext"; + // export vars used throughout the app export const REQUEST_URL = import.meta.env.REQUEST_URL; export const WEB_POOL_ID = import.meta.env.WEB_POOL_ID; @@ -15,3 +18,7 @@ if (WEB_POOL_ID !== "null" && CLIENT_ID !== "null") { region: REGION, }; } + +export const useUserStore = () => { + return useContext(UserContext); +}; diff --git a/src/main.jsx b/src/main.jsx index 3d9da8a..41e8754 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,9 +1,12 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import App from './App.jsx' +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import App from "./App.jsx"; +import { UserProvider } from "./UserContext.jsx"; -createRoot(document.getElementById('root')).render( - - - , -) +createRoot(document.getElementById("root")).render( + + + + + +); From 686416d0391626c9386646e5a782d250f1534e66 Mon Sep 17 00:00:00 2001 From: acholyn Date: Wed, 23 Oct 2024 13:10:35 +0100 Subject: [PATCH 05/20] making success modal more flexible --- src/SuccessModal.jsx | 42 ++++++++++++++++++++++++++---------------- src/styles/dialogs.css | 12 ++++++++++++ 2 files changed, 38 insertions(+), 16 deletions(-) create mode 100644 src/styles/dialogs.css diff --git a/src/SuccessModal.jsx b/src/SuccessModal.jsx index 13008ec..a4aac9c 100644 --- a/src/SuccessModal.jsx +++ b/src/SuccessModal.jsx @@ -1,26 +1,36 @@ import { Button } from "@mui/material"; -import "./styles/task-form.css"; +import "./styles/dialogs.css"; export default function SuccessModal({ - taskID, - title, - description, - campaignCode, + taskID = "", + taskTitle = "", + taskDescription = "", + campaignCode = "", setSuccessModalVisible, + isTask, }) { + // if it's a not a task, the title will come through as taskTitle + let title; + if (isTask) title = "Your submission has been successful. See details below"; + else title = taskTitle; return ( -

Your submission has been successful. See details below

-

Request ID: {taskID}

-

Task title:{title}

-

Task description:{description}

-
-

- Below is the campaign code for people to access your study on Kapta - Mobile and contribute to it. -

-
{campaignCode}
-
+

{title}

+ {isTask && ( + <> + {" "} +

Request ID: {taskID}

+

Task title:{taskTitle}

+

Task description:{taskDescription}

+
+

+ Below is the campaign code for people to access your study on + Kapta Mobile and contribute to it. +

+
{campaignCode}
+
+ + )} diff --git a/src/styles/dialogs.css b/src/styles/dialogs.css new file mode 100644 index 0000000..ec3c47b --- /dev/null +++ b/src/styles/dialogs.css @@ -0,0 +1,12 @@ +#success-dialog { + background-color: #2d5b39dd; + backdrop-filter: blur(4px); + position: absolute; + top: 15%; + width: 60vw; + border: none; + border-radius: 8px; + z-index: 3; + color: white; + text-align: center; +} From ea09ef10ef5378de11c6fc6a553401edc0b274e5 Mon Sep 17 00:00:00 2001 From: acholyn Date: Wed, 23 Oct 2024 13:11:09 +0100 Subject: [PATCH 06/20] adding basis for user + signup/signin --- package-lock.json | 1296 ++++++++++++++++++++++++++++++- package.json | 6 +- src/App.jsx | 45 +- src/LoginForm.jsx | 83 ++ src/SignUpForm.jsx | 83 ++ src/TaskForm.jsx | 11 +- src/globals.js | 2 +- src/main.jsx | 30 +- src/styles/App.css | 10 - src/styles/forms.css | 15 + src/styles/task-form.css | 19 - src/{ => utils}/UserContext.jsx | 10 +- src/utils/auth.js | 80 ++ src/utils/generators.js | 2 +- 14 files changed, 1587 insertions(+), 105 deletions(-) create mode 100644 src/LoginForm.jsx create mode 100644 src/SignUpForm.jsx create mode 100644 src/styles/forms.css delete mode 100644 src/styles/task-form.css rename src/{ => utils}/UserContext.jsx (96%) create mode 100644 src/utils/auth.js diff --git a/package-lock.json b/package-lock.json index 9884886..9b4877a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8 +8,10 @@ "name": "kapta-web", "version": "0.0.0", "dependencies": { + "@aws-sdk/client-cognito-identity-provider": "^3.670.0", "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", + "@mui/icons-material": "^6.1.3", "@mui/material": "^6.1.1", "formik": "^2.4.6", "react": "^18.3.1", @@ -29,6 +31,640 @@ "vite": "^5.4.1" } }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity-provider": { + "version": "3.670.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity-provider/-/client-cognito-identity-provider-3.670.0.tgz", + "integrity": "sha512-sVO2TrDOtdwZmerq6pROD8TZxq/6GzgZMdeUjtFts6NI72qXhESFgSMCVRGH07mmsybOj//ErSnRo5gPlVeT9w==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.670.0", + "@aws-sdk/client-sts": "3.670.0", + "@aws-sdk/core": "3.667.0", + "@aws-sdk/credential-provider-node": "3.670.0", + "@aws-sdk/middleware-host-header": "3.667.0", + "@aws-sdk/middleware-logger": "3.667.0", + "@aws-sdk/middleware-recursion-detection": "3.667.0", + "@aws-sdk/middleware-user-agent": "3.669.0", + "@aws-sdk/region-config-resolver": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@aws-sdk/util-endpoints": "3.667.0", + "@aws-sdk/util-user-agent-browser": "3.670.0", + "@aws-sdk/util-user-agent-node": "3.669.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.670.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.670.0.tgz", + "integrity": "sha512-J+oz6uSsDvk4pimMDnKJb1wsV216zTrejvMTIL4RhUD1QPIVVOpteTdUShcjZUIZnkcJZGI+cym/SFK0kuzTpg==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.667.0", + "@aws-sdk/middleware-host-header": "3.667.0", + "@aws-sdk/middleware-logger": "3.667.0", + "@aws-sdk/middleware-recursion-detection": "3.667.0", + "@aws-sdk/middleware-user-agent": "3.669.0", + "@aws-sdk/region-config-resolver": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@aws-sdk/util-endpoints": "3.667.0", + "@aws-sdk/util-user-agent-browser": "3.670.0", + "@aws-sdk/util-user-agent-node": "3.669.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.670.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.670.0.tgz", + "integrity": "sha512-4qDK2L36Q4J1lfemaHHd9ZxqKRaos3STp44qPAHf/8QyX6Uk5sXgZNVO2yWM7SIEtVKwwBh/fZAsdBkGPBfZcw==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.667.0", + "@aws-sdk/credential-provider-node": "3.670.0", + "@aws-sdk/middleware-host-header": "3.667.0", + "@aws-sdk/middleware-logger": "3.667.0", + "@aws-sdk/middleware-recursion-detection": "3.667.0", + "@aws-sdk/middleware-user-agent": "3.669.0", + "@aws-sdk/region-config-resolver": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@aws-sdk/util-endpoints": "3.667.0", + "@aws-sdk/util-user-agent-browser": "3.670.0", + "@aws-sdk/util-user-agent-node": "3.669.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.670.0" + } + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.670.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.670.0.tgz", + "integrity": "sha512-bExrNo8ZVWorS3cjMZKQnA2HWqDmAzcZoSN/cPVoPFNkHwdl1lzPxvcLzmhpIr48JHgKfybBjrbluDZfIYeEog==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.670.0", + "@aws-sdk/core": "3.667.0", + "@aws-sdk/credential-provider-node": "3.670.0", + "@aws-sdk/middleware-host-header": "3.667.0", + "@aws-sdk/middleware-logger": "3.667.0", + "@aws-sdk/middleware-recursion-detection": "3.667.0", + "@aws-sdk/middleware-user-agent": "3.669.0", + "@aws-sdk/region-config-resolver": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@aws-sdk/util-endpoints": "3.667.0", + "@aws-sdk/util-user-agent-browser": "3.670.0", + "@aws-sdk/util-user-agent-node": "3.669.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.667.0.tgz", + "integrity": "sha512-pMcDVI7Tmdsc8R3sDv0Omj/4iRParGY+uJtAfF669WnZfDfaBQaix2Mq7+Mu08vdjqO9K3gicFvjk9S1VLmOKA==", + "dependencies": { + "@aws-sdk/types": "3.667.0", + "@smithy/core": "^2.4.8", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/property-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/signature-v4": "^4.2.0", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/util-middleware": "^3.0.7", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.667.0.tgz", + "integrity": "sha512-zZbrkkaPc54WXm+QAnpuv0LPNfsts0HPPd+oCECGs7IQRaFsGj187cwvPg9RMWDFZqpm64MdBDoA8OQHsqzYCw==", + "dependencies": { + "@aws-sdk/core": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.667.0.tgz", + "integrity": "sha512-sjtybFfERZWiqTY7fswBxKQLvUkiCucOWyqh3IaPo/4nE1PXRnaZCVG0+kRBPrYIxWqiVwytvZzMJy8sVZcG0A==", + "dependencies": { + "@aws-sdk/core": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/util-stream": "^3.1.9", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.670.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.670.0.tgz", + "integrity": "sha512-TB1gacUj75leaTt2JsCTzygDSIk4ksv9uZoR7VenlgFPRktyOeT+fapwIVBeB2Qg7b9uxAY2K5XkKstDZyBEEw==", + "dependencies": { + "@aws-sdk/core": "3.667.0", + "@aws-sdk/credential-provider-env": "3.667.0", + "@aws-sdk/credential-provider-http": "3.667.0", + "@aws-sdk/credential-provider-process": "3.667.0", + "@aws-sdk/credential-provider-sso": "3.670.0", + "@aws-sdk/credential-provider-web-identity": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@smithy/credential-provider-imds": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.670.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.670.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.670.0.tgz", + "integrity": "sha512-zwNrRYzubk4CaZ7zebeDhxsm8QtNWkbGKopZPOaZSnd5uqUGRcmx4ccVRngWUK68XDP44aEUWC8iU5Pc7btpHQ==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.667.0", + "@aws-sdk/credential-provider-http": "3.667.0", + "@aws-sdk/credential-provider-ini": "3.670.0", + "@aws-sdk/credential-provider-process": "3.667.0", + "@aws-sdk/credential-provider-sso": "3.670.0", + "@aws-sdk/credential-provider-web-identity": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@smithy/credential-provider-imds": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.667.0.tgz", + "integrity": "sha512-HZHnvop32fKgsNHkdhVaul7UzQ25sEc0j9yqA4bjhtbk0ECl42kj3f1pJ+ZU/YD9ut8lMJs/vVqiOdNThVdeBw==", + "dependencies": { + "@aws-sdk/core": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.670.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.670.0.tgz", + "integrity": "sha512-5PkA8BOy4q57Vhe9AESoHKZ7vjRbElNPKjXA4qC01xY+DitClRFz4O3B9sMzFp0PHlz9nDVSXXKgq0yzF/nAag==", + "dependencies": { + "@aws-sdk/client-sso": "3.670.0", + "@aws-sdk/core": "3.667.0", + "@aws-sdk/token-providers": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.667.0.tgz", + "integrity": "sha512-t8CFlZMD/1p/8Cli3rvRiTJpjr/8BO64gw166AHgFZYSN2h95L2l1tcW0jpsc3PprA32nLg1iQVKYt4WGM4ugw==", + "dependencies": { + "@aws-sdk/core": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.667.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.667.0.tgz", + "integrity": "sha512-Z7fIAMQnPegs7JjAQvlOeWXwpMRfegh5eCoIP6VLJIeR6DLfYKbP35JBtt98R6DXslrN2RsbTogjbxPEDQfw1w==", + "dependencies": { + "@aws-sdk/types": "3.667.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.667.0.tgz", + "integrity": "sha512-PtTRNpNm/5c746jRgZCNg4X9xEJIwggkGJrF0GP9AB1ANg4pc/sF2Fvn1NtqPe9wtQ2stunJprnm5WkCHN7QiA==", + "dependencies": { + "@aws-sdk/types": "3.667.0", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.667.0.tgz", + "integrity": "sha512-U5glWD3ehFohzpUpopLtmqAlDurGWo2wRGPNgi4SwhWU7UDt6LS7E/UvJjqC0CUrjlzOw+my2A+Ncf+fisMhxQ==", + "dependencies": { + "@aws-sdk/types": "3.667.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.669.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.669.0.tgz", + "integrity": "sha512-K8ScPi45zjJrj5Y2gRqVsvKKQCQbvQBfYGcBw9ZOx9TTavH80bOCBjWg/GFnvs4f37tqVc1wMN2oGvcTF6HveQ==", + "dependencies": { + "@aws-sdk/core": "3.667.0", + "@aws-sdk/types": "3.667.0", + "@aws-sdk/util-endpoints": "3.667.0", + "@smithy/core": "^2.4.8", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.667.0.tgz", + "integrity": "sha512-iNr+JhhA902JMKHG9IwT9YdaEx6KGl6vjAL5BRNeOjfj4cZYMog6Lz/IlfOAltMtT0w88DAHDEFrBd2uO0l2eg==", + "dependencies": { + "@aws-sdk/types": "3.667.0", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.667.0.tgz", + "integrity": "sha512-ZecJlG8p6D4UTYlBHwOWX6nknVtw/OBJ3yPXTSajBjhUlj9lE2xvejI8gl4rqkyLXk7z3bki+KR4tATbMaM9yg==", + "dependencies": { + "@aws-sdk/types": "3.667.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.667.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.667.0.tgz", + "integrity": "sha512-gYq0xCsqFfQaSL/yT1Gl1vIUjtsg7d7RhnUfsXaHt8xTxOKRTdH9GjbesBjXOzgOvB0W0vfssfreSNGFlOOMJg==", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.667.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.667.0.tgz", + "integrity": "sha512-X22SYDAuQJWnkF1/q17pkX3nGw5XMD9YEUbmt87vUnRq7iyJ3JOpl6UKOBeUBaL838wA5yzdbinmCITJ/VZ1QA==", + "dependencies": { + "@aws-sdk/types": "3.667.0", + "@smithy/types": "^3.5.0", + "@smithy/util-endpoints": "^2.1.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.568.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.568.0.tgz", + "integrity": "sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.670.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.670.0.tgz", + "integrity": "sha512-iRynWWazqEcCKwGMcQcywKTDLdLvqts1Yx474U64I9OKQXXwhOwhXbF5CAPSRta86lkVNAVYJa/0Bsv45pNn1A==", + "dependencies": { + "@aws-sdk/types": "3.667.0", + "@smithy/types": "^3.5.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.669.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.669.0.tgz", + "integrity": "sha512-9jxCYrgggy2xd44ZASqI7AMiRVaSiFp+06Kg8BQSU0ijKpBJlwcsqIS8pDT/n6LxuOw2eV5ipvM2C0r1iKzrGA==", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.669.0", + "@aws-sdk/types": "3.667.0", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, "node_modules/@babel/code-frame": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", @@ -1004,26 +1640,49 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.1.tgz", - "integrity": "sha512-VdQC1tPIIcZAnf62L2M1eQif0x2vlKg3YK4kGYbtijSH4niEgI21GnstykW1vQIs+Bc6L+Hua2GATYVjilJ22A==", - "license": "MIT", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.3.tgz", + "integrity": "sha512-ajMUgdfhTb++rwqj134Cq9f4SRN8oXUqMRnY72YBnXiXai3olJLLqETheRlq3MM8wCKrbq7g6j7iWL1VvP44VQ==", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" } }, + "node_modules/@mui/icons-material": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.1.3.tgz", + "integrity": "sha512-QBQCCIMSAv6IkArTg4Hg8q2sJRhHOci8oPAlkHWFlt2ghBdy3EqyLbIELLE/bhpqhX+E/ZkPYGIUQCd5/L0owA==", + "dependencies": { + "@babel/runtime": "^7.25.6" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^6.1.3", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@mui/material": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.1.1.tgz", - "integrity": "sha512-b+eULldTqtqTCbN++2BtBWCir/1LwEYw+2mIlOt2GiEUh1EBBw4/wIukGKKNt3xrCZqRA80yLLkV6tF61Lq3cA==", - "license": "MIT", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.1.3.tgz", + "integrity": "sha512-loV5MBoMKLrK80JeWINmQ1A4eWoLv51O2dBPLJ260IAhupkB3Wol8lEQTEvvR2vO3o6xRHuXe1WaQEP6N3riqg==", "dependencies": { "@babel/runtime": "^7.25.6", - "@mui/core-downloads-tracker": "^6.1.1", - "@mui/system": "^6.1.1", - "@mui/types": "^7.2.17", - "@mui/utils": "^6.1.1", + "@mui/core-downloads-tracker": "^6.1.3", + "@mui/system": "^6.1.3", + "@mui/types": "^7.2.18", + "@mui/utils": "^6.1.3", "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.11", "clsx": "^2.1.1", @@ -1042,7 +1701,7 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^6.1.1", + "@mui/material-pigment-css": "^6.1.3", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -1069,13 +1728,12 @@ "license": "MIT" }, "node_modules/@mui/private-theming": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.1.1.tgz", - "integrity": "sha512-JlrjIdhyZUtewtdAuUsvi3ZnO0YS49IW4Mfz19ZWTlQ0sDGga6LNPVwHClWr2/zJK2we2BQx9/i8M32rgKuzrg==", - "license": "MIT", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.1.3.tgz", + "integrity": "sha512-XK5OYCM0x7gxWb/WBEySstBmn+dE3YKX7U7jeBRLm6vHU5fGUd7GiJWRirpivHjOK9mRH6E1MPIVd+ze5vguKQ==", "dependencies": { "@babel/runtime": "^7.25.6", - "@mui/utils": "^6.1.1", + "@mui/utils": "^6.1.3", "prop-types": "^15.8.1" }, "engines": { @@ -1096,13 +1754,13 @@ } }, "node_modules/@mui/styled-engine": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.1.1.tgz", - "integrity": "sha512-HJyIoMpFb11fnHuRtUILOXgq6vj4LhIlE8maG4SwP/W+E5sa7HFexhnB3vOMT7bKys4UKNxhobC8jwWxYilGsA==", - "license": "MIT", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.1.3.tgz", + "integrity": "sha512-i4yh9m+eMZE3cNERpDhVr6Wn73Yz6C7MH0eE2zZvw8d7EFkIJlCQNZd1xxGZqarD2DDq2qWHcjIOucWGhxACtA==", "dependencies": { "@babel/runtime": "^7.25.6", "@emotion/cache": "^11.13.1", + "@emotion/serialize": "^1.3.2", "@emotion/sheet": "^1.4.0", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -1129,16 +1787,15 @@ } }, "node_modules/@mui/system": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.1.1.tgz", - "integrity": "sha512-PaYsCz2tUOcpu3T0okDEsSuP/yCDIj9JZ4Tox1JovRSKIjltHpXPsXZSGr3RiWdtM1MTQMFMCZzu0+CKbyy+Kw==", - "license": "MIT", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.1.3.tgz", + "integrity": "sha512-ILaD9UsLTBLjMcep3OumJMXh1PYr7aqnkHm/L47bH46+YmSL1zWAX6tWG8swEQROzW2GvYluEMp5FreoxOOC6w==", "dependencies": { "@babel/runtime": "^7.25.6", - "@mui/private-theming": "^6.1.1", - "@mui/styled-engine": "^6.1.1", - "@mui/types": "^7.2.17", - "@mui/utils": "^6.1.1", + "@mui/private-theming": "^6.1.3", + "@mui/styled-engine": "^6.1.3", + "@mui/types": "^7.2.18", + "@mui/utils": "^6.1.3", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -1169,10 +1826,9 @@ } }, "node_modules/@mui/types": { - "version": "7.2.17", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.17.tgz", - "integrity": "sha512-oyumoJgB6jDV8JFzRqjBo2daUuHpzDjoO/e3IrRhhHo/FxJlaVhET6mcNrKHUq2E+R+q3ql0qAtvQ4rfWHhAeQ==", - "license": "MIT", + "version": "7.2.18", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.18.tgz", + "integrity": "sha512-uvK9dWeyCJl/3ocVnTOS6nlji/Knj8/tVqVX03UVTpdmTJYu/s4jtDd9Kvv0nRGE0CUSNW1UYAci7PYypjealg==", "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -1183,14 +1839,13 @@ } }, "node_modules/@mui/utils": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.1.1.tgz", - "integrity": "sha512-HlRrgdJSPbYDXPpoVMWZV8AE7WcFtAk13rWNWAEVWKSanzBBkymjz3km+Th/Srowsh4pf1fTSP1B0L116wQBYw==", - "license": "MIT", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.1.3.tgz", + "integrity": "sha512-4JBpLkjprlKjN10DGb1aiy/ii9TKbQ601uSHtAmYFAS879QZgAD7vRnv/YBE4iBbc7NXzFgbQMCOFrupXWekIA==", "dependencies": { "@babel/runtime": "^7.25.6", - "@mui/types": "^7.2.17", - "@types/prop-types": "^15.7.12", + "@mui/types": "^7.2.18", + "@types/prop-types": "^15.7.13", "clsx": "^2.1.1", "prop-types": "^15.8.1", "react-is": "^18.3.1" @@ -1215,8 +1870,7 @@ "node_modules/@mui/utils/node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT" + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", @@ -1490,6 +2144,532 @@ "win32" ] }, + "node_modules/@smithy/abort-controller": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.5.tgz", + "integrity": "sha512-DhNPnqTqPoG8aZ5dWkFOgsuY+i0GQ3CI6hMmvCoduNsnU9gUZWZBwGfDQsTTB7NvFPkom1df7jMIJWU90kuXXg==", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.9.tgz", + "integrity": "sha512-5d9oBf40qC7n2xUoHmntKLdqsyTMMo/r49+eqSIjJ73eDfEtljAxEhzIQ3bkgXJtR3xiv7YzMT/3FF3ORkjWdg==", + "dependencies": { + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "2.4.8", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.4.8.tgz", + "integrity": "sha512-x4qWk7p/a4dcf7Vxb2MODIf4OIcqNbK182WxRvZ/3oKPrf/6Fdic5sSElhO1UtXpWKBazWfqg0ZEK9xN1DsuHA==", + "dependencies": { + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.4.tgz", + "integrity": "sha512-S9bb0EIokfYEuar4kEbLta+ivlKCWOCFsLZuilkNy9i0uEUEHSi47IFLPaxqqCl+0ftKmcOTHayY5nQhAuq7+w==", + "dependencies": { + "@smithy/node-config-provider": "^3.1.8", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.9.tgz", + "integrity": "sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A==", + "dependencies": { + "@smithy/protocol-http": "^4.1.4", + "@smithy/querystring-builder": "^3.0.7", + "@smithy/types": "^3.5.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/hash-node": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.7.tgz", + "integrity": "sha512-SAGHN+QkrwcHFjfWzs/czX94ZEjPJ0CrWJS3M43WswDXVEuP4AVy9gJ3+AF6JQHZD13bojmuf/Ap/ItDeZ+Qfw==", + "dependencies": { + "@smithy/types": "^3.5.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.7.tgz", + "integrity": "sha512-Bq00GsAhHeYSuZX8Kpu4sbI9agH2BNYnqUmmbTGWOhki9NVsWn2jFr896vvoTMH8KAjNX/ErC/8t5QHuEXG+IA==", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.9.tgz", + "integrity": "sha512-t97PidoGElF9hTtLCrof32wfWMqC5g2SEJNxaVH3NjlatuNGsdxXRYO/t+RPnxA15RpYiS0f+zG7FuE2DeGgjA==", + "dependencies": { + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.4.tgz", + "integrity": "sha512-/ChcVHekAyzUbyPRI8CzPPLj6y8QRAfJngWcLMgsWxKVzw/RzBV69mSOzJYDD3pRwushA1+5tHtPF8fjmzBnrQ==", + "dependencies": { + "@smithy/middleware-serde": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-middleware": "^3.0.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.23.tgz", + "integrity": "sha512-x9PbGXxkcXIpm6L26qRSCC+eaYcHwybRmqU8LO/WM2RRlW0g8lz6FIiKbKgGvHuoK3dLZRiQVSQJveiCzwnA5A==", + "dependencies": { + "@smithy/node-config-provider": "^3.1.8", + "@smithy/protocol-http": "^4.1.4", + "@smithy/service-error-classification": "^3.0.7", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-retry/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.7.tgz", + "integrity": "sha512-VytaagsQqtH2OugzVTq4qvjkLNbWehHfGcGr0JLJmlDRrNCeZoWkWsSOw1nhS/4hyUUWF/TLGGml4X/OnEep5g==", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.7.tgz", + "integrity": "sha512-EyTbMCdqS1DoeQsO4gI7z2Gzq1MoRFAeS8GkFYIwbedB7Lp5zlLHJdg+56tllIIG5Hnf9ZWX48YKSHlsKvugGA==", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.8.tgz", + "integrity": "sha512-E0rU0DglpeJn5ge64mk8wTGEXcQwmpUTY5Zr7IzTpDLmHKiIamINERNZYrPQjg58Ck236sEKSwRSHA4CwshU6Q==", + "dependencies": { + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.2.4.tgz", + "integrity": "sha512-49reY3+JgLMFNm7uTAKBWiKCA6XSvkNp9FqhVmusm2jpVnHORYFeFZ704LShtqWfjZW/nhX+7Iexyb6zQfXYIQ==", + "dependencies": { + "@smithy/abort-controller": "^3.1.5", + "@smithy/protocol-http": "^4.1.4", + "@smithy/querystring-builder": "^3.0.7", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.7.tgz", + "integrity": "sha512-QfzLi1GPMisY7bAM5hOUqBdGYnY5S2JAlr201pghksrQv139f8iiiMalXtjczIP5f6owxFn3MINLNUNvUkgtPw==", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.4.tgz", + "integrity": "sha512-MlWK8eqj0JlpZBnWmjQLqmFp71Ug00P+m72/1xQB3YByXD4zZ+y9N4hYrR0EDmrUCZIkyATWHOXFgtavwGDTzQ==", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.7.tgz", + "integrity": "sha512-65RXGZZ20rzqqxTsChdqSpbhA6tdt5IFNgG6o7e1lnPVLCe6TNWQq4rTl4N87hTDD8mV4IxJJnvyE7brbnRkQw==", + "dependencies": { + "@smithy/types": "^3.5.0", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.7.tgz", + "integrity": "sha512-Fouw4KJVWqqUVIu1gZW8BH2HakwLz6dvdrAhXeXfeymOBrZw+hcqaWs+cS1AZPVp4nlbeIujYrKA921ZW2WMPA==", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.7.tgz", + "integrity": "sha512-91PRkTfiBf9hxkIchhRKJfl1rsplRDyBnmyFca3y0Z3x/q0JJN480S83LBd8R6sBCkm2bBbqw2FHp0Mbh+ecSA==", + "dependencies": { + "@smithy/types": "^3.5.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.8.tgz", + "integrity": "sha512-0NHdQiSkeGl0ICQKcJQ2lCOKH23Nb0EaAa7RDRId6ZqwXkw4LJyIyZ0t3iusD4bnKYDPLGy2/5e2rfUhrt0Acw==", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.2.0.tgz", + "integrity": "sha512-LafbclHNKnsorMgUkKm7Tk7oJ7xizsZ1VwqhGKqoCIrXh4fqDDp73fK99HOEEgcsQbtemmeY/BPv0vTVYYUNEQ==", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.4.0.tgz", + "integrity": "sha512-nOfJ1nVQsxiP6srKt43r2My0Gp5PLWCW2ASqUioxIiGmu6d32v4Nekidiv5qOmmtzIrmaD+ADX5SKHUuhReeBQ==", + "dependencies": { + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "@smithy/util-stream": "^3.1.9", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.5.0.tgz", + "integrity": "sha512-QN0twHNfe8mNJdH9unwsCK13GURU7oEAZqkBI+rsvpv1jrmserO+WnLE7jidR9W/1dxwZ0u/CB01mV2Gms/K2Q==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.7.tgz", + "integrity": "sha512-70UbSSR8J97c1rHZOWhl+VKiZDqHWxs/iW8ZHrHp5fCCPLSBE7GcUlUvKSle3Ca+J9LLbYCj/A79BxztBvAfpA==", + "dependencies": { + "@smithy/querystring-parser": "^3.0.7", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/util-base64": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", + "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", + "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", + "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", + "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.23.tgz", + "integrity": "sha512-Y07qslyRtXDP/C5aWKqxTPBl4YxplEELG3xRrz2dnAQ6Lq/FgNrcKWmV561nNaZmFH+EzeGOX3ZRMbU8p1T6Nw==", + "dependencies": { + "@smithy/property-provider": "^3.1.7", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.23.tgz", + "integrity": "sha512-9Y4WH7f0vnDGuHUa4lGX9e2p+sMwODibsceSV6rfkZOvMC+BY3StB2LdO1NHafpsyHJLpwAgChxQ38tFyd6vkg==", + "dependencies": { + "@smithy/config-resolver": "^3.0.9", + "@smithy/credential-provider-imds": "^3.2.4", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/property-provider": "^3.1.7", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.3.tgz", + "integrity": "sha512-34eACeKov6jZdHqS5hxBMJ4KyWKztTMulhuQ2UdOoP6vVxMLrOKUqIXAwJe/wiWMhXhydLW664B02CNpQBQ4Aw==", + "dependencies": { + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", + "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.7.tgz", + "integrity": "sha512-OVA6fv/3o7TMJTpTgOi1H5OTwnuUa8hzRzhSFDtZyNxi6OZ70L/FHattSmhE212I7b6WSOJAAmbYnvcjTHOJCA==", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.7.tgz", + "integrity": "sha512-nh1ZO1vTeo2YX1plFPSe/OXaHkLAHza5jpokNiiKX2M5YpNUv6RxGJZhpfmiR4jSvVHCjIDmILjrxKmP+/Ghug==", + "dependencies": { + "@smithy/service-error-classification": "^3.0.7", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.9.tgz", + "integrity": "sha512-7YAR0Ub3MwTMjDfjnup4qa6W8gygZMxikBhFMPESi6ASsl/rZJhwLpF/0k9TuezScCojsM0FryGdz4LZtjKPPQ==", + "dependencies": { + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/types": "^3.5.0", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", + "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@swc/core": { "version": "1.7.26", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.26.tgz", @@ -2059,6 +3239,11 @@ "dev": true, "license": "MIT" }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2120,7 +3305,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", "engines": { "node": ">=6" } @@ -2801,6 +3985,27 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-xml-parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -4577,6 +5782,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + }, "node_modules/stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", diff --git a/package.json b/package.json index d6e87f2..e1f4772 100644 --- a/package.json +++ b/package.json @@ -10,12 +10,14 @@ "preview": "vite preview" }, "dependencies": { - "react": "^18.3.1", - "react-dom": "^18.3.1", + "@aws-sdk/client-cognito-identity-provider": "^3.670.0", "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", + "@mui/icons-material": "^6.1.3", "@mui/material": "^6.1.1", "formik": "^2.4.6", + "react": "^18.3.1", + "react-dom": "^18.3.1", "yup": "^1.4.0" }, "devDependencies": { diff --git a/src/App.jsx b/src/App.jsx index 76ab6bd..2113dad 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -3,32 +3,43 @@ import "./styles/App.css"; import TaskForm from "./TaskForm"; import { useState } from "react"; import LoginForm from "./LoginForm"; +import { useUserStore } from "./globals"; export default function App() { const [isTaskFormVisible, setTaskFormVisible] = useState(false); const [isLoginFormVisible, setLoginFormVisible] = useState(false); - //TODO: add a login, hide the buttons based on login + const user = useUserStore(); + // TODO: add mapbox background thing return (
- - - - + {!isLoginFormVisible && ( + + )} + + {user.loggedIn && ( + + )}
diff --git a/src/LoginForm.jsx b/src/LoginForm.jsx new file mode 100644 index 0000000..22f4393 --- /dev/null +++ b/src/LoginForm.jsx @@ -0,0 +1,83 @@ +import { Formik, Form, Field, ErrorMessage } from "formik"; +import { Button, IconButton, TextField, useTheme } from "@mui/material"; +import HighlightOffIcon from "@mui/icons-material/HighlightOff"; +import { useState } from "react"; +import SuccessModal from "./SuccessModal"; +import "./styles/forms.css"; +import { initiateAuth } from "./utils/auth"; +import { useUserStore } from "./globals"; +// import * as Yup from "yup"; + +export default function LoginForm({ isVisible, setIsVisible }) { + const [successModalVisible, setSuccessModalVisible] = useState(false); + const user = useUserStore(); + useTheme(); + + if (!isVisible) return null; + + // formik has built in props regarding submission so we don't need to define them ourselves + const handleSubmit = async (values) => { + console.log("Form data:", values); + const { email, password } = values; + + return initiateAuth(email, password).then(({ response }) => { + if (!response) { + alert("Incorrect email or password"); + // TODO: show sign up + } else { + user.setUserDetails(response); + setSuccessModalVisible(true); + } + }); + }; + + return ( + <> + {successModalVisible && ( + + )} + setIsVisible(false)} + > + + + + {({ isSubmitting }) => ( +
+ + + + + + + {/* Submit Button */} + + + )} +
+ + ); +} diff --git a/src/SignUpForm.jsx b/src/SignUpForm.jsx new file mode 100644 index 0000000..60df930 --- /dev/null +++ b/src/SignUpForm.jsx @@ -0,0 +1,83 @@ +import { Formik, Form, Field, ErrorMessage } from "formik"; +import { Button, IconButton, TextField, useTheme } from "@mui/material"; +import HighlightOffIcon from "@mui/icons-material/HighlightOff"; +import { useState } from "react"; +import SuccessModal from "./SuccessModal"; +import "./styles/forms.css"; +import { initiateAuth } from "./utils/auth"; +import { useUserStore } from "./globals"; +// import * as Yup from "yup"; + +export default function SignUpForm({ isVisible, setIsVisible }) { + const [successModalVisible, setSuccessModalVisible] = useState(false); + const user = useUserStore(); + useTheme(); + + if (!isVisible) return null; + + // formik has built in props regarding submission so we don't need to define them ourselves + const handleSubmit = async (values) => { + console.log("Form data:", values); + const { email, password } = values; + + return initiateAuth(email, password).then(({ response }) => { + if (!response) { + alert("Incorrect email or password"); + // TODO: show sign up + } else { + user.setUserDetails(response); + setSuccessModalVisible(true); + } + }); + }; + + return ( + <> + {successModalVisible && ( + + )} + setIsVisible(false)} + > + + + + {({ isSubmitting }) => ( +
+ + + + + + + {/* Submit Button */} + + + )} +
+ + ); +} diff --git a/src/TaskForm.jsx b/src/TaskForm.jsx index 0304011..2ab1a89 100644 --- a/src/TaskForm.jsx +++ b/src/TaskForm.jsx @@ -4,7 +4,7 @@ import { REQUEST_URL } from "./globals"; import { generateTaskId, generateCampaignCode } from "./utils/generators"; import { useState } from "react"; import SuccessModal from "./SuccessModal"; -import "./styles/task-form.css"; +import "./styles/forms.css"; // import * as Yup from "yup"; // these will be dynamically taken from their login and generated @@ -73,21 +73,22 @@ export default function TaskForm({ isVisible }) { <> {successModalVisible && ( )} -

Please tell us about your task request

+ {({ isSubmitting, setFieldValue }) => ( -
+ +

Please tell us about your task request

{/* Hidden field for Created By */}
diff --git a/src/globals.js b/src/globals.js index c787442..156d5f3 100644 --- a/src/globals.js +++ b/src/globals.js @@ -1,5 +1,5 @@ import { useContext } from "react"; -import { UserContext } from "./UserContext"; +import { UserContext } from "./utils/UserContext"; // export vars used throughout the app export const REQUEST_URL = import.meta.env.REQUEST_URL; diff --git a/src/main.jsx b/src/main.jsx index 41e8754..d8e1deb 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,12 +1,38 @@ import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import App from "./App.jsx"; -import { UserProvider } from "./UserContext.jsx"; +import { UserProvider } from "./utils/UserContext.jsx"; + +import { createTheme, ThemeProvider } from "@mui/material/styles"; +import { cyan, red, purple, teal } from "@mui/material/colors"; + +const theme = createTheme({ + palette: { + mode: "dark", + primary: teal, + secondary: purple, + info: cyan, + error: red, + warning: { + main: "#E3D026", + light: "#E9DB5D", + dark: "#A29415", + contrastText: "#242105", + }, + white: { + main: "#f5f5f5", + contrastText: "#242105", + }, + }, +}); createRoot(document.getElementById("root")).render( - + {" "} + + {" "} + ); diff --git a/src/styles/App.css b/src/styles/App.css index 272852b..e397166 100644 --- a/src/styles/App.css +++ b/src/styles/App.css @@ -20,14 +20,4 @@ main { justify-content: center; align-items: center; height: 100vh; - - .btn--new-task { - color: whitesmoke; - border-color: whitesmoke; - - &:hover { - background-color: whitesmoke; - color: var(--intrinsic-grey); - } - } } diff --git a/src/styles/forms.css b/src/styles/forms.css new file mode 100644 index 0000000..a76ec8b --- /dev/null +++ b/src/styles/forms.css @@ -0,0 +1,15 @@ +.task-request-form { + width: 75%; + text-align: center; + display: flex; + flex-direction: column; +} + +.login__form { + margin-block-start: 1rem; + width: clamp(20rem, 50%, 40rem); + text-align: center; + display: flex; + flex-direction: column; + gap: 0.8rem; +} diff --git a/src/styles/task-form.css b/src/styles/task-form.css deleted file mode 100644 index 4e40ad8..0000000 --- a/src/styles/task-form.css +++ /dev/null @@ -1,19 +0,0 @@ -#success-dialog { - background-color: #2d5b39dd; - backdrop-filter: blur(4px); - position: absolute; - top: 15%; - width: 60vw; - border: none; - border-radius: 8px; - z-index: 3; - color: white; - text-align: center; -} - -.task-request-form { - width: 90%; - text-align: center; - display: flex; - flex-direction: column; -} diff --git a/src/UserContext.jsx b/src/utils/UserContext.jsx similarity index 96% rename from src/UserContext.jsx rename to src/utils/UserContext.jsx index 8cd395d..29c19a0 100644 --- a/src/UserContext.jsx +++ b/src/utils/UserContext.jsx @@ -1,5 +1,5 @@ import { useState, useCallback, createContext, useEffect } from "react"; -import { initiateAuthRefresh, signOut } from "./utils/auth"; +import { initiateAuthRefresh, signOut } from "./auth"; // Create the User Context export const UserContext = createContext(); @@ -14,10 +14,6 @@ export const UserProvider = ({ children }) => { const [email, setEmail] = useState(null); const [loggedIn, setLoggedIn] = useState(false); - useEffect(() => { - checkForDetails(); - }, [checkForDetails]); // check for details when component mounts ie app starts - const setUserDetails = useCallback((userDetails) => { const base64Payload = userDetails.idToken.split(".")[1]; const decodedIdTokenPayload = JSON.parse(atob(base64Payload)); @@ -58,6 +54,10 @@ export const UserProvider = ({ children }) => { } else return false; }, [setUserDetails]); + useEffect(() => { + checkForDetails(); + }); // check for details when component mounts ie app starts + // update localStorage values const setLocalStorage = (idToken, accessToken, refreshToken) => { localStorage.setItem("idToken", idToken || "null"); // Save as "null" if null diff --git a/src/utils/auth.js b/src/utils/auth.js new file mode 100644 index 0000000..41eefa1 --- /dev/null +++ b/src/utils/auth.js @@ -0,0 +1,80 @@ +import { + SignUpCommand, + CognitoIdentityProviderClient, + InitiateAuthCommand, + GlobalSignOutCommand, +} from "@aws-sdk/client-cognito-identity-provider"; + +import { cognito } from "../globals"; + +const validatePassword = (password) => { + const lower = "abcdefghijklmnopqrstuvwxyz"; + const upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + const digits = "0123456789"; + const special = "!@#$%^&*()"; + + if (password.length < 8) { + // It's impossible to generate a passing password so break + return "Please enter a password of at least 8 characters"; + } + + // Make sure password contains at least one character from each set + const all = (arr, fn = Boolean) => arr.every(fn); + const any = (arr, fn = Boolean) => arr.some(fn); + let charSetPresence = [ + any(lower.split("").map((item) => password.includes(item))), + any(upper.split("").map((item) => password.includes(item))), + any(digits.split("").map((item) => password.includes(item))), + any(special.split("").map((item) => password.includes(item))), + ]; + return all(charSetPresence); +}; + +const signUp = (email, password, displayName) => { + const client = new CognitoIdentityProviderClient(cognito); + + const command = new SignUpCommand({ + ClientId: cognito.userPoolClientId, + Username: email, + Password: password, + UserAttributes: [{ Name: "preferredUsername", Value: displayName }], + }); + + return client.send(command); +}; + +const initiateAuth = (email, password) => { + const client = new CognitoIdentityProviderClient(cognito); + const command = new InitiateAuthCommand({ + AuthFlow: "USER_SRP_AUTH", + AuthParameters: { + USERNAME: email, + PASSWORD: password, + }, + ClientId: cognito.userPoolClientId, + }); + return client.send(command); +}; + +const initiateAuthRefresh = (refreshToken) => { + const client = new CognitoIdentityProviderClient(cognito); + const input = { + AuthFlow: "REFRESH_TOKEN_AUTH", + ClientId: cognito.userPoolClientId, + AuthParameters: { + REFRESH_TOKEN: refreshToken, + }, + }; + const command = new InitiateAuthCommand(input); + return client.send(command); +}; + +const signOut = (access_token) => { + const client = new CognitoIdentityProviderClient(cognito); + const command = new GlobalSignOutCommand({ + AccessToken: access_token, + }); + return client.send(command); +}; + +export { signUp, validatePassword, initiateAuth, initiateAuthRefresh, signOut }; diff --git a/src/utils/generators.js b/src/utils/generators.js index dab995f..ab0e3a0 100644 --- a/src/utils/generators.js +++ b/src/utils/generators.js @@ -1,5 +1,5 @@ export const generateTaskId = () => { - return crypto.randomUUID().substring(0, 10); + return crypto.randomUUID(); }; export const generateCampaignCode = () => { From 1c462b4b26066dc21a4c1d9be01f009e7c677543 Mon Sep 17 00:00:00 2001 From: acholyn Date: Thu, 24 Oct 2024 10:34:32 +0100 Subject: [PATCH 07/20] fixing env vars not being read --- src/globals.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/globals.js b/src/globals.js index 156d5f3..0f79b9b 100644 --- a/src/globals.js +++ b/src/globals.js @@ -2,10 +2,11 @@ import { useContext } from "react"; import { UserContext } from "./utils/UserContext"; // export vars used throughout the app -export const REQUEST_URL = import.meta.env.REQUEST_URL; -export const WEB_POOL_ID = import.meta.env.WEB_POOL_ID; -export const CLIENT_ID = import.meta.env.CLIENT_ID; -export const REGION = import.meta.env.REGION; +export const REQUEST_URL = import.meta.env.VITE_REQUEST_URL; +export const WEB_POOL_ID = import.meta.env.VITE_WEB_POOL_ID; +export const CLIENT_ID = import.meta.env.VITE_CLIENT_ID; +export const REGION = import.meta.env.VITE_REGION; +export const MAPBOX_TOKEN = import.meta.env.VITE_MAPBOX_TOKEN; export let cognito = null; export let hasCognito = false; From cf06e43e111d32a2cd238a7d7ef93f1ab9d25fdc Mon Sep 17 00:00:00 2001 From: acholyn Date: Thu, 24 Oct 2024 10:34:54 +0100 Subject: [PATCH 08/20] adding mapbox --- index.html | 5 + package-lock.json | 246 ++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + src/Mapbox.jsx | 29 +++++ src/styles/mapbox.css | 8 ++ 5 files changed, 289 insertions(+) create mode 100644 src/Mapbox.jsx create mode 100644 src/styles/mapbox.css diff --git a/index.html b/index.html index dc44566..69f47cd 100644 --- a/index.html +++ b/index.html @@ -3,6 +3,11 @@ + + Kapta Web diff --git a/package-lock.json b/package-lock.json index 9b4877a..4e078a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@mui/icons-material": "^6.1.3", "@mui/material": "^6.1.1", "formik": "^2.4.6", + "mapbox-gl": "^3.7.0", "react": "^18.3.1", "react-dom": "^18.3.1", "yup": "^1.4.0" @@ -1639,6 +1640,50 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mapbox/jsonlint-lines-primitives": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", + "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mapbox/mapbox-gl-supported": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-3.0.0.tgz", + "integrity": "sha512-2XghOwu16ZwPJLOFVuIOaLbN0iKMn867evzXFyf0P22dqugezfJwLmdanAgU25ITvz1TvOfVP4jsDImlDJzcWg==" + }, + "node_modules/@mapbox/point-geometry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", + "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==" + }, + "node_modules/@mapbox/tiny-sdf": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.6.tgz", + "integrity": "sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA==" + }, + "node_modules/@mapbox/unitbezier": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", + "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==" + }, + "node_modules/@mapbox/vector-tile": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz", + "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", + "dependencies": { + "@mapbox/point-geometry": "~0.1.0" + } + }, + "node_modules/@mapbox/whoots-js": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", + "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@mui/core-downloads-tracker": { "version": "6.1.3", "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.3.tgz", @@ -2903,6 +2948,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/geojson": { + "version": "7946.0.14", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==" + }, + "node_modules/@types/geojson-vt": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@types/geojson-vt/-/geojson-vt-3.2.5.tgz", + "integrity": "sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g==", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@types/hoist-non-react-statics": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", @@ -2920,12 +2978,32 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/mapbox__point-geometry": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.4.tgz", + "integrity": "sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==" + }, + "node_modules/@types/mapbox__vector-tile": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.4.tgz", + "integrity": "sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==", + "dependencies": { + "@types/geojson": "*", + "@types/mapbox__point-geometry": "*", + "@types/pbf": "*" + } + }, "node_modules/@types/parse-json": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", "license": "MIT" }, + "node_modules/@types/pbf": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.5.tgz", + "integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==" + }, "node_modules/@types/prop-types": { "version": "15.7.13", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", @@ -2961,6 +3039,14 @@ "@types/react": "*" } }, + "node_modules/@types/supercluster": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz", + "integrity": "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@vitejs/plugin-react-swc": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.7.1.tgz", @@ -3301,6 +3387,11 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/cheap-ruler": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cheap-ruler/-/cheap-ruler-4.0.0.tgz", + "integrity": "sha512-0BJa8f4t141BYKQyn9NSQt1PguFQXMXwZiA5shfoaBYHAb2fFk2RAX+tiWMoQU+Agtzt3mdt0JtuyshAXqZ+Vw==" + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -3373,6 +3464,11 @@ "node": ">= 8" } }, + "node_modules/csscolorparser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz", + "integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==" + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -3525,6 +3621,11 @@ "csstype": "^3.0.2" } }, + "node_modules/earcut": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.0.tgz", + "integrity": "sha512-41Fs7Q/PLq1SDbqjsgcY7GA42T0jvaCNGXgGtsNdvg+Yv8eIu06bxv4/PoREkZ9nMDNwnUSG9OFB9+yv8eKhDg==" + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -4161,6 +4262,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/geojson-vt": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-4.0.2.tgz", + "integrity": "sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A==" + }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -4199,6 +4305,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gl-matrix": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -4255,6 +4366,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/grid-index": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/grid-index/-/grid-index-1.1.0.tgz", + "integrity": "sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==" + }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -4351,6 +4467,25 @@ "react-is": "^16.7.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4874,6 +5009,11 @@ "node": ">=4.0" } }, + "node_modules/kdbush": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz", + "integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==" + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -4951,6 +5091,41 @@ "loose-envify": "cli.js" } }, + "node_modules/mapbox-gl": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-3.7.0.tgz", + "integrity": "sha512-dCbVyH1uGobwv6f4QKRv2Z2wuVT/RmspsudK3sTxGRFxZi6Pd2P9axdbVyZpmGddCAREy44pHhvzvO0qgpdKAg==", + "dependencies": { + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/mapbox-gl-supported": "^3.0.0", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/tiny-sdf": "^2.0.6", + "@mapbox/unitbezier": "^0.0.1", + "@mapbox/vector-tile": "^1.3.1", + "@mapbox/whoots-js": "^3.1.0", + "@types/geojson": "^7946.0.14", + "@types/geojson-vt": "^3.2.5", + "@types/mapbox__point-geometry": "^0.1.4", + "@types/mapbox__vector-tile": "^1.3.4", + "@types/pbf": "^3.0.5", + "@types/supercluster": "^7.1.3", + "cheap-ruler": "^4.0.0", + "csscolorparser": "~1.0.3", + "earcut": "^3.0.0", + "geojson-vt": "^4.0.2", + "gl-matrix": "^3.4.3", + "grid-index": "^1.1.0", + "kdbush": "^4.0.2", + "murmurhash-js": "^1.0.0", + "pbf": "^3.2.1", + "potpack": "^2.0.0", + "quickselect": "^3.0.0", + "serialize-to-js": "^3.1.2", + "supercluster": "^8.0.1", + "tinyqueue": "^3.0.0", + "vt-pbf": "^3.1.3" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4970,6 +5145,11 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/murmurhash-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", + "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==" + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -5214,6 +5394,18 @@ "node": ">=8" } }, + "node_modules/pbf": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.3.0.tgz", + "integrity": "sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==", + "dependencies": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, "node_modules/picocolors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", @@ -5259,6 +5451,11 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/potpack": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.0.0.tgz", + "integrity": "sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw==" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5286,6 +5483,11 @@ "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==", "license": "MIT" }, + "node_modules/protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -5317,6 +5519,11 @@ ], "license": "MIT" }, + "node_modules/quickselect": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz", + "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==" + }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -5444,6 +5651,14 @@ "node": ">=4" } }, + "node_modules/resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -5571,6 +5786,14 @@ "semver": "bin/semver.js" } }, + "node_modules/serialize-to-js": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/serialize-to-js/-/serialize-to-js-3.1.2.tgz", + "integrity": "sha512-owllqNuDDEimQat7EPG0tH7JjO090xKNzUtYz6X+Sk2BXDnOCilDdNLwjWeFywG9xkJul1ULvtUQa9O4pUaY0w==", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -5793,6 +6016,14 @@ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", "license": "MIT" }, + "node_modules/supercluster": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz", + "integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==", + "dependencies": { + "kdbush": "^4.0.2" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -5837,6 +6068,11 @@ "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", "license": "MIT" }, + "node_modules/tinyqueue": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", + "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==" + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -6046,6 +6282,16 @@ } } }, + "node_modules/vt-pbf": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz", + "integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==", + "dependencies": { + "@mapbox/point-geometry": "0.1.0", + "@mapbox/vector-tile": "^1.3.1", + "pbf": "^3.2.1" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index e1f4772..18c8b4f 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@mui/icons-material": "^6.1.3", "@mui/material": "^6.1.1", "formik": "^2.4.6", + "mapbox-gl": "^3.7.0", "react": "^18.3.1", "react-dom": "^18.3.1", "yup": "^1.4.0" diff --git a/src/Mapbox.jsx b/src/Mapbox.jsx new file mode 100644 index 0000000..bb1f40c --- /dev/null +++ b/src/Mapbox.jsx @@ -0,0 +1,29 @@ +import mapboxgl from "mapbox-gl"; +import { useEffect } from "react"; +import { MAPBOX_TOKEN } from "./globals"; +import "./styles/mapbox.css"; + +export function Map() { + useEffect(() => { + mapboxgl.accessToken = MAPBOX_TOKEN; + + const map = new mapboxgl.Map({ + container: "map", + style: "mapbox://styles/mapbox/dark-v11", + zoom: 1.5, + center: [30, 50], + projection: "globe", + }); + map.on("load", () => { + map.setFog({ + color: "grey", + "high-color": "#232222", + "horizon-blend": 0.02, + "space-color": "#16161d", + "star-intensity": 0, + }); + }); + }, []); + + return
; +} diff --git a/src/styles/mapbox.css b/src/styles/mapbox.css new file mode 100644 index 0000000..43bf5e4 --- /dev/null +++ b/src/styles/mapbox.css @@ -0,0 +1,8 @@ +.mapboxgl-map { + position: fixed; + top: 0; + bottom: 0; + width: 100%; + height: 100%; + /* z-index: -1; */ +} From 612898d1024cc355fb137f638e0cba342a7b5b23 Mon Sep 17 00:00:00 2001 From: acholyn Date: Thu, 24 Oct 2024 10:35:18 +0100 Subject: [PATCH 09/20] tweaks to login + faking log in --- src/LoginForm.jsx | 90 ++++++++++++++++++++++++++++------------------- src/utils/auth.js | 1 + 2 files changed, 54 insertions(+), 37 deletions(-) diff --git a/src/LoginForm.jsx b/src/LoginForm.jsx index 22f4393..9bced87 100644 --- a/src/LoginForm.jsx +++ b/src/LoginForm.jsx @@ -20,6 +20,11 @@ export default function LoginForm({ isVisible, setIsVisible }) { console.log("Form data:", values); const { email, password } = values; + user.forceLogin(); // temp until cognito set up + setSuccessModalVisible(true); + setIsVisible(false); + + // TODO: get this to work return initiateAuth(email, password).then(({ response }) => { if (!response) { alert("Incorrect email or password"); @@ -30,7 +35,10 @@ export default function LoginForm({ isVisible, setIsVisible }) { } }); }; - + const initialValues = { + email: "", + password: "", + }; return ( <> {successModalVisible && ( @@ -40,44 +48,52 @@ export default function LoginForm({ isVisible, setIsVisible }) { isTask={false} /> )} - setIsVisible(false)} - > - - - - {({ isSubmitting }) => ( - - - +
+ setIsVisible(false)} + > + + + + {({ isSubmitting }) => ( + + + - - + + - {/* Submit Button */} - - - )} - + {/* Submit Button */} + + + )} + +
); } diff --git a/src/utils/auth.js b/src/utils/auth.js index 41eefa1..a341d68 100644 --- a/src/utils/auth.js +++ b/src/utils/auth.js @@ -44,6 +44,7 @@ const signUp = (email, password, displayName) => { }; const initiateAuth = (email, password) => { + console.log("initiating log in"); const client = new CognitoIdentityProviderClient(cognito); const command = new InitiateAuthCommand({ AuthFlow: "USER_SRP_AUTH", From 24a6ce6eb7bbb209d48ce17fbbbfdcb1a7aae218 Mon Sep 17 00:00:00 2001 From: acholyn Date: Thu, 24 Oct 2024 14:05:27 +0100 Subject: [PATCH 10/20] basis for user stuff --- src/utils/UserContext.jsx | 85 ++++++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 27 deletions(-) diff --git a/src/utils/UserContext.jsx b/src/utils/UserContext.jsx index 29c19a0..de551d2 100644 --- a/src/utils/UserContext.jsx +++ b/src/utils/UserContext.jsx @@ -14,6 +14,11 @@ export const UserProvider = ({ children }) => { const [email, setEmail] = useState(null); const [loggedIn, setLoggedIn] = useState(false); + //temp function until cognito set up + const forceLogin = () => { + setLoggedIn(true); + }; + const setUserDetails = useCallback((userDetails) => { const base64Payload = userDetails.idToken.split(".")[1]; const decodedIdTokenPayload = JSON.parse(atob(base64Payload)); @@ -32,27 +37,64 @@ export const UserProvider = ({ children }) => { ); // the states won't have been updated in time, so use the original prop }, []); - // Check for user tokens from localStorage and update user info - const checkForDetails = useCallback(() => { - const storedIdToken = localStorage.getItem("idToken"); - const storedAccessToken = localStorage.getItem("accessToken"); - const storedRefreshToken = localStorage.getItem("refreshToken"); - - let userDetails = { - accessToken: storedAccessToken, - idToken: storedIdToken, - refreshToken: storedRefreshToken, + function isTokenValid(token) { + const base64Payload = token.split(".")[1]; + const decodedToken = JSON.parse(atob(base64Payload)); + const expirationTime = decodedToken.exp; + const currentTime = Math.floor(Date.now() / 1000); + + return expirationTime > currentTime; + } + + const getLocalStorageTokens = useCallback(() => { + return { + idToken: localStorage.getItem("idToken"), + accessToken: localStorage.getItem("accessToken"), + refreshToken: localStorage.getItem("refreshToken"), }; + }, []); + + // Function to refresh tokens + const refresh = useCallback( + async (refreshToken) => { + if (refreshToken) { + const response = await initiateAuthRefresh(refreshToken); + const authResult = response.AuthenticationResult; + setUserDetails({ + accessToken: authResult.AccessToken, + idToken: authResult.IdToken, + refreshToken: authResult.RefreshToken, + }); + } + }, + [setUserDetails] + ); + + // Check for user tokens from localStorage and update user info + const checkForDetails = useCallback(async () => { + let userDetails = getLocalStorageTokens(); + // check each of the tokens is there and not "null" const userDetailsNotNull = Object.values(userDetails).every( (value) => value !== null && value !== "null" && value !== undefined ); - // Only set user details if tokens exist and are not the string "null" if (userDetailsNotNull) { - setUserDetails(userDetails); - return true; - } else return false; - }, [setUserDetails]); + const isValid = await isTokenValid(userDetails.idToken); + if (!isValid) { + await refresh(userDetails.refreshToken); + try { + userDetails = getLocalStorageTokens(); + await isTokenValid(); + setUserDetails(userDetails); + } catch (error) { + console.error("Error refreshing tokens", error); + } + } else { + setUserDetails(userDetails); + return true; + } + } else return false; // user will have to log in again + }, [setUserDetails, getLocalStorageTokens, refresh]); useEffect(() => { checkForDetails(); @@ -65,18 +107,6 @@ export const UserProvider = ({ children }) => { localStorage.setItem("refreshToken", refreshToken || "null"); }; - // Function to refresh tokens - const refresh = useCallback(() => { - if (refreshToken) { - initiateAuthRefresh({ refreshToken }).then((response) => { - const authResult = response.AuthenticationResult; - setIdToken(authResult.IdToken); - setAccessToken(authResult.AccessToken); - setRefreshToken(authResult.RefreshToken); - }); - } - }, [refreshToken]); - // Function to log out const logout = useCallback(() => { if (accessToken) { @@ -112,6 +142,7 @@ export const UserProvider = ({ children }) => { logout, setUserDetails, checkForDetails, + forceLogin, }} > {children} From 6604837b3ca216331166394f7315b375a201b20d Mon Sep 17 00:00:00 2001 From: acholyn Date: Thu, 24 Oct 2024 14:46:13 +0100 Subject: [PATCH 11/20] styling improvements to success modal + main page and using warmer palette --- src/App.jsx | 65 +++++++++++++---- src/SuccessModal.jsx | 27 ++++--- src/TaskForm.jsx | 160 +++++++++++++++++++++++------------------ src/main.jsx | 10 +-- src/styles/App.css | 37 +++++++++- src/styles/dialogs.css | 34 ++++++++- src/styles/forms.css | 24 +++++++ 7 files changed, 258 insertions(+), 99 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 2113dad..a0b4c3b 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,13 +1,21 @@ -import { Button } from "@mui/material"; +import { Button, ButtonGroup } from "@mui/material"; +import AddIcon from "@mui/icons-material/Add"; +import VisibilityIcon from "@mui/icons-material/Visibility"; import "./styles/App.css"; import TaskForm from "./TaskForm"; import { useState } from "react"; import LoginForm from "./LoginForm"; import { useUserStore } from "./globals"; +import TaskList from "./TaskList"; +import { Map } from "./Mapbox"; +import SearchForm from "./SearchForm"; export default function App() { const [isTaskFormVisible, setTaskFormVisible] = useState(false); + const [isTaskListVisible, setTaskListVisible] = useState(false); + const [isLoginFormVisible, setLoginFormVisible] = useState(false); + const [isSearchFormVisible, setSearchFormVisible] = useState(false); const user = useUserStore(); @@ -15,7 +23,7 @@ export default function App() { return (
- {!isLoginFormVisible && ( + {!isLoginFormVisible && !user.loggedIn && ( - )} + <> +
+ +
+ + + + +
+
- + + + + + + + )}
); } diff --git a/src/SuccessModal.jsx b/src/SuccessModal.jsx index a4aac9c..da573d8 100644 --- a/src/SuccessModal.jsx +++ b/src/SuccessModal.jsx @@ -1,4 +1,4 @@ -import { Button } from "@mui/material"; +import { Button, Divider } from "@mui/material"; import "./styles/dialogs.css"; export default function SuccessModal({ @@ -19,19 +19,30 @@ export default function SuccessModal({ {isTask && ( <> {" "} -

Request ID: {taskID}

-

Task title:{taskTitle}

-

Task description:{taskDescription}

+
+

+ {taskTitle} {taskID} +

+

{taskDescription}

+
+

- Below is the campaign code for people to access your study on - Kapta Mobile and contribute to it. + Below is the campaign code for people to access your study on{" "} + Kapta Mobile and contribute to it.

-
{campaignCode}
+
+ {campaignCode} + {/* todo: add auto copy to clipboard when clicked? */} +
)} -
diff --git a/src/TaskForm.jsx b/src/TaskForm.jsx index 2ab1a89..64e31a6 100644 --- a/src/TaskForm.jsx +++ b/src/TaskForm.jsx @@ -1,5 +1,6 @@ import { Formik, Form, Field, ErrorMessage } from "formik"; -import { Button, Checkbox, TextField } from "@mui/material"; +import { Box, Button, Checkbox, TextField } from "@mui/material"; +import CloudUploadIcon from "@mui/icons-material/CloudUpload"; import { REQUEST_URL } from "./globals"; import { generateTaskId, generateCampaignCode } from "./utils/generators"; import { useState } from "react"; @@ -32,11 +33,11 @@ const initialValues = { export default function TaskForm({ isVisible }) { const [successModalVisible, setSuccessModalVisible] = useState(false); + const [title, setTitle] = useState(""); + const [description, setDescription] = useState(""); if (!isVisible) return null; - var taskTitle; - var taskDescription; // formik has built in props regarding submission so we don't need to define them ourselves const handleSubmit = async (values) => { // will need to convert image to base 64 @@ -47,6 +48,7 @@ export default function TaskForm({ isVisible }) { console.log("Form data:", values); try { + console.log(`${REQUEST_URL}/requests`); const response = await fetch(`${REQUEST_URL}/requests`, { method: "PUT", headers: { @@ -61,8 +63,8 @@ export default function TaskForm({ isVisible }) { const result = await response.json(); console.log("Success:", result); - taskTitle = values.title; - taskDescription = values.description; + setTitle(values.title); + setDescription(values.description); setSuccessModalVisible(true); } catch (error) { console.error("Error:", error); @@ -73,11 +75,12 @@ export default function TaskForm({ isVisible }) { <> {successModalVisible && ( )} @@ -88,79 +91,96 @@ export default function TaskForm({ isVisible }) { > {({ isSubmitting, setFieldValue }) => (
-

Please tell us about your task request

+

Tell us about your task

{/* Hidden field for Created By */} - -
- {/* Organisation */} +
+ + + {/* Organisation */} + + + + {/* Logo */} + + + + + {/* Private to Org */} +
+ +
+ + {/* Visible on Kapta Web */} +
+ +
+ {/* Title */} + + + {/* Description */} + - {/* Logo */} - - - { - setFieldValue("logo", event.currentTarget.files[0]); - }} - /> -
- - {/* Private to Org */} -
- -
- - {/* Visible on Kapta Web */} -
- + {/* Submit Button */} +
- {/* Title */} - - - - {/* Description */} - - - - {/* Submit Button */} - )} diff --git a/src/main.jsx b/src/main.jsx index d8e1deb..41abbb9 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -4,14 +4,16 @@ import App from "./App.jsx"; import { UserProvider } from "./utils/UserContext.jsx"; import { createTheme, ThemeProvider } from "@mui/material/styles"; -import { cyan, red, purple, teal } from "@mui/material/colors"; +import { pink, red, purple, orange } from "@mui/material/colors"; const theme = createTheme({ palette: { mode: "dark", - primary: teal, - secondary: purple, - info: cyan, + primary: orange, + secondary: { + main: purple[900], + }, + info: pink, error: red, warning: { main: "#E3D026", diff --git a/src/styles/App.css b/src/styles/App.css index e397166..408046b 100644 --- a/src/styles/App.css +++ b/src/styles/App.css @@ -1,7 +1,13 @@ +@import url("https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=Krub:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;1,200;1,300;1,400;1,500;1,600;1,700&family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap"); + :root { font-family: Verdana, Geneva, Tahoma, sans-serif; --intrinsic-grey: #16161d; + --intrinsic-grey--translucent: #16161dd5; + --off-white: #eae1e1; + --rose: #f6909c; } html, body { @@ -12,12 +18,37 @@ body { #root { background-color: var(--intrinsic-grey); + z-index: -2; } main { - display: flex; - flex-direction: column; - justify-content: center; + display: grid; + /* flex-direction: column; */ align-items: center; height: 100vh; + /* z-index: -2; */ + + .btn--login { + justify-self: center; + } +} + +.btn-container { + justify-self: center; + position: absolute; + z-index: 1; + top: 5%; + display: flex; + justify-content: space-between; + width: 80vw; + + .btn-container--tasks::before { + content: "TASKS"; + position: absolute; + top: -2rem; + margin-left: 2.85rem; + font-size: 1.5rem; + font-weight: bold; + color: tomato; + } } diff --git a/src/styles/dialogs.css b/src/styles/dialogs.css index ec3c47b..a6fbc6a 100644 --- a/src/styles/dialogs.css +++ b/src/styles/dialogs.css @@ -1,12 +1,42 @@ #success-dialog { - background-color: #2d5b39dd; + background-color: #2d5b39eb; backdrop-filter: blur(4px); position: absolute; - top: 15%; + top: 20%; width: 60vw; border: none; border-radius: 8px; z-index: 3; color: white; text-align: center; + + h3 { + font-family: "Krub", sans-serif; + font-weight: 700; + font-size: 1.5rem; + color: #c9ff79; + } + .task-info__container { + border: 1px solid var(--rose); + } + .task-info { + font-size: 1.2rem; + small { + display: block; + font-size: 0.8rem; + color: #e0e1e0; + } + } + + .campaign-code { + font-size: 2rem; + letter-spacing: 0.4rem; + color: var(--rose); + font-weight: 700; + padding-bottom: 1rem; + } + + p > u { + text-decoration-color: tomato; + } } diff --git a/src/styles/forms.css b/src/styles/forms.css index a76ec8b..207c5ad 100644 --- a/src/styles/forms.css +++ b/src/styles/forms.css @@ -3,9 +3,33 @@ text-align: center; display: flex; flex-direction: column; + justify-self: center; + align-self: center; + background-color: var(--intrinsic-grey--translucent); + z-index: 1; + padding-bottom: 3rem; + margin-top: 5rem; + color: var(--off-white); + border: 1px solid tomato; + border-radius: 8px; + + .form__body { + margin-inline: 3rem; + } + + .btn--submit { + margin-top: 1rem; + } } +.login__form--container { + display: flex; + flex-direction: column; + gap: 1rem; + align-items: center; +} .login__form { + justify-self: center; margin-block-start: 1rem; width: clamp(20rem, 50%, 40rem); text-align: center; From f19ab8f169b598bf7b54a370fff4a384e56afb70 Mon Sep 17 00:00:00 2001 From: acholyn Date: Thu, 24 Oct 2024 14:58:47 +0100 Subject: [PATCH 12/20] removing kapta mobile underline --- src/SuccessModal.jsx | 2 +- src/styles/dialogs.css | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/SuccessModal.jsx b/src/SuccessModal.jsx index da573d8..c2a69d1 100644 --- a/src/SuccessModal.jsx +++ b/src/SuccessModal.jsx @@ -29,7 +29,7 @@ export default function SuccessModal({

Below is the campaign code for people to access your study on{" "} - Kapta Mobile and contribute to it. + Kapta Mobile and contribute to it.

{campaignCode} diff --git a/src/styles/dialogs.css b/src/styles/dialogs.css index a6fbc6a..60d27b9 100644 --- a/src/styles/dialogs.css +++ b/src/styles/dialogs.css @@ -35,8 +35,4 @@ font-weight: 700; padding-bottom: 1rem; } - - p > u { - text-decoration-color: tomato; - } } From bcdd65be4aa764f577614da40dc73809d9a55516 Mon Sep 17 00:00:00 2001 From: acholyn Date: Thu, 24 Oct 2024 16:06:25 +0100 Subject: [PATCH 13/20] adding tasklist and copy code to clipboard utility --- src/App.jsx | 6 +- src/SuccessModal.jsx | 27 +++++- src/TaskList.jsx | 157 +++++++++++++++++++++++++++++++++++ src/styles/task-list.css | 22 +++++ src/utils/copyToClipboard.js | 8 ++ 5 files changed, 216 insertions(+), 4 deletions(-) create mode 100644 src/TaskList.jsx create mode 100644 src/styles/task-list.css create mode 100644 src/utils/copyToClipboard.js diff --git a/src/App.jsx b/src/App.jsx index a0b4c3b..c9144e3 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -79,7 +79,11 @@ export default function App() { - + )} diff --git a/src/SuccessModal.jsx b/src/SuccessModal.jsx index c2a69d1..c1f9069 100644 --- a/src/SuccessModal.jsx +++ b/src/SuccessModal.jsx @@ -1,5 +1,7 @@ -import { Button, Divider } from "@mui/material"; +import { Button, Divider, Snackbar } from "@mui/material"; import "./styles/dialogs.css"; +import { useState } from "react"; +import { copyToClipboard } from "./utils/copyToClipboard"; export default function SuccessModal({ taskID = "", @@ -13,6 +15,15 @@ export default function SuccessModal({ let title; if (isTask) title = "Your submission has been successful. See details below"; else title = taskTitle; + + const [snackbarOpen, setSnackbarOpen] = useState(false); + + const handleCopy = async (text) => { + const success = await copyToClipboard(text); + if (success) { + setSnackbarOpen(true); + } + }; return (

{title}

@@ -31,9 +42,11 @@ export default function SuccessModal({ Below is the campaign code for people to access your study on{" "} Kapta Mobile and contribute to it.

-
+
handleCopy(campaignCode)} + > {campaignCode} - {/* todo: add auto copy to clipboard when clicked? */}
@@ -45,6 +58,14 @@ export default function SuccessModal({ > Close + + setSnackbarOpen(false)} + message="Code copied to clipboard!" + />
); } diff --git a/src/TaskList.jsx b/src/TaskList.jsx new file mode 100644 index 0000000..5a553c2 --- /dev/null +++ b/src/TaskList.jsx @@ -0,0 +1,157 @@ +import { + Button, + ButtonGroup, + Card, + CardActions, + CardContent, + CircularProgress, + Divider, + Drawer, + IconButton, + Snackbar, +} from "@mui/material"; +import HighlightOffIcon from "@mui/icons-material/HighlightOff"; +import { REQUEST_URL } from "./globals"; +import { useEffect, useState } from "react"; +import "./styles/task-list.css"; +import { copyToClipboard } from "./utils/copyToClipboard"; +export default function TaskList({ isVisible, setIsVisible, user }) { + // TODO: if given user or get user here then get all tasks created by them + const [tasks, setTasks] = useState([]); + const [metadataStore, setMetadataStore] = useState([]); + const [showMetadata, setShowMetadata] = useState(false); + const [visibleCodes, setVisibleCodes] = useState({}); + const [isLoading, setIsLoading] = useState(true); + const [snackbarOpen, setSnackbarOpen] = useState(false); + + console.log(user); + useEffect(() => { + const getMetadata = async () => { + // TODO: get metadata from dynamodb for task id, maybe combine into other function + + try { + const response = await fetch(`${REQUEST_URL}/requests`); + const result = await response.json(); + const metadata = JSON.parse(result); + console.log(metadata); + + return metadata; + } catch (error) { + console.error("Error fetching tasks:", error); + } + }; + const fetchTasks = async () => { + // TODO: get tasks from dynamodb where created_by===userID + + // TODO: get metadata from dynamodb for task id and set in state + + try { + const response = await fetch(`${REQUEST_URL}/requests`); + const result = await response.json(); + const fetchedTasks = JSON.parse(result); + console.log(fetchedTasks); + + // for task in fetchedTasks: + // metadata = getMetadata(task.taskID) + // metadataStore.append(metadata) + + setTasks(fetchedTasks); + } catch (error) { + console.error("Error fetching tasks:", error); + } finally { + setIsLoading(false); + } + }; + + if (isVisible) { + fetchTasks(); + } + }, [isVisible]); + + const toggleCodeVisibility = (taskId) => { + setVisibleCodes((prev) => ({ + ...prev, + [taskId]: !prev[taskId], + })); + }; + + const handleCopy = async (text) => { + const success = await copyToClipboard(text); + if (success) { + setSnackbarOpen(true); + } + }; + + if (!isVisible) return null; + + return ( + + setIsVisible(false)} + > + + +
+

My Tasks

+ {isLoading ? ( + + ) : ( + tasks.map((task) => ( + <> + + + + {task.task_title}{" "} + {task.task_id} + +

{task.task_description}

+ + {showMetadata && metadataStore.task_id === task.task_id && ( + // get metadata from metadataStore or fetch from dynamodb +

{metadataStore.info}

+ )} + {visibleCodes[task.task_id] && ( +

handleCopy(task.campaign_code)} + className="campaign-code" + > + {task.campaign_code} +

+ )} +
+ + + + + + +
+ + + )) + )} +
+ setSnackbarOpen(false)} + message="Code copied to clipboard!" + /> +
+ ); +} diff --git a/src/styles/task-list.css b/src/styles/task-list.css new file mode 100644 index 0000000..4b7578c --- /dev/null +++ b/src/styles/task-list.css @@ -0,0 +1,22 @@ +.task-list__drawer { + z-index: 3; +} +.task-list__content { + margin-inline: 4%; + .task-card { + margin-bottom: 0.8rem; + + .task-card__title { + display: flex; + justify-content: space-between; + strong { + font-size: larger; + } + } + } + + .campaign-code { + color: var(--rose); + font-weight: 600; + } +} diff --git a/src/utils/copyToClipboard.js b/src/utils/copyToClipboard.js new file mode 100644 index 0000000..8194ce0 --- /dev/null +++ b/src/utils/copyToClipboard.js @@ -0,0 +1,8 @@ +export const copyToClipboard = async (text) => { + try { + await navigator.clipboard.writeText(text); + return true; + } catch (err) { + console.error("Failed to copy text: ", err); + } +}; From 10cea0e9e19eae18dc24a48fb072c465842a3eca Mon Sep 17 00:00:00 2001 From: acholyn Date: Fri, 25 Oct 2024 13:42:30 +0100 Subject: [PATCH 14/20] setting user sub as id --- src/utils/UserContext.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/utils/UserContext.jsx b/src/utils/UserContext.jsx index de551d2..28812cc 100644 --- a/src/utils/UserContext.jsx +++ b/src/utils/UserContext.jsx @@ -12,6 +12,7 @@ export const UserProvider = ({ children }) => { const [refreshToken, setRefreshToken] = useState(null); const [displayName, setDisplayName] = useState(null); const [email, setEmail] = useState(null); + const [userId, setUserId] = useState(null); const [loggedIn, setLoggedIn] = useState(false); //temp function until cognito set up @@ -24,6 +25,7 @@ export const UserProvider = ({ children }) => { const decodedIdTokenPayload = JSON.parse(atob(base64Payload)); setDisplayName(decodedIdTokenPayload["preferred_username"]); setEmail(decodedIdTokenPayload["email"]); + setUserId(decodedIdTokenPayload["sub"]); setAccessToken(userDetails.accessToken); setIdToken(userDetails.idToken); @@ -137,6 +139,7 @@ export const UserProvider = ({ children }) => { refreshToken, displayName, email, + userId, loggedIn, refresh, logout, From 7d1896e2bf30feb1e4e57630dc22a7cb02fc8809 Mon Sep 17 00:00:00 2001 From: acholyn Date: Fri, 25 Oct 2024 13:42:55 +0100 Subject: [PATCH 15/20] updating task form for new and updates + some other small functionality --- src/App.jsx | 15 ++++- src/TaskForm.jsx | 26 ++++---- src/TaskList.jsx | 128 +++++++++++++++++++++++++-------------- src/styles/task-list.css | 24 +++++++- 4 files changed, 131 insertions(+), 62 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index c9144e3..7c8fe82 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -13,14 +13,18 @@ import SearchForm from "./SearchForm"; export default function App() { const [isTaskFormVisible, setTaskFormVisible] = useState(false); const [isTaskListVisible, setTaskListVisible] = useState(false); + const [taskValues, setTaskValues] = useState(null); const [isLoginFormVisible, setLoginFormVisible] = useState(false); const [isSearchFormVisible, setSearchFormVisible] = useState(false); const user = useUserStore(); - // TODO: add mapbox background thing - + const showTaskForm = (task) => { + console.log("task exists!", task); + setTaskValues(task); + setTaskFormVisible(true); + }; return (
{!isLoginFormVisible && !user.loggedIn && ( @@ -78,11 +82,16 @@ export default function App() { - + diff --git a/src/TaskForm.jsx b/src/TaskForm.jsx index 64e31a6..6ca2e0c 100644 --- a/src/TaskForm.jsx +++ b/src/TaskForm.jsx @@ -9,7 +9,6 @@ import "./styles/forms.css"; // import * as Yup from "yup"; // these will be dynamically taken from their login and generated -const userID = "12345"; const taskID = generateTaskId(); const campaignCode = generateCampaignCode(); @@ -21,20 +20,23 @@ const campaignCode = generateCampaignCode(); // title: Yup.string().required("Title is required"), // description: Yup.string().required("Description is required"), // }); -const initialValues = { - createdBy: userID, // hidden field populated dynamically - organisation: "", - logo: null, - private: false, - visible: false, - title: "", - description: "", -}; - -export default function TaskForm({ isVisible }) { + +export default function TaskForm({ isVisible, taskValues, user }) { const [successModalVisible, setSuccessModalVisible] = useState(false); const [title, setTitle] = useState(""); const [description, setDescription] = useState(""); + var initialValues = { + createdBy: user.userId, // hidden field populated dynamically + organisation: "", + logo: null, + private: false, + visible: false, + title: "", + description: "", + }; + if (taskValues) { + initialValues = { ...taskValues }; + } if (!isVisible) return null; diff --git a/src/TaskList.jsx b/src/TaskList.jsx index 5a553c2..fedd979 100644 --- a/src/TaskList.jsx +++ b/src/TaskList.jsx @@ -5,17 +5,24 @@ import { CardActions, CardContent, CircularProgress, - Divider, Drawer, IconButton, Snackbar, } from "@mui/material"; import HighlightOffIcon from "@mui/icons-material/HighlightOff"; +import DownloadIcon from "@mui/icons-material/Download"; +import VisibilityIcon from "@mui/icons-material/Visibility"; +import VisibilityOffIcon from "@mui/icons-material/VisibilityOff"; import { REQUEST_URL } from "./globals"; import { useEffect, useState } from "react"; import "./styles/task-list.css"; import { copyToClipboard } from "./utils/copyToClipboard"; -export default function TaskList({ isVisible, setIsVisible, user }) { +export default function TaskList({ + isVisible, + setIsVisible, + user, + showTaskForm, +}) { // TODO: if given user or get user here then get all tasks created by them const [tasks, setTasks] = useState([]); const [metadataStore, setMetadataStore] = useState([]); @@ -34,6 +41,7 @@ export default function TaskList({ isVisible, setIsVisible, user }) { const result = await response.json(); const metadata = JSON.parse(result); console.log(metadata); + setMetadataStore(metadata); return metadata; } catch (error) { @@ -51,9 +59,10 @@ export default function TaskList({ isVisible, setIsVisible, user }) { const fetchedTasks = JSON.parse(result); console.log(fetchedTasks); - // for task in fetchedTasks: - // metadata = getMetadata(task.taskID) - // metadataStore.append(metadata) + // fetchedTasks.forEach((task) => { + // let metadata = getMetadata(task.taskID); + // metadataStore.append(metadata); + // }); setTasks(fetchedTasks); } catch (error) { @@ -66,7 +75,7 @@ export default function TaskList({ isVisible, setIsVisible, user }) { if (isVisible) { fetchTasks(); } - }, [isVisible]); + }, [isVisible, metadataStore]); const toggleCodeVisibility = (taskId) => { setVisibleCodes((prev) => ({ @@ -82,6 +91,17 @@ export default function TaskList({ isVisible, setIsVisible, user }) { } }; + const handleDownload = () => { + // TODO: get data from s3 + console.log("handle download"); + }; + + const handleEdit = (task) => { + console.log(task); + showTaskForm(task); + setIsVisible(false); + }; + if (!isVisible) return null; return ( @@ -94,54 +114,74 @@ export default function TaskList({ isVisible, setIsVisible, user }) {
-

My Tasks

+
+

My Tasks

+ {!isLoading && Total: {tasks.length}} +
{isLoading ? ( ) : ( tasks.map((task) => ( - <> - - - - {task.task_title}{" "} - {task.task_id} - -

{task.task_description}

- - {showMetadata && metadataStore.task_id === task.task_id && ( - // get metadata from metadataStore or fetch from dynamodb -

{metadataStore.info}

- )} + + + + {task.task_title}{" "} + {task.task_id} + +
{visibleCodes[task.task_id] && ( -

handleCopy(task.campaign_code)} className="campaign-code" > {task.campaign_code} -

+ )} - - - - - - - - - - + +
+

{task.task_description}

+ + {showMetadata && metadataStore.task_id === task.task_id && ( + // get metadata from metadataStore or fetch from dynamodb +

{metadataStore.info}

+ )} +
+ + + + + + + +
)) )}
diff --git a/src/styles/task-list.css b/src/styles/task-list.css index 4b7578c..51f7b09 100644 --- a/src/styles/task-list.css +++ b/src/styles/task-list.css @@ -1,6 +1,12 @@ .task-list__drawer { z-index: 3; } +.task-list__header { + margin-inline: 4%; + display: flex; + justify-content: space-between; + align-items: center; +} .task-list__content { margin-inline: 4%; .task-card { @@ -12,11 +18,23 @@ strong { font-size: larger; } + small { + opacity: 0.8; + } } } + .code-container { + display: flex; + justify-content: flex-end; + align-items: center; - .campaign-code { - color: var(--rose); - font-weight: 600; + .show-hide-code-btn { + float: right; + } + .campaign-code { + color: var(--rose); + font-weight: 600; + margin-right: 0.8rem; + } } } From b6af8975288e6c8e0e75e5e4fe931f35a5178661 Mon Sep 17 00:00:00 2001 From: acholyn Date: Fri, 25 Oct 2024 13:58:48 +0100 Subject: [PATCH 16/20] making close button component --- src/App.jsx | 1 + src/LoginForm.jsx | 12 +++--------- src/SignUpForm.jsx | 12 +++--------- src/TaskForm.jsx | 19 +++++++++++++++++-- src/TaskList.jsx | 11 ++--------- src/styles/utils.css | 5 +++++ src/utils/CloseButton.jsx | 16 ++++++++++++++++ 7 files changed, 47 insertions(+), 29 deletions(-) create mode 100644 src/styles/utils.css create mode 100644 src/utils/CloseButton.jsx diff --git a/src/App.jsx b/src/App.jsx index 7c8fe82..21e1b62 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -84,6 +84,7 @@ export default function App() { diff --git a/src/LoginForm.jsx b/src/LoginForm.jsx index 9bced87..cb66d8b 100644 --- a/src/LoginForm.jsx +++ b/src/LoginForm.jsx @@ -1,11 +1,11 @@ import { Formik, Form, Field, ErrorMessage } from "formik"; -import { Button, IconButton, TextField, useTheme } from "@mui/material"; -import HighlightOffIcon from "@mui/icons-material/HighlightOff"; +import { Button, TextField, useTheme } from "@mui/material"; import { useState } from "react"; import SuccessModal from "./SuccessModal"; import "./styles/forms.css"; import { initiateAuth } from "./utils/auth"; import { useUserStore } from "./globals"; +import CloseButton from "./utils/CloseButton"; // import * as Yup from "yup"; export default function LoginForm({ isVisible, setIsVisible }) { @@ -49,13 +49,7 @@ export default function LoginForm({ isVisible, setIsVisible }) { /> )}
- setIsVisible(false)} - > - - + {({ isSubmitting }) => (
diff --git a/src/SignUpForm.jsx b/src/SignUpForm.jsx index 60df930..56598e5 100644 --- a/src/SignUpForm.jsx +++ b/src/SignUpForm.jsx @@ -1,11 +1,11 @@ import { Formik, Form, Field, ErrorMessage } from "formik"; -import { Button, IconButton, TextField, useTheme } from "@mui/material"; -import HighlightOffIcon from "@mui/icons-material/HighlightOff"; +import { Button, TextField, useTheme } from "@mui/material"; import { useState } from "react"; import SuccessModal from "./SuccessModal"; import "./styles/forms.css"; import { initiateAuth } from "./utils/auth"; import { useUserStore } from "./globals"; +import CloseButton from "./utils/CloseButton"; // import * as Yup from "yup"; export default function SignUpForm({ isVisible, setIsVisible }) { @@ -40,13 +40,7 @@ export default function SignUpForm({ isVisible, setIsVisible }) { isTask={false} /> )} - setIsVisible(false)} - > - - + {({ isSubmitting }) => ( diff --git a/src/TaskForm.jsx b/src/TaskForm.jsx index 6ca2e0c..f17aab4 100644 --- a/src/TaskForm.jsx +++ b/src/TaskForm.jsx @@ -6,6 +6,7 @@ import { generateTaskId, generateCampaignCode } from "./utils/generators"; import { useState } from "react"; import SuccessModal from "./SuccessModal"; import "./styles/forms.css"; +import CloseButton from "./utils/CloseButton"; // import * as Yup from "yup"; // these will be dynamically taken from their login and generated @@ -21,7 +22,12 @@ const campaignCode = generateCampaignCode(); // description: Yup.string().required("Description is required"), // }); -export default function TaskForm({ isVisible, taskValues, user }) { +export default function TaskForm({ + isVisible, + setIsVisible, + taskValues, + user, +}) { const [successModalVisible, setSuccessModalVisible] = useState(false); const [title, setTitle] = useState(""); const [description, setDescription] = useState(""); @@ -35,7 +41,15 @@ export default function TaskForm({ isVisible, taskValues, user }) { description: "", }; if (taskValues) { - initialValues = { ...taskValues }; + initialValues = { + createdBy: taskValues.created_by, + organisation: taskValues.organisation, + logo: taskValues.logo, + private: taskValues.private, + visible: taskValues.visible, + title: taskValues.task_title, + description: taskValues.task_description, + }; } if (!isVisible) return null; @@ -93,6 +107,7 @@ export default function TaskForm({ isVisible, taskValues, user }) { > {({ isSubmitting, setFieldValue }) => ( +

Tell us about your task

{/* Hidden field for Created By */}
diff --git a/src/TaskList.jsx b/src/TaskList.jsx index fedd979..231073d 100644 --- a/src/TaskList.jsx +++ b/src/TaskList.jsx @@ -6,10 +6,8 @@ import { CardContent, CircularProgress, Drawer, - IconButton, Snackbar, } from "@mui/material"; -import HighlightOffIcon from "@mui/icons-material/HighlightOff"; import DownloadIcon from "@mui/icons-material/Download"; import VisibilityIcon from "@mui/icons-material/Visibility"; import VisibilityOffIcon from "@mui/icons-material/VisibilityOff"; @@ -17,6 +15,7 @@ import { REQUEST_URL } from "./globals"; import { useEffect, useState } from "react"; import "./styles/task-list.css"; import { copyToClipboard } from "./utils/copyToClipboard"; +import CloseButton from "./utils/CloseButton"; export default function TaskList({ isVisible, setIsVisible, @@ -106,13 +105,7 @@ export default function TaskList({ return ( - setIsVisible(false)} - > - - +

My Tasks

diff --git a/src/styles/utils.css b/src/styles/utils.css new file mode 100644 index 0000000..00d8c53 --- /dev/null +++ b/src/styles/utils.css @@ -0,0 +1,5 @@ +.close-btn__container { + display: grid; + place-content: center; + width: 100%; +} diff --git a/src/utils/CloseButton.jsx b/src/utils/CloseButton.jsx new file mode 100644 index 0000000..fb52355 --- /dev/null +++ b/src/utils/CloseButton.jsx @@ -0,0 +1,16 @@ +import HighlightOffIcon from "@mui/icons-material/HighlightOff"; +import { IconButton } from "@mui/material"; +import "../styles/utils.css"; +export default function CloseButton({ setIsVisible }) { + return ( +
+ setIsVisible(false)} + > + + +
+ ); +} From 2273697e162c10cd6d434db116b4f2a4771344d6 Mon Sep 17 00:00:00 2001 From: acholyn Date: Wed, 30 Oct 2024 12:08:41 +0000 Subject: [PATCH 17/20] updating config --- eslint.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/eslint.config.js b/eslint.config.js index df8129c..0912708 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -34,6 +34,7 @@ export default [ { allowConstantExport: true }, ], "react/prop-types": "off", + "no-unused-vars": "off", }, }, ]; From d1c44f59659ce6163a186c3b2d02621ffd4cb822 Mon Sep 17 00:00:00 2001 From: acholyn Date: Wed, 30 Oct 2024 12:08:55 +0000 Subject: [PATCH 18/20] filling out signup form structure --- src/SignUpForm.jsx | 121 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 91 insertions(+), 30 deletions(-) diff --git a/src/SignUpForm.jsx b/src/SignUpForm.jsx index 56598e5..55877c2 100644 --- a/src/SignUpForm.jsx +++ b/src/SignUpForm.jsx @@ -40,38 +40,99 @@ export default function SignUpForm({ isVisible, setIsVisible }) { isTask={false} /> )} - - - {({ isSubmitting }) => ( - - - +
+ + + {({ isSubmitting }) => ( + +
+ + + + +
+ + - - + + - {/* Submit Button */} - - - )} -
+
+ + + + +
+ + {/* Submit Button */} + + + )} + +
); } From 5193a0323d8404db6ef9f8c7fba709f73eca4dbb Mon Sep 17 00:00:00 2001 From: acholyn Date: Wed, 30 Oct 2024 12:09:12 +0000 Subject: [PATCH 19/20] updating initiate auth --- src/utils/auth.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/utils/auth.js b/src/utils/auth.js index a341d68..4401fc5 100644 --- a/src/utils/auth.js +++ b/src/utils/auth.js @@ -43,18 +43,25 @@ const signUp = (email, password, displayName) => { return client.send(command); }; -const initiateAuth = (email, password) => { +const initiateAuth = async (email, password) => { console.log("initiating log in"); const client = new CognitoIdentityProviderClient(cognito); const command = new InitiateAuthCommand({ - AuthFlow: "USER_SRP_AUTH", + AuthFlow: "USER_PASSWORD_AUTH", AuthParameters: { USERNAME: email, PASSWORD: password, }, ClientId: cognito.userPoolClientId, }); - return client.send(command); + try { + return await client.send(command); + } catch (error) { + console.error("InitiateAuth Error:", error, error.name); + if (error.name === "UserNotFoundException") { + return { response: 4359 }; + } else throw error; + } }; const initiateAuthRefresh = (refreshToken) => { From 50f003fafc546cd9f5b63ea3274e205ac1d05fdf Mon Sep 17 00:00:00 2001 From: acholyn Date: Wed, 30 Oct 2024 16:48:33 +0000 Subject: [PATCH 20/20] user login + signup flows + confirmation --- src/App.jsx | 14 ++- src/LoginForm.jsx | 40 +++++-- src/SignUpForm.jsx | 180 +++++++++++++++++++++++++++++--- src/TaskForm.jsx | 3 +- src/styles/App.css | 1 + src/styles/dialogs.css | 26 +++++ src/styles/forms.css | 33 +++++- src/utils/ConfirmationModal.jsx | 51 +++++++++ src/utils/ErrorModal.jsx | 20 ++++ src/utils/UserContext.jsx | 20 ++-- src/utils/auth.js | 97 ++++++++++++----- 11 files changed, 413 insertions(+), 72 deletions(-) create mode 100644 src/utils/ConfirmationModal.jsx create mode 100644 src/utils/ErrorModal.jsx diff --git a/src/App.jsx b/src/App.jsx index 21e1b62..96ff57a 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -9,6 +9,8 @@ import { useUserStore } from "./globals"; import TaskList from "./TaskList"; import { Map } from "./Mapbox"; import SearchForm from "./SearchForm"; +import SignUpForm from "./SignUpForm"; +import ErrorModal from "./utils/ErrorModal"; export default function App() { const [isTaskFormVisible, setTaskFormVisible] = useState(false); @@ -16,6 +18,11 @@ export default function App() { const [taskValues, setTaskValues] = useState(null); const [isLoginFormVisible, setLoginFormVisible] = useState(false); + const [signUpVisible, setSignUpVisible] = useState(false); + + // const [errorModalVisible, setErrorModalVisible] = useState(false); + const [errorMsg, setErrorMsg] = useState(""); + const [isSearchFormVisible, setSearchFormVisible] = useState(false); const user = useUserStore(); @@ -27,7 +34,8 @@ export default function App() { }; return (
- {!isLoginFormVisible && !user.loggedIn && ( + {errorMsg && } + {!isLoginFormVisible && !user.loggedIn && !signUpVisible && ( + + + + ); +} diff --git a/src/utils/ErrorModal.jsx b/src/utils/ErrorModal.jsx new file mode 100644 index 0000000..b507c55 --- /dev/null +++ b/src/utils/ErrorModal.jsx @@ -0,0 +1,20 @@ +import { useState } from "react"; +import CloseButton from "./CloseButton"; + +export default function ErrorModal({ message }) { + const [isVisible, setIsVisible] = useState(true); + if (!message) { + setIsVisible(false); + return null; + } + return ( + <> + {isVisible && ( + + +

{message}

+
+ )} + + ); +} diff --git a/src/utils/UserContext.jsx b/src/utils/UserContext.jsx index 28812cc..9ff1046 100644 --- a/src/utils/UserContext.jsx +++ b/src/utils/UserContext.jsx @@ -15,27 +15,22 @@ export const UserProvider = ({ children }) => { const [userId, setUserId] = useState(null); const [loggedIn, setLoggedIn] = useState(false); - //temp function until cognito set up - const forceLogin = () => { - setLoggedIn(true); - }; - const setUserDetails = useCallback((userDetails) => { - const base64Payload = userDetails.idToken.split(".")[1]; + const base64Payload = userDetails.IdToken.split(".")[1]; const decodedIdTokenPayload = JSON.parse(atob(base64Payload)); setDisplayName(decodedIdTokenPayload["preferred_username"]); setEmail(decodedIdTokenPayload["email"]); setUserId(decodedIdTokenPayload["sub"]); - setAccessToken(userDetails.accessToken); - setIdToken(userDetails.idToken); - setRefreshToken(userDetails.refreshToken); + setAccessToken(userDetails.AccessToken); + setIdToken(userDetails.IdToken); + setRefreshToken(userDetails.RefreshToken); setLoggedIn(true); setLocalStorage( - userDetails.idToken, - userDetails.accessToken, - userDetails.refreshToken + userDetails.IdToken, + userDetails.AccessToken, + userDetails.RefreshToken ); // the states won't have been updated in time, so use the original prop }, []); @@ -145,7 +140,6 @@ export const UserProvider = ({ children }) => { logout, setUserDetails, checkForDetails, - forceLogin, }} > {children} diff --git a/src/utils/auth.js b/src/utils/auth.js index 4401fc5..cae2822 100644 --- a/src/utils/auth.js +++ b/src/utils/auth.js @@ -3,44 +3,80 @@ import { CognitoIdentityProviderClient, InitiateAuthCommand, GlobalSignOutCommand, + ConfirmSignUpCommand, + AdminGetUserCommand, } from "@aws-sdk/client-cognito-identity-provider"; import { cognito } from "../globals"; -const validatePassword = (password) => { - const lower = "abcdefghijklmnopqrstuvwxyz"; - const upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - const digits = "0123456789"; - const special = "!@#$%^&*()"; +const signUp = async (values) => { + const client = new CognitoIdentityProviderClient(cognito); + console.log(values); - if (password.length < 8) { - // It's impossible to generate a passing password so break - return "Please enter a password of at least 8 characters"; + const command = new SignUpCommand({ + ClientId: cognito.userPoolClientId, + Username: values.email, + Password: values.password, + UserAttributes: [ + { + Name: "email", + Value: values.email, + }, + { + Name: "given_name", + Value: values.givenName, + }, + { + Name: "family_name", + Value: values.familyName, + }, + { + Name: "phone_number", + Value: values.phoneNumber, + }, + { + Name: "preferred_username", + Value: values.preferredUsername, + }, + { + Name: "custom:organisation", + Value: values.organisation, + }, + ].filter((attr) => attr.Value !== undefined && attr.Value !== null), + }); + console.log("going to sign up"); + // return client.send(command); + try { + const response = await client.send(command); + return response; + // return { + // email, + // phoneNumber, + // userSub: response.UserSub, + // codeDeliveryDetails: response.CodeDeliveryDetails + // }; + } catch (error) { + console.error("signup Error:", error, error.name); + throw error; } - - // Make sure password contains at least one character from each set - const all = (arr, fn = Boolean) => arr.every(fn); - const any = (arr, fn = Boolean) => arr.some(fn); - let charSetPresence = [ - any(lower.split("").map((item) => password.includes(item))), - any(upper.split("").map((item) => password.includes(item))), - any(digits.split("").map((item) => password.includes(item))), - any(special.split("").map((item) => password.includes(item))), - ]; - return all(charSetPresence); }; -const signUp = (email, password, displayName) => { +const confirmSignUp = async (code, recipient) => { const client = new CognitoIdentityProviderClient(cognito); - - const command = new SignUpCommand({ + const command = new ConfirmSignUpCommand({ ClientId: cognito.userPoolClientId, - Username: email, - Password: password, - UserAttributes: [{ Name: "preferredUsername", Value: displayName }], + Username: recipient, + ConfirmationCode: code, }); - return client.send(command); + try { + const response = await client.send(command); + console.log("User confirmed successfully", response); + return response; + } catch (error) { + console.error("Error confirming user", error); + throw error; + } }; const initiateAuth = async (email, password) => { @@ -55,11 +91,16 @@ const initiateAuth = async (email, password) => { ClientId: cognito.userPoolClientId, }); try { - return await client.send(command); + const response = await client.send(command); + if (response.AuthenticationResult) { + return { response: response.AuthenticationResult }; + } } catch (error) { console.error("InitiateAuth Error:", error, error.name); if (error.name === "UserNotFoundException") { return { response: 4359 }; + } else if (error.name === "UserNotConfirmedException") { + return { response: 4399 }; } else throw error; } }; @@ -85,4 +126,4 @@ const signOut = (access_token) => { return client.send(command); }; -export { signUp, validatePassword, initiateAuth, initiateAuthRefresh, signOut }; +export { signUp, confirmSignUp, initiateAuth, initiateAuthRefresh, signOut };