From ed598c31a8e0af0eda6a76343945b1163133ccc0 Mon Sep 17 00:00:00 2001
From: David Gamez <1192523+davidgamez@users.noreply.github.com>
Date: Thu, 12 Oct 2023 11:14:55 -0400
Subject: [PATCH] Feat/auth flow (#125)
---
.github/workflows/web-app.yml | 59 +-
web-app/.eslintignore | 1 -
web-app/.gitignore | 4 +-
web-app/cypress/e2e/signup.cy.ts | 66 ++
web-app/package.json | 34 +-
web-app/src/.env.rename_me | 7 +
web-app/src/.env.test | 6 +
web-app/src/app/App.tsx | 23 +-
web-app/src/app/components/AppSpinner.tsx | 19 +
web-app/src/app/components/Context.tsx | 40 +
.../{util-component => components}/Footer.css | 0
.../{util-component => components}/Footer.tsx | 0
.../{util-component => components}/Header.tsx | 13 +-
web-app/src/app/hooks/index.ts | 10 +
web-app/src/app/router/ProtectedRoute.tsx | 14 +
web-app/src/app/router/Router.tsx | 13 +-
.../app/{account-page => screens}/Account.tsx | 0
.../ContactInformation.tsx | 0
.../app/{sign-in-page => screens}/SignIn.tsx | 81 +-
.../app/{sign-up-page => screens}/SingUp.tsx | 105 ++-
web-app/src/app/services/index.ts | 1 +
web-app/src/app/services/profile-service.ts | 43 +
web-app/src/app/sign-in-page/AuthContext.tsx | 34 -
web-app/src/app/store/profile-reducer.ts | 114 +++
web-app/src/app/store/profile-selectors.ts | 20 +
web-app/src/app/store/reducers.ts | 8 +
web-app/src/app/store/saga/auth-saga.ts | 106 +++
web-app/src/app/store/saga/profile-saga.ts | 27 +
web-app/src/app/store/saga/root-saga.ts | 9 +
web-app/src/app/store/selectors.ts | 12 +
web-app/src/app/store/store.ts | 52 ++
web-app/src/app/types.ts | 48 ++
web-app/src/app/util-component/Context.tsx | 14 -
web-app/src/firebase.ts | 12 +
web-app/src/index.tsx | 6 -
web-app/src/reportWebVitals.ts | 15 -
web-app/yarn.lock | 735 +++++++++++++++++-
37 files changed, 1587 insertions(+), 164 deletions(-)
create mode 100644 web-app/cypress/e2e/signup.cy.ts
create mode 100644 web-app/src/.env.rename_me
create mode 100644 web-app/src/.env.test
create mode 100644 web-app/src/app/components/AppSpinner.tsx
create mode 100644 web-app/src/app/components/Context.tsx
rename web-app/src/app/{util-component => components}/Footer.css (100%)
rename web-app/src/app/{util-component => components}/Footer.tsx (100%)
rename web-app/src/app/{util-component => components}/Header.tsx (92%)
create mode 100644 web-app/src/app/hooks/index.ts
create mode 100644 web-app/src/app/router/ProtectedRoute.tsx
rename web-app/src/app/{account-page => screens}/Account.tsx (100%)
rename web-app/src/app/{contact-information-page => screens}/ContactInformation.tsx (100%)
rename web-app/src/app/{sign-in-page => screens}/SignIn.tsx (57%)
rename web-app/src/app/{sign-up-page => screens}/SingUp.tsx (50%)
create mode 100644 web-app/src/app/services/index.ts
create mode 100644 web-app/src/app/services/profile-service.ts
delete mode 100644 web-app/src/app/sign-in-page/AuthContext.tsx
create mode 100644 web-app/src/app/store/profile-reducer.ts
create mode 100644 web-app/src/app/store/profile-selectors.ts
create mode 100644 web-app/src/app/store/reducers.ts
create mode 100644 web-app/src/app/store/saga/auth-saga.ts
create mode 100644 web-app/src/app/store/saga/profile-saga.ts
create mode 100644 web-app/src/app/store/saga/root-saga.ts
create mode 100644 web-app/src/app/store/selectors.ts
create mode 100644 web-app/src/app/store/store.ts
create mode 100644 web-app/src/app/types.ts
delete mode 100644 web-app/src/app/util-component/Context.tsx
create mode 100644 web-app/src/firebase.ts
delete mode 100644 web-app/src/reportWebVitals.ts
diff --git a/.github/workflows/web-app.yml b/.github/workflows/web-app.yml
index 7f9292d0f..97972b347 100644
--- a/.github/workflows/web-app.yml
+++ b/.github/workflows/web-app.yml
@@ -36,9 +36,7 @@ jobs:
**/.eslintcache
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/yarn-lock.json') }}
restore-keys: |
- ${{ runner.os }}-build-${{ env.cache-name }}-
- ${{ runner.os }}-build-
- ${{ runner.os }}-
+ ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/yarn-lock.json') }}
- name: Install dependencies
working-directory: web-app
@@ -51,7 +49,7 @@ jobs:
- name: Cypress test
uses: cypress-io/github-action@v6
with:
- start: yarn start
+ start: yarn start:test
wait-on: 'npx wait-on --timeout 120000 http://127.0.0.1:3000'
working-directory: web-app
@@ -113,31 +111,52 @@ jobs:
**/.eslintcache
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/yarn-lock.json') }}
restore-keys: |
- ${{ runner.os }}-build-${{ env.cache-name }}-
- ${{ runner.os }}-build-
- ${{ runner.os }}-
+ ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/yarn-lock.json') }}
- name: Install dependencies
working-directory: web-app
run: yarn install --frozen-lockfile
- - name: Set Firebase project name
+ - name: Set Firebase project properties
working-directory: web-app
run: |
if [[ $GITHUB_EVENT_NAME == 'push' && $GITHUB_REF == 'refs/heads/main' ]]; then
echo "Setting FIREBASE_PROJECT to 'pushed to main branch'"
echo "FIREBASE_PROJECT=qa" >> $GITHUB_ENV
+ echo "REACT_APP_FIREBASE_API_KEY=${{ secrets.QA_REACT_APP_FIREBASE_API_KEY }}" >> $GITHUB_ENV
+ echo "REACT_APP_FIREBASE_AUTH_DOMAIN=${{ secrets.QA_REACT_APP_FIREBASE_AUTH_DOMAIN }}" >> $GITHUB_ENV
+ echo "REACT_APP_FIREBASE_PROJECT_ID=${{ secrets.QA_REACT_APP_FIREBASE_PROJECT_ID }}" >> $GITHUB_ENV
+ echo "REACT_APP_FIREBASE_STORAGE_BUCKET=${{ secrets.QA_REACT_APP_FIREBASE_STORAGE_BUCKET }}" >> $GITHUB_ENV
+ echo "REACT_APP_FIREBASE_MESSAGING_SENDER_ID=${{ secrets.QA_REACT_APP_FIREBASE_MESSAGING_SENDER_ID }}" >> $GITHUB_ENV
+ echo "REACT_REACT_APP_FIREBASE_APP_ID=${{ secrets.QA_REACT_APP_FIREBASE_APP_ID }}" >> $GITHUB_ENV
elif [[ $GITHUB_EVENT_NAME == 'release' ]]; then
echo "Setting FIREBASE_PROJECT to 'release'"
echo "FIREBASE_PROJECT=prod" >> $GITHUB_ENV
+ echo "REACT_APP_FIREBASE_API_KEY=${{ secrets.PROD_REACT_APP_FIREBASE_API_KEY }}" >> $GITHUB_ENV
+ echo "REACT_APP_FIREBASE_AUTH_DOMAIN=${{ secrets.PROD_REACT_APP_FIREBASE_AUTH_DOMAIN }}" >> $GITHUB_ENV
+ echo "REACT_APP_FIREBASE_PROJECT_ID=${{ secrets.PROD_REACT_APP_FIREBASE_PROJECT_ID }}" >> $GITHUB_ENV
+ echo "REACT_APP_FIREBASE_STORAGE_BUCKET=${{ secrets.PROD_REACT_APP_FIREBASE_STORAGE_BUCKET }}" >> $GITHUB_ENV
+ echo "REACT_APP_FIREBASE_MESSAGING_SENDER_ID=${{ secrets.PROD_REACT_APP_FIREBASE_MESSAGING_SENDER_ID }}" >> $GITHUB_ENV
+ echo "REACT_REACT_APP_FIREBASE_APP_ID=${{ secrets.PROD_REACT_APP_FIREBASE_APP_ID }}" >> $GITHUB_ENV
else
echo "Setting FIREBASE_PROJECT to 'dev'"
echo "FIREBASE_PROJECT=dev" >> $GITHUB_ENV
+ echo "REACT_APP_FIREBASE_API_KEY=${{ secrets.DEV_REACT_APP_FIREBASE_API_KEY }}" >> $GITHUB_ENV
+ echo "REACT_APP_FIREBASE_AUTH_DOMAIN=${{ secrets.DEV_REACT_APP_FIREBASE_AUTH_DOMAIN }}" >> $GITHUB_ENV
+ echo "REACT_APP_FIREBASE_PROJECT_ID=${{ secrets.DEV_REACT_APP_FIREBASE_PROJECT_ID }}" >> $GITHUB_ENV
+ echo "REACT_APP_FIREBASE_STORAGE_BUCKET=${{ secrets.DEV_REACT_APP_FIREBASE_STORAGE_BUCKET }}" >> $GITHUB_ENV
+ echo "REACT_APP_FIREBASE_MESSAGING_SENDER_ID=${{ secrets.DEV_REACT_APP_FIREBASE_MESSAGING_SENDER_ID }}" >> $GITHUB_ENV
+ echo "REACT_REACT_APP_FIREBASE_APP_ID=${{ secrets.DEV_REACT_APP_FIREBASE_APP_ID }}" >> $GITHUB_ENV
fi
+ - name: Populate Variables
+ working-directory: web-app
+ run: |
+ ../scripts/replace-variables.sh -in_file src/.env.rename_me -out_file src/.env.${{ env.FIREBASE_PROJECT }} -variables REACT_APP_FIREBASE_API_KEY,REACT_APP_FIREBASE_AUTH_DOMAIN,REACT_APP_FIREBASE_PROJECT_ID,REACT_APP_FIREBASE_STORAGE_BUCKET,REACT_APP_FIREBASE_MESSAGING_SENDER_ID,REACT_REACT_APP_FIREBASE_APP_ID
+
- name: Build
working-directory: web-app
- run: yarn build
+ run: yarn build:${FIREBASE_PROJECT}
- name: Select Firebase Project
working-directory: web-app
@@ -155,8 +174,28 @@ jobs:
env:
PR_ID: ${{ github.event.number }}
+ - name: Check for Existing Comment
+ id: check-comment
+ working-directory: web-app
+ if: ${{ github.event_name == 'pull_request' }}
+ run: |
+ HOSTING_URL=$(npx firebase hosting:channel:list | grep "pr-${{ env.PR_ID }}" | awk '{print $7}')
+ COMMENT="Preview Firebase Hosting URL: $HOSTING_URL"
+ COMMENTS=$(curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/${{ github.repository }}/issues/${{ env.PR_ID }}/comments")
+
+ JQ_CHECK=`echo "$COMMENTS" | jq -r ".[] | select(.body == \"$COMMENT\")"`
+ if [ -z "$JQ_CHECK" ]; then
+ echo "Comment does not exist."
+ echo "comment_exists=false" >> $GITHUB_OUTPUT
+ else
+ echo "Comment already exists."
+ echo "comment_exists=true" >> $GITHUB_OUTPUT
+ fi
+ env:
+ PR_ID: ${{ github.event.number }}
+
- name: Comment on PR with Hosting URL (PR Preview)
- if: ${{ github.event_name == 'pull_request' && github.event.action == 'opened' }}
+ if: ${{ github.event_name == 'pull_request' && steps.check-comment.outputs.comment_exists == 'false' }}
working-directory: web-app
run: |
HOSTING_URL=$(npx firebase hosting:channel:list | grep "pr-${{ env.PR_ID }}" | awk '{print $7}')
diff --git a/web-app/.eslintignore b/web-app/.eslintignore
index 957734943..3e2e84b08 100644
--- a/web-app/.eslintignore
+++ b/web-app/.eslintignore
@@ -1,3 +1,2 @@
build/
node_modules/
-src/reportWebVitals.ts
diff --git a/web-app/.gitignore b/web-app/.gitignore
index 83b6a020a..172d43e8a 100644
--- a/web-app/.gitignore
+++ b/web-app/.gitignore
@@ -26,4 +26,6 @@ yarn-error.log*
# Cypress
cypress/screenshots
-cypress/videos
\ No newline at end of file
+cypress/videos
+
+.env.*
\ No newline at end of file
diff --git a/web-app/cypress/e2e/signup.cy.ts b/web-app/cypress/e2e/signup.cy.ts
new file mode 100644
index 000000000..89dccc33a
--- /dev/null
+++ b/web-app/cypress/e2e/signup.cy.ts
@@ -0,0 +1,66 @@
+import { passwordValidatioError } from '../../src/app/types';
+
+describe('Sign up screen', () => {
+ beforeEach(() => {
+ cy.visit('/sign-up');
+ });
+
+ it('should render components', () => {
+ cy.get('input[id="email"]').should('exist');
+ cy.get('input[id="password"]').should('exist');
+ cy.get('input[id="confirmPassword"]').should('exist');
+ cy.get('button[id="sign-up-button"]').should('exist');
+ });
+
+ it('should show the password error when password length is less than 12', () => {
+ cy.get('input[id="password"]')
+ .should('exist')
+ .type('short', { force: true });
+
+ cy.get('[data-testid=passwordError]')
+ .should('exist')
+ .contains(passwordValidatioError);
+ });
+
+ it('should show the password error when password do not contain lowercase', () => {
+ cy.get('input[id="password"]')
+ .should('exist')
+ .type('UPPERCASE_10_!', { force: true });
+
+ cy.get('[data-testid=passwordError]')
+ .should('exist')
+ .contains(passwordValidatioError);
+ });
+
+ it('should show the password error when password do not contain uppercase', () => {
+ cy.get('input[id="password"]')
+ .should('exist')
+ .type('lowercase_10_!', { force: true });
+
+ cy.get('[data-testid=passwordError]')
+ .should('exist')
+ .contains(passwordValidatioError);
+ });
+
+ it('should not show the password error when password is valid', () => {
+ cy.get('input[id="password"]')
+ .should('exist')
+ .type('UP_lowercase_10_!', { force: true });
+
+ cy.get('[data-testid=passwordError]').should('not.exist');
+ });
+
+ it('should show the password error when password do not match', () => {
+ cy.get('input[id="password"]')
+ .should('exist')
+ .type('UP_lowercase_10_!', { force: true });
+
+ cy.get('input[id="confirmPassword"]')
+ .should('exist')
+ .type('UP_lowercase_11_!', { force: true });
+
+ cy.get('[data-testid=confirmPasswordError]')
+ .should('exist')
+ .contains('Passwords do not match');
+ });
+});
diff --git a/web-app/package.json b/web-app/package.json
index 9f97e3311..0d2967a24 100644
--- a/web-app/package.json
+++ b/web-app/package.json
@@ -7,31 +7,36 @@
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.14.9",
"@mui/material": "^5.14.9",
- "@types/jest": "^27.5.2",
- "@types/material-ui": "^0.21.12",
- "@types/node": "^16.18.50",
- "@types/react": "^18.2.21",
- "@types/react-dom": "^18.2.7",
+ "@reduxjs/toolkit": "^1.9.6",
+ "firebase": "^10.4.0",
+ "formik": "^2.4.5",
"react": "^17.0.0 || ^18.0.0",
"react-dom": "^17.0.0 || ^18.0.0",
+ "react-loading-overlay-ts": "^2.0.2",
+ "react-redux": "^8.1.3",
"react-router-dom": "^6.16.0",
"react-scripts": "5.0.1",
+ "redux-persist": "^6.0.0",
+ "redux-saga": "^1.2.3",
"typeface-muli": "^1.1.13",
- "web-vitals": "^2.1.4"
+ "yup": "^1.3.2"
},
"peerDependencies": {
"react": "^17.0.0 || ^18.0.0",
"react-dom": "^17.0.0 || ^18.0.0"
},
"scripts": {
- "start": "DISABLE_ESLINT_PLUGIN=true react-scripts start",
- "build": "CI=false react-scripts build",
+ "start:dev": "env-cmd -f src/.env.dev react-scripts start",
+ "start:test": "env-cmd -f src/.env.test react-scripts start",
+ "build:dev": "CI=false env-cmd -f src/.env.dev react-scripts build",
+ "build:qa": "CI=false env-cmd -f src/.env.qa react-scripts build",
+ "start:prod": "env-cmd -f src/.env.prod react-scripts start",
"test": "react-scripts test",
"eject": "react-scripts eject",
"lint": "eslint 'src/app/**/*.{js,ts,tsx}'",
"lint:fix": "eslint 'src/app/**/*.{js,ts,tsx}' --fix",
- "cypress-run": "cypress run",
- "cypress-open": "cypress open"
+ "cypress:run": "cypress run",
+ "cypress:open": "cypress open"
},
"eslintConfig": {
"extends": [
@@ -53,14 +58,23 @@
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
+ "@react-firebase/auth": "^0.2.10",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/cypress": "^1.1.3",
+ "@types/jest": "^27.5.2",
+ "@types/material-ui": "^0.21.12",
+ "@types/node": "^16.18.50",
+ "@types/react": "^18.2.25",
+ "@types/react-dom": "^18.2.7",
+ "@types/react-redux": "^7.1.27",
"@types/react-router-dom": "^5.3.3",
+ "@types/redux-saga": "^0.10.5",
"@typescript-eslint/eslint-plugin": "^6.7.0",
"@typescript-eslint/parser": "^6.7.0",
"cypress": "^13.2.0",
+ "env-cmd": "^10.1.0",
"eslint": "^8.49.0",
"eslint-config-prettier": "^9.0.0",
"eslint-config-standard-with-typescript": "^39.0.0",
diff --git a/web-app/src/.env.rename_me b/web-app/src/.env.rename_me
new file mode 100644
index 000000000..3de040313
--- /dev/null
+++ b/web-app/src/.env.rename_me
@@ -0,0 +1,7 @@
+DISABLE_ESLINT_PLUGIN=true
+REACT_APP_FIREBASE_API_KEY={{REACT_APP_FIREBASE_API_KEY}}
+REACT_APP_FIREBASE_AUTH_DOMAIN={{REACT_APP_FIREBASE_AUTH_DOMAIN}}
+REACT_APP_FIREBASE_PROJECT_ID={{REACT_APP_FIREBASE_PROJECT_ID}}
+REACT_APP_FIREBASE_STORAGE_BUCKET={{REACT_APP_FIREBASE_STORAGE_BUCKET}}
+REACT_APP_FIREBASE_MESSAGING_SENDER_ID={{REACT_APP_FIREBASE_MESSAGING_SENDER_ID}}
+REACT_APP_FIREBASE_APP_ID={{REACT_APP_FIREBASE_APP_ID}}
\ No newline at end of file
diff --git a/web-app/src/.env.test b/web-app/src/.env.test
new file mode 100644
index 000000000..1567b781f
--- /dev/null
+++ b/web-app/src/.env.test
@@ -0,0 +1,6 @@
+DISABLE_ESLINT_PLUGIN=true
+REACT_APP_FIREBASE_API_KEY="REACT_APP_FIREBASE_API_KEY"
+REACT_APP_FIREBASE_PROJECT_ID="REACT_APP_FIREBASE_PROJECT_ID"
+REACT_APP_FIREBASE_STORAGE_BUCKET="REACT_APP_FIREBASE_STORAGE_BUCKET"
+REACT_APP_FIREBASE_MESSAGING_SENDER_ID="REACT_APP_FIREBASE_MESSAGING_SENDER_ID"
+REACT_APP_FIREBASE_APP_ID="REACT_APP_FIREBASE_APP_ID"
\ No newline at end of file
diff --git a/web-app/src/app/App.tsx b/web-app/src/app/App.tsx
index 0641e6abe..e005efeac 100644
--- a/web-app/src/app/App.tsx
+++ b/web-app/src/app/App.tsx
@@ -1,21 +1,24 @@
import './App.css';
import AppRouter from './router/Router';
-import ContextProviders from './util-component/Context';
-import Footer from './util-component/Footer';
-import Header from './util-component/Header';
+import ContextProviders from './components/Context';
+import Footer from './components/Footer';
+import Header from './components/Header';
import { BrowserRouter } from 'react-router-dom';
+import AppSpinner from './components/AppSpinner';
function App(): React.ReactElement {
require('typeface-muli'); // Load font
return (
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
);
}
diff --git a/web-app/src/app/components/AppSpinner.tsx b/web-app/src/app/components/AppSpinner.tsx
new file mode 100644
index 000000000..09b698010
--- /dev/null
+++ b/web-app/src/app/components/AppSpinner.tsx
@@ -0,0 +1,19 @@
+import * as React from 'react';
+import type ContextProviderProps from '../interface/ContextProviderProps';
+import LoadingOverlay from 'react-loading-overlay-ts';
+import { useSelector } from 'react-redux';
+import { selectLoadingApp } from '../store/selectors';
+
+/**
+ * This adds a spinner to the entire application
+ */
+const AppSpinner: React.FC = ({ children }) => {
+ const isActive = useSelector(selectLoadingApp);
+ return (
+
+ {children}
+
+ );
+};
+
+export default AppSpinner;
diff --git a/web-app/src/app/components/Context.tsx b/web-app/src/app/components/Context.tsx
new file mode 100644
index 000000000..ac0f7bf2b
--- /dev/null
+++ b/web-app/src/app/components/Context.tsx
@@ -0,0 +1,40 @@
+import React, { useEffect } from 'react';
+import type ContextProviderProps from '../interface/ContextProviderProps';
+import { Provider } from 'react-redux';
+import { store } from '../store/store';
+import { PersistGate } from 'redux-persist/integration/react';
+import { persistStore } from 'redux-persist';
+import { useAppDispatch } from '../hooks';
+import { resetProfileErrors } from '../store/profile-reducer';
+
+const persistor = persistStore(store);
+/**
+ * This component is used to wrap the entire application
+ */
+const AppContent: React.FC = ({ children }) => {
+ const dispatch = useAppDispatch();
+
+ useEffect(() => {
+ // This function will run when the component is first loaded
+ // Clean errros from previous session
+ dispatch(resetProfileErrors());
+ }, []);
+ return (
+
+ {children}
+
+ );
+};
+
+/**
+ * This component is used to wrap the entire application adding the store provider and reseting the errors from previous session
+ */
+const ContextProviders: React.FC = ({ children }) => {
+ return (
+
+ {children}
+
+ );
+};
+
+export default ContextProviders;
diff --git a/web-app/src/app/util-component/Footer.css b/web-app/src/app/components/Footer.css
similarity index 100%
rename from web-app/src/app/util-component/Footer.css
rename to web-app/src/app/components/Footer.css
diff --git a/web-app/src/app/util-component/Footer.tsx b/web-app/src/app/components/Footer.tsx
similarity index 100%
rename from web-app/src/app/util-component/Footer.tsx
rename to web-app/src/app/components/Footer.tsx
diff --git a/web-app/src/app/util-component/Header.tsx b/web-app/src/app/components/Header.tsx
similarity index 92%
rename from web-app/src/app/util-component/Header.tsx
rename to web-app/src/app/components/Header.tsx
index 9b89b9902..7ddfa1f87 100644
--- a/web-app/src/app/util-component/Header.tsx
+++ b/web-app/src/app/components/Header.tsx
@@ -15,7 +15,6 @@ import {
Button,
} from '@mui/material';
import MenuIcon from '@mui/icons-material/Menu';
-import { useAuth } from '../sign-in-page/AuthContext';
import {
type NavigationHandler,
SIGN_IN_TARGET,
@@ -29,6 +28,10 @@ import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogContentText from '@mui/material/DialogContentText';
import DialogTitle from '@mui/material/DialogTitle';
+import { useSelector } from 'react-redux';
+import { logout } from '../store/profile-reducer';
+import { useAppDispatch } from '../hooks';
+import { selectIsAuthenticated } from '../store/selectors';
const drawerWidth = 240;
const websiteTile = 'Mobility Database';
@@ -37,7 +40,7 @@ const DrawerContent: React.FC<{
onClick: React.MouseEventHandler;
onNavigationClick: NavigationHandler;
}> = ({ onClick, onNavigationClick }) => {
- const { isAuthenticated } = useAuth();
+ const isAuthenticated = useSelector(selectIsAuthenticated);
return (
@@ -68,7 +71,8 @@ export default function DrawerAppBar(): React.ReactElement {
const [openDialog, setOpenDialog] = React.useState(false);
const navigateTo = useNavigate();
- const { isAuthenticated, logout } = useAuth();
+ const isAuthenticated = useSelector(selectIsAuthenticated);
+ const dispatch = useAppDispatch();
const handleDrawerToggle = (): void => {
setMobileOpen((prevState) => !prevState);
@@ -83,8 +87,7 @@ export default function DrawerAppBar(): React.ReactElement {
};
const confirmLogout = (): void => {
- logout();
- navigateTo(SIGN_IN_TARGET);
+ dispatch(logout({ redirectScreen: SIGN_IN_TARGET, navigateTo }));
setOpenDialog(false);
};
diff --git a/web-app/src/app/hooks/index.ts b/web-app/src/app/hooks/index.ts
new file mode 100644
index 000000000..7b7937824
--- /dev/null
+++ b/web-app/src/app/hooks/index.ts
@@ -0,0 +1,10 @@
+import {
+ type TypedUseSelectorHook,
+ useDispatch,
+ useSelector,
+} from 'react-redux';
+import { type RootState, type AppDispatch } from '../store/store';
+
+// Use throughout your app instead of plain `useDispatch` and `useSelector`
+export const useAppDispatch = (): AppDispatch => useDispatch();
+export const useAppSelector: TypedUseSelectorHook = useSelector;
diff --git a/web-app/src/app/router/ProtectedRoute.tsx b/web-app/src/app/router/ProtectedRoute.tsx
new file mode 100644
index 000000000..3763fdf10
--- /dev/null
+++ b/web-app/src/app/router/ProtectedRoute.tsx
@@ -0,0 +1,14 @@
+import { Navigate, Outlet } from 'react-router-dom';
+import { useSelector } from 'react-redux';
+import { selectIsAuthenticated } from '../store/selectors';
+
+/**
+ * This component is used to protect routes that require authentication.
+ */
+export const ProtectedRoute = (): JSX.Element => {
+ const isAuthenticated = useSelector(selectIsAuthenticated);
+ if (!isAuthenticated) {
+ return ;
+ }
+ return ;
+};
diff --git a/web-app/src/app/router/Router.tsx b/web-app/src/app/router/Router.tsx
index 21d1cac3d..d95a754a1 100644
--- a/web-app/src/app/router/Router.tsx
+++ b/web-app/src/app/router/Router.tsx
@@ -1,9 +1,10 @@
import React from 'react';
import { Routes, Route } from 'react-router-dom';
-import SignIn from '../sign-in-page/SignIn';
-import SignUp from '../sign-up-page/SingUp';
-import Account from '../account-page/Account';
-import ContactInformation from '../contact-information-page/ContactInformation';
+import SignIn from '../screens/SignIn';
+import SignUp from '../screens/SingUp';
+import Account from '../screens/Account';
+import ContactInformation from '../screens/ContactInformation';
+import { ProtectedRoute } from './ProtectedRoute';
export const AppRouter: React.FC = () => {
return (
@@ -11,7 +12,9 @@ export const AppRouter: React.FC = () => {
} />
} />
} />
- } />
+ }>
+ } />
+
} />
);
diff --git a/web-app/src/app/account-page/Account.tsx b/web-app/src/app/screens/Account.tsx
similarity index 100%
rename from web-app/src/app/account-page/Account.tsx
rename to web-app/src/app/screens/Account.tsx
diff --git a/web-app/src/app/contact-information-page/ContactInformation.tsx b/web-app/src/app/screens/ContactInformation.tsx
similarity index 100%
rename from web-app/src/app/contact-information-page/ContactInformation.tsx
rename to web-app/src/app/screens/ContactInformation.tsx
diff --git a/web-app/src/app/sign-in-page/SignIn.tsx b/web-app/src/app/screens/SignIn.tsx
similarity index 57%
rename from web-app/src/app/sign-in-page/SignIn.tsx
rename to web-app/src/app/screens/SignIn.tsx
index 61c3d9f5f..361b5bdb8 100644
--- a/web-app/src/app/sign-in-page/SignIn.tsx
+++ b/web-app/src/app/screens/SignIn.tsx
@@ -2,8 +2,6 @@ import * as React from 'react';
import Button from '@mui/material/Button';
import CssBaseline from '@mui/material/CssBaseline';
import TextField from '@mui/material/TextField';
-import FormControlLabel from '@mui/material/FormControlLabel';
-import Checkbox from '@mui/material/Checkbox';
import Link from '@mui/material/Link';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
@@ -11,21 +9,52 @@ import Container from '@mui/material/Container';
import GoogleIcon from '@mui/icons-material/Google';
import GitHubIcon from '@mui/icons-material/GitHub';
import { useNavigate } from 'react-router-dom';
-import { useAuth } from './AuthContext';
+import { useAppDispatch } from '../hooks';
+import { login } from '../store/profile-reducer';
+import { type EmailLogin } from '../types';
+import { useSelector } from 'react-redux';
+import { useFormik } from 'formik';
+import * as Yup from 'yup';
+import { Alert } from '@mui/material';
+import {
+ selectEmailLoginError,
+ selectIsAuthenticated,
+} from '../store/selectors';
export default function SignIn(): React.ReactElement {
+ const dispatch = useAppDispatch();
const navigateTo = useNavigate();
- const handleSubmit = (event: React.FormEvent): void => {
- event.preventDefault();
- const data = new FormData(event.currentTarget);
- console.log({
- email: data.get('email'),
- password: data.get('password'),
- });
- navigateTo('/account');
- };
+ const isAuthenticated = useSelector(selectIsAuthenticated);
+ const emailLoginError = useSelector(selectEmailLoginError);
- const { login } = useAuth();
+ const SignInSchema = Yup.object().shape({
+ email: Yup.string().email().required('Email is required'),
+
+ password: Yup.string()
+ .required('Password is required')
+ .min(12, 'Password is too short - should be 12 chars minimum'),
+ });
+
+ const formik = useFormik({
+ initialValues: {
+ email: '',
+ password: '',
+ },
+ validationSchema: SignInSchema,
+ onSubmit: (values) => {
+ const emailLogin: EmailLogin = {
+ email: values.email,
+ password: values.password,
+ };
+ dispatch(login(emailLogin));
+ },
+ });
+
+ React.useEffect(() => {
+ if (isAuthenticated) {
+ navigateTo('/account');
+ }
+ }, [isAuthenticated]);
return (
@@ -51,7 +80,7 @@ export default function SignIn(): React.ReactElement {
+ {formik.errors.email != null ? (
+ {formik.errors.email}
+ ) : null}
- {formik.errors.password}
+ ) : null}
+ {/* TODO: Add remember me functionality
+ }
label='Remember me'
sx={{ width: '100%' }}
- />
+ /> */}
+ {emailLoginError != null ? (
+ {emailLoginError.message}
+ ) : null}
): void => {
- event.preventDefault();
- const data = new FormData(event.currentTarget);
- console.log(data);
- };
-
const navigateTo = useNavigate();
+ const dispatch = useAppDispatch();
+ const signUpError = useSelector(selectSignUpError);
+ const SignUpSchema = Yup.object().shape({
+ email: Yup.string().email().required('Email is required'),
+ password: Yup.string()
+ .required('Password is required')
+ .matches(
+ /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[.;!@#$%^&*])(?=.{12,})/,
+ passwordValidatioError,
+ ),
+ confirmPassword: Yup.string().oneOf(
+ [Yup.ref('password'), ''],
+ 'Passwords do not match',
+ ),
+ });
+
+ const formik = useFormik({
+ initialValues: {
+ email: '',
+ password: '',
+ confirmPassword: '',
+ },
+ validationSchema: SignUpSchema,
+ onSubmit: (values) => {
+ dispatch(
+ signUp({
+ email: values.email,
+ password: values.password,
+ redirectScreen: '/account',
+ navigateTo,
+ }),
+ );
+ },
+ });
return (
@@ -42,7 +78,17 @@ export default function SignUp(): React.ReactElement {
Already have an account? Sign In Here.
-
+
+ {formik.errors.email != null ? (
+ {formik.errors.email}
+ ) : null}
+ {formik.errors.password != null ? (
+
+ {formik.errors.password}
+
+ ) : null}
+ {formik.errors.confirmPassword != null ? (
+
+ {formik.errors.confirmPassword}
+
+ ) : null}
}
label='I agree to the terms and conditions'
+ sx={{ width: '100%' }}
/>
+
+ {signUpError != null ? (
+ {signUpError.message}
+ ) : null}
-
=> {
+ try {
+ const user = app.auth().currentUser;
+ if (user !== null && !user.emailVerified) {
+ await user.sendEmailVerification();
+ }
+ } catch (error) {
+ // Nothing to do if the email verification fails
+ // eslint-disable-next-line no-console
+ console.log(error);
+ }
+};
+
+/**
+ * Return the current user or null if the user is not logged in.
+ */
+export const getUserFromSession = (): User | null => {
+ const currentUser = app.auth().currentUser;
+ if (currentUser === null) {
+ return null;
+ }
+ return {
+ fullname: currentUser?.displayName ?? undefined,
+ email: currentUser?.email ?? '',
+ // Organization cannot be retrieved from the current user
+ organization: undefined,
+ };
+};
+
+export const getUserOrganization = async (): Promise => {
+ throw new Error('Not implemented');
+};
+
+export const saveOrganization = async (): Promise => {
+ throw new Error('Not implemented');
+};
diff --git a/web-app/src/app/sign-in-page/AuthContext.tsx b/web-app/src/app/sign-in-page/AuthContext.tsx
deleted file mode 100644
index 6749ee8e1..000000000
--- a/web-app/src/app/sign-in-page/AuthContext.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import React, { useState, useContext } from 'react';
-import type ContextProviderProps from '../interface/ContextProviderProps';
-
-interface AuthContextProps {
- isAuthenticated: boolean;
- login: () => void;
- logout: () => void;
-}
-
-const AuthContext = React.createContext(
- undefined,
-);
-
-export const useAuth = (): AuthContextProps => {
- return useContext(AuthContext) as AuthContextProps;
-};
-
-export const AuthProvider: React.FC = ({ children }) => {
- const [isAuthenticated, setIsAuthenticated] = useState(false);
-
- const login = (): void => {
- setIsAuthenticated(true);
- };
-
- const logout = (): void => {
- setIsAuthenticated(false);
- };
-
- return (
-
- {children}
-
- );
-};
diff --git a/web-app/src/app/store/profile-reducer.ts b/web-app/src/app/store/profile-reducer.ts
new file mode 100644
index 000000000..efad7b7c3
--- /dev/null
+++ b/web-app/src/app/store/profile-reducer.ts
@@ -0,0 +1,114 @@
+import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
+import {
+ type AppError,
+ type EmailLogin,
+ type AppErrors,
+ type User,
+} from '../types';
+import { type NavigateFunction } from 'react-router-dom';
+
+interface UserProfileState {
+ status:
+ | 'unauthenticated'
+ | 'login_in'
+ | 'authenticated'
+ | 'login_out'
+ | 'sign_up'
+ | 'loading_organization';
+ errors: AppErrors;
+ user: User | undefined;
+}
+
+const initialState: UserProfileState = {
+ status: 'unauthenticated',
+ user: undefined,
+ errors: {
+ SignUp: null,
+ Login: null,
+ Logout: null,
+ },
+};
+
+export const userProfileSlice = createSlice({
+ name: 'userProfile',
+ initialState,
+ reducers: {
+ login: (state, action: PayloadAction) => {
+ state.status = 'login_in';
+ state.errors = { ...initialState.errors };
+ },
+ loginSuccess: (state, action: PayloadAction) => {
+ state.status = 'authenticated';
+ state.errors = { ...initialState.errors };
+ state.user = action.payload;
+ },
+ loginFail: (state, action: PayloadAction) => {
+ state.errors = { ...initialState.errors, Login: action.payload };
+ state.status = 'unauthenticated';
+ },
+ logout: (
+ state,
+ action: PayloadAction<{
+ redirectScreen: string;
+ navigateTo: NavigateFunction;
+ }>,
+ ) => {
+ state.status = 'login_out';
+ state.errors = { ...initialState.errors };
+ },
+ logoutSucess: (state) => {
+ state.status = 'unauthenticated';
+ },
+ logoutFail: (state) => {
+ state.status = 'unauthenticated';
+ },
+ signUp: (
+ state,
+ action: PayloadAction<{
+ email: string;
+ password: string;
+ redirectScreen: string;
+ navigateTo: NavigateFunction;
+ }>,
+ ) => {
+ state.status = 'sign_up';
+ state.errors = { ...initialState.errors };
+ },
+ signUpSuccess: (state, action: PayloadAction) => {
+ state.status = 'authenticated';
+ state.user = action.payload;
+ state.errors = { ...initialState.errors };
+ },
+ signUpFail: (state, action: PayloadAction) => {
+ state.status = 'unauthenticated';
+ state.errors = { ...initialState.errors, SignUp: action.payload };
+ },
+ loadOrganization: (state) => {
+ state.status = 'loading_organization';
+ },
+ loadOrganizationSuccess: (state, action: PayloadAction) => {
+ state.status = 'authenticated';
+ if (state.user !== undefined) {
+ state.user.organization = action.payload;
+ }
+ },
+ resetProfileErrors: (state) => {
+ state.errors = { ...initialState.errors };
+ },
+ },
+});
+
+export const {
+ login,
+ loginSuccess,
+ loginFail,
+ logout,
+ logoutSucess,
+ logoutFail,
+ signUp,
+ signUpSuccess,
+ signUpFail,
+ resetProfileErrors,
+} = userProfileSlice.actions;
+
+export default userProfileSlice.reducer;
diff --git a/web-app/src/app/store/profile-selectors.ts b/web-app/src/app/store/profile-selectors.ts
new file mode 100644
index 000000000..ae05bab91
--- /dev/null
+++ b/web-app/src/app/store/profile-selectors.ts
@@ -0,0 +1,20 @@
+import { type AppError, ErrorSource, type User } from '../types';
+import { type RootState } from './store';
+
+export const selectUserProfile = (state: RootState): User | undefined =>
+ state.userProfile.user;
+
+export const selectIsAuthenticated = (state: RootState): boolean =>
+ state.userProfile.status === 'authenticated' ||
+ state.userProfile.status === 'loading_organization';
+
+export const selectErrorBySource = (
+ state: RootState,
+ source: ErrorSource,
+): AppError | null => state.userProfile.errors[source];
+
+export const selectEmailLoginError = (state: RootState): AppError | null =>
+ selectErrorBySource(state, ErrorSource.Login);
+
+export const selectSignUpError = (state: RootState): AppError | null =>
+ selectErrorBySource(state, ErrorSource.SignUp);
diff --git a/web-app/src/app/store/reducers.ts b/web-app/src/app/store/reducers.ts
new file mode 100644
index 000000000..14e29e3c0
--- /dev/null
+++ b/web-app/src/app/store/reducers.ts
@@ -0,0 +1,8 @@
+import { combineReducers } from 'redux';
+import profileReducer from './profile-reducer';
+
+const rootReducer = combineReducers({
+ userProfile: profileReducer,
+});
+
+export default rootReducer;
diff --git a/web-app/src/app/store/saga/auth-saga.ts b/web-app/src/app/store/saga/auth-saga.ts
new file mode 100644
index 000000000..5a7413f81
--- /dev/null
+++ b/web-app/src/app/store/saga/auth-saga.ts
@@ -0,0 +1,106 @@
+import { app } from '../../../firebase';
+import { type PayloadAction } from '@reduxjs/toolkit';
+import { call, put, takeLatest } from 'redux-saga/effects';
+import {
+ type AppError,
+ USER_PROFILE_LOGIN,
+ USER_PROFILE_LOGOUT,
+ USER_PROFILE_SIGNUP,
+ type User,
+ USER_PROFILE_SIGNUP_SUCCESS,
+} from '../../types';
+import 'firebase/compat/auth';
+import {
+ loginFail,
+ loginSuccess,
+ logoutSucess,
+ signUpFail,
+ signUpSuccess,
+} from '../profile-reducer';
+import { FirebaseError } from '@firebase/util';
+import { type NavigateFunction } from 'react-router-dom';
+import { getUserFromSession, sendEmailVerification } from '../../services';
+
+const getAppError = (error: unknown): AppError => {
+ const appError: AppError = {
+ code: 'unknown',
+ message: 'Unknown error',
+ };
+ if (error instanceof FirebaseError) {
+ appError.code = error.code;
+ let message = error.message;
+ if (error.message.startsWith('Firebase: ')) {
+ message = error.message.substring('Firebase: '.length);
+ }
+ appError.message = message;
+ } else {
+ appError.message = error as string;
+ }
+ return appError;
+};
+
+function* emailLoginSaga({
+ payload: { email, password },
+}: PayloadAction<{ email: string; password: string }>): Generator<
+ unknown,
+ void,
+ User
+> {
+ try {
+ yield app.auth().signInWithEmailAndPassword(email, password);
+ const user = yield call(getUserFromSession);
+ yield put(loginSuccess(user));
+ } catch (error) {
+ yield put(loginFail(getAppError(error)));
+ }
+}
+
+function* logoutSaga({
+ payload: { redirectScreen, navigateTo },
+}: PayloadAction<{
+ redirectScreen: string;
+ navigateTo: NavigateFunction;
+}>): Generator {
+ try {
+ yield app.auth().signOut();
+ yield put(logoutSucess());
+ navigateTo(redirectScreen);
+ } catch (error) {
+ yield put(loginFail(getAppError(error)));
+ }
+}
+
+function* signUpSaga({
+ payload: { email, password, redirectScreen, navigateTo },
+}: PayloadAction<{
+ email: string;
+ password: string;
+ redirectScreen: string;
+ navigateTo: NavigateFunction;
+}>): Generator {
+ try {
+ yield app.auth().createUserWithEmailAndPassword(email, password);
+ const user = getUserFromSession();
+ if (user === null) {
+ throw new Error('User not found');
+ }
+ yield put(signUpSuccess(user));
+ navigateTo(redirectScreen);
+ } catch (error) {
+ yield put(signUpFail(getAppError(error)));
+ }
+}
+
+function* sendEmailVerificationSaga(): Generator {
+ try {
+ yield call(sendEmailVerification);
+ } catch (error) {
+ yield put(signUpFail(getAppError(error)));
+ }
+}
+export function* watchAuth(): Generator {
+ yield takeLatest(USER_PROFILE_LOGIN, emailLoginSaga);
+ yield takeLatest(USER_PROFILE_LOGOUT, logoutSaga);
+ yield takeLatest(USER_PROFILE_SIGNUP, signUpSaga);
+ yield takeLatest(USER_PROFILE_SIGNUP_SUCCESS, sendEmailVerificationSaga);
+}
diff --git a/web-app/src/app/store/saga/profile-saga.ts b/web-app/src/app/store/saga/profile-saga.ts
new file mode 100644
index 000000000..931869f14
--- /dev/null
+++ b/web-app/src/app/store/saga/profile-saga.ts
@@ -0,0 +1,27 @@
+import { type StrictEffect, call, takeLatest } from 'redux-saga/effects';
+import {
+ USER_PROFILE_LOAD_ORGANIZATION,
+ USER_PROFILE_LOAD_ORGANIZATION_SUCCESS,
+ type User,
+} from '../../types';
+import { getUserOrganization } from '../../services';
+
+function* loadUserOrganizationSaga(): Generator {
+ try {
+ const organization = yield call(getUserOrganization);
+ console.log(organization);
+ } catch (error) {}
+}
+
+// eslint-disable-next-line require-yield
+function* saveUserOrganizationSaga(): Generator {
+ throw new Error('Not implemented');
+}
+
+export function* watchProfile(): Generator {
+ yield takeLatest(USER_PROFILE_LOAD_ORGANIZATION, loadUserOrganizationSaga);
+ yield takeLatest(
+ USER_PROFILE_LOAD_ORGANIZATION_SUCCESS,
+ saveUserOrganizationSaga,
+ );
+}
diff --git a/web-app/src/app/store/saga/root-saga.ts b/web-app/src/app/store/saga/root-saga.ts
new file mode 100644
index 000000000..4246ef292
--- /dev/null
+++ b/web-app/src/app/store/saga/root-saga.ts
@@ -0,0 +1,9 @@
+import { all } from 'redux-saga/effects';
+import { watchAuth } from './auth-saga';
+import { watchProfile } from './profile-saga';
+
+const rootSaga = function* (): Generator {
+ yield all([watchAuth(), watchProfile()]);
+};
+
+export default rootSaga;
diff --git a/web-app/src/app/store/selectors.ts b/web-app/src/app/store/selectors.ts
new file mode 100644
index 000000000..82198416d
--- /dev/null
+++ b/web-app/src/app/store/selectors.ts
@@ -0,0 +1,12 @@
+import { type RootState } from './store';
+
+export * from './profile-selectors';
+
+export const selectLoadingApp = (state: RootState): boolean => {
+ return (
+ state.userProfile.status === 'loading_organization' ||
+ state.userProfile.status === 'login_in' ||
+ state.userProfile.status === 'login_out' ||
+ state.userProfile.status === 'sign_up'
+ );
+};
diff --git a/web-app/src/app/store/store.ts b/web-app/src/app/store/store.ts
new file mode 100644
index 000000000..6471f8bca
--- /dev/null
+++ b/web-app/src/app/store/store.ts
@@ -0,0 +1,52 @@
+import {
+ persistReducer,
+ FLUSH,
+ REHYDRATE,
+ PAUSE,
+ PERSIST,
+ PURGE,
+ REGISTER,
+} from 'redux-persist';
+import storage from 'redux-persist/lib/storage/session';
+import {
+ configureStore,
+ type ThunkAction,
+ type Action,
+} from '@reduxjs/toolkit';
+import createSagaMiddleware from '@redux-saga/core';
+import rootSaga from './saga/root-saga';
+
+import rootReducer from './reducers';
+
+const persistConfig = {
+ key: 'root',
+ version: 1,
+ storage,
+};
+
+const persistedReducer = persistReducer(persistConfig, rootReducer);
+
+const sagaMiddleware = createSagaMiddleware();
+
+export const store = configureStore({
+ reducer: persistedReducer,
+ middleware: (getDefaultMiddleware) => [
+ ...getDefaultMiddleware({
+ serializableCheck: {
+ ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
+ },
+ }),
+ sagaMiddleware,
+ ],
+});
+
+sagaMiddleware.run(rootSaga);
+
+export type RootState = ReturnType;
+export type AppDispatch = typeof store.dispatch;
+export type AppThunk = ThunkAction<
+ ReturnType,
+ RootState,
+ unknown,
+ Action
+>;
diff --git a/web-app/src/app/types.ts b/web-app/src/app/types.ts
new file mode 100644
index 000000000..eeec6927f
--- /dev/null
+++ b/web-app/src/app/types.ts
@@ -0,0 +1,48 @@
+export type ChildrenElement =
+ | string
+ | JSX.Element
+ | JSX.Element[]
+ | (() => JSX.Element);
+
+export interface EmailLogin {
+ email: string;
+ password: string;
+}
+
+export interface User {
+ fullname?: string;
+ email: string;
+ organization?: string;
+}
+
+export const USER_PROFILE = 'userProfile';
+
+export const USER_PROFILE_LOGIN = `${USER_PROFILE}/login`;
+export const USER_PROFILE_LOGIN_SUCCESS = `${USER_PROFILE}/loginSuccess`;
+export const USER_PROFILE_LOGIN_FAIL = `${USER_PROFILE}/loginFail`;
+export const USER_PROFILE_LOGOUT = `${USER_PROFILE}/logout`;
+export const USER_PROFILE_LOGOUT_SUCCESS = `${USER_PROFILE}/logoutSuccess`;
+export const USER_PROFILE_SIGNUP = `${USER_PROFILE}/signUp`;
+export const USER_PROFILE_SIGNUP_SUCCESS = `${USER_PROFILE}/signUpSuccess`;
+export const USER_PROFILE_SIGNUP_FAIL = `${USER_PROFILE}/signUpFail`;
+export const USER_PROFILE_LOAD_ORGANIZATION = `${USER_PROFILE}/loadOrganization`;
+export const USER_PROFILE_LOAD_ORGANIZATION_SUCCESS = `${USER_PROFILE}/loadOrganizationSuccess`;
+
+export enum ErrorSource {
+ SignUp = 'SignUp',
+ Login = 'Login',
+ Logout = 'Logout',
+}
+
+export interface AppError {
+ code: string | 'unknown';
+ message: string;
+ source?: ErrorSource;
+}
+
+export type AppErrors = {
+ [Property in ErrorSource]: AppError | null;
+};
+
+export const passwordValidatioError =
+ 'Password must contain at least one uppercase letter, one lowercase letter, one digit, one special char(!@#$%^&*) and be at least 12 chars long';
diff --git a/web-app/src/app/util-component/Context.tsx b/web-app/src/app/util-component/Context.tsx
deleted file mode 100644
index aea7b168e..000000000
--- a/web-app/src/app/util-component/Context.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import React from 'react';
-import { AuthProvider } from '../sign-in-page/AuthContext';
-import type ContextProviderProps from '../interface/ContextProviderProps';
-
-const ContextProviders: React.FC = ({ children }) => {
- return (
-
- {/* Add other providers here */}
- {children}
-
- );
-};
-
-export default ContextProviders;
diff --git a/web-app/src/firebase.ts b/web-app/src/firebase.ts
new file mode 100644
index 000000000..907ca454d
--- /dev/null
+++ b/web-app/src/firebase.ts
@@ -0,0 +1,12 @@
+import firebase from 'firebase/compat/app';
+
+const firebaseConfig = {
+ apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
+ authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
+ projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
+ storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
+ messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
+ appId: process.env.REACT_APP_FIREBASE_APP_ID,
+};
+
+export const app = firebase.initializeApp(firebaseConfig);
diff --git a/web-app/src/index.tsx b/web-app/src/index.tsx
index 4679e75fd..f2bfe6fe5 100644
--- a/web-app/src/index.tsx
+++ b/web-app/src/index.tsx
@@ -2,7 +2,6 @@ import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './app/App';
-import reportWebVitals from './reportWebVitals';
import { theme } from './app/Theme';
import { ThemeProvider } from '@mui/material/styles';
@@ -16,8 +15,3 @@ root.render(
,
);
-
-// If you want to start measuring performance in your app, pass a function
-// to log results (for example: reportWebVitals(console.log))
-// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
-reportWebVitals();
diff --git a/web-app/src/reportWebVitals.ts b/web-app/src/reportWebVitals.ts
deleted file mode 100644
index 7fc2a19b9..000000000
--- a/web-app/src/reportWebVitals.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { type ReportHandler } from 'web-vitals';
-
-const reportWebVitals = (onPerfEntry?: ReportHandler) => {
- if (onPerfEntry && onPerfEntry instanceof Function) {
- import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
- getCLS(onPerfEntry);
- getFID(onPerfEntry);
- getFCP(onPerfEntry);
- getLCP(onPerfEntry);
- getTTFB(onPerfEntry);
- });
- }
-};
-
-export default reportWebVitals;
diff --git a/web-app/yarn.lock b/web-app/yarn.lock
index ec341139b..12695752b 100644
--- a/web-app/yarn.lock
+++ b/web-app/yarn.lock
@@ -1133,6 +1133,13 @@
dependencies:
regenerator-runtime "^0.14.0"
+"@babel/runtime@^7.12.1", "@babel/runtime@^7.6.3":
+ version "7.23.1"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.1.tgz#72741dc4d413338a91dcb044a86f3c0bc402646d"
+ integrity sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==
+ dependencies:
+ regenerator-runtime "^0.14.0"
+
"@babel/template@^7.22.15", "@babel/template@^7.22.5", "@babel/template@^7.3.3":
version "7.22.15"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38"
@@ -1329,7 +1336,7 @@
enabled "2.0.x"
kuler "^2.0.0"
-"@emotion/babel-plugin@^11.11.0":
+"@emotion/babel-plugin@^11.10.6", "@emotion/babel-plugin@^11.11.0":
version "11.11.0"
resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz#c2d872b6a7767a9d176d007f5b31f7d504bb5d6c"
integrity sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==
@@ -1346,7 +1353,7 @@
source-map "^0.5.7"
stylis "4.2.0"
-"@emotion/cache@^11.11.0":
+"@emotion/cache@^11.10.5", "@emotion/cache@^11.11.0":
version "11.11.0"
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.11.0.tgz#809b33ee6b1cb1a625fef7a45bc568ccd9b8f3ff"
integrity sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==
@@ -1357,6 +1364,17 @@
"@emotion/weak-memoize" "^0.3.1"
stylis "4.2.0"
+"@emotion/css@11.10.6":
+ version "11.10.6"
+ resolved "https://registry.yarnpkg.com/@emotion/css/-/css-11.10.6.tgz#5d226fdd8ef2a46d28e4eb09f66dc01a3bda5a04"
+ integrity sha512-88Sr+3heKAKpj9PCqq5A1hAmAkoSIvwEq1O2TwDij7fUtsJpdkV4jMTISSTouFeRvsGvXIpuSuDQ4C1YdfNGXw==
+ dependencies:
+ "@emotion/babel-plugin" "^11.10.6"
+ "@emotion/cache" "^11.10.5"
+ "@emotion/serialize" "^1.1.1"
+ "@emotion/sheet" "^1.2.1"
+ "@emotion/utils" "^1.2.0"
+
"@emotion/hash@^0.9.1":
version "0.9.1"
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.1.tgz#4ffb0055f7ef676ebc3a5a91fb621393294e2f43"
@@ -1388,7 +1406,7 @@
"@emotion/weak-memoize" "^0.3.1"
hoist-non-react-statics "^3.3.1"
-"@emotion/serialize@^1.1.2":
+"@emotion/serialize@^1.1.1", "@emotion/serialize@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.2.tgz#017a6e4c9b8a803bd576ff3d52a0ea6fa5a62b51"
integrity sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==
@@ -1399,7 +1417,7 @@
"@emotion/utils" "^1.2.1"
csstype "^3.0.2"
-"@emotion/sheet@^1.2.2":
+"@emotion/sheet@^1.2.1", "@emotion/sheet@^1.2.2":
version "1.2.2"
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.2.tgz#d58e788ee27267a14342303e1abb3d508b6d0fec"
integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==
@@ -1426,7 +1444,7 @@
resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz#08de79f54eb3406f9daaf77c76e35313da963963"
integrity sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==
-"@emotion/utils@^1.2.1":
+"@emotion/utils@^1.2.0", "@emotion/utils@^1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.1.tgz#bbab58465738d31ae4cb3dbb6fc00a5991f755e4"
integrity sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==
@@ -1468,6 +1486,378 @@
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.49.0.tgz#86f79756004a97fa4df866835093f1df3d03c333"
integrity sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==
+"@firebase/analytics-compat@0.2.6":
+ version "0.2.6"
+ resolved "https://registry.yarnpkg.com/@firebase/analytics-compat/-/analytics-compat-0.2.6.tgz#50063978c42f13eb800e037e96ac4b17236841f4"
+ integrity sha512-4MqpVLFkGK7NJf/5wPEEP7ePBJatwYpyjgJ+wQHQGHfzaCDgntOnl9rL2vbVGGKCnRqWtZDIWhctB86UWXaX2Q==
+ dependencies:
+ "@firebase/analytics" "0.10.0"
+ "@firebase/analytics-types" "0.8.0"
+ "@firebase/component" "0.6.4"
+ "@firebase/util" "1.9.3"
+ tslib "^2.1.0"
+
+"@firebase/analytics-types@0.8.0":
+ version "0.8.0"
+ resolved "https://registry.yarnpkg.com/@firebase/analytics-types/-/analytics-types-0.8.0.tgz#551e744a29adbc07f557306530a2ec86add6d410"
+ integrity sha512-iRP+QKI2+oz3UAh4nPEq14CsEjrjD6a5+fuypjScisAh9kXKFvdJOZJDwk7kikLvWVLGEs9+kIUS4LPQV7VZVw==
+
+"@firebase/analytics@0.10.0":
+ version "0.10.0"
+ resolved "https://registry.yarnpkg.com/@firebase/analytics/-/analytics-0.10.0.tgz#9c6986acd573c6c6189ffb52d0fd63c775db26d7"
+ integrity sha512-Locv8gAqx0e+GX/0SI3dzmBY5e9kjVDtD+3zCFLJ0tH2hJwuCAiL+5WkHuxKj92rqQj/rvkBUCfA1ewlX2hehg==
+ dependencies:
+ "@firebase/component" "0.6.4"
+ "@firebase/installations" "0.6.4"
+ "@firebase/logger" "0.4.0"
+ "@firebase/util" "1.9.3"
+ tslib "^2.1.0"
+
+"@firebase/app-check-compat@0.3.7":
+ version "0.3.7"
+ resolved "https://registry.yarnpkg.com/@firebase/app-check-compat/-/app-check-compat-0.3.7.tgz#e150f61d653a0f2043a34dcb995616a717161839"
+ integrity sha512-cW682AxsyP1G+Z0/P7pO/WT2CzYlNxoNe5QejVarW2o5ZxeWSSPAiVEwpEpQR/bUlUmdeWThYTMvBWaopdBsqw==
+ dependencies:
+ "@firebase/app-check" "0.8.0"
+ "@firebase/app-check-types" "0.5.0"
+ "@firebase/component" "0.6.4"
+ "@firebase/logger" "0.4.0"
+ "@firebase/util" "1.9.3"
+ tslib "^2.1.0"
+
+"@firebase/app-check-interop-types@0.3.0":
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.0.tgz#b27ea1397cb80427f729e4bbf3a562f2052955c4"
+ integrity sha512-xAxHPZPIgFXnI+vb4sbBjZcde7ZluzPPaSK7Lx3/nmuVk4TjZvnL8ONnkd4ERQKL8WePQySU+pRcWkh8rDf5Sg==
+
+"@firebase/app-check-types@0.5.0":
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/@firebase/app-check-types/-/app-check-types-0.5.0.tgz#1b02826213d7ce6a1cf773c329b46ea1c67064f4"
+ integrity sha512-uwSUj32Mlubybw7tedRzR24RP8M8JUVR3NPiMk3/Z4bCmgEKTlQBwMXrehDAZ2wF+TsBq0SN1c6ema71U/JPyQ==
+
+"@firebase/app-check@0.8.0":
+ version "0.8.0"
+ resolved "https://registry.yarnpkg.com/@firebase/app-check/-/app-check-0.8.0.tgz#b531ec40900af9c3cf1ec63de9094a0ddd733d6a"
+ integrity sha512-dRDnhkcaC2FspMiRK/Vbp+PfsOAEP6ZElGm9iGFJ9fDqHoPs0HOPn7dwpJ51lCFi1+2/7n5pRPGhqF/F03I97g==
+ dependencies:
+ "@firebase/component" "0.6.4"
+ "@firebase/logger" "0.4.0"
+ "@firebase/util" "1.9.3"
+ tslib "^2.1.0"
+
+"@firebase/app-compat@0.2.19":
+ version "0.2.19"
+ resolved "https://registry.yarnpkg.com/@firebase/app-compat/-/app-compat-0.2.19.tgz#ba0651166924fa344b4591a746ea493fdd609f13"
+ integrity sha512-QkJDqYqjhvs4fTMcRVXQkP9hbo5yfoJXDWkhU4VA5Vzs8Qsp76VPzYbqx5SD5OmBy+bz/Ot1UV8qySPGI4aKuw==
+ dependencies:
+ "@firebase/app" "0.9.19"
+ "@firebase/component" "0.6.4"
+ "@firebase/logger" "0.4.0"
+ "@firebase/util" "1.9.3"
+ tslib "^2.1.0"
+
+"@firebase/app-types@0.9.0":
+ version "0.9.0"
+ resolved "https://registry.yarnpkg.com/@firebase/app-types/-/app-types-0.9.0.tgz#35b5c568341e9e263b29b3d2ba0e9cfc9ec7f01e"
+ integrity sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==
+
+"@firebase/app@0.9.19":
+ version "0.9.19"
+ resolved "https://registry.yarnpkg.com/@firebase/app/-/app-0.9.19.tgz#d2b8a4cf47eb429e441dd661c291dd7312fd69de"
+ integrity sha512-t/SHyZ3xWkR77ZU9VMoobDNFLdDKQ5xqoCAn4o16gTsA1C8sJ6ZOMZ02neMOPxNHuQXVE4tA8ukilnDbnK7uJA==
+ dependencies:
+ "@firebase/component" "0.6.4"
+ "@firebase/logger" "0.4.0"
+ "@firebase/util" "1.9.3"
+ idb "7.1.1"
+ tslib "^2.1.0"
+
+"@firebase/auth-compat@0.4.6":
+ version "0.4.6"
+ resolved "https://registry.yarnpkg.com/@firebase/auth-compat/-/auth-compat-0.4.6.tgz#413568be48d23a17aa14438b8aad86556bd1e132"
+ integrity sha512-pKp1d4fSf+yoy1EBjTx9ISxlunqhW0vTICk0ByZ3e+Lp6ZIXThfUy4F1hAJlEafD/arM0oepRiAh7LXS1xn/BA==
+ dependencies:
+ "@firebase/auth" "1.3.0"
+ "@firebase/auth-types" "0.12.0"
+ "@firebase/component" "0.6.4"
+ "@firebase/util" "1.9.3"
+ node-fetch "2.6.7"
+ tslib "^2.1.0"
+
+"@firebase/auth-interop-types@0.2.1":
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/@firebase/auth-interop-types/-/auth-interop-types-0.2.1.tgz#78884f24fa539e34a06c03612c75f222fcc33742"
+ integrity sha512-VOaGzKp65MY6P5FI84TfYKBXEPi6LmOCSMMzys6o2BN2LOsqy7pCuZCup7NYnfbk5OkkQKzvIfHOzTm0UDpkyg==
+
+"@firebase/auth-types@0.12.0":
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/@firebase/auth-types/-/auth-types-0.12.0.tgz#f28e1b68ac3b208ad02a15854c585be6da3e8e79"
+ integrity sha512-pPwaZt+SPOshK8xNoiQlK5XIrS97kFYc3Rc7xmy373QsOJ9MmqXxLaYssP5Kcds4wd2qK//amx/c+A8O2fVeZA==
+
+"@firebase/auth@1.3.0":
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/@firebase/auth/-/auth-1.3.0.tgz#514d77309fdef5cc0ae81d5f57cb07bdaf6822d7"
+ integrity sha512-vjK4CHbY9aWdiVOrKi6mpa8z6uxeaf7LB/MZTHuZOiGHMcUoTGB6TeMbRShyqk1uaMrxhhZ5Ar/dR0965E1qyA==
+ dependencies:
+ "@firebase/component" "0.6.4"
+ "@firebase/logger" "0.4.0"
+ "@firebase/util" "1.9.3"
+ node-fetch "2.6.7"
+ tslib "^2.1.0"
+
+"@firebase/component@0.6.4":
+ version "0.6.4"
+ resolved "https://registry.yarnpkg.com/@firebase/component/-/component-0.6.4.tgz#8981a6818bd730a7554aa5e0516ffc9b1ae3f33d"
+ integrity sha512-rLMyrXuO9jcAUCaQXCMjCMUsWrba5fzHlNK24xz5j2W6A/SRmK8mZJ/hn7V0fViLbxC0lPMtrK1eYzk6Fg03jA==
+ dependencies:
+ "@firebase/util" "1.9.3"
+ tslib "^2.1.0"
+
+"@firebase/database-compat@1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@firebase/database-compat/-/database-compat-1.0.1.tgz#ab0acbbfb0031080cc16504cef6d00c95cf27ff1"
+ integrity sha512-ky82yLIboLxtAIWyW/52a6HLMVTzD2kpZlEilVDok73pNPLjkJYowj8iaIWK5nTy7+6Gxt7d00zfjL6zckGdXQ==
+ dependencies:
+ "@firebase/component" "0.6.4"
+ "@firebase/database" "1.0.1"
+ "@firebase/database-types" "1.0.0"
+ "@firebase/logger" "0.4.0"
+ "@firebase/util" "1.9.3"
+ tslib "^2.1.0"
+
+"@firebase/database-types@1.0.0":
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/@firebase/database-types/-/database-types-1.0.0.tgz#3f7f71c2c3fd1e29d15fce513f14dae2e7543f2a"
+ integrity sha512-SjnXStoE0Q56HcFgNQ+9SsmJc0c8TqGARdI/T44KXy+Ets3r6x/ivhQozT66bMnCEjJRywYoxNurRTMlZF8VNg==
+ dependencies:
+ "@firebase/app-types" "0.9.0"
+ "@firebase/util" "1.9.3"
+
+"@firebase/database@1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@firebase/database/-/database-1.0.1.tgz#28830f1d0c05ec2f7014658a3165129cec891bcb"
+ integrity sha512-VAhF7gYwunW4Lw/+RQZvW8dlsf2r0YYqV9W0Gi2Mz8+0TGg1mBJWoUtsHfOr8kPJXhcLsC4eP/z3x6L/Fvjk/A==
+ dependencies:
+ "@firebase/auth-interop-types" "0.2.1"
+ "@firebase/component" "0.6.4"
+ "@firebase/logger" "0.4.0"
+ "@firebase/util" "1.9.3"
+ faye-websocket "0.11.4"
+ tslib "^2.1.0"
+
+"@firebase/firestore-compat@0.3.18":
+ version "0.3.18"
+ resolved "https://registry.yarnpkg.com/@firebase/firestore-compat/-/firestore-compat-0.3.18.tgz#f087d65cbd175e2340beb87527f24482b651e12e"
+ integrity sha512-hkqv4mb1oScKbEtzfcK8Go8c0VpDWmbAvbD6B6XnphLqi27pkXgo9Rp+aSKlD7cBL29VMEekP5bEm9lSVfZpNw==
+ dependencies:
+ "@firebase/component" "0.6.4"
+ "@firebase/firestore" "4.2.0"
+ "@firebase/firestore-types" "3.0.0"
+ "@firebase/util" "1.9.3"
+ tslib "^2.1.0"
+
+"@firebase/firestore-types@3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@firebase/firestore-types/-/firestore-types-3.0.0.tgz#f3440d5a1cc2a722d361b24cefb62ca8b3577af3"
+ integrity sha512-Meg4cIezHo9zLamw0ymFYBD4SMjLb+ZXIbuN7T7ddXN6MGoICmOTq3/ltdCGoDCS2u+H1XJs2u/cYp75jsX9Qw==
+
+"@firebase/firestore@4.2.0":
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/@firebase/firestore/-/firestore-4.2.0.tgz#637e21eadee5e8b6e75c1d5bf4741385dd1e128e"
+ integrity sha512-iKZqIdOBJpJUcwY5airLX0W04TLrQSJuActOP1HG5WoIY5oyGTQE4Ml7hl5GW7mBqFieT4ojtUuDXj6MLrn1lA==
+ dependencies:
+ "@firebase/component" "0.6.4"
+ "@firebase/logger" "0.4.0"
+ "@firebase/util" "1.9.3"
+ "@firebase/webchannel-wrapper" "0.10.3"
+ "@grpc/grpc-js" "~1.9.0"
+ "@grpc/proto-loader" "^0.7.8"
+ node-fetch "2.6.7"
+ tslib "^2.1.0"
+
+"@firebase/functions-compat@0.3.5":
+ version "0.3.5"
+ resolved "https://registry.yarnpkg.com/@firebase/functions-compat/-/functions-compat-0.3.5.tgz#7a532d3a9764c6d5fbc1ec5541a989a704326647"
+ integrity sha512-uD4jwgwVqdWf6uc3NRKF8cSZ0JwGqSlyhPgackyUPe+GAtnERpS4+Vr66g0b3Gge0ezG4iyHo/EXW/Hjx7QhHw==
+ dependencies:
+ "@firebase/component" "0.6.4"
+ "@firebase/functions" "0.10.0"
+ "@firebase/functions-types" "0.6.0"
+ "@firebase/util" "1.9.3"
+ tslib "^2.1.0"
+
+"@firebase/functions-types@0.6.0":
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/@firebase/functions-types/-/functions-types-0.6.0.tgz#ccd7000dc6fc668f5acb4e6a6a042a877a555ef2"
+ integrity sha512-hfEw5VJtgWXIRf92ImLkgENqpL6IWpYaXVYiRkFY1jJ9+6tIhWM7IzzwbevwIIud/jaxKVdRzD7QBWfPmkwCYw==
+
+"@firebase/functions@0.10.0":
+ version "0.10.0"
+ resolved "https://registry.yarnpkg.com/@firebase/functions/-/functions-0.10.0.tgz#c630ddf12cdf941c25bc8d554e30c3226cd560f6"
+ integrity sha512-2U+fMNxTYhtwSpkkR6WbBcuNMOVaI7MaH3cZ6UAeNfj7AgEwHwMIFLPpC13YNZhno219F0lfxzTAA0N62ndWzA==
+ dependencies:
+ "@firebase/app-check-interop-types" "0.3.0"
+ "@firebase/auth-interop-types" "0.2.1"
+ "@firebase/component" "0.6.4"
+ "@firebase/messaging-interop-types" "0.2.0"
+ "@firebase/util" "1.9.3"
+ node-fetch "2.6.7"
+ tslib "^2.1.0"
+
+"@firebase/installations-compat@0.2.4":
+ version "0.2.4"
+ resolved "https://registry.yarnpkg.com/@firebase/installations-compat/-/installations-compat-0.2.4.tgz#b5557c897b4cd3635a59887a8bf69c3731aaa952"
+ integrity sha512-LI9dYjp0aT9Njkn9U4JRrDqQ6KXeAmFbRC0E7jI7+hxl5YmRWysq5qgQl22hcWpTk+cm3es66d/apoDU/A9n6Q==
+ dependencies:
+ "@firebase/component" "0.6.4"
+ "@firebase/installations" "0.6.4"
+ "@firebase/installations-types" "0.5.0"
+ "@firebase/util" "1.9.3"
+ tslib "^2.1.0"
+
+"@firebase/installations-types@0.5.0":
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/@firebase/installations-types/-/installations-types-0.5.0.tgz#2adad64755cd33648519b573ec7ec30f21fb5354"
+ integrity sha512-9DP+RGfzoI2jH7gY4SlzqvZ+hr7gYzPODrbzVD82Y12kScZ6ZpRg/i3j6rleto8vTFC8n6Len4560FnV1w2IRg==
+
+"@firebase/installations@0.6.4":
+ version "0.6.4"
+ resolved "https://registry.yarnpkg.com/@firebase/installations/-/installations-0.6.4.tgz#20382e33e6062ac5eff4bede8e468ed4c367609e"
+ integrity sha512-u5y88rtsp7NYkCHC3ElbFBrPtieUybZluXyzl7+4BsIz4sqb4vSAuwHEUgCgCeaQhvsnxDEU6icly8U9zsJigA==
+ dependencies:
+ "@firebase/component" "0.6.4"
+ "@firebase/util" "1.9.3"
+ idb "7.0.1"
+ tslib "^2.1.0"
+
+"@firebase/logger@0.4.0":
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/@firebase/logger/-/logger-0.4.0.tgz#15ecc03c452525f9d47318ad9491b81d1810f113"
+ integrity sha512-eRKSeykumZ5+cJPdxxJRgAC3G5NknY2GwEbKfymdnXtnT0Ucm4pspfR6GT4MUQEDuJwRVbVcSx85kgJulMoFFA==
+ dependencies:
+ tslib "^2.1.0"
+
+"@firebase/messaging-compat@0.2.4":
+ version "0.2.4"
+ resolved "https://registry.yarnpkg.com/@firebase/messaging-compat/-/messaging-compat-0.2.4.tgz#323ca48deef77065b4fcda3cfd662c4337dffcfd"
+ integrity sha512-lyFjeUhIsPRYDPNIkYX1LcZMpoVbBWXX4rPl7c/rqc7G+EUea7IEtSt4MxTvh6fDfPuzLn7+FZADfscC+tNMfg==
+ dependencies:
+ "@firebase/component" "0.6.4"
+ "@firebase/messaging" "0.12.4"
+ "@firebase/util" "1.9.3"
+ tslib "^2.1.0"
+
+"@firebase/messaging-interop-types@0.2.0":
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.0.tgz#6056f8904a696bf0f7fdcf5f2ca8f008e8f6b064"
+ integrity sha512-ujA8dcRuVeBixGR9CtegfpU4YmZf3Lt7QYkcj693FFannwNuZgfAYaTmbJ40dtjB81SAu6tbFPL9YLNT15KmOQ==
+
+"@firebase/messaging@0.12.4":
+ version "0.12.4"
+ resolved "https://registry.yarnpkg.com/@firebase/messaging/-/messaging-0.12.4.tgz#ccb49df5ab97d5650c9cf5b8c77ddc34daafcfe0"
+ integrity sha512-6JLZct6zUaex4g7HI3QbzeUrg9xcnmDAPTWpkoMpd/GoSVWH98zDoWXMGrcvHeCAIsLpFMe4MPoZkJbrPhaASw==
+ dependencies:
+ "@firebase/component" "0.6.4"
+ "@firebase/installations" "0.6.4"
+ "@firebase/messaging-interop-types" "0.2.0"
+ "@firebase/util" "1.9.3"
+ idb "7.0.1"
+ tslib "^2.1.0"
+
+"@firebase/performance-compat@0.2.4":
+ version "0.2.4"
+ resolved "https://registry.yarnpkg.com/@firebase/performance-compat/-/performance-compat-0.2.4.tgz#95cbf32057b5d9f0c75d804bc50e6ed3ba486274"
+ integrity sha512-nnHUb8uP9G8islzcld/k6Bg5RhX62VpbAb/Anj7IXs/hp32Eb2LqFPZK4sy3pKkBUO5wcrlRWQa6wKOxqlUqsg==
+ dependencies:
+ "@firebase/component" "0.6.4"
+ "@firebase/logger" "0.4.0"
+ "@firebase/performance" "0.6.4"
+ "@firebase/performance-types" "0.2.0"
+ "@firebase/util" "1.9.3"
+ tslib "^2.1.0"
+
+"@firebase/performance-types@0.2.0":
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/@firebase/performance-types/-/performance-types-0.2.0.tgz#400685f7a3455970817136d9b48ce07a4b9562ff"
+ integrity sha512-kYrbr8e/CYr1KLrLYZZt2noNnf+pRwDq2KK9Au9jHrBMnb0/C9X9yWSXmZkFt4UIdsQknBq8uBB7fsybZdOBTA==
+
+"@firebase/performance@0.6.4":
+ version "0.6.4"
+ resolved "https://registry.yarnpkg.com/@firebase/performance/-/performance-0.6.4.tgz#0ad766bfcfab4f386f4fe0bef43bbcf505015069"
+ integrity sha512-HfTn/bd8mfy/61vEqaBelNiNnvAbUtME2S25A67Nb34zVuCSCRIX4SseXY6zBnOFj3oLisaEqhVcJmVPAej67g==
+ dependencies:
+ "@firebase/component" "0.6.4"
+ "@firebase/installations" "0.6.4"
+ "@firebase/logger" "0.4.0"
+ "@firebase/util" "1.9.3"
+ tslib "^2.1.0"
+
+"@firebase/remote-config-compat@0.2.4":
+ version "0.2.4"
+ resolved "https://registry.yarnpkg.com/@firebase/remote-config-compat/-/remote-config-compat-0.2.4.tgz#1f494c81a6c9560b1f9ca1b4fbd4bbbe47cf4776"
+ integrity sha512-FKiki53jZirrDFkBHglB3C07j5wBpitAaj8kLME6g8Mx+aq7u9P7qfmuSRytiOItADhWUj7O1JIv7n9q87SuwA==
+ dependencies:
+ "@firebase/component" "0.6.4"
+ "@firebase/logger" "0.4.0"
+ "@firebase/remote-config" "0.4.4"
+ "@firebase/remote-config-types" "0.3.0"
+ "@firebase/util" "1.9.3"
+ tslib "^2.1.0"
+
+"@firebase/remote-config-types@0.3.0":
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/@firebase/remote-config-types/-/remote-config-types-0.3.0.tgz#689900dcdb3e5c059e8499b29db393e4e51314b4"
+ integrity sha512-RtEH4vdcbXZuZWRZbIRmQVBNsE7VDQpet2qFvq6vwKLBIQRQR5Kh58M4ok3A3US8Sr3rubYnaGqZSurCwI8uMA==
+
+"@firebase/remote-config@0.4.4":
+ version "0.4.4"
+ resolved "https://registry.yarnpkg.com/@firebase/remote-config/-/remote-config-0.4.4.tgz#6a496117054de58744bc9f382d2a6d1e14060c65"
+ integrity sha512-x1ioTHGX8ZwDSTOVp8PBLv2/wfwKzb4pxi0gFezS5GCJwbLlloUH4YYZHHS83IPxnua8b6l0IXUaWd0RgbWwzQ==
+ dependencies:
+ "@firebase/component" "0.6.4"
+ "@firebase/installations" "0.6.4"
+ "@firebase/logger" "0.4.0"
+ "@firebase/util" "1.9.3"
+ tslib "^2.1.0"
+
+"@firebase/storage-compat@0.3.2":
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/@firebase/storage-compat/-/storage-compat-0.3.2.tgz#51a97170fd652a516f729f82b97af369e5a2f8d7"
+ integrity sha512-wvsXlLa9DVOMQJckbDNhXKKxRNNewyUhhbXev3t8kSgoCotd1v3MmqhKKz93ePhDnhHnDs7bYHy+Qa8dRY6BXw==
+ dependencies:
+ "@firebase/component" "0.6.4"
+ "@firebase/storage" "0.11.2"
+ "@firebase/storage-types" "0.8.0"
+ "@firebase/util" "1.9.3"
+ tslib "^2.1.0"
+
+"@firebase/storage-types@0.8.0":
+ version "0.8.0"
+ resolved "https://registry.yarnpkg.com/@firebase/storage-types/-/storage-types-0.8.0.tgz#f1e40a5361d59240b6e84fac7fbbbb622bfaf707"
+ integrity sha512-isRHcGrTs9kITJC0AVehHfpraWFui39MPaU7Eo8QfWlqW7YPymBmRgjDrlOgFdURh6Cdeg07zmkLP5tzTKRSpg==
+
+"@firebase/storage@0.11.2":
+ version "0.11.2"
+ resolved "https://registry.yarnpkg.com/@firebase/storage/-/storage-0.11.2.tgz#c5e0316543fe1c4026b8e3910f85ad73f5b77571"
+ integrity sha512-CtvoFaBI4hGXlXbaCHf8humajkbXhs39Nbh6MbNxtwJiCqxPy9iH3D3CCfXAvP0QvAAwmJUTK3+z9a++Kc4nkA==
+ dependencies:
+ "@firebase/component" "0.6.4"
+ "@firebase/util" "1.9.3"
+ node-fetch "2.6.7"
+ tslib "^2.1.0"
+
+"@firebase/util@1.9.3":
+ version "1.9.3"
+ resolved "https://registry.yarnpkg.com/@firebase/util/-/util-1.9.3.tgz#45458dd5cd02d90e55c656e84adf6f3decf4b7ed"
+ integrity sha512-DY02CRhOZwpzO36fHpuVysz6JZrscPiBXD0fXp6qSrL9oNOx5KWICKdR95C0lSITzxp0TZosVyHqzatE8JbcjA==
+ dependencies:
+ tslib "^2.1.0"
+
+"@firebase/webchannel-wrapper@0.10.3":
+ version "0.10.3"
+ resolved "https://registry.yarnpkg.com/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.10.3.tgz#c894a21e8c911830e36bbbba55903ccfbc7a7e25"
+ integrity sha512-+ZplYUN3HOpgCfgInqgdDAbkGGVzES1cs32JJpeqoh87SkRobGXElJx+1GZSaDqzFL+bYiX18qEcBK76mYs8uA==
+
"@floating-ui/core@^1.4.2":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.5.0.tgz#5c05c60d5ae2d05101c3021c1a2a350ddc027f8c"
@@ -1548,7 +1938,15 @@
"@grpc/proto-loader" "^0.7.0"
"@types/node" ">=12.12.47"
-"@grpc/proto-loader@^0.7.0":
+"@grpc/grpc-js@~1.9.0":
+ version "1.9.4"
+ resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.9.4.tgz#6cf152869910c2ac3429eee08c1dbdc84e7bafea"
+ integrity sha512-oEnzYiDuEsBydZBtP84BkpduLsE1nSAO4KrhTLHRzNrIQE647fhchmosTQsJdCo8X9zBBt+l5+fNk+m/yCFJ/Q==
+ dependencies:
+ "@grpc/proto-loader" "^0.7.8"
+ "@types/node" ">=12.12.47"
+
+"@grpc/proto-loader@^0.7.0", "@grpc/proto-loader@^0.7.8":
version "0.7.10"
resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.10.tgz#6bf26742b1b54d0a473067743da5d3189d06d720"
integrity sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==
@@ -2157,6 +2555,68 @@
resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==
+"@react-firebase/auth@^0.2.10":
+ version "0.2.10"
+ resolved "https://registry.yarnpkg.com/@react-firebase/auth/-/auth-0.2.10.tgz#1f58a6c9dd7f89ec1ca03d0620929f1a6bac9c82"
+ integrity sha512-qeRv96HI5Y3nPRwO3Zi4QnYYeJstVu8lDih+QQbTeuadUUngchGw5Tp7dseOY5u3q/+e+kF2hV7BUkfYt7FS8g==
+ dependencies:
+ lodash.get "^4.4.2"
+ render-and-add-props "^0.5.0"
+
+"@redux-saga/core@^1.2.3":
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/@redux-saga/core/-/core-1.2.3.tgz#882ed9ac58b5f42c6abb23349542315b871de305"
+ integrity sha512-U1JO6ncFBAklFTwoQ3mjAeQZ6QGutsJzwNBjgVLSWDpZTRhobUzuVDS1qH3SKGJD8fvqoaYOjp6XJ3gCmeZWgA==
+ dependencies:
+ "@babel/runtime" "^7.6.3"
+ "@redux-saga/deferred" "^1.2.1"
+ "@redux-saga/delay-p" "^1.2.1"
+ "@redux-saga/is" "^1.1.3"
+ "@redux-saga/symbols" "^1.1.3"
+ "@redux-saga/types" "^1.2.1"
+ redux "^4.0.4"
+ typescript-tuple "^2.2.1"
+
+"@redux-saga/deferred@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/@redux-saga/deferred/-/deferred-1.2.1.tgz#aca373a08ccafd6f3481037f2f7ee97f2c87c3ec"
+ integrity sha512-cmin3IuuzMdfQjA0lG4B+jX+9HdTgHZZ+6u3jRAOwGUxy77GSlTi4Qp2d6PM1PUoTmQUR5aijlA39scWWPF31g==
+
+"@redux-saga/delay-p@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/@redux-saga/delay-p/-/delay-p-1.2.1.tgz#e72ac4731c5080a21f75b61bedc31cb639d9e446"
+ integrity sha512-MdiDxZdvb1m+Y0s4/hgdcAXntpUytr9g0hpcOO1XFVyyzkrDu3SKPgBFOtHn7lhu7n24ZKIAT1qtKyQjHqRd+w==
+ dependencies:
+ "@redux-saga/symbols" "^1.1.3"
+
+"@redux-saga/is@^1.1.3":
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/@redux-saga/is/-/is-1.1.3.tgz#b333f31967e87e32b4e6b02c75b78d609dd4ad73"
+ integrity sha512-naXrkETG1jLRfVfhOx/ZdLj0EyAzHYbgJWkXbB3qFliPcHKiWbv/ULQryOAEKyjrhiclmr6AMdgsXFyx7/yE6Q==
+ dependencies:
+ "@redux-saga/symbols" "^1.1.3"
+ "@redux-saga/types" "^1.2.1"
+
+"@redux-saga/symbols@^1.1.3":
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/@redux-saga/symbols/-/symbols-1.1.3.tgz#b731d56201719e96dc887dc3ae9016e761654367"
+ integrity sha512-hCx6ZvU4QAEUojETnX8EVg4ubNLBFl1Lps4j2tX7o45x/2qg37m3c6v+kSp8xjDJY+2tJw4QB3j8o8dsl1FDXg==
+
+"@redux-saga/types@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/@redux-saga/types/-/types-1.2.1.tgz#9403f51c17cae37edf870c6bc0c81c1ece5ccef8"
+ integrity sha512-1dgmkh+3so0+LlBWRhGA33ua4MYr7tUOj+a9Si28vUi0IUFNbff1T3sgpeDJI/LaC75bBYnQ0A3wXjn0OrRNBA==
+
+"@reduxjs/toolkit@^1.9.6":
+ version "1.9.6"
+ resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.9.6.tgz#fc968b45fe5b17ff90932c4556960d9c1078365a"
+ integrity sha512-Gc4ikl90ORF4viIdAkY06JNUnODjKfGxZRwATM30EdHq8hLSVoSrwXne5dd739yenP5bJxAX7tLuOWK5RPGtrw==
+ dependencies:
+ immer "^9.0.21"
+ redux "^4.2.1"
+ redux-thunk "^2.4.2"
+ reselect "^4.1.8"
+
"@remix-run/router@1.9.0":
version "1.9.0"
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.9.0.tgz#9033238b41c4cbe1e961eccb3f79e2c588328cf6"
@@ -2204,6 +2664,11 @@
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.4.0.tgz#77e948b9760bd22736a5d26e335a690f76fda37b"
integrity sha512-cEjvTPU32OM9lUFegJagO0mRnIn+rbqrG89vV8/xLnLFX0DoR0r1oy5IlTga71Q7uT3Qus7qm7wgeiMT/+Irlg==
+"@scarf/scarf@1.1.1":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@scarf/scarf/-/scarf-1.1.1.tgz#d8b9f20037b3a37dbf8dcdc4b3b72f9285bfce35"
+ integrity sha512-VGbKDbk1RFIaSmdVb0cNjjWJoRWRI/Weo23AjRCC2nryO0iAS8pzsToJfPVPtVs74WHw4L1UTADNdIYRLkirZQ==
+
"@sideway/address@^4.1.3":
version "4.1.4"
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0"
@@ -2571,6 +3036,14 @@
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64"
integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==
+"@types/hoist-non-react-statics@^3.3.0", "@types/hoist-non-react-statics@^3.3.1":
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#dc1e9ded53375d37603c479cc12c693b0878aa2a"
+ integrity sha512-YIQtIg4PKr7ZyqNPZObpxfHsHEmuB8dXCxd6qVcGuQVDK2bpsF7bYNnBJ4Nn7giuACZg+WewExgrtAJ3XnA4Xw==
+ dependencies:
+ "@types/react" "*"
+ hoist-non-react-statics "^3.3.0"
+
"@types/html-minifier-terser@^6.0.0":
version "6.1.0"
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35"
@@ -2738,6 +3211,16 @@
dependencies:
"@types/react" "*"
+"@types/react-redux@^7.1.27":
+ version "7.1.27"
+ resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.27.tgz#1afb31f7354bf787e162c10ff3fa19bafa9e6b57"
+ integrity sha512-xj7d9z32p1K/eBmO+OEy+qfaWXtcPlN8f1Xk3Ne0p/ZRQ867RI5bQ/bpBtxbqU1AHNhKJSgGvld/P2myU2uYkg==
+ dependencies:
+ "@types/hoist-non-react-statics" "^3.3.0"
+ "@types/react" "*"
+ hoist-non-react-statics "^3.3.0"
+ redux "^4.0.0"
+
"@types/react-router-dom@^5.3.3":
version "5.3.3"
resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.3.tgz#e9d6b4a66fcdbd651a5f106c2656a30088cc1e83"
@@ -2762,7 +3245,7 @@
dependencies:
"@types/react" "*"
-"@types/react@*", "@types/react@^18.2.21":
+"@types/react@*":
version "18.2.22"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.22.tgz#abe778a1c95a07fa70df40a52d7300a40b949ccb"
integrity sha512-60fLTOLqzarLED2O3UQImc/lsNRgG0jE/a1mPW9KjMemY0LMITWEsbS4VvZ4p6rorEHd5YKxxmMKSDK505GHpA==
@@ -2771,6 +3254,22 @@
"@types/scheduler" "*"
csstype "^3.0.2"
+"@types/react@^18.2.25":
+ version "18.2.25"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.25.tgz#99fa44154132979e870ff409dc5b6e67f06f0199"
+ integrity sha512-24xqse6+VByVLIr+xWaQ9muX1B4bXJKXBbjszbld/UEDslGLY53+ZucF44HCmLbMPejTzGG9XgR+3m2/Wqu1kw==
+ dependencies:
+ "@types/prop-types" "*"
+ "@types/scheduler" "*"
+ csstype "^3.0.2"
+
+"@types/redux-saga@^0.10.5":
+ version "0.10.5"
+ resolved "https://registry.yarnpkg.com/@types/redux-saga/-/redux-saga-0.10.5.tgz#80bf21078379ebc97387dbe56e44467b5677fa85"
+ integrity sha512-sqan7rs+PylBB5u2reL1hm+f0pDsZY1D1/o1vtgjrS4ErjxthK59zKCQyZhSLRcQDiOh3Pwp4fNQyn4A0CTdGA==
+ dependencies:
+ redux-saga "*"
+
"@types/resolve@1.17.1":
version "1.17.1"
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6"
@@ -2864,6 +3363,11 @@
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.4.tgz#2b38784cd16957d3782e8e2b31c03bc1d13b4d65"
integrity sha512-IDaobHimLQhjwsQ/NMwRVfa/yL7L/wriQPMhw1ZJall0KX6E1oxk29XMDeilW5qTIg5aoiqf5Udy8U/51aNoQQ==
+"@types/use-sync-external-store@^0.0.3":
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43"
+ integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==
+
"@types/ws@^8.5.5":
version "8.5.5"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb"
@@ -5140,6 +5644,11 @@ deep-is@^0.1.3, deep-is@~0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
+deepmerge@^2.1.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170"
+ integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==
+
deepmerge@^4.2.2:
version "4.3.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
@@ -5545,6 +6054,14 @@ entities@~2.1.0:
resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==
+env-cmd@^10.1.0:
+ version "10.1.0"
+ resolved "https://registry.yarnpkg.com/env-cmd/-/env-cmd-10.1.0.tgz#c7f5d3b550c9519f137fdac4dd8fb6866a8c8c4b"
+ integrity sha512-mMdWTT9XKN7yNth/6N6g2GuKuJTsKMDHlQFUDacb/heQRRWOTIZ42t1rMHnQu4jYxU1ajdTeJM+9eEETlqToMA==
+ dependencies:
+ commander "^4.0.0"
+ cross-spawn "^7.0.0"
+
env-paths@^2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
@@ -6331,7 +6848,7 @@ fastq@^1.6.0:
dependencies:
reusify "^1.0.4"
-faye-websocket@^0.11.3:
+faye-websocket@0.11.4, faye-websocket@^0.11.3:
version "0.11.4"
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da"
integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==
@@ -6532,6 +7049,38 @@ firebase-tools@^12.5.4:
winston-transport "^4.4.0"
ws "^7.2.3"
+firebase@^10.4.0:
+ version "10.4.0"
+ resolved "https://registry.yarnpkg.com/firebase/-/firebase-10.4.0.tgz#8b3c94765d69ebe706ff02e6bb0ed48092900fa6"
+ integrity sha512-3Z8WsNwA7kbcKGZ+nrTZ/ES518pk0K440ZJYD8nUNKN5hV6ll+unhUw30t1msedN6yIFjhsC/9OwT4Z0ohwO2w==
+ dependencies:
+ "@firebase/analytics" "0.10.0"
+ "@firebase/analytics-compat" "0.2.6"
+ "@firebase/app" "0.9.19"
+ "@firebase/app-check" "0.8.0"
+ "@firebase/app-check-compat" "0.3.7"
+ "@firebase/app-compat" "0.2.19"
+ "@firebase/app-types" "0.9.0"
+ "@firebase/auth" "1.3.0"
+ "@firebase/auth-compat" "0.4.6"
+ "@firebase/database" "1.0.1"
+ "@firebase/database-compat" "1.0.1"
+ "@firebase/firestore" "4.2.0"
+ "@firebase/firestore-compat" "0.3.18"
+ "@firebase/functions" "0.10.0"
+ "@firebase/functions-compat" "0.3.5"
+ "@firebase/installations" "0.6.4"
+ "@firebase/installations-compat" "0.2.4"
+ "@firebase/messaging" "0.12.4"
+ "@firebase/messaging-compat" "0.2.4"
+ "@firebase/performance" "0.6.4"
+ "@firebase/performance-compat" "0.2.4"
+ "@firebase/remote-config" "0.4.4"
+ "@firebase/remote-config-compat" "0.2.4"
+ "@firebase/storage" "0.11.2"
+ "@firebase/storage-compat" "0.3.2"
+ "@firebase/util" "1.9.3"
+
flat-cache@^3.0.4:
version "3.1.0"
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.1.0.tgz#0e54ab4a1a60fe87e2946b6b00657f1c99e1af3f"
@@ -6622,6 +7171,20 @@ form-data@~2.3.2:
combined-stream "^1.0.6"
mime-types "^2.1.12"
+formik@^2.4.5:
+ version "2.4.5"
+ resolved "https://registry.yarnpkg.com/formik/-/formik-2.4.5.tgz#f899b5b7a6f103a8fabb679823e8fafc7e0ee1b4"
+ integrity sha512-Gxlht0TD3vVdzMDHwkiNZqJ7Mvg77xQNfmBRrNtvzcHZs72TJppSTDKHpImCMJZwcWPBJ8jSQQ95GJzXFf1nAQ==
+ dependencies:
+ "@types/hoist-non-react-statics" "^3.3.1"
+ deepmerge "^2.1.1"
+ hoist-non-react-statics "^3.3.0"
+ lodash "^4.17.21"
+ lodash-es "^4.17.21"
+ react-fast-compare "^2.0.1"
+ tiny-warning "^1.0.2"
+ tslib "^2.0.0"
+
forwarded@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
@@ -7185,7 +7748,7 @@ heap-js@^2.2.0:
resolved "https://registry.yarnpkg.com/heap-js/-/heap-js-2.3.0.tgz#8eed2cede31ec312aa696eef1d4df0565841f183"
integrity sha512-E5303mzwQ+4j/n2J0rDvEPBN7GKjhis10oHiYOgjxsmxYgqG++hz9NyLLOXttzH8as/DyiBHYpUrJTZWYaMo8Q==
-hoist-non-react-statics@^3.3.1:
+hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
@@ -7415,7 +7978,12 @@ icss-utils@^5.0.0, icss-utils@^5.1.0:
resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae"
integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==
-idb@^7.0.1:
+idb@7.0.1:
+ version "7.0.1"
+ resolved "https://registry.yarnpkg.com/idb/-/idb-7.0.1.tgz#d2875b3a2f205d854ee307f6d196f246fea590a7"
+ integrity sha512-UUxlE7vGWK5RfB/fDwEGgRf84DY/ieqNha6msMV99UsEMQhJ1RwbCd8AYBj3QMgnE3VZnfQvm4oKVCJTYlqIgg==
+
+idb@7.1.1, idb@^7.0.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/idb/-/idb-7.1.1.tgz#d910ded866d32c7ced9befc5bfdf36f572ced72b"
integrity sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==
@@ -7437,7 +8005,7 @@ ignore@^5.2.0, ignore@^5.2.4:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324"
integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==
-immer@^9.0.7:
+immer@^9.0.21, immer@^9.0.7:
version "9.0.21"
resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176"
integrity sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==
@@ -9017,6 +9585,11 @@ locate-path@^6.0.0:
dependencies:
p-locate "^5.0.0"
+lodash-es@^4.17.21:
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
+ integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
+
lodash._objecttypes@~2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz#7c0b7f69d98a1f76529f890b0cdb1b4dfec11c11"
@@ -9047,6 +9620,11 @@ lodash.flatten@^4.4.0:
resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
integrity sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==
+lodash.get@^4.4.2:
+ version "4.4.2"
+ resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
+ integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==
+
lodash.includes@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
@@ -9600,6 +10178,13 @@ node-emoji@^1.11.0:
dependencies:
lodash "^4.17.21"
+node-fetch@2.6.7:
+ version "2.6.7"
+ resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
+ integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
+ dependencies:
+ whatwg-url "^5.0.0"
+
node-fetch@^2.6.1, node-fetch@^2.6.7, node-fetch@^2.6.9:
version "2.7.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
@@ -10848,6 +11433,11 @@ prop-types@^15.6.2, prop-types@^15.8.1:
object-assign "^4.1.1"
react-is "^16.13.1"
+property-expr@^2.0.5:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.5.tgz#278bdb15308ae16af3e3b9640024524f4dc02cb4"
+ integrity sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==
+
proto-list@~1.2.1:
version "1.2.4"
resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
@@ -11128,6 +11718,11 @@ react-error-overlay@^6.0.11:
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb"
integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==
+react-fast-compare@^2.0.1:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
+ integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
+
react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
@@ -11143,6 +11738,27 @@ react-is@^18.0.0, react-is@^18.2.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
+react-loading-overlay-ts@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/react-loading-overlay-ts/-/react-loading-overlay-ts-2.0.2.tgz#ecc4bb176002e6670871dc2534a32442fb5057cc"
+ integrity sha512-dy1GhUOz91aPmvvuT85WUFBCG7NX1ZJBKbeHgGHSC+gss6u9/6AfpFgDnXlJLxGpv+VbbeCXye8ETy+BX3SS4A==
+ dependencies:
+ "@emotion/css" "11.10.6"
+ "@scarf/scarf" "1.1.1"
+ react-transition-group "4.4.5"
+
+react-redux@^8.1.3:
+ version "8.1.3"
+ resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.1.3.tgz#4fdc0462d0acb59af29a13c27ffef6f49ab4df46"
+ integrity sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==
+ dependencies:
+ "@babel/runtime" "^7.12.1"
+ "@types/hoist-non-react-statics" "^3.3.1"
+ "@types/use-sync-external-store" "^0.0.3"
+ hoist-non-react-statics "^3.3.2"
+ react-is "^18.0.0"
+ use-sync-external-store "^1.0.0"
+
react-refresh@^0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046"
@@ -11218,7 +11834,7 @@ react-scripts@5.0.1:
optionalDependencies:
fsevents "^2.3.2"
-react-transition-group@^4.4.5:
+react-transition-group@4.4.5, react-transition-group@^4.4.5:
version "4.4.5"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1"
integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==
@@ -11300,6 +11916,30 @@ redeyed@~2.1.0:
dependencies:
esprima "~4.0.0"
+redux-persist@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-6.0.0.tgz#b4d2972f9859597c130d40d4b146fecdab51b3a8"
+ integrity sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==
+
+redux-saga@*, redux-saga@^1.2.3:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/redux-saga/-/redux-saga-1.2.3.tgz#7362f78a0235868daf1210f8862a95906ac018a1"
+ integrity sha512-HDe0wTR5nhd8Xr5xjGzoyTbdAw6rjy1GDplFt3JKtKN8/MnkQSRqK/n6aQQhpw5NI4ekDVOaW+w4sdxPBaCoTQ==
+ dependencies:
+ "@redux-saga/core" "^1.2.3"
+
+redux-thunk@^2.4.2:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.2.tgz#b9d05d11994b99f7a91ea223e8b04cf0afa5ef3b"
+ integrity sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==
+
+redux@^4.0.0, redux@^4.0.4, redux@^4.2.1:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197"
+ integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==
+ dependencies:
+ "@babel/runtime" "^7.9.2"
+
reflect.getprototypeof@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz#aaccbf41aca3821b87bb71d9dcbc7ad0ba50a3f3"
@@ -11393,6 +12033,11 @@ relateurl@^0.2.7:
resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==
+render-and-add-props@^0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/render-and-add-props/-/render-and-add-props-0.5.0.tgz#79e35b2251936ec187188322826e56401c2ef70b"
+ integrity sha512-jIVQsWxGDHwpMcI0d9w6OxF/GCtwL02Lyqk8aGjyF6LyR9GPLY+1ONwPjs6kbQ2d6gTApWk8aprZkBli0tpG/w==
+
renderkid@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-3.0.0.tgz#5fd823e4d6951d37358ecc9a58b1f06836b6268a"
@@ -11459,6 +12104,11 @@ requizzle@^0.2.3:
dependencies:
lodash "^4.17.21"
+reselect@^4.1.8:
+ version "4.1.8"
+ resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.8.tgz#3f5dc671ea168dccdeb3e141236f69f02eaec524"
+ integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==
+
resolve-cwd@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
@@ -12600,6 +13250,16 @@ thunky@^1.0.2:
resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d"
integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
+tiny-case@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/tiny-case/-/tiny-case-1.0.3.tgz#d980d66bc72b5d5a9ca86fb7c9ffdb9c898ddd03"
+ integrity sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==
+
+tiny-warning@^1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
+ integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
+
titleize@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/titleize/-/titleize-3.0.0.tgz#71c12eb7fdd2558aa8a44b0be83b8a76694acd53"
@@ -12641,6 +13301,11 @@ toidentifier@1.0.1:
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
+toposort@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330"
+ integrity sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==
+
tough-cookie@^4.0.0, tough-cookie@^4.1.3:
version "4.1.3"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf"
@@ -12730,7 +13395,7 @@ tslib@^1.8.1:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
-tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.5.0, tslib@^2.6.0:
+tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.5.0, tslib@^2.6.0:
version "2.6.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
@@ -12788,6 +13453,11 @@ type-fest@^0.21.3:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
+type-fest@^2.19.0:
+ version "2.19.0"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b"
+ integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==
+
type-fest@^3.0.0:
version "3.13.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.13.1.tgz#bb744c1f0678bea7543a2d1ec24e83e68e8c8706"
@@ -12852,6 +13522,25 @@ typeface-muli@^1.1.13:
resolved "https://registry.yarnpkg.com/typeface-muli/-/typeface-muli-1.1.13.tgz#e68fa5a2e9467f0ba250b3795ada812033b8227e"
integrity sha512-FrbDCi7OKz6Mg8oK3bwAg3OTy3sGHXqb3ekSnOyHhvVCfSF/aiRTjv/KE5RJ1CfddhYDlBuTCQFNUfOOSlHObw==
+typescript-compare@^0.0.2:
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/typescript-compare/-/typescript-compare-0.0.2.tgz#7ee40a400a406c2ea0a7e551efd3309021d5f425"
+ integrity sha512-8ja4j7pMHkfLJQO2/8tut7ub+J3Lw2S3061eJLFQcvs3tsmJKp8KG5NtpLn7KcY2w08edF74BSVN7qJS0U6oHA==
+ dependencies:
+ typescript-logic "^0.0.0"
+
+typescript-logic@^0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/typescript-logic/-/typescript-logic-0.0.0.tgz#66ebd82a2548f2b444a43667bec120b496890196"
+ integrity sha512-zXFars5LUkI3zP492ls0VskH3TtdeHCqu0i7/duGt60i5IGPIpAHE/DWo5FqJ6EjQ15YKXrt+AETjv60Dat34Q==
+
+typescript-tuple@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/typescript-tuple/-/typescript-tuple-2.2.1.tgz#7d9813fb4b355f69ac55032e0363e8bb0f04dad2"
+ integrity sha512-Zcr0lbt8z5ZdEzERHAMAniTiIKerFCMgd7yjq1fPnDJ43et/k9twIFQMUYff9k5oXcsQ0WpvFcgzK2ZKASoW6Q==
+ dependencies:
+ typescript-compare "^0.0.2"
+
typescript@^5.2.2:
version "5.2.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78"
@@ -13024,6 +13713,11 @@ url-parse@^1.5.3:
querystringify "^2.1.1"
requires-port "^1.0.0"
+use-sync-external-store@^1.0.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
+ integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
+
util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
@@ -13141,11 +13835,6 @@ wcwidth@^1.0.1:
dependencies:
defaults "^1.0.3"
-web-vitals@^2.1.4:
- version "2.1.4"
- resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-2.1.4.tgz#76563175a475a5e835264d373704f9dde718290c"
- integrity sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg==
-
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
@@ -13753,6 +14442,16 @@ yocto-queue@^0.1.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
+yup@^1.3.2:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/yup/-/yup-1.3.2.tgz#afffc458f1513ed386e6aaf4bcaa4e67a9e270dc"
+ integrity sha512-6KCM971iQtJ+/KUaHdrhVr2LDkfhBtFPRnsG1P8F4q3uUVQ2RfEM9xekpha9aA4GXWJevjM10eDcPQ1FfWlmaQ==
+ dependencies:
+ property-expr "^2.0.5"
+ tiny-case "^1.0.3"
+ toposort "^2.0.2"
+ type-fest "^2.19.0"
+
zip-stream@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.1.1.tgz#1337fe974dbaffd2fa9a1ba09662a66932bd7135"