diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 45a961f..3acf042 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,7 +1,7 @@ # These are supported funding model platforms github: [librarian-org] -patreon: # Replace with a single Patreon username +patreon: librarianorg open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel diff --git a/package.json b/package.json index 4c1250c..af07447 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "url": "https://github.com/librarian-org/librarian/issues" }, "productName": "librarian", - "version": "1.0.0-alpha.1", + "version": "1.0.0-alpha.2", "description": "The Librarian app is ideal for schools libraries and for someone who have many books.", "main": ".webpack/main", "scripts": { @@ -104,6 +104,7 @@ "react-spinners-kit": "^1.9.1", "react-spring": "^9.3.2", "react-table": "^7.7.0", + "recharts": "^2.1.13", "reflect-metadata": "^0.1.13", "sqlite3": "^5.0.2", "styled-components": "^5.3.3", diff --git a/src/app/@types/index.d.ts b/src/app/@types/index.d.ts index 7feba3f..dae105b 100644 --- a/src/app/@types/index.d.ts +++ b/src/app/@types/index.d.ts @@ -1,6 +1,6 @@ export interface Api { send: (channel: string, data: unknown) => void; - sendSync: (channel: string, data: unknown) => unknown; + sendSync: (channel: string, data?: unknown) => unknown; on: (channel: string, listener: (event: any, ...arg: any) => void) => void; once: (channel: string, listener: (event: any, ...arg: any) => void) => void; removeListener: (channel: string, listener: (event: any, ...arg: any) => void) => void; diff --git a/src/app/components/Dashboard/index.tsx b/src/app/components/Dashboard/index.tsx new file mode 100644 index 0000000..af2ef03 --- /dev/null +++ b/src/app/components/Dashboard/index.tsx @@ -0,0 +1,64 @@ +import React, { useEffect, useState } from 'react'; +import { FaHandshake } from 'react-icons/fa'; +import { FiAlertTriangle, FiBook, FiUsers } from 'react-icons/fi'; +import i18n from '../../i18n'; +import Panel from '../Panel'; +import PanelBody from '../Panel/PanelBody'; +import PanelFooter from '../Panel/PanelFooter'; +import { Container, Row, Wrapper } from './styles'; + +const Dashboard: React.FC = () => { + const [registeredSamples, setRegisteredSamples] = useState(0); + const [registeredPeople, setRegisteredPeople] = useState(0); + const [activeBorrows, setActiveBorrows] = useState(0); + const [overdueBorrows, setOverdueBorrows] = useState(0); + + useEffect(() => { + const samples = window.api.sendSync('regiteredSamples') as number; + setRegisteredSamples(samples); + + const people = window.api.sendSync('regiteredPeople') as number; + setRegisteredPeople(people); + + const actives = window.api.sendSync('activeBorrows') as number; + setActiveBorrows(actives); + + const overdues = window.api.sendSync('overdueBorrows') as number; + setOverdueBorrows(overdues); + }, []); + + return ( + + + + + {overdueBorrows} + + {i18n.t('dashboard.overdueBorrows')} + + + + {activeBorrows} + + {i18n.t('dashboard.activeBorrows')} + + + + {registeredSamples} + + {i18n.t('dashboard.registeredSamples')} + + + + {registeredPeople} + + {i18n.t('dashboard.registeredPeople')} + + + + + + ); +}; + +export default Dashboard; diff --git a/src/app/components/Dashboard/styles.ts b/src/app/components/Dashboard/styles.ts new file mode 100644 index 0000000..5727d4c --- /dev/null +++ b/src/app/components/Dashboard/styles.ts @@ -0,0 +1,24 @@ +import styled from 'styled-components'; + +export const Container = styled.div` + display: flex; + flex-direction: column; + width: 100vw; + padding: 24px; +`; + +export const Wrapper = styled.div` + padding: 24px; + display: flex; + flex-direction: column; + justify-content: space-around; +`; + +export const Row = styled.div` + display: flex; + flex-direction: row; + justify-content: space-between; + & > div { + margin: 16px; + } +`; diff --git a/src/app/components/Panel/PanelBody/index.tsx b/src/app/components/Panel/PanelBody/index.tsx new file mode 100644 index 0000000..5bb3723 --- /dev/null +++ b/src/app/components/Panel/PanelBody/index.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { IconBaseProps } from 'react-icons'; + +import { Container } from './styles'; + +interface PanelBodyProps { + icon?: React.ComponentType; +} + +const PanelBody: React.FC = ({ + children, + icon: Icon, +}) => { + return ( + + {children} + {Icon && } + + ); +}; + +export default PanelBody; diff --git a/src/app/components/Panel/PanelBody/styles.ts b/src/app/components/Panel/PanelBody/styles.ts new file mode 100644 index 0000000..88229cc --- /dev/null +++ b/src/app/components/Panel/PanelBody/styles.ts @@ -0,0 +1,9 @@ +import styled from 'styled-components'; + +export const Container = styled.h1` + display: flex; + justify-content: space-between; + flex-direction: row; + padding: 16px; + color: #D1D1D1; +`; diff --git a/src/app/components/Panel/PanelFooter/index.tsx b/src/app/components/Panel/PanelFooter/index.tsx new file mode 100644 index 0000000..34ac39c --- /dev/null +++ b/src/app/components/Panel/PanelFooter/index.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +import { Container } from './styles'; + +interface PanelFooterProps { + color: string; +} + +const PanelFooter: React.FC = ({ color, children }) => { + return {children}; +}; + +export default PanelFooter; diff --git a/src/app/components/Panel/PanelFooter/styles.ts b/src/app/components/Panel/PanelFooter/styles.ts new file mode 100644 index 0000000..bdde1e2 --- /dev/null +++ b/src/app/components/Panel/PanelFooter/styles.ts @@ -0,0 +1,19 @@ +import styled from 'styled-components'; +import {shade} from 'polished'; + +interface ContainerProps { + backgroundColor: string; +} + +export const Container = styled.div` + display: flex; + flex-direction: row; + width: 100%; + color: #D1D1D1; + background: ${props => shade(0.1, props.backgroundColor)}; + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; + padding: 16px; + padding-top: 8px; + padding-bottom: 8px; +`; diff --git a/src/app/components/Panel/index.tsx b/src/app/components/Panel/index.tsx new file mode 100644 index 0000000..349f6a0 --- /dev/null +++ b/src/app/components/Panel/index.tsx @@ -0,0 +1,17 @@ +import React from 'react'; + +import { Container } from './styles'; + +interface PanelProps { + color: string; +} + +const Panel: React.FC = ({ color, children }) => { + return ( + + {children} + + ); +}; + +export default Panel; diff --git a/src/app/components/Panel/styles.ts b/src/app/components/Panel/styles.ts new file mode 100644 index 0000000..7530272 --- /dev/null +++ b/src/app/components/Panel/styles.ts @@ -0,0 +1,15 @@ +import styled from 'styled-components'; + +interface ContainerProps { + backgroundColor: string; +} + +export const Container = styled.div` + border-radius: 10px; + display: flex; + flex-direction: column; + width: 90%; + background-color: ${props => props.backgroundColor}; + color: #D1D1D1; +`; + diff --git a/src/app/components/Tabs/TabHeader/index.tsx b/src/app/components/Tabs/TabHeader/index.tsx index a7b38b6..1b6f9fa 100644 --- a/src/app/components/Tabs/TabHeader/index.tsx +++ b/src/app/components/Tabs/TabHeader/index.tsx @@ -2,6 +2,7 @@ import React, { useRef } from 'react'; import { Tab } from '../Tab'; import { FiX, FiBook, FiUser, FiSettings } from 'react-icons/fi'; import { FaHandshake } from 'react-icons/fa'; +import { AiFillDashboard } from 'react-icons/ai'; import { Container } from './styles'; import Translator from '../../I18n/Translator'; import { DropTargetMonitor, useDrag, useDrop, XYCoord } from 'react-dnd'; @@ -103,6 +104,7 @@ const TabHeader: React.FC = ({ moveTab, index, title, id, onClic {tab.type === 'person' && ()} {tab.type === 'borrow' && ()} {tab.type === 'settings' && ()} + {tab.type === 'dashboard' && ()}
diff --git a/src/app/components/Tabs/index.tsx b/src/app/components/Tabs/index.tsx index ef5eac4..e4887f2 100644 --- a/src/app/components/Tabs/index.tsx +++ b/src/app/components/Tabs/index.tsx @@ -4,9 +4,6 @@ import React, { useMemo, useState, useRef, - MutableRefObject, - Ref, - RefObject, } from 'react'; import { on, off } from '../../util/EventHandler'; import { v4 } from 'uuid'; @@ -20,6 +17,7 @@ import Borrow from '../Borrow'; import Title from '../Title'; import Person from '../Person'; import Settings from '../Settings'; +import Dashboard from '../Dashboard'; import { DndProvider } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; import update from 'immutability-helper'; @@ -47,7 +45,6 @@ const Tabs: React.FC = () => { const [, setAction] = useState('list'); const globalSave = useRef(null); - const lastTab = useCallback((): Tab => { return tabItems[tabItems.length - 1]; }, [tabItems]); @@ -166,6 +163,10 @@ const Tabs: React.FC = () => { handleCreateTab('settings', Actions.update, {}); }, [handleCreateTab]); + const dashboardTab = useCallback(() => { + handleCreateTab('dashboard', Actions.create, {}); + }, [handleCreateTab]); + const quickSearch = useCallback(() => { setOpen((oldState) => !oldState); }, []); @@ -184,6 +185,7 @@ const Tabs: React.FC = () => { { event: 'titleTab', handler: titleTab }, { event: 'settingsTab', handler: settingsTab }, { event: 'quickSearch', handler: quickSearch }, + { event: 'dashboardTab', handler: dashboardTab }, { event: 'save', handler: save }, ], [ @@ -193,6 +195,7 @@ const Tabs: React.FC = () => { titleTab, settingsTab, quickSearch, + dashboardTab, save, ] ); @@ -281,6 +284,9 @@ const Tabs: React.FC = () => { {tab.type === 'settings' && ( )} + {tab.type === 'dashboard' && ( + + )} ) )} diff --git a/src/app/util/AppEventHandler.ts b/src/app/util/AppEventHandler.ts index fb68c78..15c595f 100644 --- a/src/app/util/AppEventHandler.ts +++ b/src/app/util/AppEventHandler.ts @@ -18,6 +18,7 @@ export default class { registerCallbacks(): void { window.api.on(AppEvent.languageChange, this.languageChange.bind(this)); + this.forward(AppEvent.dashboardTab); this.forward(AppEvent.borrowTab); this.forward(AppEvent.personTab); this.forward(AppEvent.titleTab); diff --git a/src/common/AppEvent.ts b/src/common/AppEvent.ts index a929eae..c53588f 100644 --- a/src/common/AppEvent.ts +++ b/src/common/AppEvent.ts @@ -1,5 +1,6 @@ export enum AppEvent { languageChange = 'languageChange', + dashboardTab = 'dashboardTab', borrowTab = 'borrowTab', personTab = 'personTab', titleTab = 'titleTab', diff --git a/src/electron/Main.ts b/src/electron/Main.ts index 481615a..201c360 100644 --- a/src/electron/Main.ts +++ b/src/electron/Main.ts @@ -18,6 +18,7 @@ import I18NextAdapter from './infra/i18n/I18NextAdapter'; import I18nAdapter from './data/protocols/I18n/I18n'; import Resource from './data/protocols/Resource/Resource'; import EntitiesConfigs from './database/EntitiesConfigs'; +import { Repository } from './data/protocols'; export default class Main { private connection: Connection; @@ -105,8 +106,15 @@ export default class Main { listener.listenerName, async (event: IpcMainEvent, content: Event[]) => { try { + let repository: Repository; + if (content[0] === undefined) { + repository = repositoryFactory.make(listener.repositoryName); + event.returnValue = await repository.execute(); + return; + } + const { value, entity } = content[0]; - const repository = repositoryFactory.make( + repository = repositoryFactory.make( listener.repositoryName, entity ); diff --git a/src/electron/data/protocols/Menu/MenuActionHandler.ts b/src/electron/data/protocols/Menu/MenuActionHandler.ts index 04203d0..788b5d7 100644 --- a/src/electron/data/protocols/Menu/MenuActionHandler.ts +++ b/src/electron/data/protocols/Menu/MenuActionHandler.ts @@ -3,6 +3,7 @@ import { BrowserWindow, MenuItem } from 'electron'; type ElectronWindow = BrowserWindow | undefined; export interface MenuActionHandler { + showDashboard: (menuItem: MenuItem, win: ElectronWindow) => void; newBorrow: (menuItem: MenuItem, win: ElectronWindow) => void; newPerson: (menuItem: MenuItem, win: ElectronWindow) => void; newTitle: (menuItem: MenuItem, win: ElectronWindow) => void; diff --git a/src/electron/data/protocols/db/Repository.ts b/src/electron/data/protocols/db/Repository.ts index 9aa2bf2..b1e7dfc 100644 --- a/src/electron/data/protocols/db/Repository.ts +++ b/src/electron/data/protocols/db/Repository.ts @@ -5,5 +5,5 @@ import typeORM from 'typeorm'; export interface Repository { repository: typeORM.Repository; getInstance(): RepositoryBase; - execute(content: unknown): Promise; + execute(content?: unknown): Promise; } diff --git a/src/electron/infra/db/RepositoryBase.ts b/src/electron/infra/db/RepositoryBase.ts index 880f334..39ed955 100644 --- a/src/electron/infra/db/RepositoryBase.ts +++ b/src/electron/infra/db/RepositoryBase.ts @@ -10,7 +10,7 @@ export default class RepositoryBase implements Repository { } // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async execute(content: unknown): Promise { + public async execute(content?: unknown): Promise { throw new Error('Must be implemented on children classes.'); } } diff --git a/src/electron/infra/db/factories/RepositoryFactory.ts b/src/electron/infra/db/factories/RepositoryFactory.ts index 15f34b4..3f88ae7 100644 --- a/src/electron/infra/db/factories/RepositoryFactory.ts +++ b/src/electron/infra/db/factories/RepositoryFactory.ts @@ -20,7 +20,12 @@ export default class RepositoryFactory { }); } - public make(repository: string, entity: string): Repository { + public make(repository: string, entity?: string): Repository { + if (!entity) { + const dynamicRepository = this.products[repository].getInstance(); + return dynamicRepository; + } + const typeOrmRepository = this.connection.getRepository(entity); const dynamicRepository = this.products[repository].getInstance(typeOrmRepository); diff --git a/src/electron/infra/db/repositories/dashboard/ActiveBorrowsRepository.ts b/src/electron/infra/db/repositories/dashboard/ActiveBorrowsRepository.ts new file mode 100644 index 0000000..86a23fe --- /dev/null +++ b/src/electron/infra/db/repositories/dashboard/ActiveBorrowsRepository.ts @@ -0,0 +1,35 @@ +import { getConnection } from 'typeorm'; +import RepositoryBase from '../../RepositoryBase'; + +export class ActiveBorrowsRepository extends RepositoryBase { + private static instance: ActiveBorrowsRepository = null; + + static repositoryName = 'ActiveBorrows'; + + private constructor() { + super(); + this.repository = getConnection().getRepository('Borrow'); + } + + public static getInstance(): ActiveBorrowsRepository { + if (!ActiveBorrowsRepository.instance) { + ActiveBorrowsRepository.instance = new ActiveBorrowsRepository(); + } + + return ActiveBorrowsRepository.instance; + } + + public async execute(): Promise { + try { + return await this.repository.count({ + where: { + returnedAt: null, + isReservation: false, + }, + }); + } catch (err) { + console.log(err); + throw err; + } + } +} diff --git a/src/electron/infra/db/repositories/dashboard/OverdueBorrowsRepository.ts b/src/electron/infra/db/repositories/dashboard/OverdueBorrowsRepository.ts new file mode 100644 index 0000000..24b9d83 --- /dev/null +++ b/src/electron/infra/db/repositories/dashboard/OverdueBorrowsRepository.ts @@ -0,0 +1,38 @@ +import RepositoryBase from '../../RepositoryBase'; +import { getConnection, LessThan } from 'typeorm'; +import { DateUtils } from 'typeorm/util/DateUtils'; + +export class OverdueBorrowsRepository extends RepositoryBase { + private static instance: OverdueBorrowsRepository = null; + + static repositoryName = 'OverdueBorrows'; + + private constructor() { + super(); + this.repository = getConnection().getRepository('Borrow'); + } + + public static getInstance(): OverdueBorrowsRepository { + if (!OverdueBorrowsRepository.instance) { + OverdueBorrowsRepository.instance = new OverdueBorrowsRepository(); + } + return OverdueBorrowsRepository.instance; + } + + public async execute(): Promise { + try { + return await this.repository.count({ + where: { + returnedAt: null, + estimatedReturn: LessThan( + DateUtils.mixedDateToUtcDatetimeString(new Date()) + ), + isReservation: false, + }, + }); + } catch (err) { + console.log(err); + throw err; + } + } +} diff --git a/src/electron/infra/db/repositories/dashboard/RegisteredPeopleRepository.ts b/src/electron/infra/db/repositories/dashboard/RegisteredPeopleRepository.ts new file mode 100644 index 0000000..e31134c --- /dev/null +++ b/src/electron/infra/db/repositories/dashboard/RegisteredPeopleRepository.ts @@ -0,0 +1,30 @@ +import RepositoryBase from '../../RepositoryBase'; +import { getConnection } from 'typeorm'; + +export class RegisteredPeopleRepository extends RepositoryBase { + private static instance: RegisteredPeopleRepository = null; + + static repositoryName = 'RegisteredPeople'; + + private constructor() { + super(); + this.repository = getConnection().getRepository('User'); + } + + public static getInstance(): RegisteredPeopleRepository { + if (!RegisteredPeopleRepository.instance) { + RegisteredPeopleRepository.instance = new RegisteredPeopleRepository(); + } + + return RegisteredPeopleRepository.instance; + } + + public async execute(): Promise { + try { + return await this.repository.count(); + } catch (err) { + console.log(err); + throw err; + } + } +} diff --git a/src/electron/infra/db/repositories/dashboard/RegisteredSamplesRepository.ts b/src/electron/infra/db/repositories/dashboard/RegisteredSamplesRepository.ts new file mode 100644 index 0000000..1eaa7b6 --- /dev/null +++ b/src/electron/infra/db/repositories/dashboard/RegisteredSamplesRepository.ts @@ -0,0 +1,30 @@ +import RepositoryBase from '../../RepositoryBase'; +import { getConnection } from 'typeorm'; + +export class RegisteredSamplesRepository extends RepositoryBase { + private static instance: RegisteredSamplesRepository = null; + + static repositoryName = 'RegisteredSamples'; + + private constructor() { + super(); + this.repository = getConnection().getRepository('TitlePublisher'); + } + + public static getInstance(): RegisteredSamplesRepository { + if (!RegisteredSamplesRepository.instance) { + RegisteredSamplesRepository.instance = new RegisteredSamplesRepository(); + } + + return RegisteredSamplesRepository.instance; + } + + public async execute(): Promise { + try { + return await this.repository.count(); + } catch (err) { + console.log(err); + throw err; + } + } +} diff --git a/src/electron/infra/db/repositories/index.ts b/src/electron/infra/db/repositories/index.ts index 01cf22b..03ae29f 100644 --- a/src/electron/infra/db/repositories/index.ts +++ b/src/electron/infra/db/repositories/index.ts @@ -23,3 +23,7 @@ export * from './user/ChangePasswordRepository'; export * from './user/UserCreateRepository'; export * from './user/UserLoginRepository'; export * from './user/UserUpdateRepository'; +export * from './dashboard/RegisteredSamplesRepository'; +export * from './dashboard/RegisteredPeopleRepository'; +export * from './dashboard/ActiveBorrowsRepository'; +export * from './dashboard/OverdueBorrowsRepository'; diff --git a/src/electron/infra/listeners/ListenersConfigs.ts b/src/electron/infra/listeners/ListenersConfigs.ts index d6fcfde..a901010 100644 --- a/src/electron/infra/listeners/ListenersConfigs.ts +++ b/src/electron/infra/listeners/ListenersConfigs.ts @@ -39,6 +39,11 @@ export default class ListenersConfigs { { listenerName: 'changePassword', repositoryName: 'ChangePassword' }, { listenerName: 'globalSearch', repositoryName: 'GlobalSearch' }, + + { listenerName: 'regiteredSamples', repositoryName: 'RegisteredSamples' }, + { listenerName: 'regiteredPeople', repositoryName: 'RegisteredPeople' }, + { listenerName: 'overdueBorrows', repositoryName: 'OverdueBorrows' }, + { listenerName: 'activeBorrows', repositoryName: 'ActiveBorrows' }, ]; return products; diff --git a/src/electron/infra/menu/DefaultMenu.ts b/src/electron/infra/menu/DefaultMenu.ts index 3eb3940..c7ce47e 100644 --- a/src/electron/infra/menu/DefaultMenu.ts +++ b/src/electron/infra/menu/DefaultMenu.ts @@ -16,6 +16,12 @@ export default class DefaultMenu implements MenuBuildTemplate { { label: this.i18nAdapter.translate('menu.file.label'), submenu: [ + { + label: this.i18nAdapter.translate('menu.file.showDashboard'), + accelerator: 'Ctrl+D', + click: this.actionHandler.showDashboard, + }, + { type: 'separator' }, { label: this.i18nAdapter.translate('menu.file.newBorrow'), accelerator: 'Ctrl+B', diff --git a/src/electron/infra/menu/NativeMenuActionHandler.ts b/src/electron/infra/menu/NativeMenuActionHandler.ts index 5f211af..97e7104 100644 --- a/src/electron/infra/menu/NativeMenuActionHandler.ts +++ b/src/electron/infra/menu/NativeMenuActionHandler.ts @@ -16,6 +16,12 @@ export default class NativeMenuActionHandlers implements MenuActionHandler { this.showLogs = this.showLogs.bind(this); } + public showDashboard(_menuItem: MenuItem, win: ElectronWindow): void { + if (win) { + win.webContents.send(AppEvent.dashboardTab); + } + } + public newBorrow(_menuItem: MenuItem, win: ElectronWindow): void { if (win) { win.webContents.send(AppEvent.borrowTab, actionCreate); diff --git a/src/locales/en-US/common.json b/src/locales/en-US/common.json index 114e6a2..8234b9f 100644 --- a/src/locales/en-US/common.json +++ b/src/locales/en-US/common.json @@ -4,6 +4,7 @@ "menu": { "file": { "label": "&File", + "showDashboard": "Show Dashboard", "newBorrow": "New Borrow", "newPerson": "New Person", "newTitle": "New Title", @@ -62,6 +63,13 @@ "of": "of", "itemsPerPage": "Items per page" }, + "dashboard": { + "label": "Dashboard", + "overdueBorrows": "Overdue borrows", + "activeBorrows": "Active borrows", + "registeredSamples": "Registered samples", + "registeredPeople": "Registered People" + }, "borrow": { "label": "Borrow", "borrow": "Borrow", diff --git a/src/locales/pt-BR/common.json b/src/locales/pt-BR/common.json index 2637a8a..580e18f 100644 --- a/src/locales/pt-BR/common.json +++ b/src/locales/pt-BR/common.json @@ -4,6 +4,7 @@ "menu": { "file": { "label": "&Arquivo", + "showDashboard": "Exibir o Dashboard", "newBorrow": "Novo Empréstimo", "newPerson": "Nova Pessoa", "newTitle": "Novo Título", @@ -62,6 +63,13 @@ "of": "de", "itemsPerPage": "Itens por página" }, + "dashboard": { + "label": "Dashboard", + "overdueBorrows": "Empréstimos atrasados", + "activeBorrows": "Empréstimos ativos", + "registeredSamples": "Exemplares cadastrados", + "registeredPeople": "Pessoas cadastradas" + }, "borrow": { "label": "Empréstimo", "borrow": "Empréstimo", diff --git a/yarn.lock b/yarn.lock index 4282e30..7c23891 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1012,6 +1012,13 @@ "@babel/helper-validator-option" "^7.16.7" "@babel/plugin-transform-typescript" "^7.16.7" +"@babel/runtime@^7.1.2": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" + integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/runtime@^7.12.0", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.0", "@babel/runtime@^7.14.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.16.3" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5" @@ -3532,6 +3539,11 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== +classnames@^2.2.5: + version "2.3.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" + integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== + clean-css@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.2.2.tgz#d3a7c6ee2511011e051719838bdcf8314dc4548d" @@ -4084,6 +4096,11 @@ css-to-react-native@^3.0.0: css-color-keywords "^1.0.0" postcss-value-parser "^4.0.2" +css-unit-converter@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.2.tgz#4c77f5a1954e6dbff60695ecb214e3270436ab21" + integrity sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA== + css-what@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.1.0.tgz#3f7b707aadf633baf62c2ceb8579b545bb40f7fe" @@ -4128,6 +4145,67 @@ currently-unhandled@^0.4.1: dependencies: array-find-index "^1.0.1" +"d3-array@2 - 3", "d3-array@2.10.0 - 3": + version "3.2.0" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.0.tgz#15bf96cd9b7333e02eb8de8053d78962eafcff14" + integrity sha512-3yXFQo0oG3QCxbF06rMPFyGRMGJNS7NvsV1+2joOjbBE+9xvWQ8+GcMJAjRCzw06zQ3/arXeJgbPYcjUCuC+3g== + dependencies: + internmap "1 - 2" + +"d3-color@1 - 3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2" + integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== + +"d3-format@1 - 3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641" + integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA== + +"d3-interpolate@1.2.0 - 3", d3-interpolate@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d" + integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g== + dependencies: + d3-color "1 - 3" + +"d3-path@1 - 3": + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.0.1.tgz#f09dec0aaffd770b7995f1a399152bf93052321e" + integrity sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w== + +d3-scale@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396" + integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ== + dependencies: + d3-array "2.10.0 - 3" + d3-format "1 - 3" + d3-interpolate "1.2.0 - 3" + d3-time "2.1.1 - 3" + d3-time-format "2 - 4" + +d3-shape@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.1.0.tgz#c8a495652d83ea6f524e482fca57aa3f8bc32556" + integrity sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ== + dependencies: + d3-path "1 - 3" + +"d3-time-format@2 - 4": + version "4.1.0" + resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a" + integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg== + dependencies: + d3-time "1 - 3" + +"d3-time@1 - 3", "d3-time@2.1.1 - 3": + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.0.0.tgz#65972cb98ae2d4954ef5c932e8704061335d4975" + integrity sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ== + dependencies: + d3-array "2 - 3" + dargs@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" @@ -4193,6 +4271,11 @@ decamelize@^1.1.0, decamelize@^1.1.2, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +decimal.js-light@^2.4.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934" + integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== + decimal.js@^10.2.1: version "10.3.1" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" @@ -4414,6 +4497,13 @@ dom-converter@^0.2.0: dependencies: utila "~0.4" +dom-helpers@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8" + integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA== + dependencies: + "@babel/runtime" "^7.1.2" + dom-helpers@^5.0.1: version "5.2.1" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" @@ -4990,7 +5080,7 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= -eventemitter3@^4.0.0: +eventemitter3@^4.0.0, eventemitter3@^4.0.1: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== @@ -5148,6 +5238,11 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-equals@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-2.0.4.tgz#3add9410585e2d7364c2deeb6a707beadb24b927" + integrity sha512-caj/ZmjHljPrZtbzJ3kfH5ia/k4mTJe/qSiXAGzxZWRZgsgDV0cvNaQULqUX8t0/JVlzzEdYOwCN5DmzTxoD4w== + fast-glob@^3.1.1: version "3.2.7" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1" @@ -6282,6 +6377,11 @@ internal-slot@^1.0.3: has "^1.0.3" side-channel "^1.0.4" +"internmap@1 - 2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009" + integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== + ip@^1.1.0, ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" @@ -7404,7 +7504,7 @@ lodash.truncate@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= -lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0: +lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -8956,7 +9056,7 @@ react-icons@^4.3.1: resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.3.1.tgz#2fa92aebbbc71f43d2db2ed1aed07361124e91ca" integrity sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ== -react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: +react-is@^16.10.2, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -8966,7 +9066,7 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-lifecycles-compat@^3.0.0: +react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== @@ -8981,6 +9081,13 @@ react-modal@^3.14.4: react-lifecycles-compat "^3.0.0" warning "^4.0.3" +react-resize-detector@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/react-resize-detector/-/react-resize-detector-7.1.2.tgz#8ef975dd8c3d56f9a5160ac382ef7136dcd2d86c" + integrity sha512-zXnPJ2m8+6oq9Nn8zsep/orts9vQv3elrpA+R8XTcW7DVVUJ9vwDwMXaBtykAYjMnkCIaOoK9vObyR7ZgFNlOw== + dependencies: + lodash "^4.17.21" + react-router-dom@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.2.1.tgz#32ec81829152fbb8a7b045bf593a22eadf019bec" @@ -9009,6 +9116,14 @@ react-select@^5.2.1: prop-types "^15.6.0" react-transition-group "^4.3.0" +react-smooth@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/react-smooth/-/react-smooth-2.0.1.tgz#74c7309916d6ccca182c4b30c8992f179e6c5a05" + integrity sha512-Own9TA0GPPf3as4vSwFhDouVfXP15ie/wIHklhyKBH5AN6NFtdk0UpHBnonV11BtqDkAWlt40MOUc+5srmW7NA== + dependencies: + fast-equals "^2.0.0" + react-transition-group "2.9.0" + react-spinners-kit@^1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/react-spinners-kit/-/react-spinners-kit-1.9.1.tgz#516a7de8e80702def006be481062382ab3c38066" @@ -9037,6 +9152,16 @@ react-table@^7.7.0: resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.7.0.tgz#e2ce14d7fe3a559f7444e9ecfe8231ea8373f912" integrity sha512-jBlj70iBwOTvvImsU9t01LjFjy4sXEtclBovl3mTiqjz23Reu0DKnRza4zlLtOPACx6j2/7MrQIthIK1Wi+LIA== +react-transition-group@2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d" + integrity sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg== + dependencies: + dom-helpers "^3.4.0" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react-lifecycles-compat "^3.0.4" + react-transition-group@^4.3.0: version "4.4.2" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470" @@ -9173,6 +9298,30 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +recharts-scale@^0.4.4: + version "0.4.5" + resolved "https://registry.yarnpkg.com/recharts-scale/-/recharts-scale-0.4.5.tgz#0969271f14e732e642fcc5bd4ab270d6e87dd1d9" + integrity sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w== + dependencies: + decimal.js-light "^2.4.1" + +recharts@^2.1.13: + version "2.1.13" + resolved "https://registry.yarnpkg.com/recharts/-/recharts-2.1.13.tgz#61801acf3e13896b07dc6a8b38cbdd648480d0b7" + integrity sha512-9VWu2nzExmfiMFDHKqRFhYlJVmjzQGVKH5rBetXR4EuyEXuu3Y6cVxQuNEdusHhbm4SoPPrVDCwlBdREL3sQPA== + dependencies: + classnames "^2.2.5" + d3-interpolate "^3.0.1" + d3-scale "^4.0.2" + d3-shape "^3.1.0" + eventemitter3 "^4.0.1" + lodash "^4.17.19" + react-is "^16.10.2" + react-resize-detector "^7.1.2" + react-smooth "^2.0.1" + recharts-scale "^0.4.4" + reduce-css-calc "^2.1.8" + redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" @@ -9189,6 +9338,14 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" +reduce-css-calc@^2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz#7ef8761a28d614980dc0c982f772c93f7a99de03" + integrity sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg== + dependencies: + css-unit-converter "^1.1.1" + postcss-value-parser "^3.3.0" + redux@^4.1.1: version "4.1.2" resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.2.tgz#140f35426d99bb4729af760afcf79eaaac407104"