diff --git a/frontend/.env b/frontend/.env index 1786301..4ea5339 100644 --- a/frontend/.env +++ b/frontend/.env @@ -1 +1,8 @@ -REACT_APP_DEVCHALLENGE_API=http://localhost:3333 \ No newline at end of file +REACT_APP_DEVCHALLENGE_API=http://localhost:3333 +REACT_APP_FIREBASE_APIKEY=AIzaSyDlSDvVRBTuEBULLJ7F1Zny9uEKvm34U8I +REACT_APP_FIREBASE_AUTHDOMAIN=devchallenge-16edf.firebaseapp.com +REACT_APP_FIREBASE_PROJECTID=devchallenge-16edf +REACT_APP_FIREBASE_STORAGEBUCKET=devchallenge-16edf.appspot.com +REACT_APP_FIREBASE_MESSAGINGSENDERID=457450543508 +REACT_APP_FIREBASE_APPID=1:457450543508:web:6afbe9416f72e9830b5781 +REACT_APP_FIREBASE_MEASUREMENTID=G-KKVZ30J836 \ No newline at end of file diff --git a/frontend/.env.example b/frontend/.env.example new file mode 100644 index 0000000..7d2dcfa --- /dev/null +++ b/frontend/.env.example @@ -0,0 +1,8 @@ +REACT_APP_DEVCHALLENGE_API=http://localhost:3333 +REACT_APP_FIREBASE_APIKEY= +REACT_APP_FIREBASE_AUTHDOMAIN= +REACT_APP_FIREBASE_PROJECTID= +REACT_APP_FIREBASE_STORAGEBUCKET= +REACT_APP_FIREBASE_MESSAGINGSENDERID= +REACT_APP_FIREBASE_APPID= +REACT_APP_FIREBASE_MEASUREMENTID= \ No newline at end of file diff --git a/frontend/.gitignore b/frontend/.gitignore index e40d1f1..46beb45 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -13,6 +13,7 @@ # misc .DS_Store +.env .env.local .env.development.local .env.test.local @@ -21,3 +22,4 @@ npm-debug.log* yarn-debug.log* yarn-error.log* .vscode +yarn.lock diff --git a/frontend/package.json b/frontend/package.json index 97abb5e..54c8600 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,61 +1,64 @@ { - "name": "frontend", - "version": "0.1.0", - "private": true, - "dependencies": { - "@fortawesome/fontawesome-svg-core": "^1.2.28", - "@fortawesome/free-brands-svg-icons": "^5.13.0", - "@fortawesome/free-solid-svg-icons": "^5.13.0", - "@fortawesome/react-fontawesome": "^0.1.9", - "@lottiefiles/react-lottie-player": "^1.0.2", - "@testing-library/jest-dom": "^4.2.4", - "@testing-library/react": "^9.3.2", - "@testing-library/user-event": "^7.1.2", - "axios": "^0.21.2", - "dotenv": "^8.2.0", - "react": "^16.13.1", - "react-awesome-slider": "^4.1.0", - "react-dom": "^16.13.1", - "react-icons": "^3.11.0", - "react-loading-skeleton": "^2.0.1", - "react-router-dom": "^5.2.0", - "react-scripts": "3.4.1", - "react-toastify": "^6.0.5", - "react-web-vector-icons": "^1.0.2", - "styled-components": "^5.1.1", - "styled-media-query": "^2.1.2" - }, - "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" - }, - "eslintConfig": { - "extends": "react-app" - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, - "devDependencies": { - "babel-eslint": "^10.1.0", - "eslint": "^6.8.0", - "eslint-config-airbnb": "^18.1.0", - "eslint-config-prettier": "^6.11.0", - "eslint-plugin-import": "^2.20.2", - "eslint-plugin-jsx-a11y": "^6.2.3", - "eslint-plugin-prettier": "^3.1.3", - "eslint-plugin-react": "^7.20.0", - "eslint-plugin-react-hooks": "^2.5.1", - "prettier": "^2.0.5" - } + "name": "frontend", + "version": "0.1.0", + "private": true, + "dependencies": { + "@fortawesome/fontawesome-svg-core": "^1.2.28", + "@fortawesome/free-brands-svg-icons": "^5.13.0", + "@fortawesome/free-solid-svg-icons": "^5.13.0", + "@fortawesome/react-fontawesome": "^0.1.9", + "@lottiefiles/react-lottie-player": "^1.0.2", + "@testing-library/jest-dom": "^4.2.4", + "@testing-library/react": "^9.3.2", + "@testing-library/user-event": "^7.1.2", + "axios": "^0.21.2", + "dotenv": "^8.2.0", + "firebase": "^10.4.0", + "formik": "^2.4.4", + "react": "^16.13.1", + "react-awesome-slider": "^4.1.0", + "react-dom": "^16.13.1", + "react-icons": "^3.11.0", + "react-loading-skeleton": "^2.0.1", + "react-router-dom": "^5.2.0", + "react-scripts": "3.4.1", + "react-toastify": "^6.0.5", + "react-web-vector-icons": "^1.0.2", + "styled-components": "^5.1.1", + "styled-media-query": "^2.1.2", + "yup": "^1.2.0" + }, + "scripts": { + "start": "react-scripts --openssl-legacy-provider start ", + "build": "react-scripts --openssl-legacy-provider build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": "react-app" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "devDependencies": { + "babel-eslint": "^10.1.0", + "eslint": "^6.8.0", + "eslint-config-airbnb": "^18.1.0", + "eslint-config-prettier": "^6.11.0", + "eslint-plugin-import": "^2.20.2", + "eslint-plugin-jsx-a11y": "^6.2.3", + "eslint-plugin-prettier": "^3.1.3", + "eslint-plugin-react": "^7.20.0", + "eslint-plugin-react-hooks": "^2.5.1", + "prettier": "^2.0.5" + } } diff --git a/frontend/src/App.js b/frontend/src/App.js index c368411..bb4745e 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -1,8 +1,17 @@ +import './services/firebase-config'; import React from 'react'; import Routes from './routes'; +import { ChallengesContextProvider } from './contexts/ChallengesContext'; +import { AuthContextProvider } from './contexts/AuthContext'; function App() { - return ; + return ( + + + + + + ); } export default App; diff --git a/frontend/src/components/CategoriesList/styled.js b/frontend/src/components/CategoriesList/styled.js index 4a4dcdb..2ae9241 100644 --- a/frontend/src/components/CategoriesList/styled.js +++ b/frontend/src/components/CategoriesList/styled.js @@ -6,4 +6,5 @@ export const Techs = styled.section` flex-wrap: wrap; align-items: center; justify-content: center; + gap: 16px; `; diff --git a/frontend/src/components/CategoryListItem/index.js b/frontend/src/components/CategoryListItem/index.js index 365c5c3..3cde766 100644 --- a/frontend/src/components/CategoryListItem/index.js +++ b/frontend/src/components/CategoryListItem/index.js @@ -4,13 +4,12 @@ import Icon from 'react-web-vector-icons'; import * as S from './styled'; export default function CategoryListItem({ category }) { - const type = category.name.replace('-', '').toLowerCase(); return ( diff --git a/frontend/src/components/CategoryListItem/styled.js b/frontend/src/components/CategoryListItem/styled.js index b88bc99..c909175 100644 --- a/frontend/src/components/CategoryListItem/styled.js +++ b/frontend/src/components/CategoryListItem/styled.js @@ -5,11 +5,7 @@ import { Link } from 'react-router-dom'; export const Anchor = styled(Link)` text-decoration: none; - margin: 0 25px 0 25px; - ${media.lessThan('medium')` - margin: 15px 0px 10px 0px; - `} `; export const Card = styled.div` @@ -17,7 +13,7 @@ export const Card = styled.div` height: 300px; background-color: var(--secondary); border-radius: 20px; - + cursor: pointer; display: flex; flex-direction: column; @@ -38,5 +34,3 @@ export const Title = styled.h2` margin-top: 0.6em; `} `; - - diff --git a/frontend/src/components/ChallengeCard/index.js b/frontend/src/components/ChallengeCard/index.js index 3bc709d..4cd4ed7 100644 --- a/frontend/src/components/ChallengeCard/index.js +++ b/frontend/src/components/ChallengeCard/index.js @@ -2,13 +2,23 @@ import React, { useState, useEffect } from 'react'; import * as S from './styled'; -const colorMatch = { - beginner: 'nephritis', - intermediate: 'pumpkin', - advanced: 'pomegranate', - Mobile: 'blue', - Frontend: 'red', - Backend: 'light-purple', +const colorMatch = (option) => { + switch (option) { + case 'Iniciante': + return 'nephritis'; + case 'Intermediário': + return 'pumpkin'; + case 'Avançado': + return 'pomegranate'; + case 'Mobile': + return 'blue'; + case 'Front-end': + return 'red'; + case 'Back-end': + return 'light-purple'; + default: + return 'green'; + } }; function ChallengeCard({ challenge, progress, redirect, buttonText }) { @@ -34,24 +44,21 @@ function ChallengeCard({ challenge, progress, redirect, buttonText }) { }, [challenge.techs]); return ( - - + + {techs.map((item) => ( -

