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"