├── .eslintrc.json ├── .gitignore ├── .prettierrc ├── .prettierrc.json ├── LICENSE ├── README.md ├── babel.config.js ├── index.html ├── jest.config.js ├── package.json ├── pnpm-lock.yaml ├── setupTests.ts ├── src ├── App.scss ├── App.tsx ├── __mocks__ │ ├── axios.mock.tsx │ ├── index.ts │ ├── morty.mock.tsx │ └── store.mock.tsx ├── __tests__ │ ├── Login.test.tsx │ └── ModifyButton.test.tsx ├── adapters │ ├── index.ts │ └── user.adapter.ts ├── components │ ├── CustomDialog.tsx │ ├── Dialog.tsx │ ├── Input.tsx │ ├── RouteGuard.tsx │ ├── Select.tsx │ └── index.ts ├── contexts │ ├── index.ts │ └── selected-user.context.ts ├── favicon.svg ├── hooks │ ├── asyncComponentClean.hook.ts │ ├── index.ts │ ├── useFetchAndLoad.ts │ └── useYupValidationResolver.ts ├── index.css ├── logo.svg ├── main.tsx ├── models │ ├── axios-call.model.ts │ ├── index.ts │ ├── regex.model.ts │ ├── select-options.model.ts │ ├── subject-manager.model.ts │ └── user.model.ts ├── pages │ ├── Dashboard │ │ ├── DashboardSuperFix.tsx │ │ └── index.ts │ ├── Login │ │ ├── Login.tsx │ │ └── index.ts │ └── index.ts ├── redux │ ├── states │ │ ├── index.ts │ │ └── user.ts │ └── store.ts ├── services │ └── public.service.ts ├── styled-components │ ├── index.ts │ ├── input-error.styled.component.ts │ └── layout.styled.component.ts ├── theme.ts ├── utilities │ ├── format-date-mmmd.utility.ts │ ├── format-snake-case.utility.ts │ ├── get-yesterday-unix-utility.ts │ ├── index.ts │ ├── load-abort-axios.utility.ts │ └── snackbar.utility.ts └── vite-env.d.ts ├── svgTransform.js ├── tsconfig.json ├── vercel.json ├── vite.config.ts └── yarn.lock /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "airbnb", 4 | "plugin:prettier/recommended", 5 | "prettier/react" 6 | ], 7 | "env": { 8 | "browser": true, 9 | "commonjs": true, 10 | "es6": true, 11 | "jest": true, 12 | "node": true 13 | }, 14 | "rules": { 15 | "jsx-a11y/href-no-hash": ["off"], 16 | "react/jsx-filename-extension": ["warn", { "extensions": [".js", ".jsx"] }], 17 | "max-len": [ 18 | "warn", 19 | { 20 | "code": 140, 21 | "tabWidth": 2, 22 | "comments": 140, 23 | "ignoreComments": false, 24 | "ignoreTrailingComments": true, 25 | "ignoreUrls": true, 26 | "ignoreStrings": true, 27 | "ignoreTemplateLiterals": true, 28 | "ignoreRegExpLiterals": true 29 | } 30 | ] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 140, 4 | "trailingComma": "none" 5 | } 6 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 140, 3 | "singleQuote": true, 4 | "trailingComma": "all" 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Alan Buscaglia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Estructura de Proyecto de ReactJs con Clean Architecture - Documentación Detallada 2 | 3 | **Autor**: Gentleman 4 | 5 | **Fuente del Video**: [Cómo estructurar tu project de ReactJs? Aplicamos Clean Architecture en Front End - #part1](https://www.youtube.com/watch?v=5LqhlCd2_nE) 6 | 7 | ## Introducción 8 | 9 | Este documento proporciona una guía detallada sobre la aplicación de la Clean Architecture en proyectos de ReactJs, orientándonos hacia una estructuración eficiente y una gestión eficaz del estado y de los componentes. 10 | 11 | ## Clean Architecture 12 | 13 | ### Conceptos Básicos 14 | 15 | - **Dominio/Modelos/State**: Define las entidades y su estado durante el ciclo de vida de la aplicación. 16 | - **Casos de Uso**: Operaciones que se pueden realizar sobre los modelos. 17 | - **Adaptadores**: Facilitan la conversión de datos externos para que puedan ser manejados por la aplicación. 18 | 19 | ### Beneficios 20 | 21 | - Organización clara del código. 22 | - Facilidad de mantenimiento. 23 | - Escalabilidad del proyecto. 24 | 25 | ## Estructura de Carpetas Sugerida 26 | 27 | ``` 28 | src/ 29 | ├── adapters/ 30 | │ ├── index.js 31 | │ └── userAdapter.js 32 | ├── components/ 33 | │ ├── UserComponent.jsx 34 | │ └── AdminComponent.jsx 35 | ├── models/ 36 | │ └── userModel.js 37 | ├── hooks/ 38 | │ ├── useUser.js 39 | │ └── useAdmin.js 40 | ├── services/ 41 | │ └── userService.js 42 | ├── utilities/ 43 | │ └── formatter.js 44 | ├── interceptors/ 45 | │ └── authInterceptor.js 46 | ├── contexts/ 47 | │ └── UserContext.js 48 | └── pages/ 49 | └── Home/ 50 | ├── Home.jsx 51 | ├── components/ 52 | │ └── HomeButton.jsx 53 | ├── models/ 54 | │ └── homeModel.js 55 | ├── hooks/ 56 | │ └── useHomeLogic.js 57 | ├── services/ 58 | │ └── homeService.js 59 | └── utilities/ 60 | └── homeFormatter.js 61 | ``` 62 | 63 | ### Descripción Detallada de la Estructura 64 | 65 | #### Pages/Home 66 | 67 | La carpeta `Home` funciona como un contenedor (Container Pattern), encapsulando toda la lógica y los componentes específicos de la página de inicio. Esto permite una mejor organización y un enfoque modular: 68 | 69 | - **Home.jsx**: El componente principal que actúa como el contenedor. 70 | - **Components**: Componentes específicos de la página Home, como `HomeButton.jsx`. 71 | - **Models**: Define modelos específicos utilizados en la página Home. 72 | - **Hooks**: Hooks personalizados para la lógica de negocio de la página Home. 73 | - **Services**: Servicios que interactúan con APIs o recursos externos específicos para Home. 74 | - **Utilities**: Herramientas y funciones de ayuda específicas para la página Home. 75 | 76 | ### La Regla del Alcance 77 | 78 | La regla del alcance es fundamental para entender y organizar la reutilización y la carga perezosa de componentes: 79 | 80 | - **Components en Root**: Accesibles y reutilizables por toda la aplicación. 81 | - **Components en Funcionalidades Específicas**: Utilizados solo dentro de su contexto específico, como en la carpeta `Home`. 82 | 83 | ### Beneficios de la Regla del Alcance 84 | 85 | - **Claridad y Escalabilidad**: Facilita la comprensión del alcance y uso de los componentes, y mejora la escalabilidad del proyecto. 86 | - **Optimización de la Carga**: Permite una carga más eficiente al cargar solo los componentes necesarios para la funcionalidad solicitada. 87 | 88 | ## Conclusión 89 | 90 | Adoptar la Clean Architecture en ReactJs con un enfoque estructurado y modular facilita el desarrollo y mantenimiento de aplicaciones complejas y escalables. La inclusión del patrón de contenedor dentro de las páginas específicas, como se muestra en la carpeta `Home`, proporciona un marco claro para la organización y optimización de los componentes y la lógica de negocio. 91 | 92 | ### Patrón Contenedor en la Estructura de Carpetas de ReactJs 93 | 94 | #### Uso del Patrón Contenedor 95 | 96 | El patrón contenedor es una estrategia de diseño que encapsula los componentes y la lógica específicos de una funcionalidad dentro de su propia subcarpeta bajo `pages`. Esto facilita la organización modular del código y mejora la carga perezosa de los componentes. Aquí te muestro cómo se implementa típicamente dentro de la carpeta `Home`: 97 | 98 | ``` 99 | src/ 100 | └── pages/ 101 | └── Home/ 102 | ├── Home.jsx # Contenedor principal 103 | ├── components/ # Componentes específicos de Home 104 | │ └── HomeButton.jsx 105 | ├── models/ # Modelos usados en Home 106 | │ └── homeModel.js 107 | ├── hooks/ # Hooks específicos de Home 108 | │ └── useHomeLogic.js 109 | ├── services/ # Servicios usados por Home 110 | │ └── homeService.js 111 | └── utilities/ # Funciones auxiliares para Home 112 | └── homeFormatter.js 113 | ``` 114 | 115 | #### Detalles del Patrón Contenedor 116 | 117 | - **Home.jsx**: Actúa como el componente principal que coordina y gestiona los otros componentes específicos dentro de la carpeta `Home`. Este componente funciona como un "contenedor" que encapsula toda la lógica y estado específicos de la página de inicio. 118 | - **Components**: Contiene todos los componentes visuales que solo son relevantes para la página `Home`. Por ejemplo, `HomeButton.jsx` podría ser un botón personalizado solo usado en esta página. 119 | - **Models**: Define estructuras de datos que son únicas para la página `Home`, como `homeModel.js` que podría contener definiciones de estado y lógica de negocio específicas. 120 | - **Hooks**: Agrupa todos los hooks que son utilizados únicamente dentro de la página `Home`, como `useHomeLogic.js`, que puede contener lógica de manejo de estado o efectos secundarios. 121 | - **Services**: Los servicios que realiza llamadas a APIs o realiza operaciones de datos que solo conciernen a la página `Home`. 122 | - **Utilities**: Funciones de ayuda o herramientas específicas que son utilizadas solamente dentro del contexto de `Home`, como formateadores, validadores, o calculadoras. 123 | 124 | #### Beneficios del Patrón Contenedor 125 | 126 | - **Encapsulación**: Aísla la lógica y componentes de una página específica, haciendo el código más fácil de entender y mantener. 127 | - **Modularidad**: Facilita el manejo de cada funcionalidad como un módulo independiente, lo cual es ideal para el mantenimiento y la prueba de unidades. 128 | - **Optimización de la Carga**: Mejora la carga perezosa al asegurar que solo se carguen los recursos necesarios cuando se accede a la página `Home`. 129 | 130 | --- 131 | 132 | Este enfoque del patrón contenedor en la estructura del proyecto de ReactJs es crucial para mantener una arquitectura limpia y organizada, asegurando que cada componente y pieza de lógica tenga un lugar bien definido y un propósito claro. 133 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [['es2015', '@babel/preset-env', '@babel/preset-react'], '@babel/preset-typescript'], 3 | }; 4 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // The root of your source code, typically /src 3 | // `` is a token Jest substitutes 4 | roots: ['/src'], 5 | 6 | // Jest transformations -- this adds support for TypeScript 7 | // using ts-jest 8 | transform: { 9 | '^.+\\.tsx?$': 'ts-jest', 10 | '^.+\\.ts?$': 'ts-jest', 11 | '^.+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '/svgTransform.js' 12 | }, 13 | // testPathIgnorePatterns: ['./node_modules/'], 14 | // Test spec file resolution pattern 15 | // Matches parent folder `__tests__` and filename 16 | // should contain `test` or `spec`. 17 | testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$', 18 | 19 | // Module file extensions for importing 20 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 21 | moduleNameMapper: { 22 | '^@/(.*)$': '/src/$1', 23 | '^.+\\.(css|less|scss)$': 'babel-jest', 24 | d3: '/node_modules/d3/dist/d3.min.js' 25 | }, 26 | testEnvironment: 'jsdom', 27 | setupFilesAfterEnv: ['/setupTests.ts'] 28 | }; 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gp-react-js", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "build": "vite build", 6 | "local": "vite", 7 | "staging": "vite --mode staging", 8 | "dev": "vite --mode dev", 9 | "prod": "vite --mode prod", 10 | "serve": "vite preview", 11 | "test": "jest", 12 | "test:coverage": "jest --coverage", 13 | "test:watch": "jest --watch", 14 | "analyze": "source-map-explorer 'build/static/js/*.js'" 15 | }, 16 | "dependencies": { 17 | "@emotion/react": "^11.8.1", 18 | "@emotion/styled": "^11.8.1", 19 | "@mui/icons-material": "^5.4.4", 20 | "@mui/material": "^5.4.4", 21 | "@mui/styled-engine-sc": "^5.4.2", 22 | "@mui/x-data-grid": "^5.5.1", 23 | "@reduxjs/toolkit": "^1.8.0", 24 | "axios": "^0.26.0", 25 | "context": "^1.2.1", 26 | "d3": "^7.3.0", 27 | "eslint": "^8.10.0", 28 | "eslint-plugin-react-hooks": "^4.3.0", 29 | "install": "^0.13.0", 30 | "notistack": "^2.0.3", 31 | "react": "^17.0.2", 32 | "react-dom": "^17.0.2", 33 | "react-hook-form": "^7.27.1", 34 | "react-is": "^17.0.2", 35 | "react-redux": "^7.2.6", 36 | "react-router": "^6.2.2", 37 | "react-router-dom": "^6.2.2", 38 | "sass": "^1.49.9", 39 | "styled-components": "^5.3.3", 40 | "yup": "^0.32.11" 41 | }, 42 | "devDependencies": { 43 | "@babel/core": "^7.17.5", 44 | "@babel/eslint-parser": "^7.17.0", 45 | "@babel/preset-env": "^7.16.11", 46 | "@babel/preset-react": "^7.16.7", 47 | "@testing-library/dom": "^8.11.3", 48 | "@testing-library/jest-dom": "^5.16.2", 49 | "@testing-library/react": "^12.1.3", 50 | "@testing-library/user-event": "^13.5.0", 51 | "@types/d3": "^7.1.0", 52 | "@types/jest": "^27.4.1", 53 | "@types/react": "^17.0.39", 54 | "@types/react-dom": "^17.0.13", 55 | "@types/react-redux": "^7.1.23", 56 | "@types/react-router": "^5.1.18", 57 | "@types/styled-components": "^5.1.24", 58 | "@vitejs/plugin-react": "^1.2.0", 59 | "babel-jest": "^27.5.1", 60 | "eslint-config-airbnb": "^19.0.4", 61 | "eslint-config-prettier": "^8.5.0", 62 | "eslint-plugin-import": "^2.25.4", 63 | "eslint-plugin-jsx-a11y": "^6.5.1", 64 | "eslint-plugin-prettier": "^4.0.0", 65 | "eslint-plugin-react": "^7.29.3", 66 | "jest": "^27.5.1", 67 | "prettier": "^2.5.1", 68 | "react-test-renderer": "^17.0.2", 69 | "rollup": "^2.69.1", 70 | "rxjs": "^7.5.4", 71 | "source-map-explorer": "^2.5.2", 72 | "ts-jest": "^27.1.3", 73 | "typescript": "^4.6.2", 74 | "vite": "^2.8.6" 75 | }, 76 | "main": "babel.config.js", 77 | "repository": { 78 | "type": "git", 79 | "url": "git+https://github.com/erudit-ai/Erudit-Front-End.git" 80 | }, 81 | "keywords": [], 82 | "author": "", 83 | "license": "ISC", 84 | "bugs": { 85 | "url": "https://github.com/erudit-ai/Erudit-Front-End/issues" 86 | }, 87 | "homepage": "https://github.com/erudit-ai/Erudit-Front-End#readme", 88 | "description": "" 89 | } 90 | -------------------------------------------------------------------------------- /setupTests.ts: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom/extend-expect'; 2 | -------------------------------------------------------------------------------- /src/App.scss: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | 40 | button { 41 | font-size: calc(10px + 2vmin); 42 | } 43 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import store from '@/redux/store'; 2 | import { ThemeProvider } from '@emotion/react'; 3 | import { SnackbarProvider } from 'notistack'; 4 | import React, { lazy, Suspense } from 'react'; 5 | import { Provider } from 'react-redux'; 6 | import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom'; 7 | import './App.scss'; 8 | import { AppContainer } from './styled-components'; 9 | import theme from './theme'; 10 | import { SnackbarUtilsConfigurator } from './utilities'; 11 | 12 | // Routes 13 | const DashboardSuperFix = lazy(() => import('@/pages/Dashboard/DashboardSuperFix')); 14 | const Login = lazy(() => import('@/pages/Login/Login')); 15 | 16 | const App = () => { 17 | return ( 18 | 19 | 20 | 21 | 22 | 23 | Loading ...}> 24 | 25 | 26 | 27 | } /> 28 | } /> 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | ); 38 | }; 39 | 40 | export default App; 41 | -------------------------------------------------------------------------------- /src/__mocks__/axios.mock.tsx: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export const axiosMock = axios as jest.Mocked; 4 | -------------------------------------------------------------------------------- /src/__mocks__/index.ts: -------------------------------------------------------------------------------- 1 | export * from './axios.mock'; 2 | export * from './morty.mock'; 3 | export * from './store.mock'; 4 | -------------------------------------------------------------------------------- /src/__mocks__/morty.mock.tsx: -------------------------------------------------------------------------------- 1 | export const mortyMock = { 2 | id: 2, 3 | name: 'Morty Smith', 4 | status: 'Alive', 5 | species: 'Human', 6 | type: '', 7 | gender: 'Male', 8 | origin: { name: 'unknown', url: '' }, 9 | location: { name: 'Citadel of Ricks', url: 'https://rickandmortyapi.com/api/location/3' }, 10 | image: 'https://rickandmortyapi.com/api/character/avatar/2.jpeg', 11 | episode: [ 12 | 'https://rickandmortyapi.com/api/episode/1', 13 | 'https://rickandmortyapi.com/api/episode/2', 14 | 'https://rickandmortyapi.com/api/episode/3', 15 | 'https://rickandmortyapi.com/api/episode/4', 16 | 'https://rickandmortyapi.com/api/episode/5', 17 | 'https://rickandmortyapi.com/api/episode/6', 18 | 'https://rickandmortyapi.com/api/episode/7', 19 | 'https://rickandmortyapi.com/api/episode/8', 20 | 'https://rickandmortyapi.com/api/episode/9', 21 | 'https://rickandmortyapi.com/api/episode/10', 22 | 'https://rickandmortyapi.com/api/episode/11', 23 | 'https://rickandmortyapi.com/api/episode/12', 24 | 'https://rickandmortyapi.com/api/episode/13', 25 | 'https://rickandmortyapi.com/api/episode/14', 26 | 'https://rickandmortyapi.com/api/episode/15', 27 | 'https://rickandmortyapi.com/api/episode/16', 28 | 'https://rickandmortyapi.com/api/episode/17', 29 | 'https://rickandmortyapi.com/api/episode/18', 30 | 'https://rickandmortyapi.com/api/episode/19', 31 | 'https://rickandmortyapi.com/api/episode/20', 32 | 'https://rickandmortyapi.com/api/episode/21', 33 | 'https://rickandmortyapi.com/api/episode/22', 34 | 'https://rickandmortyapi.com/api/episode/23', 35 | 'https://rickandmortyapi.com/api/episode/24', 36 | 'https://rickandmortyapi.com/api/episode/25', 37 | 'https://rickandmortyapi.com/api/episode/26', 38 | 'https://rickandmortyapi.com/api/episode/27', 39 | 'https://rickandmortyapi.com/api/episode/28', 40 | 'https://rickandmortyapi.com/api/episode/29', 41 | 'https://rickandmortyapi.com/api/episode/30', 42 | 'https://rickandmortyapi.com/api/episode/31', 43 | 'https://rickandmortyapi.com/api/episode/32', 44 | 'https://rickandmortyapi.com/api/episode/33', 45 | 'https://rickandmortyapi.com/api/episode/34', 46 | 'https://rickandmortyapi.com/api/episode/35', 47 | 'https://rickandmortyapi.com/api/episode/36', 48 | 'https://rickandmortyapi.com/api/episode/37', 49 | 'https://rickandmortyapi.com/api/episode/38', 50 | 'https://rickandmortyapi.com/api/episode/39', 51 | 'https://rickandmortyapi.com/api/episode/40', 52 | 'https://rickandmortyapi.com/api/episode/41', 53 | 'https://rickandmortyapi.com/api/episode/42', 54 | 'https://rickandmortyapi.com/api/episode/43', 55 | 'https://rickandmortyapi.com/api/episode/44', 56 | 'https://rickandmortyapi.com/api/episode/45', 57 | 'https://rickandmortyapi.com/api/episode/46', 58 | 'https://rickandmortyapi.com/api/episode/47', 59 | 'https://rickandmortyapi.com/api/episode/48', 60 | 'https://rickandmortyapi.com/api/episode/49', 61 | 'https://rickandmortyapi.com/api/episode/50', 62 | 'https://rickandmortyapi.com/api/episode/51' 63 | ], 64 | url: 'https://rickandmortyapi.com/api/character/2', 65 | created: '2017-11-04T18:50:21.651Z' 66 | }; 67 | -------------------------------------------------------------------------------- /src/__mocks__/store.mock.tsx: -------------------------------------------------------------------------------- 1 | import { userSlice } from '@/redux/states/user'; 2 | import { AppStore } from '@/redux/store'; 3 | import { configureStore } from '@reduxjs/toolkit'; 4 | 5 | export const store = configureStore({ 6 | reducer: { 7 | user: userSlice.reducer 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /src/__tests__/Login.test.tsx: -------------------------------------------------------------------------------- 1 | import { Login } from '@/pages'; 2 | import store from '@/redux/store'; 3 | import { axiosMock, mortyMock } from '@/__mocks__'; 4 | import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react/pure'; 5 | import { Provider } from 'react-redux'; 6 | 7 | jest.mock('axios'); 8 | 9 | jest.mock('@/pages/Login/components/ModifyButton', () => ({ 10 | __esModule: true, 11 | default: () =>
mocked ModifyButton
12 | })); 13 | 14 | describe('Login', () => { 15 | const mockedAxios = axiosMock; 16 | mockedAxios.get.mockResolvedValue({ data: mortyMock }); 17 | afterEach(jest.clearAllMocks); 18 | afterEach(cleanup); 19 | 20 | it('renders Logic component', async () => { 21 | const component = render( 22 | 23 | 24 | 25 | ); 26 | expect(component).toBeTruthy; 27 | }); 28 | 29 | it('has to call the login endpoint and update the store', async () => { 30 | render( 31 | 32 | 33 | 34 | ); 35 | const button = screen.getByRole('button', { name: 'Login' }); 36 | expect(button).toBeInTheDocument(); 37 | fireEvent.click(button); 38 | await waitFor(() => { 39 | expect(mockedAxios.get).toHaveBeenCalled(); 40 | expect(screen.getByText('{"name":"Morty Smith","gender":"Male","status":"Alive"}')); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /src/__tests__/ModifyButton.test.tsx: -------------------------------------------------------------------------------- 1 | import { ModifyButton } from '@/pages'; 2 | import store from '@/redux/store'; 3 | import { cleanup, render, screen, fireEvent, waitFor } from '@testing-library/react/pure'; 4 | import { Provider } from 'react-redux'; 5 | 6 | jest.mock('axios'); 7 | 8 | describe('ModifyButton', () => { 9 | afterEach(jest.clearAllMocks); 10 | afterEach(cleanup); 11 | 12 | it('renders ModifyButton component', async () => { 13 | const component = render( 14 | 15 | 16 | 17 | ); 18 | expect(component).toBeTruthy; 19 | }); 20 | 21 | it('has to modify the user state on modify button click', async () => { 22 | render( 23 | 24 | 25 | 26 | ); 27 | const button = screen.getByRole('button', { name: 'Modify User' }); 28 | expect(button).toBeInTheDocument(); 29 | fireEvent.click(button); 30 | await waitFor(() => { 31 | expect(store.getState().user).toEqual({ name: 'Carlos', gender: '', status: '' }); 32 | }); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /src/adapters/index.ts: -------------------------------------------------------------------------------- 1 | export * from './user.adapter'; 2 | -------------------------------------------------------------------------------- /src/adapters/user.adapter.ts: -------------------------------------------------------------------------------- 1 | export const createUserAdapter = (user: any) => ({ 2 | name: user.data.name, 3 | gender: user.data.gender, 4 | status: user.data.status 5 | }); 6 | -------------------------------------------------------------------------------- /src/components/CustomDialog.tsx: -------------------------------------------------------------------------------- 1 | import { SubjectManager } from '@/models/subject-manager.model'; 2 | import Dialog from '@mui/material/Dialog'; 3 | import { useEffect, useState } from 'react'; 4 | import { Subscription } from 'rxjs'; 5 | 6 | interface Props { 7 | children: React.ReactNode; 8 | } 9 | 10 | export const dialogOpenSubject$ = new SubjectManager(); 11 | export const dialogCloseSubject$ = new SubjectManager(); 12 | 13 | export const CustomDialog = ({ children }: Props) => { 14 | const [open, setOpen] = useState(false); 15 | let openSubject$ = new Subscription(); 16 | let closeSubject$ = new Subscription(); 17 | 18 | useEffect(() => { 19 | openSubject$ = dialogOpenSubject$.getSubject.subscribe(() => handleClickOpen()); 20 | closeSubject$ = dialogCloseSubject$.getSubject.subscribe(() => handleClose()); 21 | return () => { 22 | openSubject$.unsubscribe(); 23 | closeSubject$.unsubscribe(); 24 | }; 25 | }, []); 26 | 27 | const handleClickOpen = () => { 28 | setOpen(true); 29 | }; 30 | 31 | const handleClose = () => { 32 | setOpen(false); 33 | }; 34 | 35 | const handleExit = () => { 36 | dialogCloseSubject$.setSubject = false; 37 | }; 38 | 39 | return ( 40 |
41 | handleExit()} aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description"> 42 | {children} 43 | 44 |
45 | ); 46 | }; 47 | 48 | export default CustomDialog; 49 | -------------------------------------------------------------------------------- /src/components/Dialog.tsx: -------------------------------------------------------------------------------- 1 | import { SubjectManager } from '@/models/subject-manager.model'; 2 | import Button from '@mui/material/Button'; 3 | import Dialog from '@mui/material/Dialog'; 4 | import DialogActions from '@mui/material/DialogActions'; 5 | import React, { useEffect, useState } from 'react'; 6 | import { Subscription } from 'rxjs'; 7 | 8 | interface Props { 9 | children: React.ReactNode; 10 | } 11 | 12 | export const dialogOpenSubject$ = new SubjectManager(); 13 | export const dialogCloseSubject$ = new SubjectManager(); 14 | 15 | export const AlertDialog = ({ children }: Props) => { 16 | const [open, setOpen] = useState(false); 17 | let openSubject$ = new Subscription(); 18 | let closeSubject$ = new Subscription(); 19 | 20 | useEffect(() => { 21 | // inicio, cambios 22 | return () => { 23 | //destruccion 24 | }; 25 | }, []); 26 | useEffect(() => { 27 | openSubject$ = dialogOpenSubject$.getSubject.subscribe(() => handleClickOpen()); 28 | return () => { 29 | openSubject$.unsubscribe(); 30 | closeSubject$.unsubscribe(); 31 | }; 32 | }, []); 33 | const handleClickOpen = () => { 34 | setOpen(true); 35 | dialogCloseSubject$.setSubject = true; 36 | }; 37 | 38 | const handleClose = (value = false) => { 39 | setOpen(false); 40 | dialogCloseSubject$.setSubject = value; 41 | }; 42 | 43 | return ( 44 |
45 | handleClose(false)} 48 | aria-labelledby="alert-dialog-title" 49 | aria-describedby="alert-dialog-description" 50 | > 51 | {children} 52 | 53 | 56 | 59 | 60 | 61 |
62 | ); 63 | }; 64 | 65 | export default AlertDialog; 66 | -------------------------------------------------------------------------------- /src/components/Input.tsx: -------------------------------------------------------------------------------- 1 | import { InputError } from '@/styled-components'; 2 | import { InputBaseProps, TextField } from '@mui/material'; 3 | import { FieldErrors, UseFormRegister, UseFormTrigger } from 'react-hook-form'; 4 | 5 | const formValidation = (errors: FieldErrors, errorKey: string) => { 6 | return errors[errorKey] ? {errors[errorKey].message} : ''; 7 | }; 8 | 9 | interface InputProps { 10 | register: UseFormRegister; 11 | name: string; 12 | errors?: FieldErrors; 13 | label?: string; 14 | type: InputType; 15 | inputProps?: InputBaseProps['inputProps']; 16 | disabled?: boolean; 17 | trigger?: UseFormTrigger; 18 | } 19 | 20 | export enum InputType { 21 | NUMBER = 'number', 22 | PASSWORD = 'password', 23 | SEARCH = 'search', 24 | TEXT = 'text', 25 | HIDDEN = 'hidden', 26 | CHECKBOX = 'checkbox' 27 | } 28 | 29 | export const Input = ({ register, name, errors, label = '', type, inputProps, disabled = false, trigger }: InputProps) => { 30 | return ( 31 |
32 | trigger && trigger()} 43 | fullWidth 44 | /> 45 | {errors && formValidation(errors, name)} 46 |
47 | ); 48 | }; 49 | 50 | export default Input; 51 | -------------------------------------------------------------------------------- /src/components/RouteGuard.tsx: -------------------------------------------------------------------------------- 1 | import { AppStore } from '@/redux/store'; 2 | import { useSelector } from 'react-redux'; 3 | import { Navigate, Outlet } from 'react-router-dom'; 4 | 5 | export const RouteGuard = () => { 6 | const userState = useSelector((state: AppStore) => state.user); 7 | return userState.name ? : ; 8 | }; 9 | 10 | export default RouteGuard; 11 | -------------------------------------------------------------------------------- /src/components/Select.tsx: -------------------------------------------------------------------------------- 1 | import { SelectOption } from '@/models'; 2 | import { InputError } from '@/styled-components'; 3 | import { InputBaseProps, MenuItem, TextField } from '@mui/material'; 4 | import { FieldErrors, UseFormRegister } from 'react-hook-form'; 5 | 6 | const formValidation = (errors: FieldErrors, errorKey: string) => { 7 | return errors[errorKey] ? {errors[errorKey].message} : ''; 8 | }; 9 | 10 | interface SelectProps { 11 | register: UseFormRegister; 12 | name: string; 13 | errors: FieldErrors; 14 | label: string; 15 | options: SelectOption[]; 16 | inputProps?: InputBaseProps['inputProps']; 17 | } 18 | 19 | export const CustomSelect = ({ register, name, errors, label, options, inputProps }: SelectProps) => { 20 | return ( 21 |
22 | 35 | {options.map((option) => ( 36 | 37 | {option.label} 38 | 39 | ))} 40 | 41 | {formValidation(errors, name)} 42 |
43 | ); 44 | }; 45 | 46 | export default CustomSelect; 47 | -------------------------------------------------------------------------------- /src/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as CustomDialog } from './CustomDialog'; 2 | export { default as Dialog } from './Dialog'; 3 | export { default as Input } from './Input'; 4 | export { default as RouteGuard } from './RouteGuard'; 5 | export { default as Select } from './Select'; 6 | -------------------------------------------------------------------------------- /src/contexts/index.ts: -------------------------------------------------------------------------------- 1 | export * from './selected-user.context'; 2 | -------------------------------------------------------------------------------- /src/contexts/selected-user.context.ts: -------------------------------------------------------------------------------- 1 | import { User } from '@/models'; 2 | import { createContext } from 'react'; 3 | 4 | export const SelectedUserContext = createContext({ 5 | selectedUser: {} as User, 6 | setSelectedUser: (user: User) => {} 7 | }); 8 | -------------------------------------------------------------------------------- /src/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/hooks/asyncComponentClean.hook.ts: -------------------------------------------------------------------------------- 1 | import { AxiosResponse } from 'axios'; 2 | import { useEffect } from 'react'; 3 | 4 | export const useAsync = ( 5 | asyncFn: () => Promise>, 6 | successFunction: Function, 7 | returnFunction: Function, 8 | dependencies: any[] = [] 9 | ) => { 10 | useEffect(() => { 11 | let isActive = true; 12 | asyncFn().then((result) => { 13 | if (isActive) successFunction(result.data); 14 | }); 15 | return () => { 16 | returnFunction && returnFunction(); 17 | isActive = false; 18 | }; 19 | }, dependencies); 20 | }; 21 | -------------------------------------------------------------------------------- /src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './asyncComponentClean.hook'; 2 | export { default as useFetchAndLoad } from './useFetchAndLoad'; 3 | export * from './useYupValidationResolver'; 4 | -------------------------------------------------------------------------------- /src/hooks/useFetchAndLoad.ts: -------------------------------------------------------------------------------- 1 | import { AxiosCall } from '@/models'; 2 | import { AxiosResponse } from 'axios'; 3 | import { useEffect, useState } from 'react'; 4 | 5 | const useFetchAndLoad = () => { 6 | const [loading, setLoading] = useState(false); 7 | let controller: AbortController; 8 | 9 | const callEndpoint = async (axiosCall: AxiosCall) => { 10 | if (axiosCall.controller) controller = axiosCall.controller; 11 | setLoading(true); 12 | let result = {} as AxiosResponse; 13 | try { 14 | result = await axiosCall.call; 15 | } catch (err: any) { 16 | setLoading(false); 17 | throw err; 18 | } 19 | setLoading(false); 20 | return result; 21 | }; 22 | 23 | const cancelEndpoint = () => { 24 | setLoading(false); 25 | controller && controller.abort(); 26 | }; 27 | 28 | useEffect(() => { 29 | return () => { 30 | cancelEndpoint(); 31 | }; 32 | }, []); 33 | 34 | return { loading, callEndpoint }; 35 | }; 36 | 37 | export default useFetchAndLoad; 38 | -------------------------------------------------------------------------------- /src/hooks/useYupValidationResolver.ts: -------------------------------------------------------------------------------- 1 | import { useCallback } from 'react'; 2 | 3 | export const useYupValidationResolver = (validationSchema: any) => 4 | useCallback( 5 | async (data) => { 6 | try { 7 | const values = await validationSchema.validate(data, { 8 | abortEarly: false 9 | }); 10 | 11 | return { 12 | values, 13 | errors: {} 14 | }; 15 | } catch (errors: any) { 16 | return { 17 | values: {}, 18 | errors: errors.inner.reduce( 19 | ( 20 | allErrors: any, 21 | currentError: { path: any; type: any; message: any } 22 | ) => ({ 23 | ...allErrors, 24 | [currentError.path]: { 25 | type: currentError.type ?? 'validation', 26 | message: currentError.message 27 | } 28 | }), 29 | {} 30 | ) 31 | }; 32 | } 33 | }, 34 | [validationSchema] 35 | ); 36 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /src/models/axios-call.model.ts: -------------------------------------------------------------------------------- 1 | import { AxiosResponse } from 'axios'; 2 | 3 | export interface AxiosCall { 4 | call: Promise>; 5 | controller?: AbortController; 6 | } 7 | -------------------------------------------------------------------------------- /src/models/index.ts: -------------------------------------------------------------------------------- 1 | export * from './axios-call.model'; 2 | export * from './regex.model'; 3 | export * from './select-options.model'; 4 | export * from './subject-manager.model'; 5 | export * from './user.model'; 6 | -------------------------------------------------------------------------------- /src/models/regex.model.ts: -------------------------------------------------------------------------------- 1 | export const PasswordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[$*.{}?"!@#%&/,><':;|_~`^\]\[)(]).{8,99}/; 2 | export const EmailRegex = /^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$/; 3 | export const DomainRegex = /^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9](?:\.[a-zA-Z]{2,})+$/; 4 | -------------------------------------------------------------------------------- /src/models/select-options.model.ts: -------------------------------------------------------------------------------- 1 | export interface SelectOption { 2 | label: string; 3 | value: string; 4 | } 5 | -------------------------------------------------------------------------------- /src/models/subject-manager.model.ts: -------------------------------------------------------------------------------- 1 | import { Subject } from 'rxjs'; 2 | 3 | export class SubjectManager { 4 | private subject = new Subject(); 5 | 6 | get getSubject() { 7 | return this.subject.asObservable(); 8 | } 9 | 10 | set setSubject(value: T) { 11 | this.subject.next(value); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/models/user.model.ts: -------------------------------------------------------------------------------- 1 | export interface User { 2 | name: string; 3 | gender: string; 4 | status: string; 5 | } 6 | -------------------------------------------------------------------------------- /src/pages/Dashboard/DashboardSuperFix.tsx: -------------------------------------------------------------------------------- 1 | import { useAsync, useFetchAndLoad } from '@/hooks'; 2 | import { getCoolMorty, getCoolRick } from '@/services/public.service'; 3 | import { LayoutContainer } from '@/styled-components'; 4 | import { Tab, Tabs } from '@mui/material'; 5 | import { useState } from 'react'; 6 | 7 | const Component1 = () => { 8 | const { loading, callEndpoint } = useFetchAndLoad(); 9 | const [morty, setMorty] = useState(null); 10 | const getApiData = async () => await callEndpoint(getCoolMorty()); 11 | 12 | const adaptMorty = (data: any) => { 13 | setMorty(data.name); 14 | }; 15 | 16 | useAsync(getApiData, adaptMorty, () => {}); 17 | return
{loading ? 'LOADING' : morty}
; 18 | }; 19 | 20 | function Component2() { 21 | const { loading, callEndpoint } = useFetchAndLoad(); 22 | const [rick, setRick] = useState(null); 23 | 24 | const getApiData = async () => await callEndpoint(getCoolRick()); 25 | 26 | const adaptRick = (data: any) => { 27 | setRick(data.name); 28 | }; 29 | 30 | useAsync(getApiData, adaptRick, () => {}, []); 31 | return
{loading ? 'LOADING' : rick}
; 32 | } 33 | 34 | export const DashboardSuperFix = () => { 35 | const [value, setValue] = useState(0); 36 | 37 | const handleChange = (event: any, newValue: any) => { 38 | setValue(newValue); 39 | }; 40 | 41 | function a11yProps(index: number) { 42 | return { 43 | id: `simple-tab-${index}`, 44 | 'aria-controls': `simple-tabpanel-${index}` 45 | }; 46 | } 47 | 48 | return ( 49 | 50 |
51 |
Buenas buenas mi gente !
52 | 53 | 54 | 55 | 56 |
{value === 0 ? : }
57 |
58 |
59 | ); 60 | }; 61 | 62 | export default DashboardSuperFix; 63 | -------------------------------------------------------------------------------- /src/pages/Dashboard/index.ts: -------------------------------------------------------------------------------- 1 | export { default as DashboardSuperFix } from './DashboardSuperFix'; 2 | -------------------------------------------------------------------------------- /src/pages/Login/Login.tsx: -------------------------------------------------------------------------------- 1 | import { createUserAdapter } from '@/adapters'; 2 | import { useFetchAndLoad } from '@/hooks'; 3 | import { createUser, modifyUser } from '@/redux/states/user'; 4 | import { AppStore } from '@/redux/store'; 5 | import { login } from '@/services/public.service'; 6 | import { Button } from '@mui/material'; 7 | import { useDispatch, useSelector } from 'react-redux'; 8 | 9 | export const Login = () => { 10 | const { loading, callEndpoint } = useFetchAndLoad(); 11 | const dispatch = useDispatch(); 12 | const userState = useSelector((store: AppStore) => store.user); 13 | const handleClick = async () => { 14 | const morty = await callEndpoint(login()); 15 | dispatch(createUser(createUserAdapter(morty))); 16 | }; 17 | const handleModify = () => { 18 | dispatch(modifyUser({ name: 'Gentleman' })); 19 | }; 20 | 21 | return ( 22 | <> 23 | {loading ? ( 24 |
25 |

LOADING

26 |
27 | ) : ( 28 | <> 29 | 32 | 35 |
36 |

{JSON.stringify(userState)}

37 |
38 | 39 | )} 40 | 41 | ); 42 | }; 43 | 44 | export default Login; 45 | -------------------------------------------------------------------------------- /src/pages/Login/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Login } from './Login'; 2 | -------------------------------------------------------------------------------- /src/pages/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Dashboard'; 2 | export * from './Login'; 3 | -------------------------------------------------------------------------------- /src/redux/states/index.ts: -------------------------------------------------------------------------------- 1 | export { default as user } from './user'; 2 | -------------------------------------------------------------------------------- /src/redux/states/user.ts: -------------------------------------------------------------------------------- 1 | import { User } from '@/models'; 2 | import { createSlice } from '@reduxjs/toolkit'; 3 | 4 | export const UserEmptyState: User = { 5 | name: '', 6 | gender: '', 7 | status: '' 8 | }; 9 | 10 | export const userSlice = createSlice({ 11 | name: 'user', 12 | initialState: UserEmptyState, 13 | reducers: { 14 | createUser: (state, action) => action.payload, 15 | modifyUser: (state, action) => ({ ...state, ...action.payload }), 16 | resetUser: () => UserEmptyState 17 | } 18 | }); 19 | 20 | export const { createUser, modifyUser, resetUser } = userSlice.actions; 21 | 22 | export default userSlice.reducer; 23 | -------------------------------------------------------------------------------- /src/redux/store.ts: -------------------------------------------------------------------------------- 1 | import { configureStore } from '@reduxjs/toolkit'; 2 | import { userSlice } from './states/user'; 3 | 4 | export interface AppStore { 5 | user: any; 6 | } 7 | 8 | export default configureStore({ 9 | reducer: { 10 | user: userSlice.reducer 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /src/services/public.service.ts: -------------------------------------------------------------------------------- 1 | import { User } from '@/models'; 2 | import { loadAbort } from '@/utilities'; 3 | import axios from 'axios'; 4 | 5 | export const login = () => { 6 | const controller = loadAbort(); 7 | return { 8 | call: axios.get('https://rickandmortyapi.com/api/character/2', { signal: controller.signal }), 9 | controller 10 | }; 11 | }; 12 | 13 | export const getMorty = () => { 14 | return axios.get('https://rickandmortyapi.com/api/character/2'); 15 | }; 16 | 17 | export const getRick = () => { 18 | return axios.get('https://rickandmortyapi.com/api/character/1'); 19 | }; 20 | 21 | export const getCoolMorty = () => { 22 | const controller = loadAbort(); 23 | return { call: axios.get('https://rickandmortyapi.com/api/character/2', { signal: controller.signal }), controller }; 24 | }; 25 | 26 | export const getCoolRick = () => { 27 | const controller = loadAbort(); 28 | return { call: axios.get('https://rickandmortyapi.com/api/character/1', { signal: controller.signal }), controller }; 29 | }; 30 | -------------------------------------------------------------------------------- /src/styled-components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './input-error.styled.component'; 2 | export * from './layout.styled.component'; 3 | -------------------------------------------------------------------------------- /src/styled-components/input-error.styled.component.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const InputError = styled.p` 4 | color: #d32f2f; 5 | text-align: left; 6 | margin-bottom: 0; 7 | ` -------------------------------------------------------------------------------- /src/styled-components/layout.styled.component.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const LayoutContainer = styled.div` 4 | display: flex; 5 | flex-direction: column; 6 | gap: 20px; 7 | flex-grow: 1; 8 | padding-right: 20px; 9 | border-radius: 4px; 10 | max-width: 1440px; 11 | padding: 4rem; 12 | `; 13 | 14 | export const MainContainer = styled.div` 15 | background-color: white; 16 | position: relative; 17 | overflow-y: auto; 18 | padding: 20px 20px 20px 20px; 19 | border-radius: 4px; 20 | min-width: 794px; 21 | height: 100%; 22 | `; 23 | 24 | export const AppContainer = styled.div` 25 | justify-content: center; 26 | background-color: #f4f5ff; 27 | display: flex; 28 | `; 29 | -------------------------------------------------------------------------------- /src/theme.ts: -------------------------------------------------------------------------------- 1 | import { createTheme } from '@mui/material'; 2 | 3 | const theme = createTheme({ 4 | typography: { 5 | button: { 6 | textTransform: 'none', 7 | width: 'auto', 8 | height: 'auto' 9 | } 10 | } 11 | }); 12 | 13 | export default theme; 14 | -------------------------------------------------------------------------------- /src/utilities/format-date-mmmd.utility.ts: -------------------------------------------------------------------------------- 1 | export const FormatDateMMMD = (dateString: string): string => { 2 | const date = new Date(dateString); 3 | return `${date.toLocaleDateString('en-us', { month: 'short' })} ${date.getDate()}`; 4 | }; 5 | -------------------------------------------------------------------------------- /src/utilities/format-snake-case.utility.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc formats a string to snake case. 3 | * @param element- the string to format. 4 | * @return string 5 | */ 6 | export const formatToSnakeCase = (element: string): string => { 7 | return element.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`); 8 | }; 9 | -------------------------------------------------------------------------------- /src/utilities/get-yesterday-unix-utility.ts: -------------------------------------------------------------------------------- 1 | export const GetYesterdayUnix = () => { 2 | const today = new Date(); 3 | today.setDate(today.getDate() - 1); 4 | return today.getTime(); 5 | }; 6 | -------------------------------------------------------------------------------- /src/utilities/index.ts: -------------------------------------------------------------------------------- 1 | export * from './format-date-mmmd.utility'; 2 | export * from './format-snake-case.utility'; 3 | export * from './get-yesterday-unix-utility'; 4 | export * from './load-abort-axios.utility'; 5 | export * from './snackbar.utility'; 6 | -------------------------------------------------------------------------------- /src/utilities/load-abort-axios.utility.ts: -------------------------------------------------------------------------------- 1 | export const loadAbort = () => { 2 | const controller = new AbortController(); 3 | return controller; 4 | }; 5 | -------------------------------------------------------------------------------- /src/utilities/snackbar.utility.ts: -------------------------------------------------------------------------------- 1 | import { useSnackbar, VariantType, WithSnackbarProps } from 'notistack'; 2 | import React from 'react'; 3 | 4 | let useSnackbarRef: WithSnackbarProps; 5 | export const SnackbarUtilsConfigurator: React.FC = () => { 6 | useSnackbarRef = useSnackbar(); 7 | return null; 8 | }; 9 | 10 | export const SnackbarUtilities = { 11 | success(msg: string) { 12 | this.toast(msg, 'success'); 13 | }, 14 | warning(msg: string) { 15 | this.toast(msg, 'warning'); 16 | }, 17 | info(msg: string) { 18 | this.toast(msg, 'info'); 19 | }, 20 | error(msg: string) { 21 | this.toast(msg, 'error'); 22 | }, 23 | toast(msg: string, variant: VariantType = 'default') { 24 | useSnackbarRef.enqueueSnackbar(msg, { variant }); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /svgTransform.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | process(src, filename, config, options) { 5 | return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';'; 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "baseUrl": ".", 5 | "paths": { "@/*": ["./src/*"] }, 6 | "useDefineForClassFields": true, 7 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 8 | "allowJs": false, 9 | "skipLibCheck": false, 10 | "esModuleInterop": false, 11 | "allowSyntheticDefaultImports": true, 12 | "isolatedModules": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "ESNext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "noEmit": true, 19 | "jsx": "react-jsx", 20 | "types": ["node", "jest", "vite/client", "@testing-library/jest-dom"] 21 | }, 22 | "include": ["./src"] 23 | } 24 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [{ "source": "/(.*)", "destination": "/" }] 3 | } 4 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import react from '@vitejs/plugin-react'; 2 | import path from 'path'; 3 | import { defineConfig, loadEnv } from 'vite'; 4 | 5 | export default ({ mode }: { mode: string }) => { 6 | return defineConfig({ 7 | plugins: [react()], 8 | resolve: { 9 | alias: { 10 | '@': path.resolve(__dirname, './src') 11 | } 12 | }, 13 | define: { 'process.env': { ...loadEnv(mode, process.cwd()) } }, 14 | server: { 15 | proxy: { 16 | '^/assets': { 17 | target: 'http://localhost:3000/' 18 | } 19 | } 20 | } 21 | }); 22 | }; 23 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@babel/code-frame@^7.14.5", "@babel/code-frame@^7.15.8": 6 | version "7.15.8" 7 | resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.15.8.tgz#45990c47adadb00c03677baa89221f7cc23d2503" 8 | integrity sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg== 9 | dependencies: 10 | "@babel/highlight" "^7.14.5" 11 | 12 | "@babel/compat-data@^7.15.0": 13 | version "7.15.0" 14 | resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.15.0.tgz#2dbaf8b85334796cafbb0f5793a90a2fc010b176" 15 | integrity sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA== 16 | 17 | "@babel/core@^7.15.5": 18 | version "7.15.8" 19 | resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.15.8.tgz#195b9f2bffe995d2c6c159e72fe525b4114e8c10" 20 | integrity sha512-3UG9dsxvYBMYwRv+gS41WKHno4K60/9GPy1CJaH6xy3Elq8CTtvtjT5R5jmNhXfCYLX2mTw+7/aq5ak/gOE0og== 21 | dependencies: 22 | "@babel/code-frame" "^7.15.8" 23 | "@babel/generator" "^7.15.8" 24 | "@babel/helper-compilation-targets" "^7.15.4" 25 | "@babel/helper-module-transforms" "^7.15.8" 26 | "@babel/helpers" "^7.15.4" 27 | "@babel/parser" "^7.15.8" 28 | "@babel/template" "^7.15.4" 29 | "@babel/traverse" "^7.15.4" 30 | "@babel/types" "^7.15.6" 31 | convert-source-map "^1.7.0" 32 | debug "^4.1.0" 33 | gensync "^1.0.0-beta.2" 34 | json5 "^2.1.2" 35 | semver "^6.3.0" 36 | source-map "^0.5.0" 37 | 38 | "@babel/generator@^7.15.4", "@babel/generator@^7.15.8": 39 | version "7.15.8" 40 | resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.15.8.tgz#fa56be6b596952ceb231048cf84ee499a19c0cd1" 41 | integrity sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g== 42 | dependencies: 43 | "@babel/types" "^7.15.6" 44 | jsesc "^2.5.1" 45 | source-map "^0.5.0" 46 | 47 | "@babel/helper-annotate-as-pure@^7.14.5", "@babel/helper-annotate-as-pure@^7.15.4": 48 | version "7.15.4" 49 | resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.15.4.tgz#3d0e43b00c5e49fdb6c57e421601a7a658d5f835" 50 | integrity sha512-QwrtdNvUNsPCj2lfNQacsGSQvGX8ee1ttrBrcozUP2Sv/jylewBP/8QFe6ZkBsC8T/GYWonNAWJV4aRR9AL2DA== 51 | dependencies: 52 | "@babel/types" "^7.15.4" 53 | 54 | "@babel/helper-compilation-targets@^7.15.4": 55 | version "7.15.4" 56 | resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz#cf6d94f30fbefc139123e27dd6b02f65aeedb7b9" 57 | integrity sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ== 58 | dependencies: 59 | "@babel/compat-data" "^7.15.0" 60 | "@babel/helper-validator-option" "^7.14.5" 61 | browserslist "^4.16.6" 62 | semver "^6.3.0" 63 | 64 | "@babel/helper-function-name@^7.15.4": 65 | version "7.15.4" 66 | resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz#845744dafc4381a4a5fb6afa6c3d36f98a787ebc" 67 | integrity sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw== 68 | dependencies: 69 | "@babel/helper-get-function-arity" "^7.15.4" 70 | "@babel/template" "^7.15.4" 71 | "@babel/types" "^7.15.4" 72 | 73 | "@babel/helper-get-function-arity@^7.15.4": 74 | version "7.15.4" 75 | resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz#098818934a137fce78b536a3e015864be1e2879b" 76 | integrity sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA== 77 | dependencies: 78 | "@babel/types" "^7.15.4" 79 | 80 | "@babel/helper-hoist-variables@^7.15.4": 81 | version "7.15.4" 82 | resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz#09993a3259c0e918f99d104261dfdfc033f178df" 83 | integrity sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA== 84 | dependencies: 85 | "@babel/types" "^7.15.4" 86 | 87 | "@babel/helper-member-expression-to-functions@^7.15.4": 88 | version "7.15.4" 89 | resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz#bfd34dc9bba9824a4658b0317ec2fd571a51e6ef" 90 | integrity sha512-cokOMkxC/BTyNP1AlY25HuBWM32iCEsLPI4BHDpJCHHm1FU2E7dKWWIXJgQgSFiu4lp8q3bL1BIKwqkSUviqtA== 91 | dependencies: 92 | "@babel/types" "^7.15.4" 93 | 94 | "@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.14.5", "@babel/helper-module-imports@^7.15.4": 95 | version "7.15.4" 96 | resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz#e18007d230632dea19b47853b984476e7b4e103f" 97 | integrity sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA== 98 | dependencies: 99 | "@babel/types" "^7.15.4" 100 | 101 | "@babel/helper-module-transforms@^7.15.8": 102 | version "7.15.8" 103 | resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.15.8.tgz#d8c0e75a87a52e374a8f25f855174786a09498b2" 104 | integrity sha512-DfAfA6PfpG8t4S6npwzLvTUpp0sS7JrcuaMiy1Y5645laRJIp/LiLGIBbQKaXSInK8tiGNI7FL7L8UvB8gdUZg== 105 | dependencies: 106 | "@babel/helper-module-imports" "^7.15.4" 107 | "@babel/helper-replace-supers" "^7.15.4" 108 | "@babel/helper-simple-access" "^7.15.4" 109 | "@babel/helper-split-export-declaration" "^7.15.4" 110 | "@babel/helper-validator-identifier" "^7.15.7" 111 | "@babel/template" "^7.15.4" 112 | "@babel/traverse" "^7.15.4" 113 | "@babel/types" "^7.15.6" 114 | 115 | "@babel/helper-optimise-call-expression@^7.15.4": 116 | version "7.15.4" 117 | resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz#f310a5121a3b9cc52d9ab19122bd729822dee171" 118 | integrity sha512-E/z9rfbAOt1vDW1DR7k4SzhzotVV5+qMciWV6LaG1g4jeFrkDlJedjtV4h0i4Q/ITnUu+Pk08M7fczsB9GXBDw== 119 | dependencies: 120 | "@babel/types" "^7.15.4" 121 | 122 | "@babel/helper-plugin-utils@^7.14.5": 123 | version "7.14.5" 124 | resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" 125 | integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== 126 | 127 | "@babel/helper-replace-supers@^7.15.4": 128 | version "7.15.4" 129 | resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz#52a8ab26ba918c7f6dee28628b07071ac7b7347a" 130 | integrity sha512-/ztT6khaXF37MS47fufrKvIsiQkx1LBRvSJNzRqmbyeZnTwU9qBxXYLaaT/6KaxfKhjs2Wy8kG8ZdsFUuWBjzw== 131 | dependencies: 132 | "@babel/helper-member-expression-to-functions" "^7.15.4" 133 | "@babel/helper-optimise-call-expression" "^7.15.4" 134 | "@babel/traverse" "^7.15.4" 135 | "@babel/types" "^7.15.4" 136 | 137 | "@babel/helper-simple-access@^7.15.4": 138 | version "7.15.4" 139 | resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz#ac368905abf1de8e9781434b635d8f8674bcc13b" 140 | integrity sha512-UzazrDoIVOZZcTeHHEPYrr1MvTR/K+wgLg6MY6e1CJyaRhbibftF6fR2KU2sFRtI/nERUZR9fBd6aKgBlIBaPg== 141 | dependencies: 142 | "@babel/types" "^7.15.4" 143 | 144 | "@babel/helper-split-export-declaration@^7.15.4": 145 | version "7.15.4" 146 | resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz#aecab92dcdbef6a10aa3b62ab204b085f776e257" 147 | integrity sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw== 148 | dependencies: 149 | "@babel/types" "^7.15.4" 150 | 151 | "@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.9", "@babel/helper-validator-identifier@^7.15.7": 152 | version "7.15.7" 153 | resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" 154 | integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== 155 | 156 | "@babel/helper-validator-option@^7.14.5": 157 | version "7.14.5" 158 | resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" 159 | integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== 160 | 161 | "@babel/helpers@^7.15.4": 162 | version "7.15.4" 163 | resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.15.4.tgz#5f40f02050a3027121a3cf48d497c05c555eaf43" 164 | integrity sha512-V45u6dqEJ3w2rlryYYXf6i9rQ5YMNu4FLS6ngs8ikblhu2VdR1AqAd6aJjBzmf2Qzh6KOLqKHxEN9+TFbAkAVQ== 165 | dependencies: 166 | "@babel/template" "^7.15.4" 167 | "@babel/traverse" "^7.15.4" 168 | "@babel/types" "^7.15.4" 169 | 170 | "@babel/highlight@^7.14.5": 171 | version "7.14.5" 172 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" 173 | integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== 174 | dependencies: 175 | "@babel/helper-validator-identifier" "^7.14.5" 176 | chalk "^2.0.0" 177 | js-tokens "^4.0.0" 178 | 179 | "@babel/parser@^7.15.4", "@babel/parser@^7.15.8": 180 | version "7.15.8" 181 | resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.8.tgz#7bacdcbe71bdc3ff936d510c15dcea7cf0b99016" 182 | integrity sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA== 183 | 184 | "@babel/plugin-syntax-jsx@^7.14.5": 185 | version "7.14.5" 186 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.14.5.tgz#000e2e25d8673cce49300517a3eda44c263e4201" 187 | integrity sha512-ohuFIsOMXJnbOMRfX7/w7LocdR6R7whhuRD4ax8IipLcLPlZGJKkBxgHp++U4N/vKyU16/YDQr2f5seajD3jIw== 188 | dependencies: 189 | "@babel/helper-plugin-utils" "^7.14.5" 190 | 191 | "@babel/plugin-transform-react-jsx-development@^7.14.5": 192 | version "7.14.5" 193 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.14.5.tgz#1a6c73e2f7ed2c42eebc3d2ad60b0c7494fcb9af" 194 | integrity sha512-rdwG/9jC6QybWxVe2UVOa7q6cnTpw8JRRHOxntG/h6g/guAOe6AhtQHJuJh5FwmnXIT1bdm5vC2/5huV8ZOorQ== 195 | dependencies: 196 | "@babel/plugin-transform-react-jsx" "^7.14.5" 197 | 198 | "@babel/plugin-transform-react-jsx-self@^7.14.9": 199 | version "7.14.9" 200 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.14.9.tgz#33041e665453391eb6ee54a2ecf3ba1d46bd30f4" 201 | integrity sha512-Fqqu0f8zv9W+RyOnx29BX/RlEsBRANbOf5xs5oxb2aHP4FKbLXxIaVPUiCti56LAR1IixMH4EyaixhUsKqoBHw== 202 | dependencies: 203 | "@babel/helper-plugin-utils" "^7.14.5" 204 | 205 | "@babel/plugin-transform-react-jsx-source@^7.14.5": 206 | version "7.14.5" 207 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.14.5.tgz#79f728e60e6dbd31a2b860b0bf6c9765918acf1d" 208 | integrity sha512-1TpSDnD9XR/rQ2tzunBVPThF5poaYT9GqP+of8fAtguYuI/dm2RkrMBDemsxtY0XBzvW7nXjYM0hRyKX9QYj7Q== 209 | dependencies: 210 | "@babel/helper-plugin-utils" "^7.14.5" 211 | 212 | "@babel/plugin-transform-react-jsx@^7.14.5", "@babel/plugin-transform-react-jsx@^7.14.9": 213 | version "7.14.9" 214 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.14.9.tgz#3314b2163033abac5200a869c4de242cd50a914c" 215 | integrity sha512-30PeETvS+AeD1f58i1OVyoDlVYQhap/K20ZrMjLmmzmC2AYR/G43D4sdJAaDAqCD3MYpSWbmrz3kES158QSLjw== 216 | dependencies: 217 | "@babel/helper-annotate-as-pure" "^7.14.5" 218 | "@babel/helper-module-imports" "^7.14.5" 219 | "@babel/helper-plugin-utils" "^7.14.5" 220 | "@babel/plugin-syntax-jsx" "^7.14.5" 221 | "@babel/types" "^7.14.9" 222 | 223 | "@babel/runtime@^7.9.2": 224 | version "7.15.4" 225 | resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a" 226 | integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw== 227 | dependencies: 228 | regenerator-runtime "^0.13.4" 229 | 230 | "@babel/template@^7.15.4": 231 | version "7.15.4" 232 | resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.15.4.tgz#51898d35dcf3faa670c4ee6afcfd517ee139f194" 233 | integrity sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg== 234 | dependencies: 235 | "@babel/code-frame" "^7.14.5" 236 | "@babel/parser" "^7.15.4" 237 | "@babel/types" "^7.15.4" 238 | 239 | "@babel/traverse@^7.15.4", "@babel/traverse@^7.4.5": 240 | version "7.15.4" 241 | resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.15.4.tgz#ff8510367a144bfbff552d9e18e28f3e2889c22d" 242 | integrity sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA== 243 | dependencies: 244 | "@babel/code-frame" "^7.14.5" 245 | "@babel/generator" "^7.15.4" 246 | "@babel/helper-function-name" "^7.15.4" 247 | "@babel/helper-hoist-variables" "^7.15.4" 248 | "@babel/helper-split-export-declaration" "^7.15.4" 249 | "@babel/parser" "^7.15.4" 250 | "@babel/types" "^7.15.4" 251 | debug "^4.1.0" 252 | globals "^11.1.0" 253 | 254 | "@babel/types@^7.14.9", "@babel/types@^7.15.4", "@babel/types@^7.15.6": 255 | version "7.15.6" 256 | resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.15.6.tgz#99abdc48218b2881c058dd0a7ab05b99c9be758f" 257 | integrity sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig== 258 | dependencies: 259 | "@babel/helper-validator-identifier" "^7.14.9" 260 | to-fast-properties "^2.0.0" 261 | 262 | "@emotion/is-prop-valid@^0.8.8": 263 | version "0.8.8" 264 | resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a" 265 | integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA== 266 | dependencies: 267 | "@emotion/memoize" "0.7.4" 268 | 269 | "@emotion/memoize@0.7.4": 270 | version "0.7.4" 271 | resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb" 272 | integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw== 273 | 274 | "@emotion/stylis@^0.8.4": 275 | version "0.8.5" 276 | resolved "https://registry.yarnpkg.com/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04" 277 | integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ== 278 | 279 | "@emotion/unitless@^0.7.4": 280 | version "0.7.5" 281 | resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" 282 | integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== 283 | 284 | "@reduxjs/toolkit@^1.6.2": 285 | version "1.6.2" 286 | resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.6.2.tgz#2f2b5365df77dd6697da28fdf44f33501ed9ba37" 287 | integrity sha512-HbfI/hOVrAcMGAYsMWxw3UJyIoAS9JTdwddsjlr5w3S50tXhWb+EMyhIw+IAvCVCLETkzdjgH91RjDSYZekVBA== 288 | dependencies: 289 | immer "^9.0.6" 290 | redux "^4.1.0" 291 | redux-thunk "^2.3.0" 292 | reselect "^4.0.0" 293 | 294 | "@rollup/pluginutils@^4.1.1": 295 | version "4.1.1" 296 | resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.1.tgz#1d4da86dd4eded15656a57d933fda2b9a08d47ec" 297 | integrity sha512-clDjivHqWGXi7u+0d2r2sBi4Ie6VLEAzWMIkvJLnDmxoOhBYOTfzGbOQBA32THHm11/LiJbd01tJUpJsbshSWQ== 298 | dependencies: 299 | estree-walker "^2.0.1" 300 | picomatch "^2.2.2" 301 | 302 | "@types/prop-types@*": 303 | version "15.7.4" 304 | resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11" 305 | integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ== 306 | 307 | "@types/react-dom@^17.0.0": 308 | version "17.0.10" 309 | resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.10.tgz#d6972ec018d23cf22b99597f1289343d99ea9d9d" 310 | integrity sha512-8oz3NAUId2z/zQdFI09IMhQPNgIbiP8Lslhv39DIDamr846/0spjZK0vnrMak0iB8EKb9QFTTIdg2Wj2zH5a3g== 311 | dependencies: 312 | "@types/react" "*" 313 | 314 | "@types/react@*", "@types/react@^17.0.0": 315 | version "17.0.33" 316 | resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.33.tgz#e01ae3de7613dac1094569880bb3792732203ad5" 317 | integrity sha512-pLWntxXpDPaU+RTAuSGWGSEL2FRTNyRQOjSWDke/rxRg14ncsZvx8AKWMWZqvc1UOaJIAoObdZhAWvRaHFi5rw== 318 | dependencies: 319 | "@types/prop-types" "*" 320 | "@types/scheduler" "*" 321 | csstype "^3.0.2" 322 | 323 | "@types/scheduler@*": 324 | version "0.16.2" 325 | resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" 326 | integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== 327 | 328 | "@vitejs/plugin-react@^1.0.0": 329 | version "1.0.7" 330 | resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-1.0.7.tgz#d542003afbae875f86fb89f3811a0f7c0c9479f5" 331 | integrity sha512-dzxzohFOAVVXpGlFn6Uvw2xaSLp80Vjmg2e5G1XdMV266vVKrcDqg9CWP/AiJiXuubNUdgy1k4E8dNXI6WCyhw== 332 | dependencies: 333 | "@babel/core" "^7.15.5" 334 | "@babel/plugin-transform-react-jsx" "^7.14.9" 335 | "@babel/plugin-transform-react-jsx-development" "^7.14.5" 336 | "@babel/plugin-transform-react-jsx-self" "^7.14.9" 337 | "@babel/plugin-transform-react-jsx-source" "^7.14.5" 338 | "@rollup/pluginutils" "^4.1.1" 339 | react-refresh "^0.10.0" 340 | resolve "^1.20.0" 341 | 342 | ansi-styles@^3.2.1: 343 | version "3.2.1" 344 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 345 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== 346 | dependencies: 347 | color-convert "^1.9.0" 348 | 349 | anymatch@~3.1.2: 350 | version "3.1.2" 351 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" 352 | integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== 353 | dependencies: 354 | normalize-path "^3.0.0" 355 | picomatch "^2.0.4" 356 | 357 | axios@^0.24.0: 358 | version "0.24.0" 359 | resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" 360 | integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== 361 | dependencies: 362 | follow-redirects "^1.14.4" 363 | 364 | "babel-plugin-styled-components@>= 1.12.0": 365 | version "1.13.3" 366 | resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-1.13.3.tgz#1f1cb3927d4afa1e324695c78f690900e3d075bc" 367 | integrity sha512-meGStRGv+VuKA/q0/jXxrPNWEm4LPfYIqxooDTdmh8kFsP/Ph7jJG5rUPwUPX3QHUvggwdbgdGpo88P/rRYsVw== 368 | dependencies: 369 | "@babel/helper-annotate-as-pure" "^7.15.4" 370 | "@babel/helper-module-imports" "^7.15.4" 371 | babel-plugin-syntax-jsx "^6.18.0" 372 | lodash "^4.17.11" 373 | 374 | babel-plugin-syntax-jsx@^6.18.0: 375 | version "6.18.0" 376 | resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" 377 | integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY= 378 | 379 | binary-extensions@^2.0.0: 380 | version "2.2.0" 381 | resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" 382 | integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== 383 | 384 | braces@~3.0.2: 385 | version "3.0.2" 386 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" 387 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== 388 | dependencies: 389 | fill-range "^7.0.1" 390 | 391 | browserslist@^4.16.6: 392 | version "4.17.5" 393 | resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.5.tgz#c827bbe172a4c22b123f5e337533ceebadfdd559" 394 | integrity sha512-I3ekeB92mmpctWBoLXe0d5wPS2cBuRvvW0JyyJHMrk9/HmP2ZjrTboNAZ8iuGqaEIlKguljbQY32OkOJIRrgoA== 395 | dependencies: 396 | caniuse-lite "^1.0.30001271" 397 | electron-to-chromium "^1.3.878" 398 | escalade "^3.1.1" 399 | node-releases "^2.0.1" 400 | picocolors "^1.0.0" 401 | 402 | camelize@^1.0.0: 403 | version "1.0.0" 404 | resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b" 405 | integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs= 406 | 407 | caniuse-lite@^1.0.30001271: 408 | version "1.0.30001272" 409 | resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001272.tgz#8e9790ff995e9eb6e1f4c45cd07ddaa87cddbb14" 410 | integrity sha512-DV1j9Oot5dydyH1v28g25KoVm7l8MTxazwuiH3utWiAS6iL/9Nh//TGwqFEeqqN8nnWYQ8HHhUq+o4QPt9kvYw== 411 | 412 | chalk@^2.0.0: 413 | version "2.4.2" 414 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" 415 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== 416 | dependencies: 417 | ansi-styles "^3.2.1" 418 | escape-string-regexp "^1.0.5" 419 | supports-color "^5.3.0" 420 | 421 | "chokidar@>=3.0.0 <4.0.0": 422 | version "3.5.2" 423 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" 424 | integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== 425 | dependencies: 426 | anymatch "~3.1.2" 427 | braces "~3.0.2" 428 | glob-parent "~5.1.2" 429 | is-binary-path "~2.1.0" 430 | is-glob "~4.0.1" 431 | normalize-path "~3.0.0" 432 | readdirp "~3.6.0" 433 | optionalDependencies: 434 | fsevents "~2.3.2" 435 | 436 | color-convert@^1.9.0: 437 | version "1.9.3" 438 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" 439 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== 440 | dependencies: 441 | color-name "1.1.3" 442 | 443 | color-name@1.1.3: 444 | version "1.1.3" 445 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 446 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= 447 | 448 | convert-source-map@^1.7.0: 449 | version "1.8.0" 450 | resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" 451 | integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== 452 | dependencies: 453 | safe-buffer "~5.1.1" 454 | 455 | css-color-keywords@^1.0.0: 456 | version "1.0.0" 457 | resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05" 458 | integrity sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU= 459 | 460 | css-to-react-native@^3.0.0: 461 | version "3.0.0" 462 | resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-3.0.0.tgz#62dbe678072a824a689bcfee011fc96e02a7d756" 463 | integrity sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ== 464 | dependencies: 465 | camelize "^1.0.0" 466 | css-color-keywords "^1.0.0" 467 | postcss-value-parser "^4.0.2" 468 | 469 | csstype@^3.0.2: 470 | version "3.0.9" 471 | resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.9.tgz#6410af31b26bd0520933d02cbc64fce9ce3fbf0b" 472 | integrity sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw== 473 | 474 | debug@^4.1.0: 475 | version "4.3.2" 476 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" 477 | integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== 478 | dependencies: 479 | ms "2.1.2" 480 | 481 | electron-to-chromium@^1.3.878: 482 | version "1.3.884" 483 | resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.884.tgz#0cd8c3a80271fd84a81f284c60fb3c9ecb33c166" 484 | integrity sha512-kOaCAa+biA98PwH5BpCkeUeTL6mCeg8p3Q3OhqzPyqhu/5QUnWAN2wr/3IK8xMQxIV76kfoQpP+Bn/wij/jXrg== 485 | 486 | esbuild-android-arm64@0.13.10: 487 | version "0.13.10" 488 | resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.13.10.tgz#3545c71bf01e8b36535681078cdb0191c8654452" 489 | integrity sha512-1sCdVAq64yMp2Uhlu+97/enFxpmrj31QHtThz7K+/QGjbHa7JZdBdBsZCzWJuntKHZ+EU178tHYkvjaI9z5sGg== 490 | 491 | esbuild-darwin-64@0.13.10: 492 | version "0.13.10" 493 | resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.13.10.tgz#143e34d7f5d3860cc681c64c860f531e60496b5b" 494 | integrity sha512-XlL+BYZ2h9cz3opHfFgSHGA+iy/mljBFIRU9q++f9SiBXEZTb4gTW/IENAD1l9oKH0FdO9rUpyAfV+lM4uAxrg== 495 | 496 | esbuild-darwin-arm64@0.13.10: 497 | version "0.13.10" 498 | resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.10.tgz#456a044b789d5d256af8d264314da5217ca9fcd1" 499 | integrity sha512-RZMMqMTyActMrXKkW71IQO8B0tyQm0Bm+ZJQWNaHJchL5LlqazJi7rriwSocP+sKLszHhsyTEBBh6qPdw5g5yQ== 500 | 501 | esbuild-freebsd-64@0.13.10: 502 | version "0.13.10" 503 | resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.10.tgz#dcd829a4a95226716faae8a2f378f08688f921b6" 504 | integrity sha512-pf4BEN9reF3jvZEZdxljVgOv5JS4kuYFCI78xk+2HWustbLvTP0b9XXfWI/OD0ZLWbyLYZYIA+VbVe4tdAklig== 505 | 506 | esbuild-freebsd-arm64@0.13.10: 507 | version "0.13.10" 508 | resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.10.tgz#116c254b3eb1b9d1dd6f12e0271967de4512ca09" 509 | integrity sha512-j9PUcuNWmlxr4/ry4dK/s6zKh42Jhh/N5qnAAj7tx3gMbkIHW0JBoVSbbgp97p88X9xgKbXx4lG2sJDhDWmsYQ== 510 | 511 | esbuild-linux-32@0.13.10: 512 | version "0.13.10" 513 | resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.13.10.tgz#131971622c38e5aa014303a494a1b5c3cc90f2be" 514 | integrity sha512-imtdHG5ru0xUUXuc2ofdtyw0fWlHYXV7JjF7oZHgmn0b+B4o4Nr6ZON3xxoo1IP8wIekW+7b9exIf/MYq0QV7w== 515 | 516 | esbuild-linux-64@0.13.10: 517 | version "0.13.10" 518 | resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.13.10.tgz#48826c388abd5dde3fc098a8ef38d8b548674f93" 519 | integrity sha512-O7fzQIH2e7GC98dvoTH0rad5BVLm9yU3cRWfEmryCEIFTwbNEWCEWOfsePuoGOHRtSwoVY1hPc21CJE4/9rWxQ== 520 | 521 | esbuild-linux-arm64@0.13.10: 522 | version "0.13.10" 523 | resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.10.tgz#0be9ffc92e30641869c7fbca0ec5d30fa8cbddd6" 524 | integrity sha512-bkGxN67S2n0PF4zhh87/92kBTsH2xXLuH6T5omReKhpXdJZF5SVDSk5XU/nngARzE+e6QK6isK060Dr5uobzNw== 525 | 526 | esbuild-linux-arm@0.13.10: 527 | version "0.13.10" 528 | resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.13.10.tgz#8c15bcaa41a022c834f049a71a7d1fbade507532" 529 | integrity sha512-R2Jij4A0K8BcmBehvQeUteQEcf24Y2YZ6mizlNFuJOBPxe3vZNmkZ4mCE7Pf1tbcqA65qZx8J3WSHeGJl9EsJA== 530 | 531 | esbuild-linux-mips64le@0.13.10: 532 | version "0.13.10" 533 | resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.10.tgz#5bb33a2bc82e9c78ed724f345a8359610ddc9695" 534 | integrity sha512-UDNO5snJYOLWrA2uOUxM/PVbzzh2TR7Zf2i8zCCuFlYgvAb/81XO+Tasp3YAElDpp4VGqqcpBXLtofa9nrnJGA== 535 | 536 | esbuild-linux-ppc64le@0.13.10: 537 | version "0.13.10" 538 | resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.10.tgz#18703cd0d52447d97486735b8e79fba7d81eac65" 539 | integrity sha512-xu6J9rMWu1TcEGuEmoc8gsTrJCEPsf+QtxK4IiUZNde9r4Q4nlRVah4JVZP3hJapZgZJcxsse0XiKXh1UFdOeA== 540 | 541 | esbuild-netbsd-64@0.13.10: 542 | version "0.13.10" 543 | resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.10.tgz#3ecb06158aadb5b7396a5b7632069181b1591c56" 544 | integrity sha512-d+Gr0ScMC2J83Bfx/ZvJHK0UAEMncctwgjRth9d4zppYGLk/xMfFKxv5z1ib8yZpQThafq8aPm8AqmFIJrEesw== 545 | 546 | esbuild-openbsd-64@0.13.10: 547 | version "0.13.10" 548 | resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.10.tgz#3a6950b1d955de921ac52f7af0b4865e89d6e4f1" 549 | integrity sha512-OuCYc+bNKumBvxflga+nFzZvxsgmWQW+z4rMGIjM5XIW0nNbGgRc5p/0PSDv0rTdxAmwCpV69fezal0xjrDaaA== 550 | 551 | esbuild-sunos-64@0.13.10: 552 | version "0.13.10" 553 | resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.13.10.tgz#ad407f721a8b5727fca31958b5eab1b0232e2d73" 554 | integrity sha512-gUkgivZK11bD56wDoLsnYrsOHD/zHzzLSdqKcIl3wRMulfHpRBpoX8gL0dbWr+8N9c+1HDdbNdvxSRmZ4RCVwg== 555 | 556 | esbuild-windows-32@0.13.10: 557 | version "0.13.10" 558 | resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.13.10.tgz#ddaaa0b6e172df6512edc7a91bd2456615cfa914" 559 | integrity sha512-C1xJ54E56dGWRaYcTnRy7amVZ9n1/D/D2/qVw7e5EtS7p+Fv/yZxxgqyb1hMGKXgtFYX4jMpU5eWBF/AsYrn+A== 560 | 561 | esbuild-windows-64@0.13.10: 562 | version "0.13.10" 563 | resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.13.10.tgz#93d861abf36bf71b6e61f5cbd2e42762ce5cb83a" 564 | integrity sha512-6+EXEXopEs3SvPFAHcps2Krp/FvqXXsOQV33cInmyilb0ZBEQew4MIoZtMIyB3YXoV6//dl3i6YbPrFZaWEinQ== 565 | 566 | esbuild-windows-arm64@0.13.10: 567 | version "0.13.10" 568 | resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.10.tgz#85a2d338aaa8b0cd1d8ecbe9150def9a608e8947" 569 | integrity sha512-xTqM/XKhORo6u9S5I0dNJWEdWoemFjogLUTVLkQMVyUV3ZuMChahVA+bCqKHdyX55pCFxD/8v2fm3/sfFMWN+g== 570 | 571 | esbuild@^0.13.2: 572 | version "0.13.10" 573 | resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.13.10.tgz#e3d24d59f1d8b2130d746ca858efcb80e1d99b26" 574 | integrity sha512-0NfCsnAh5XatHIx6Cu93wpR2v6opPoOMxONYhaAoZKzGYqAE+INcDeX2wqMdcndvPQdWCuuCmvlnsh0zmbHcSQ== 575 | optionalDependencies: 576 | esbuild-android-arm64 "0.13.10" 577 | esbuild-darwin-64 "0.13.10" 578 | esbuild-darwin-arm64 "0.13.10" 579 | esbuild-freebsd-64 "0.13.10" 580 | esbuild-freebsd-arm64 "0.13.10" 581 | esbuild-linux-32 "0.13.10" 582 | esbuild-linux-64 "0.13.10" 583 | esbuild-linux-arm "0.13.10" 584 | esbuild-linux-arm64 "0.13.10" 585 | esbuild-linux-mips64le "0.13.10" 586 | esbuild-linux-ppc64le "0.13.10" 587 | esbuild-netbsd-64 "0.13.10" 588 | esbuild-openbsd-64 "0.13.10" 589 | esbuild-sunos-64 "0.13.10" 590 | esbuild-windows-32 "0.13.10" 591 | esbuild-windows-64 "0.13.10" 592 | esbuild-windows-arm64 "0.13.10" 593 | 594 | escalade@^3.1.1: 595 | version "3.1.1" 596 | resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" 597 | integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== 598 | 599 | escape-string-regexp@^1.0.5: 600 | version "1.0.5" 601 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 602 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 603 | 604 | eslint-plugin-react-hooks@^4.2.0: 605 | version "4.2.0" 606 | resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz#8c229c268d468956334c943bb45fc860280f5556" 607 | integrity sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ== 608 | 609 | estree-walker@^2.0.1: 610 | version "2.0.2" 611 | resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" 612 | integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== 613 | 614 | fill-range@^7.0.1: 615 | version "7.0.1" 616 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" 617 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== 618 | dependencies: 619 | to-regex-range "^5.0.1" 620 | 621 | follow-redirects@^1.14.4: 622 | version "1.14.4" 623 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.4.tgz#838fdf48a8bbdd79e52ee51fb1c94e3ed98b9379" 624 | integrity sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g== 625 | 626 | fsevents@~2.3.2: 627 | version "2.3.2" 628 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 629 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== 630 | 631 | function-bind@^1.1.1: 632 | version "1.1.1" 633 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 634 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 635 | 636 | gensync@^1.0.0-beta.2: 637 | version "1.0.0-beta.2" 638 | resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" 639 | integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== 640 | 641 | glob-parent@~5.1.2: 642 | version "5.1.2" 643 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" 644 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== 645 | dependencies: 646 | is-glob "^4.0.1" 647 | 648 | globals@^11.1.0: 649 | version "11.12.0" 650 | resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" 651 | integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== 652 | 653 | has-flag@^3.0.0: 654 | version "3.0.0" 655 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 656 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= 657 | 658 | has@^1.0.3: 659 | version "1.0.3" 660 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" 661 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 662 | dependencies: 663 | function-bind "^1.1.1" 664 | 665 | hoist-non-react-statics@^3.0.0: 666 | version "3.3.2" 667 | resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" 668 | integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== 669 | dependencies: 670 | react-is "^16.7.0" 671 | 672 | immer@^9.0.6: 673 | version "9.0.6" 674 | resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.6.tgz#7a96bf2674d06c8143e327cbf73539388ddf1a73" 675 | integrity sha512-G95ivKpy+EvVAnAab4fVa4YGYn24J1SpEktnJX7JJ45Bd7xqME/SCplFzYFmTbrkwZbQ4xJK1xMTUYBkN6pWsQ== 676 | 677 | is-binary-path@~2.1.0: 678 | version "2.1.0" 679 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" 680 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== 681 | dependencies: 682 | binary-extensions "^2.0.0" 683 | 684 | is-core-module@^2.2.0: 685 | version "2.8.0" 686 | resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.0.tgz#0321336c3d0925e497fd97f5d95cb114a5ccd548" 687 | integrity sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw== 688 | dependencies: 689 | has "^1.0.3" 690 | 691 | is-extglob@^2.1.1: 692 | version "2.1.1" 693 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 694 | integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= 695 | 696 | is-glob@^4.0.1, is-glob@~4.0.1: 697 | version "4.0.3" 698 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" 699 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== 700 | dependencies: 701 | is-extglob "^2.1.1" 702 | 703 | is-number@^7.0.0: 704 | version "7.0.0" 705 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" 706 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 707 | 708 | "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: 709 | version "4.0.0" 710 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" 711 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== 712 | 713 | jsesc@^2.5.1: 714 | version "2.5.2" 715 | resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" 716 | integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== 717 | 718 | json5@^2.1.2: 719 | version "2.2.0" 720 | resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" 721 | integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== 722 | dependencies: 723 | minimist "^1.2.5" 724 | 725 | lodash@^4.17.11: 726 | version "4.17.21" 727 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" 728 | integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== 729 | 730 | loose-envify@^1.1.0: 731 | version "1.4.0" 732 | resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" 733 | integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== 734 | dependencies: 735 | js-tokens "^3.0.0 || ^4.0.0" 736 | 737 | minimist@^1.2.5: 738 | version "1.2.5" 739 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" 740 | integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== 741 | 742 | ms@2.1.2: 743 | version "2.1.2" 744 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 745 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 746 | 747 | nanoid@^3.1.30: 748 | version "3.1.30" 749 | resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362" 750 | integrity sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ== 751 | 752 | node-releases@^2.0.1: 753 | version "2.0.1" 754 | resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" 755 | integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== 756 | 757 | normalize-path@^3.0.0, normalize-path@~3.0.0: 758 | version "3.0.0" 759 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" 760 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== 761 | 762 | object-assign@^4.1.1: 763 | version "4.1.1" 764 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 765 | integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= 766 | 767 | path-parse@^1.0.6: 768 | version "1.0.7" 769 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" 770 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== 771 | 772 | picocolors@^1.0.0: 773 | version "1.0.0" 774 | resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" 775 | integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== 776 | 777 | picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2: 778 | version "2.3.0" 779 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" 780 | integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== 781 | 782 | postcss-value-parser@^4.0.2: 783 | version "4.1.0" 784 | resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" 785 | integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== 786 | 787 | postcss@^8.3.8: 788 | version "8.3.11" 789 | resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.11.tgz#c3beca7ea811cd5e1c4a3ec6d2e7599ef1f8f858" 790 | integrity sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA== 791 | dependencies: 792 | nanoid "^3.1.30" 793 | picocolors "^1.0.0" 794 | source-map-js "^0.6.2" 795 | 796 | prettier@2.4.1: 797 | version "2.4.1" 798 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.4.1.tgz#671e11c89c14a4cfc876ce564106c4a6726c9f5c" 799 | integrity sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA== 800 | 801 | react-dom@^17.0.0: 802 | version "17.0.2" 803 | resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" 804 | integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== 805 | dependencies: 806 | loose-envify "^1.1.0" 807 | object-assign "^4.1.1" 808 | scheduler "^0.20.2" 809 | 810 | react-is@^16.7.0: 811 | version "16.13.1" 812 | resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" 813 | integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== 814 | 815 | react-refresh@^0.10.0: 816 | version "0.10.0" 817 | resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.10.0.tgz#2f536c9660c0b9b1d500684d9e52a65e7404f7e3" 818 | integrity sha512-PgidR3wST3dDYKr6b4pJoqQFpPGNKDSCDx4cZoshjXipw3LzO7mG1My2pwEzz2JVkF+inx3xRpDeQLFQGH/hsQ== 819 | 820 | react@^17.0.0: 821 | version "17.0.2" 822 | resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" 823 | integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== 824 | dependencies: 825 | loose-envify "^1.1.0" 826 | object-assign "^4.1.1" 827 | 828 | readdirp@~3.6.0: 829 | version "3.6.0" 830 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" 831 | integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== 832 | dependencies: 833 | picomatch "^2.2.1" 834 | 835 | redux-thunk@^2.3.0: 836 | version "2.4.0" 837 | resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.0.tgz#ac89e1d6b9bdb9ee49ce69a69071be41bbd82d67" 838 | integrity sha512-/y6ZKQNU/0u8Bm7ROLq9Pt/7lU93cT0IucYMrubo89ENjxPa7i8pqLKu6V4X7/TvYovQ6x01unTeyeZ9lgXiTA== 839 | 840 | redux@^4.1.0: 841 | version "4.1.2" 842 | resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.2.tgz#140f35426d99bb4729af760afcf79eaaac407104" 843 | integrity sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw== 844 | dependencies: 845 | "@babel/runtime" "^7.9.2" 846 | 847 | regenerator-runtime@^0.13.4: 848 | version "0.13.9" 849 | resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" 850 | integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== 851 | 852 | reselect@^4.0.0: 853 | version "4.1.1" 854 | resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.1.tgz#7e2c110efbf3d70df3482604bcf2bc0ab571346a" 855 | integrity sha512-Jjt8Us6hAWJpjucyladHvUGR+q1mHHgWtGDXlhvvKyNyIeQ3bjuWLDX0bsTLhbm/gd4iXEACBlODUHBlLWiNnA== 856 | 857 | resolve@^1.20.0: 858 | version "1.20.0" 859 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" 860 | integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== 861 | dependencies: 862 | is-core-module "^2.2.0" 863 | path-parse "^1.0.6" 864 | 865 | rollup@^2.57.0: 866 | version "2.58.3" 867 | resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.58.3.tgz#71a08138d9515fb65043b6a18618b2ed9ac8d239" 868 | integrity sha512-ei27MSw1KhRur4p87Q0/Va2NAYqMXOX++FNEumMBcdreIRLURKy+cE2wcDJKBn0nfmhP2ZGrJkP1XPO+G8FJQw== 869 | optionalDependencies: 870 | fsevents "~2.3.2" 871 | 872 | rxjs@^7.4.0: 873 | version "7.4.0" 874 | resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.4.0.tgz#a12a44d7eebf016f5ff2441b87f28c9a51cebc68" 875 | integrity sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w== 876 | dependencies: 877 | tslib "~2.1.0" 878 | 879 | safe-buffer@~5.1.1: 880 | version "5.1.2" 881 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 882 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== 883 | 884 | sass@^1.43.4: 885 | version "1.43.4" 886 | resolved "https://registry.yarnpkg.com/sass/-/sass-1.43.4.tgz#68c7d6a1b004bef49af0d9caf750e9b252105d1f" 887 | integrity sha512-/ptG7KE9lxpGSYiXn7Ar+lKOv37xfWsZRtFYal2QHNigyVQDx685VFT/h7ejVr+R8w7H4tmUgtulsKl5YpveOg== 888 | dependencies: 889 | chokidar ">=3.0.0 <4.0.0" 890 | 891 | scheduler@^0.20.2: 892 | version "0.20.2" 893 | resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" 894 | integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== 895 | dependencies: 896 | loose-envify "^1.1.0" 897 | object-assign "^4.1.1" 898 | 899 | semver@^6.3.0: 900 | version "6.3.0" 901 | resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" 902 | integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== 903 | 904 | shallowequal@^1.1.0: 905 | version "1.1.0" 906 | resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" 907 | integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== 908 | 909 | source-map-js@^0.6.2: 910 | version "0.6.2" 911 | resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" 912 | integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug== 913 | 914 | source-map@^0.5.0: 915 | version "0.5.7" 916 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" 917 | integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= 918 | 919 | styled-components@^5.3.3: 920 | version "5.3.3" 921 | resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.3.3.tgz#312a3d9a549f4708f0fb0edc829eb34bde032743" 922 | integrity sha512-++4iHwBM7ZN+x6DtPPWkCI4vdtwumQ+inA/DdAsqYd4SVgUKJie5vXyzotA00ttcFdQkCng7zc6grwlfIfw+lw== 923 | dependencies: 924 | "@babel/helper-module-imports" "^7.0.0" 925 | "@babel/traverse" "^7.4.5" 926 | "@emotion/is-prop-valid" "^0.8.8" 927 | "@emotion/stylis" "^0.8.4" 928 | "@emotion/unitless" "^0.7.4" 929 | babel-plugin-styled-components ">= 1.12.0" 930 | css-to-react-native "^3.0.0" 931 | hoist-non-react-statics "^3.0.0" 932 | shallowequal "^1.1.0" 933 | supports-color "^5.5.0" 934 | 935 | supports-color@^5.3.0, supports-color@^5.5.0: 936 | version "5.5.0" 937 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 938 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 939 | dependencies: 940 | has-flag "^3.0.0" 941 | 942 | to-fast-properties@^2.0.0: 943 | version "2.0.0" 944 | resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" 945 | integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= 946 | 947 | to-regex-range@^5.0.1: 948 | version "5.0.1" 949 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" 950 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 951 | dependencies: 952 | is-number "^7.0.0" 953 | 954 | tslib@~2.1.0: 955 | version "2.1.0" 956 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" 957 | integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== 958 | 959 | typescript@^4.3.2: 960 | version "4.4.4" 961 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c" 962 | integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA== 963 | 964 | vite@^2.6.4: 965 | version "2.6.13" 966 | resolved "https://registry.yarnpkg.com/vite/-/vite-2.6.13.tgz#16b3ec85a66d5b461ac29a903874d4357f9af432" 967 | integrity sha512-+tGZ1OxozRirTudl4M3N3UTNJOlxdVo/qBl2IlDEy/ZpTFcskp+k5ncNjayR3bRYTCbqSOFz2JWGN1UmuDMScA== 968 | dependencies: 969 | esbuild "^0.13.2" 970 | postcss "^8.3.8" 971 | resolve "^1.20.0" 972 | rollup "^2.57.0" 973 | optionalDependencies: 974 | fsevents "~2.3.2" 975 | --------------------------------------------------------------------------------