+

{item}

))}
- + {challenge.type} - + {challenge.level} @@ -60,10 +67,14 @@ function ChallengeCard({ challenge, progress, redirect, buttonText }) { {progress && }
- +

{challenge.name}

-

{challenge.description}

{' '} +

+ {challenge.description.length > 120 + ? challenge.description.substr(0, 120) + '...' + : challenge.description} +

{' '}
{buttonText} diff --git a/frontend/src/components/ChallengeCard/styled.js b/frontend/src/components/ChallengeCard/styled.js index bc87fea..3eef4cc 100644 --- a/frontend/src/components/ChallengeCard/styled.js +++ b/frontend/src/components/ChallengeCard/styled.js @@ -44,6 +44,8 @@ export const CardImage = styled.div` img { transition: 0.3s; width: 100%; + aspect-ratio: 16/9; + object-fit: cover; } img:hover { transform: scale(1.1); diff --git a/frontend/src/components/Footer/index.js b/frontend/src/components/Footer/index.js index c6fe560..9bb085f 100644 --- a/frontend/src/components/Footer/index.js +++ b/frontend/src/components/Footer/index.js @@ -1,52 +1,57 @@ import React from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faGithub, faDiscord, faInstagram, faTwitter } from '@fortawesome/free-brands-svg-icons'; +import { + faGithub, + faDiscord, + // faInstagram, + // faTwitter, +} from '@fortawesome/free-brands-svg-icons'; import * as S from './styled'; function Footer() { - return ( - - - + + {/* - - - - - */} + + + + {/* - - - - - - - DevChallenge - - - ); + */} + + + + + + DevChallenge + + + ); } -export default Footer; \ No newline at end of file +export default Footer; diff --git a/frontend/src/components/Header/index.js b/frontend/src/components/Header/index.js index f30c5ea..d37c081 100644 --- a/frontend/src/components/Header/index.js +++ b/frontend/src/components/Header/index.js @@ -49,7 +49,7 @@ export default function Header() { Comunidade -
  • + {/*
  • Entrar -
  • + */} diff --git a/frontend/src/components/Newsletter/index.js b/frontend/src/components/Newsletter/index.js index 41c98f8..9ce37e8 100644 --- a/frontend/src/components/Newsletter/index.js +++ b/frontend/src/components/Newsletter/index.js @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { Player } from '@lottiefiles/react-lottie-player'; -import api from '../../services/api'; import ToastNotification from '../../utils/toast'; +import { addEmailIntoNewsletter } from '../../services/NewsLetterFunctions'; import * as S from './styled'; @@ -11,16 +11,15 @@ export default function Newsletter() { async function handleSubscribe(e) { e.preventDefault(); - const data = { - email, - }; - try { - await api.post('newsletter', data); - ToastNotification.notify( - 'success', - 'Feito! Você será o primeiro a saber sobre novos desafios :)' - ); + const response = await addEmailIntoNewsletter(email); + if (response) { + ToastNotification.notify( + 'success', + 'Feito! Você será o primeiro a saber sobre novos desafios :)' + ); + setEmail(''); + } } catch (err) { ToastNotification.notify( 'error', @@ -46,7 +45,7 @@ export default function Newsletter() { Inscreva-se para ser o primeiro a saber sobre novos desafios - :) + 😀 { + setUser(null); + setIsLogged(false); + removeKeyLocalStorage('UID'); + localStorage.clear(); + }) + .catch((error) => { + ToastNotification.notify( + 'error', + `Houve um erro ao tentar sair. Tente novamente mais tarde` + ); + }); + } + + function handleForgotUser(email) { + if (isStringEmpty(email)) { + ToastNotification.notify( + 'error', + 'O campo email não foi preenchido' + ); + return true; + } + + sendPasswordResetEmail(authentication, email) + .then(() => { + ToastNotification.notify( + 'success', + 'Foi enviado um email com as instruções de recuperação.' + ); + }) + .catch((err) => { + console.log('handleForgotUser error', err); + ToastNotification.notify('error', AuthErrorHandler[err.code]); + }); + } + + async function getUserByID(id) { + const usersRef = doc(db, 'users', id); + const userSnap = await getDoc(usersRef); + const user = userSnap.data(); + + return user; + } + + function signInUser(email, password) { + if (isStringEmpty(email)) { + const status = { + status: false, + message: 'O campo email não foi preenchido', + }; + return status; + } + if (isStringEmpty(password)) { + const status = { + status: false, + message: 'O campo senha não foi preenchido', + }; + return status; + } + + return signInWithEmailAndPassword(authentication, email, password) + .then(async (re) => { + setIsLogged(true); + setKeyLocalStorage('UID', re.user.uid); + const currentUser = await getUserByID(re.user.uid); + setUser(currentUser); + const status = { + user: currentUser, + status: true, + }; + return status; + }) + .catch((err) => { + const status = { + status: false, + message: AuthErrorHandler[err.code], + err, + }; + + console.log('signInUser error', status); + return status; + }); + } + + async function RegisterUser({ email, password, user }) { + return createUserWithEmailAndPassword(authentication, email, password) + .then(async (re) => { + const newUser = { + uid: re.user.uid, + ...user, + }; + try { + await setDoc(doc(db, 'users', re.user.uid), newUser); + + setKeyLocalStorage('UID', re.user.uid); + + setUser(newUser); + return true; + } catch (err) { + ToastNotification.notify( + 'error', + 'Houve um erro ao cadastrar o usuario. Tente novamente mais tarde' + ); + return false; + } + }) + .catch((err) => { + ToastNotification.notify('error', AuthErrorHandler[err.code]); + return false; + }); + } + + useEffect(() => { + const value = localStorage.getItem('@isMenuHide'); + if (value !== null) { + setIsMenuHide(JSON.parse(value)); + } else { + localStorage.setItem('@isMenuHide', 'false'); + } + }, []); + + useEffect(() => { + const unsubscribe = authentication.onAuthStateChanged(async (user) => { + if (user) { + setIsLogged(true); + setKeyLocalStorage('UID', user.uid); + const loggedUser = await getUserByID(user.uid); + setUser(loggedUser); + } else { + setIsLogged(false); + } + }); + + return () => { + unsubscribe(); + }; + }, []); + + return ( + + {props.children} + + ); +} diff --git a/frontend/src/contexts/ChallengesContext.js b/frontend/src/contexts/ChallengesContext.js new file mode 100644 index 0000000..adee370 --- /dev/null +++ b/frontend/src/contexts/ChallengesContext.js @@ -0,0 +1,134 @@ +/* eslint-disable no-restricted-globals */ +import { + addDoc, + collection, + deleteDoc, + doc, + getDoc, + getDocs, + orderBy, + query, + setDoc, +} from 'firebase/firestore'; +import { + deleteObject, + getDownloadURL, + ref, + uploadBytesResumable, +} from 'firebase/storage'; +import React, { createContext, useCallback, useEffect, useState } from 'react'; +import { db, storage } from '../services/firebase-config'; + +export function deleteImageFromStorage(imagePath) { + if (!imagePath) return; + + const imageRef = ref(storage, imagePath); + + return deleteObject(imageRef) + .then(() => { + return true; + }) + .catch((error) => { + console.log('deleteImageFromStorage error', error); + return false; + }); +} + +export async function uploadImageAsync(file, path) { + if (!file) return; + + const storageRef = ref( + storage, + `/${path}/${Date.now()}-${encodeURI(file.name)}` + ); + await uploadBytesResumable(storageRef, file); + + return getDownloadURL(storageRef); +} + +export const ChallengesContext = createContext({}); + +export function ChallengesContextProvider(props) { + const [challengesList, setChallengesList] = useState([]); + + const [selectedChallenge, setSelectedChallenge] = useState(false); + + async function getAllChallenges() { + const contactsRef = collection(db, 'challenges'); + const result = getDocs(query(contactsRef, orderBy('createdAt', 'desc'))) + .then((snap) => { + let challengeList = []; + snap.docs.forEach((doc) => { + challengeList.push({ ...doc.data(), id: doc.id }); + }); + return challengeList; + }) + .catch((error) => { + console.log('getAllChallenges error', error); + return []; + }); + + return result; + } + + async function getChallengeByID(id) { + const challengeRef = doc(db, 'challenges', id); + const challengeSnap = await getDoc(challengeRef); + const challenge = challengeSnap.data(); + return challenge; + } + + async function updateChallenge(data) { + await setDoc(doc(db, 'challenges', data.id), data); + + await updateChallengesList(); + } + + async function addChallenge(data) { + const challengeRef = collection(db, 'challenges'); + await addDoc(challengeRef, data); + + await updateChallengesList(); + } + + async function handleDeleteChallenge(id) { + const response = confirm( + 'Deseja realmente deletar esse registro? Esta ação é irreverssível' + ); + if (response) { + const challenge = await getChallengeByID(id); + deleteImageFromStorage(challenge.avatar); + await deleteDoc(doc(db, 'challenges', id)); + } + await updateChallengesList(); + } + + const updateChallengesList = useCallback(async () => { + const response = await getAllChallenges(); + setChallengesList(response); + }, []); + + useEffect(() => { + const executeAsync = async () => { + await updateChallengesList(); + }; + executeAsync(); + }, [updateChallengesList]); + + return ( + + {props.children} + + ); +} diff --git a/frontend/src/hooks/useAuth.js b/frontend/src/hooks/useAuth.js new file mode 100644 index 0000000..e5ecaff --- /dev/null +++ b/frontend/src/hooks/useAuth.js @@ -0,0 +1,7 @@ +import { useContext } from 'react'; +import { AuthContext } from '../contexts/AuthContext'; + +export function useAuth() { + const value = useContext(AuthContext); + return value; +} diff --git a/frontend/src/hooks/useChallenges.js b/frontend/src/hooks/useChallenges.js new file mode 100644 index 0000000..985abf3 --- /dev/null +++ b/frontend/src/hooks/useChallenges.js @@ -0,0 +1,7 @@ +import { useContext } from 'react'; +import { ChallengesContext } from '../contexts/ChallengesContext'; + +export function useChallenges() { + const value = useContext(ChallengesContext); + return value; +} diff --git a/frontend/src/pages/Admin/Challenges/index.js b/frontend/src/pages/Admin/Challenges/index.js new file mode 100644 index 0000000..fb2aeea --- /dev/null +++ b/frontend/src/pages/Admin/Challenges/index.js @@ -0,0 +1,645 @@ +import { + faBars, + faCheck, + faEdit, + faExclamationCircle, + faImage, + faPlus, + faTimes, + faTrash, +} from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { useFormik } from 'formik'; +import React, { useEffect, useMemo, useState } from 'react'; +import { useLocation } from 'react-router-dom'; +import * as Yup from 'yup'; +import { uploadImageAsync } from '../../../contexts/ChallengesContext'; +import { useChallenges } from '../../../hooks/useChallenges'; +import ToastNotification from '../../../utils/toast'; +import * as S from './styled'; + +export default function AdminChallenges() { + const location = useLocation(); + const [open, setOpen] = useState(false); + const { + challengesList, + addChallenge, + handleDeleteChallenge, + } = useChallenges(); + const [clicked, setClicked] = useState(false); + + const [imagePreview, setImagePreview] = useState(null); + + const formSchema = useMemo(() => { + return Yup.object().shape({ + name: Yup.string().required('O campo Nome é obrigatório'), + description: Yup.string().required( + 'O campo Descrição é obrigatório' + ), + type: Yup.string().required('O campo Tipo é obrigatório'), + level: Yup.string().required('O campo Dificuldade é obrigatório'), + techs: Yup.array() + .of(Yup.string()) + .min(1, 'Selecione pelo menos uma linguagem') + .required('O campo Linguagens é obrigatório'), + background: Yup.mixed() + .test('fileFormat', 'A imagem deve ser PNG ou JPG', (value) => { + if (!value) { + return true; + } + const supportedFormats = ['image/png', 'image/jpeg']; + return supportedFormats.includes(value.type); + }) + .required('O campo Imagem é obrigatório'), + active: Yup.boolean(), + githubRepository: Yup.string().required( + 'O campo Repositório é obrigatório' + ), + username: Yup.string().required( + 'O campo Usuário github é obrigatório' + ), + }); + }, []); + + const formik = useFormik({ + initialValues: { + name: '', + description: '', + type: '', + level: '', + username: '', + githubRepository: '', + techs: [], + background: null, + active: true, + }, + validationSchema: formSchema, + onSubmit: (values) => { + handleSubmitForm(values); + }, + }); + + const handleRemoveTech = (index) => { + const result = formik.values.techs.filter( + (_, currentIndex) => !(currentIndex === index) + ); + formik.setFieldValue('techs', result); + }; + + const handleSubmitForm = async (formValues) => { + let uploadURLImage = await uploadImageAsync( + formValues.background, + 'challenges' + ); + + const data = { + type: formValues.type, + level: formValues.level, + techs: formValues.techs, + githubRepository: formValues.githubRepository, + username: formValues.username, + background: uploadURLImage, + name: formValues.name, + description: formValues.description, + createdAt: Date.now(), + active: formValues.active, + }; + + await addChallenge(data); + ToastNotification.notify('success', 'Desafio adicionado :)'); + setOpen(false); + }; + + useEffect(() => { + setClicked(false); + }, [location]); + + return ( + <> + {open && ( + + + + Adicionar Desafio + + setOpen(false)} + style={{ + backgroundColor: 'var(--tertiary)', + }} + > + + + + + + + + {!imagePreview ? ( + <> + Enviar imagem + + + ) : ( + + )} + { + formik.setFieldValue( + 'background', + event.target.files[0] + ); + + console.log( + URL.createObjectURL( + event.target.files[0] + ) + ); + setImagePreview( + URL.createObjectURL( + event.target.files[0] + ) + ); + }} + accept="image/*" + /> + + {formik.errors.background && ( + + + {formik.errors.background} + + )} + + + Nome do desafio + + formik.setFieldValue( + 'name', + event.target.value + ) + } + /> + {formik.errors.name && ( + + + {formik.errors.name} + + )} + + + + + Nome do Usuário github + + + formik.setFieldValue( + 'username', + event.target.value + ) + } + /> + {formik.errors.username && ( + + + {formik.errors.username} + + )} + + + + + Link do repositório + + + formik.setFieldValue( + 'githubRepository', + event.target.value + ) + } + /> + {formik.errors.githubRepository && ( + + + {formik.errors.githubRepository} + + )} + + + + Tipo + + formik.setFieldValue( + 'type', + event.target.value + ) + } + > + + + + + {formik.errors.type && ( + + + {formik.errors.type} + + )} + + + + Dificuldade + + formik.setFieldValue( + 'level', + event.target.value + ) + } + > + + + + + {formik.errors.level && ( + + + {formik.errors.level} + + )} + + + + Linguagens + + formik.setFieldValue('techs', [ + ...formik.values.techs, + event.target.value, + ]) + } + > + + + + + + + + + + + + + + + + + {formik.errors.techs && ( + + + {formik.errors.techs} + + )} + {formik.values.techs.length > 0 && + formik.values.techs.map( + (tech, index) => ( + + + {tech} + + + handleRemoveTech( + index + ) + } + style={{ + backgroundColor: + 'var(--red)', + }} + > + + + + ) + )} + + + + Descrição + + formik.setFieldValue( + 'description', + event.target.value + ) + } + > + {formik.values.description} + + + {formik.errors.description && ( + + + {formik.errors.description} + + )} + + + Adicionar desafio + + + + + + )} + +
    + + + DevChallenge + + + + Area administrativa + +
    + + setClicked(!clicked)}> + {!clicked ? ( + + ) : ( + + )} + + + +
      + {/*
    • + + Início + +
    • */} +
    • + + Desafios + +
    • + {/*
    • + + Comunidade + +
    • */} +
    +
    +
    + + + + Desafios cadastrados + setOpen(true)}> + + Adicionar + + + + + + + + + + + + + + + + + {challengesList.map((item) => ( + + {console.log(item)} + + + + + + + + + ))} + +
    + + Imagem + + + + Nome do desafio + + + + Tecnologia + + + + Tipo + + + + Dificuldade + + + + Visível + + + + Ações + +
    + + + {item.name} + + {item.type} + + + {item.techs.join(',')} + + + {item.level} + + + + + + + + handleDeleteChallenge(item.id) + } + style={{ + backgroundColor: '#E50000', + }} + > + + + + {/* + + */} + +
    +
    + + ); +} diff --git a/frontend/src/pages/Admin/Challenges/styled.js b/frontend/src/pages/Admin/Challenges/styled.js new file mode 100644 index 0000000..befc250 --- /dev/null +++ b/frontend/src/pages/Admin/Challenges/styled.js @@ -0,0 +1,313 @@ +import styled from 'styled-components'; +import media from 'styled-media-query'; +import { NavLink } from 'react-router-dom'; + +export const UploadLabel = styled.label` + width: 320px; + aspect-ratio: 16/9; + border: 1px dashed var(--white); + border-radius: 8px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 16px; + + font-size: 16px; + font-weigth: bold; + color: var(--white); + cursor: pointer; + + transition: 0.2s; + &:hover { + border: 1px dashed var(--yellow); + color: var(--yellow); + } +`; + +export const Form = styled.form` + display: flex; + flex-direction: column; + gap: 16px; + width: 100%; +`; + +export const InputGroup = styled.div` + display: flex; + flex-direction: column; + gap: 16px; + width: 100%; +`; +export const InputLabel = styled.label` + color: white; + font-weight: normal; +`; + +export const Textarea = styled.textarea` + min-height: 96px; + width: 100%; + border-radius: 6px; + padding: 4px 16px; + font-size: 14px; + border: none; + + &:focus { + border: 2px solid var(--yellow); + } +`; + +export const Select = styled.select` + height: 48px; + width: 100%; + border-radius: 6px; + padding: 4px 16px; + font-size: 14px; + border: none; + + &:focus { + border: 2px solid var(--yellow); + } +`; + +export const InputText = styled.input` + height: 48px; + width: 100%; + border-radius: 6px; + padding: 4px 16px; + font-size: 14px; + border: none; + + &:focus { + border: 2px solid var(--yellow); + } +`; + +export const Modal = styled.div` + position: fixed; + width: 100vw; + height: 100vh; + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + background-color: #000000cc; + overflow: hidden; +`; + +export const ModalCard = styled.div` + width: 80%; + max-height: 70vh; + overflow-x: hidden; + overflow-y: auto; + display: flex; + flex-direction: column; + background-color: var(--tertiary); + padding: 16px 24px; + border-radius: 8px; +`; + +export const Row = styled.div` + display: flex; +`; + +export const Col = styled.div` + display: flex; + flex-direction: column; +`; + +export const Subtitle = styled.h2` + color: white; +`; + +export const Paragraph = styled.p` + color: white; + font-weight: normal; +`; + +export const IconButton = styled.button` + width: 32px; + height: 32px; + background-color: var(--yellow); + color: var(--white); + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + border-radius: 100%; + cursor: pointer; + transition: 0.2s; + + &:hover { + filter: contrast(0.7); + } +`; + +export const Button = styled.button` + padding: 8px 16px; + background-color: var(--yellow); + color: var(--white); + display: flex; + flex-direction: row; + gap: 8px; + border-radius: 8px; + cursor: pointer; + transition: 0.2s; + + &:hover { + filter: contrast(0.7); + } +`; + +export const ImageRow = styled.img` + width: 48px; + height: 48px; + border-radius: 4px; + object-fit: cover; +`; + +export const Main = styled.main` + display: flex; + flex-direction: column; + + padding: 16px 80px; + margin-top: 40px; + + font-weight: normal; + + ${media.between('medium', 'large')` + padding: 16px 60px; + `} + + ${media.lessThan('medium')` + display: flex; + align-items: flex-start; + flex-direction: column; + padding: 8px; + `} +`; + +export const Header = styled.header` + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid #212121; + + padding: 0px 80px 16px 80px; + margin-top: 40px; + + font-weight: normal; + + ${media.between('medium', 'large')` + padding: 0px 60px 16px 60px; + `} + + ${media.lessThan('medium')` + display: flex; + align-items: flex-start; + flex-direction: column; + justify-content: center; + padding: 0; + position: relative; + `} +`; + +export const Title = styled.div` + font-size: 30px; + font-weight: bold; + color: var(--white); + + span { + color: var(--yellow); + } + + ${media.lessThan('medium')` + margin-left: 20px; + `} +`; + +export const MenuMobile = styled.div` + display: none; + + transition: 0.25s; + + ${media.lessThan('medium')` + display: block; + position: absolute; + font-size: 25px; + color: var(--white); + top: 2; + right: 0; + margin-right: 20px; + cursor: pointer; + + z-index: 9999; + `} +`; + +export const Menu = styled.div` + ul { + display: flex; + align-items: center; + + list-style-type: none; + + font-size: 20px; + color: var(--white); + font-weight: 400; + + li ~ li { + margin-left: 20px; + } + + li:hover { + opacity: 0.8; + } + + ${media.lessThan('medium')` + background-color: var(--primary); + flex-direction: column; + width: 100%; + position: absolute; + justify-content: space-around; + height: 300px; + align-items: center; + margin-top: 0.5em; + top: -100%; + transform: ${({ open }) => + open ? 'translateY(-100%)' : 'translateY(0)'}; + transition: transform 0.5s ease; + z-index: 1000; + li ~ li { + margin-left: 0; + } + `} + } +`; + +export const StyledLink = styled(NavLink)` + text-decoration: none; + color: var(--white); + + cursor: pointer; + + &.is-active { + color: var(--yellow); + } +`; + +export const ButtonLink = styled(NavLink)` + background-color: var(--quaternary); + + width: 100px; + padding: 5px 30px; + border-radius: 25px; + + text-align: center; + text-decoration: none; + color: var(--white); + + &.is-active { + background-color: var(--yellow); + color: var(--tertiary); + } +`; diff --git a/frontend/src/pages/Admin/Login/index.js b/frontend/src/pages/Admin/Login/index.js new file mode 100644 index 0000000..8ab27ca --- /dev/null +++ b/frontend/src/pages/Admin/Login/index.js @@ -0,0 +1,196 @@ +import { + faExclamationCircle, + faEye, + faEyeSlash, +} from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { useFormik } from 'formik'; +import React, { useMemo, useState } from 'react'; +import { useHistory } from 'react-router-dom'; +import * as Yup from 'yup'; +import { useAuth } from '../../../hooks/useAuth'; +import * as S from './styled'; + +export default function AdminLogin() { + const { signInUser, RegisterUser } = useAuth(); + const history = useHistory(); + + const [isShow, setIsShow] = useState(false); + const [showRegister, setShowRegister] = useState(false); + + const formSchema = useMemo(() => { + return Yup.object().shape({ + email: Yup.string().required('O campo Email é obrigatório'), + password: Yup.string() + .min(6, 'A senha deve ter pelo menos 6 caracteres') + .required('A senha é obrigatória'), + }); + }, []); + + const formik = useFormik({ + initialValues: { + email: '', + password: '', + }, + validationSchema: formSchema, + onSubmit: (values) => { + handleSubmitForm(values); + }, + }); + + const handleSubmitForm = async (formValues) => { + try { + const isLogged = await signInUser( + formValues.email, + formValues.password + ); + if (isLogged.status) { + if (isLogged.user.active) { + history.push('/admin/challenges'); + } else { + alert( + 'Solicite permissão ao administrador para acessar o sistema' + ); + } + } else { + alert(isLogged.message); + setShowRegister(true); + } + } catch (error) { + console.log('error', error); + } + }; + + const handleRegister = async () => { + try { + const isRegistered = await RegisterUser({ + email: formik.values.email, + password: formik.values.password, + user: { + email: formik.values.email, + password: formik.values.password, + active: false, + }, + }); + if (isRegistered) { + alert( + 'Solicite a liberação do acesso ao administrador do sistema' + ); + } else { + alert('Houve um problema ao cadastrar'); + setShowRegister(true); + } + } catch (error) { + console.log('error', error); + } + }; + + return ( + <> + + + + + DevChallenge + + Desafie-se, aprenda e cresça! + + + + Email + + formik.setFieldValue( + 'email', + event.target.value + ) + } + /> + {formik.errors.email && ( + + + {formik.errors.email} + + )} + + + Senha + + + formik.setFieldValue( + 'password', + event.target.value + ) + } + /> + + setIsShow(!isShow)} + style={{ + backgroundColor: 'var(--yellow)', + }} + > + + + + {formik.errors.password && ( + + + {formik.errors.password} + + )} + + + + Entrar + + {showRegister && ( + + Cadastrar + + )} + + + + + ); +} diff --git a/frontend/src/pages/Admin/Login/styled.js b/frontend/src/pages/Admin/Login/styled.js new file mode 100644 index 0000000..a3f1a78 --- /dev/null +++ b/frontend/src/pages/Admin/Login/styled.js @@ -0,0 +1,226 @@ +import styled from 'styled-components'; +import media from 'styled-media-query'; +import { NavLink } from 'react-router-dom'; + +export const UploadLabel = styled.label` + width: 320px; + aspect-ratio: 16/9; + border: 1px dashed var(--white); + border-radius: 8px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 16px; + + font-size: 16px; + font-weigth: bold; + color: var(--white); + cursor: pointer; + + transition: 0.2s; + &:hover { + border: 1px dashed var(--yellow); + color: var(--yellow); + } +`; + +export const Form = styled.form` + display: flex; + flex-direction: column; + gap: 16px; + width: 100%; +`; + +export const InputGroup = styled.div` + display: flex; + flex-direction: column; + gap: 16px; + width: 100%; +`; +export const InputLabel = styled.label` + color: white; + font-weight: normal; +`; + +export const Textarea = styled.textarea` + min-height: 96px; + width: 100%; + border-radius: 6px; + padding: 4px 16px; + font-size: 14px; + border: none; + + &:focus { + border: 2px solid var(--yellow); + } +`; + +export const Select = styled.select` + height: 48px; + width: 100%; + border-radius: 6px; + padding: 4px 16px; + font-size: 14px; + border: none; + + &:focus { + border: 2px solid var(--yellow); + } +`; + +export const InputText = styled.input` + height: 48px; + width: 100%; + border-radius: 6px; + padding: 4px 16px; + font-size: 14px; + border: none; + + &:focus { + border: 2px solid var(--yellow); + } +`; + +export const Modal = styled.div` + position: fixed; + width: 100vw; + height: 100vh; + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + background-color: #000000cc; + overflow: hidden; +`; + +export const ModalCard = styled.div` + width: 80%; + max-height: 70vh; + overflow-x: hidden; + overflow-y: auto; + display: flex; + flex-direction: column; + background-color: var(--tertiary); + padding: 16px 24px; + border-radius: 8px; +`; + +export const Row = styled.div` + display: flex; +`; + +export const Col = styled.div` + display: flex; + flex-direction: column; +`; + +export const Subtitle = styled.h2` + color: white; +`; + +export const Paragraph = styled.p` + color: white; + font-weight: normal; +`; + +export const IconButton = styled.button` + width: 32px; + height: 32px; + background-color: var(--yellow); + color: var(--white); + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + border-radius: 100%; + cursor: pointer; + transition: 0.2s; + + &:hover { + filter: contrast(0.7); + } +`; + +export const Button = styled.button` + padding: 8px 16px; + background-color: var(--yellow); + color: var(--white); + display: flex; + flex-direction: row; + gap: 8px; + border-radius: 8px; + cursor: pointer; + transition: 0.2s; + + &:hover { + filter: contrast(0.7); + } +`; + +export const ImageRow = styled.img` + width: 48px; + height: 48px; + border-radius: 4px; + object-fit: cover; +`; + +export const Main = styled.main` + display: flex; + flex-direction: column; + + padding: 16px 80px; + margin-top: 40px; + + font-weight: normal; + + ${media.between('medium', 'large')` + padding: 16px 60px; + `} + + ${media.lessThan('medium')` + display: flex; + align-items: flex-start; + flex-direction: column; + padding: 8px; + `} +`; + +export const Header = styled.header` + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid #212121; + + padding: 0px 80px 16px 80px; + margin-top: 40px; + + font-weight: normal; + + ${media.between('medium', 'large')` + padding: 0px 60px 16px 60px; + `} + + ${media.lessThan('medium')` + display: flex; + align-items: flex-start; + flex-direction: column; + justify-content: center; + padding: 0; + position: relative; + `} +`; + +export const Title = styled.div` + font-size: 30px; + font-weight: bold; + color: var(--white); + + span { + color: var(--yellow); + } + + ${media.lessThan('medium')` + margin-left: 20px; + `} +`; diff --git a/frontend/src/pages/Challenges/index.js b/frontend/src/pages/Challenges/index.js index 87c85bd..b6ce1d0 100644 --- a/frontend/src/pages/Challenges/index.js +++ b/frontend/src/pages/Challenges/index.js @@ -1,8 +1,8 @@ -import React, { useEffect, useState } from 'react'; -import api from '../../services/api'; -import ChallengesSkeleton from '../../components/ChallengesSkeleton'; +import React, { useEffect, useMemo, useState } from 'react'; import ChallengeCard from '../../components/ChallengeCard'; +import ChallengesSkeleton from '../../components/ChallengesSkeleton'; import Header from '../../components/Header'; +import { useChallenges } from '../../hooks/useChallenges'; import * as S from './styled'; const languages = [ @@ -14,57 +14,43 @@ const languages = [ ]; const types = [ - { id: 1, name: 'Frontend' }, - { id: 2, name: 'Backend' }, + { id: 1, name: 'Front-end' }, + { id: 2, name: 'Back-end' }, { id: 3, name: 'Mobile' }, ]; export default function Challenges({ location }) { - const [challenges, setChallenges] = useState([]); - const [filteredChallenges, setFilteredChallenges] = useState([]); + const { challengesList } = useChallenges(); const [loading, setLoading] = useState(true); const [languageFilter, setLanguageFilter] = useState(''); - const [typeFilter, setTypeFilter] = useState(location.search.split('=')[1]); + const [typeFilter, setTypeFilter] = useState(''); - function capitalize(s) { - return s && s[0].toUpperCase() + s.slice(1); - } + const filteredChallenges = useMemo(() => { + const activeOnly = challengesList.filter((item) => item.active); - useEffect(() => { - window.scrollTo(0, 0); - async function loadChallenges() { - const response = await api.get('/challenges'); + const languageFiltered = + languageFilter === '' + ? activeOnly + : activeOnly.filter((item) => + item.techs.includes(languageFilter) + ); - setChallenges(response.data); - setFilteredChallenges(response.data); + const typeFiltered = + typeFilter === '' + ? languageFiltered + : languageFiltered.filter((item) => item.type === typeFilter); - setLoading(false); - } + setLoading(false); - loadChallenges(); - }, [location]); + return typeFiltered; + }, [challengesList, languageFilter, typeFilter]); useEffect(() => { - let filtered = challenges; - if (typeFilter) { - filtered = filtered.filter( - (challenge) => - challenge.type.toLowerCase() === typeFilter.toLowerCase() - ); - } - if (languageFilter) { - filtered = filtered.filter((challenge) => { - const [ techs ] = challenge.techs; - const serializedTechs = techs.split(', '); - const hasSelectedTech = - serializedTechs.includes(languageFilter) || - serializedTechs.includes('Free Choice'); - - return hasSelectedTech; - }); + if (location.search.includes('?type=')) { + const queryTypeParam = location.search.replace('?type=', ''); + setTypeFilter(queryTypeParam); } - setFilteredChallenges(filtered); - }, [typeFilter, languageFilter, challenges]); + }, [location]); return ( <> @@ -80,11 +66,11 @@ export default function Challenges({ location }) { onChange={(e) => { setTypeFilter(e.target.value); }} - defaultValue={capitalize(typeFilter)} + value={typeFilter} > {types.map((type) => ( - ))} @@ -120,9 +106,9 @@ export default function Challenges({ location }) { return ( ); })} diff --git a/frontend/src/pages/Challenges/styled.js b/frontend/src/pages/Challenges/styled.js index ece3cbe..66c2a63 100644 --- a/frontend/src/pages/Challenges/styled.js +++ b/frontend/src/pages/Challenges/styled.js @@ -14,7 +14,6 @@ export const Section = styled.section` flex-wrap: wrap; max-width: 100vw; align-items: center; - justify-content: center; `; export const Head = styled.div` diff --git a/frontend/src/pages/Detail/index.js b/frontend/src/pages/Detail/index.js index dec6bb7..0355cc1 100644 --- a/frontend/src/pages/Detail/index.js +++ b/frontend/src/pages/Detail/index.js @@ -8,6 +8,7 @@ import DevCard from '../../components/DevCard'; import * as S from './styled'; import Header from '../../components/Header'; +import { useChallenges } from '../../hooks/useChallenges'; const includes = [ { @@ -48,34 +49,48 @@ const starts = [ }, ]; -const colorMatch = { - beginner: 'nephritis', - intermediate: 'pumpkin', - advanced: 'pomegranate', - Mobile: 'blue', - Frontend: 'red', - Backend: 'light-purple', +const colorMatch = (option) => { + switch (option) { + case 'Iniciante': + return 'nephritis'; + case 'Intermediário': + return 'pumpkin'; + case 'Avançado': + return 'pomegranate'; + case 'Mobile': + return 'blue'; + case 'Front-end': + return 'red'; + case 'Back-end': + return 'light-purple'; + default: + return 'green'; + } }; export default function Detail() { + const { challengesList } = useChallenges(); const [challenge, setChallenge] = useState({}); const [techs, setTechs] = useState([]); - const [dev, setDev] = useState({}); + // const [dev, setDev] = useState({}); const [images, setImages] = useState([]); const { id } = useParams(); useEffect(() => { window.scrollTo(0, 0); async function loadChallenge() { - const response = await api.get(`/challenges/${id}`); - setChallenge(response.data); - setDev(response.data.dev_id); - setImages(response.data.images); - setTechs(response.data.techs); + const response = challengesList.filter((item) => item.id === id); + + setChallenge(response[0]); + // setDev(response.data.dev_id); + setImages([response[0]?.background]); + setTechs(response[0]?.techs); } loadChallenge(); - }, [id]); + }, [id, challengesList]); + + if (!challenge || !techs || !images) return null; return ( <> @@ -84,30 +99,30 @@ export default function Detail() { -

    {challenge.name}

    +

    {challenge?.name}

    - - {challenge.level} + + {challenge?.level} - - {challenge.type} + + {challenge?.type} - {techs[0]?.split(', ').map((item, idx) => ( + {techs?.map((item, idx) => ( {item} ))} - {challenge.description} + {challenge?.description} Iniciar desafio @@ -131,13 +146,13 @@ export default function Detail() {

    Sobre o desafio

    -

    Seu desafio é {challenge.brief}.

    +

    Seu desafio é {challenge?.brief}.

    O que está incluso?

    - {challenge.type === 'Backend' ? ( + {challenge?.type === 'Backend' ? ( Readme com instruções de requisitos e as rotas da @@ -166,11 +181,11 @@ export default function Detail() {
    diff --git a/frontend/src/pages/Detail/styled.js b/frontend/src/pages/Detail/styled.js index 2ea2aff..eb29b38 100644 --- a/frontend/src/pages/Detail/styled.js +++ b/frontend/src/pages/Detail/styled.js @@ -172,11 +172,14 @@ export const ChallengeLink = styled.a` `; export const Demo = styled.div` - width: 47em; + width: 42em; + padding: 0 8px; border-radius: 10px; .image { height: 100%; + aspect-ratio: 1/1; + object-fit: cover; } .slider { diff --git a/frontend/src/routes.js b/frontend/src/routes.js index 0bc93a5..d69c6d3 100644 --- a/frontend/src/routes.js +++ b/frontend/src/routes.js @@ -1,23 +1,47 @@ import React from 'react'; -import { BrowserRouter, Switch, Route, Redirect } from 'react-router-dom'; +import { BrowserRouter, Redirect, Route, Switch } from 'react-router-dom'; import { ToastContainer } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; -import Home from './pages/Home'; +import Footer from './components/Footer'; import Challenges from './pages/Challenges'; +import Dashboard from './pages/Dashboard'; import Detail from './pages/Detail'; import Devs from './pages/Devs'; -import Submit from './pages/Submit'; +import Home from './pages/Home'; import MyChallenges from './pages/MyChallenges'; -import Dashboard from './pages/Dashboard'; -import Footer from './components/Footer'; +import Submit from './pages/Submit'; import ToDoChallenge from './pages/ToDoChallenge'; -import { Container } from './styles/GlobalStyles'; import ConstructPage from './pages/ConstructPage'; +import { Container } from './styles/GlobalStyles'; + +import { useAuth } from './hooks/useAuth'; +import AdminChallenges from './pages/Admin/Challenges'; +import AdminLogin from './pages/Admin/Login'; const logged = false; +const ProtectedRoute = ({ component: Component, isLogged, ...rest }) => { + return ( + + isLogged ? ( + + ) : ( + + ) + } + /> + ); +}; + const LoggedRoute = ({ component: Component, ...rest }) => ( ( ); function Routes() { + const { isLogged } = useAuth(); + return ( + + + + diff --git a/frontend/src/services/NewsLetterFunctions.js b/frontend/src/services/NewsLetterFunctions.js new file mode 100644 index 0000000..822f88b --- /dev/null +++ b/frontend/src/services/NewsLetterFunctions.js @@ -0,0 +1,49 @@ +import { addDoc, collection, getDocs, query, where } from 'firebase/firestore'; +import ToastNotification from '../utils/toast'; +import { db } from './firebase-config'; + +async function addEmailIntoNewsletter(email) { + try { + // Verificando se o email ja está cadastrado + const newsletterRef = collection(db, 'newsletter'); + const newsletterSnap = await getDocs( + query(newsletterRef, where('email', '==', email)) + ) + .then((snap) => { + const newsletterArray = []; + snap.docs.forEach((doc) => { + newsletterArray.push(doc.data()); + }); + + return newsletterArray.length === 0; + }) + .catch((error) => { + console.log('newsletterSnap error', error); + }); + + if (!newsletterSnap) { + ToastNotification.notify( + 'error', + 'Parece que você já cadastrou este email.' + ); + + return false; + } + + const data = { + email, + optInDate: Date.now(), + active: true, + optOutDate: '', + }; + + await addDoc(newsletterRef, data); + + return true; + } catch (error) { + console.log('addEmailIntoNewsletter', error); + return false; + } +} + +export { addEmailIntoNewsletter }; diff --git a/frontend/src/services/firebase-config.js b/frontend/src/services/firebase-config.js new file mode 100644 index 0000000..ae898c4 --- /dev/null +++ b/frontend/src/services/firebase-config.js @@ -0,0 +1,19 @@ +import { initializeApp } from 'firebase/app'; +import { getAuth } from 'firebase/auth'; +import { getFirestore } from 'firebase/firestore'; +import { getStorage } from 'firebase/storage'; + +const firebaseConfig = { + apiKey: process.env.REACT_APP_FIREBASE_APIKEY, + authDomain: process.env.REACT_APP_FIREBASE_AUTHDOMAIN, + projectId: process.env.REACT_APP_FIREBASE_PROJECTID, + storageBucket: process.env.REACT_APP_FIREBASE_STORAGEBUCKET, + messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGINGSENDERID, + appId: process.env.REACT_APP_FIREBASE_APPID, + measurementId: process.env.REACT_APP_FIREBASE_MEASUREMENTID, +}; + +const app = initializeApp(firebaseConfig); +export const authentication = getAuth(app); +export const db = getFirestore(app); +export const storage = getStorage(app); diff --git a/frontend/src/utils/handleFirebaseError.js b/frontend/src/utils/handleFirebaseError.js new file mode 100644 index 0000000..0e92d70 --- /dev/null +++ b/frontend/src/utils/handleFirebaseError.js @@ -0,0 +1,18 @@ +export const AuthErrorHandler = { + generic: + 'Houve um erro ao cadastrar o usuario. Tente novamente mais tarde.', + 'auth/weak-password': + 'Senha digitada é muito fraca. Tente usar mais de 6 caracteres com números e caracteres especiais.', + 'auth/email-already-in-use': 'O email fornecido ja está em uso.', + 'auth/email-already-exists': + 'O e-mail fornecido já está em uso por outro usuário.', + 'auth/internal-error': + 'Erro interno do servidor. Tente novamente mais tarde.', + 'auth/invalid-email': 'Email inválido.', + 'auth/invalid-login-credentials': 'Email ou senha inválido.', + 'auth/user-not-found': + 'Não há registro de usuário existente correspondente ao identificador fornecido.', + 'auth/wrong-password': 'Senha digitada não esta correta', + 'resource-exhausted': + 'Servidores em manutenção. Se esse erro persistir, contate o administrador do sistema.', +}; diff --git a/frontend/src/utils/string.js b/frontend/src/utils/string.js new file mode 100644 index 0000000..1527f1e --- /dev/null +++ b/frontend/src/utils/string.js @@ -0,0 +1,3 @@ +export const isStringEmpty = (text) => { + return text === ''; +}; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index ccabab1..f2978d7 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1122,6 +1122,378 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46" integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA== +"@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== + "@fortawesome/fontawesome-common-types@^0.2.28": version "0.2.28" resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.28.tgz#1091bdfe63b3f139441e9cba27aa022bff97d8b2" @@ -1155,6 +1527,24 @@ dependencies: prop-types "^15.7.2" +"@grpc/grpc-js@~1.9.0": + version "1.9.3" + resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.9.3.tgz#811cc49966ab7ed96efa31d213e80d671fd13839" + integrity sha512-b8iWtdrYIeT5fdZdS4Br/6h/kuk0PW5EVBUGk1amSbrpL8DlktJD43CdcCWwRdd6+jgwHhADSbL9CsNnm6EUPA== + dependencies: + "@grpc/proto-loader" "^0.7.8" + "@types/node" ">=12.12.47" + +"@grpc/proto-loader@^0.7.8": + version "0.7.9" + resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.9.tgz#3ca68236f1a0d77566dafa53c715eb31d096279a" + integrity sha512-YJsOehVXzgurc+lLAxYnlSMc1p/Gu6VAvnfx0ATi2nzvr0YZcjhmZDeY8SeAKv1M7zE3aEJH0Xo9mK1iZ8GYoQ== + dependencies: + lodash.camelcase "^4.3.0" + long "^5.0.0" + protobufjs "^7.2.4" + yargs "^17.7.2" + "@hapi/address@2.x.x": version "2.1.4" resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5" @@ -1365,6 +1755,59 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== + dependencies: + "@protobufjs/aspromise" "^1.1.1" + "@protobufjs/inquire" "^1.1.0" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== + "@sheerun/mutationobserver-shim@^0.3.2": version "0.3.3" resolved "https://registry.yarnpkg.com/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz#5405ee8e444ed212db44e79351f0c70a582aae25" @@ -1607,6 +2050,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.2.tgz#ace1880c03594cc3e80206d96847157d8e7fa349" integrity sha512-bnoqK579sAYrQbp73wwglccjJ4sfRdKU7WNEZ5FW4K2U6Kc0/eZ5kvXG0JKsEKFB50zrFmfFt52/cvBbZa7eXg== +"@types/node@>=12.12.47", "@types/node@>=13.7.0": + version "20.6.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.6.1.tgz#8b589bba9b2af0128796461a0979764562687e6f" + integrity sha512-4LcJvuXQlv4lTHnxwyHQZ3uR9Zw2j7m1C9DfuwoTFQQP4Pmu04O6IfLYgMmHoOCt0nosItLLZAH+sOrRE0Bo8g== + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" @@ -2165,6 +2613,11 @@ ansi-regex@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -3173,6 +3626,15 @@ cliui@^5.0.0: strip-ansi "^5.2.0" wrap-ansi "^5.1.0" +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + clone-deep@^0.2.4: version "0.2.4" resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-0.2.4.tgz#4e73dd09e9fb971cc38670c5dced9c1896481cc6" @@ -3841,6 +4303,11 @@ deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +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== + default-gateway@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" @@ -4257,6 +4724,11 @@ es6-symbol@^3.1.1, es6-symbol@~3.1.3: d "^1.0.1" ext "^1.1.2" +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -4768,6 +5240,13 @@ fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +faye-websocket@0.11.4: + version "0.11.4" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + faye-websocket@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" @@ -4918,6 +5397,38 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" +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@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" @@ -4995,6 +5506,19 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" +formik@^2.4.4: + version "2.4.4" + resolved "https://registry.yarnpkg.com/formik/-/formik-2.4.4.tgz#41b775db867713fd4ea34930068391b01749b69c" + integrity sha512-MV99upag7fCC3JfsI60WcxhymwNZnJUcMcnGuoz6mDf78SUfBbKjmfcA9LzHx4lEmjzmOflhP7oqz+ZQv5eStg== + dependencies: + 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.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -5102,7 +5626,7 @@ get-caller-file@^1.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== -get-caller-file@^2.0.1: +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -5368,7 +5892,7 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0: +hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0: 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== @@ -5548,6 +6072,16 @@ icss-utils@^4.0.0, icss-utils@^4.1.1: dependencies: postcss "^7.0.14" +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: + version "7.1.1" + resolved "https://registry.yarnpkg.com/idb/-/idb-7.1.1.tgz#d910ded866d32c7ced9befc5bfdf36f572ced72b" + integrity sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ== + identity-obj-proxy@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz#94d2bda96084453ef36fbc5aaec37e0f79f1fc14" @@ -6866,11 +7400,21 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.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._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== + lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -6906,11 +7450,21 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + loglevel@^1.6.6: version "1.6.7" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.7.tgz#b3e034233188c68b889f5b862415306f565e2c56" integrity sha512-cY2eLFrQSAfVPhCgH1s7JI73tMbg9YC3v3+ZHVW67sBS7UxWzNEk/ZBbSfLykBWHp33dqqtOv82gjhKEi81T/A== +long@^5.0.0: + version "5.2.3" + resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" + integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== + loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -7339,6 +7893,13 @@ no-case@^3.0.3: lower-case "^2.0.1" tslib "^1.10.0" +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-forge@0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" @@ -8824,6 +9385,29 @@ prop-types@^15.6.2, prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.8.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== + +protobufjs@^7.2.4: + version "7.2.5" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.5.tgz#45d5c57387a6d29a17aab6846dcc283f9b8e7f2d" + integrity sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/node" ">=13.7.0" + long "^5.0.0" + proxy-addr@~2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" @@ -9033,6 +9617,11 @@ react-error-overlay@^6.0.7: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108" integrity sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA== +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-icons@^3.11.0: version "3.11.0" resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-3.11.0.tgz#2ca2903dfab8268ca18ebd8cc2e879921ec3b254" @@ -10217,6 +10806,15 @@ string-width@^4.1.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" +string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string.prototype.matchall@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.2.tgz#48bb510326fb9fdeb6a33ceaa81a6ea04ef7648e" @@ -10296,6 +10894,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -10523,12 +11128,17 @@ timsort@^0.3.0: resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= +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-invariant@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw== -tiny-warning@^1.0.0, tiny-warning@^1.0.3: +tiny-warning@^1.0.0, tiny-warning@^1.0.2, tiny-warning@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== @@ -10592,6 +11202,11 @@ toidentifier@1.0.0: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== +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@^2.3.3, tough-cookie@^2.3.4, tough-cookie@^2.5.0, tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" @@ -10607,6 +11222,11 @@ tr46@^1.0.1: dependencies: punycode "^2.1.0" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + ts-pnp@1.1.6, ts-pnp@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.6.tgz#389a24396d425a0d3162e96d2b4638900fdc289a" @@ -10617,6 +11237,11 @@ tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== +tslib@^2.0.0, tslib@^2.1.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + tsutils@^3.17.1: version "3.17.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" @@ -10658,6 +11283,11 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +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-is@~1.6.17, type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -10969,6 +11599,11 @@ web-animation-club@^0.6.0: resolved "https://registry.yarnpkg.com/web-animation-club/-/web-animation-club-0.6.0.tgz#c1dd79646b21e4265b5f95ae9cb8b93b73e05e47" integrity sha512-9W+EQu1HiaPLe/7WZlhJ2ULqQ4VL80RPDYW+ZcjfTKp6ayOuT1k3SVO6+tu0VBRmOqueJ/mrG+rjjInIv8Aglg== +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" @@ -11139,6 +11774,14 @@ whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + whatwg-url@^6.4.1: version "6.5.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" @@ -11347,6 +11990,15 @@ wrap-ansi@^5.1.0: string-width "^3.0.0" strip-ansi "^5.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -11409,6 +12061,11 @@ xtend@^4.0.0, xtend@~4.0.1: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" @@ -11442,6 +12099,11 @@ yargs-parser@^13.1.2: camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + yargs@12.0.5: version "12.0.5" resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" @@ -11475,3 +12137,26 @@ yargs@^13.3.0: which-module "^2.0.0" y18n "^4.0.0" yargs-parser "^13.1.2" + +yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yup@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/yup/-/yup-1.2.0.tgz#9e51af0c63bdfc9be0fdc6c10aa0710899d8aff6" + integrity sha512-PPqYKSAXjpRCgLgLKVGPA33v5c/WgEx3wi6NFjIiegz90zSwyMpvTFp/uGcVnnbx6to28pgnzp/q8ih3QRjLMQ== + dependencies: + property-expr "^2.0.5" + tiny-case "^1.0.3" + toposort "^2.0.2" + type-fest "^2.19.0"