diff --git a/docker-compose.yml b/docker-compose.yml index 1f326c81f36..8e4773ce027 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -47,6 +47,17 @@ services: - base environment: - COUNTRY_CONFIG_URL_INTERNAL=http://countryconfig:3040 + + events: + image: opencrvs/ocrvs-events:${VERSION} + #platform: linux/amd64 + build: + context: . + dockerfile: ./packages/events/Dockerfile + restart: unless-stopped + depends_on: + - base + gateway: image: opencrvs/ocrvs-gateway:${VERSION} #platform: linux/amd64 diff --git a/packages/client/package.json b/packages/client/package.json index 3026ec3e2a7..e0be658b4ec 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -45,7 +45,7 @@ "@types/nock": "^10.0.3", "@types/pdfmake": "^0.2.0", "@types/react-redux": "^7.1.5", - "@types/react-router": "^5.1.2", + "@types/react-router-dom": "^5.1.2", "@types/react-tooltip": "^4.2.4", "@types/redux-sentry-middleware": "^0.2.0", "@types/styled-components": "^5.1.3", @@ -86,7 +86,7 @@ "react-intl": "5.25.1", "react-page-visibility": "^3.2.1", "react-redux": "^7.1.1", - "react-router": "^5.1.2", + "react-router-dom": "^5.1.2", "react-stickynode": "^2.0.1", "react-tooltip": "^4.2.21", "react-transition-group": "^4.2.1", diff --git a/packages/client/src/App.tsx b/packages/client/src/App.tsx index 6123a7fbb50..ce82f7c167e 100644 --- a/packages/client/src/App.tsx +++ b/packages/client/src/App.tsx @@ -42,7 +42,7 @@ import { ConnectedRouter } from 'connected-react-router' import { History, Location } from 'history' import * as React from 'react' import { Provider } from 'react-redux' -import { Route, Switch } from 'react-router' +import { Route, Switch } from 'react-router-dom' import { AppStore } from './store' import { CorrectionForm, CorrectionReviewForm } from './views/CorrectionForm' import { VerifyCorrector } from './views/CorrectionForm/VerifyCorrector' diff --git a/packages/client/src/components/Header/Header.tsx b/packages/client/src/components/Header/Header.tsx index 7af99a9525d..16cb7fde138 100644 --- a/packages/client/src/components/Header/Header.tsx +++ b/packages/client/src/components/Header/Header.tsx @@ -44,7 +44,7 @@ import { import * as React from 'react' import { injectIntl, WrappedComponentProps as IntlShapeProps } from 'react-intl' import { connect } from 'react-redux' -import { RouteComponentProps, withRouter } from 'react-router' +import { RouteComponentProps, withRouter } from 'react-router-dom' import { TEAM_USER_LIST } from '@client/navigation/routes' import { setAdvancedSearchParam } from '@client/search/advancedSearch/actions' import { advancedSearchInitialState } from '@client/search/advancedSearch/reducer' diff --git a/packages/client/src/components/Header/HistoryNavigator.tsx b/packages/client/src/components/Header/HistoryNavigator.tsx index 6c34bc9a4e1..dbfbb623963 100644 --- a/packages/client/src/components/Header/HistoryNavigator.tsx +++ b/packages/client/src/components/Header/HistoryNavigator.tsx @@ -10,7 +10,7 @@ */ import React from 'react' import { Button } from '@opencrvs/components/lib/Button' -import { useHistory } from 'react-router' +import { useHistory } from 'react-router-dom' import { useDispatch, useSelector } from 'react-redux' import { getUserDetails } from '@client/profile/profileSelectors' import { diff --git a/packages/client/src/components/Notification.tsx b/packages/client/src/components/Notification.tsx index 55545d7c305..d4640ea5075 100644 --- a/packages/client/src/components/Notification.tsx +++ b/packages/client/src/components/Notification.tsx @@ -10,7 +10,7 @@ */ import * as React from 'react' import { connect } from 'react-redux' -import { withRouter, RouteComponentProps } from 'react-router' +import { withRouter, RouteComponentProps } from 'react-router-dom' import { messages } from '@client/i18n/messages/views/notifications' import { userMessages } from '@client/i18n/messages/user' import { WrappedComponentProps as IntlShapeProps, injectIntl } from 'react-intl' diff --git a/packages/client/src/components/Page.tsx b/packages/client/src/components/Page.tsx index d1a60ba15b6..afbf65783b8 100644 --- a/packages/client/src/components/Page.tsx +++ b/packages/client/src/components/Page.tsx @@ -10,7 +10,7 @@ */ import * as React from 'react' import styled from 'styled-components' -import { RouteComponentProps, withRouter } from 'react-router' +import { RouteComponentProps, withRouter } from 'react-router-dom' import { connect } from 'react-redux' import { IStoreState } from '@opencrvs/client/src/store' import { setInitialDeclarations } from '@client/declarations' diff --git a/packages/client/src/components/ProtectedPage.tsx b/packages/client/src/components/ProtectedPage.tsx index 08373911ec1..914597302a3 100644 --- a/packages/client/src/components/ProtectedPage.tsx +++ b/packages/client/src/components/ProtectedPage.tsx @@ -12,7 +12,7 @@ import * as React from 'react' import PageVisibility from 'react-page-visibility' import { Unlock } from '@client/views/Unlock/Unlock' import { storage } from '@client/storage' -import { withRouter, RouteComponentProps } from 'react-router' +import { withRouter, RouteComponentProps } from 'react-router-dom' import { isMobileDevice } from '@client/utils/commonUtils' import IdleTimer from 'react-idle-timer' import { USER_DETAILS, UserDetails } from '@client/utils/userUtils' diff --git a/packages/client/src/components/ProtectedRoute.tsx b/packages/client/src/components/ProtectedRoute.tsx index 2822dc82afd..a56e37ea806 100644 --- a/packages/client/src/components/ProtectedRoute.tsx +++ b/packages/client/src/components/ProtectedRoute.tsx @@ -9,7 +9,7 @@ * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ import * as React from 'react' -import { Redirect, Route } from 'react-router' +import { Redirect, Route } from 'react-router-dom' import { connect } from 'react-redux' import { IStoreState } from '@client/store' import { getAuthenticated } from '@client/profile/profileSelectors' diff --git a/packages/client/src/components/ScrollToTop.tsx b/packages/client/src/components/ScrollToTop.tsx index 241961fc21e..e481616e582 100644 --- a/packages/client/src/components/ScrollToTop.tsx +++ b/packages/client/src/components/ScrollToTop.tsx @@ -9,7 +9,7 @@ * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ import React, { useEffect, ReactNode } from 'react' -import { withRouter, RouteComponentProps } from 'react-router' +import { withRouter, RouteComponentProps } from 'react-router-dom' type ScrollToTopProps = RouteComponentProps & { children?: ReactNode } diff --git a/packages/client/src/components/TransitionWrapper.tsx b/packages/client/src/components/TransitionWrapper.tsx index 69332124a3e..a93294b0ac2 100644 --- a/packages/client/src/components/TransitionWrapper.tsx +++ b/packages/client/src/components/TransitionWrapper.tsx @@ -16,7 +16,7 @@ import { PAGE_TRANSITIONS_EXIT_TIME } from '@client/utils/constants' import * as routes from '@client/navigation/routes' -import { matchPath } from 'react-router' +import { matchPath } from 'react-router-dom' import { Location } from 'history' function isPathExactmatch(pathname: string, routesPath: string): boolean { diff --git a/packages/client/src/components/charts/RegRatesLineChart.tsx b/packages/client/src/components/charts/RegRatesLineChart.tsx index eebfb98b042..2f5ad7462e5 100644 --- a/packages/client/src/components/charts/RegRatesLineChart.tsx +++ b/packages/client/src/components/charts/RegRatesLineChart.tsx @@ -27,25 +27,6 @@ interface IProps extends WrappedComponentProps { completenessRateTime?: CompletenessRateTime } -interface IActiveState { - value: number - stroke: string -} -interface IState { - legendMarginTop: number - legendMarginLeft: number - chartTop: number - chartRight: number - chartBottom: number - chartLeft: number - maximizeXAxisInterval?: boolean - legendLayout: LegendProps['layout'] - activeLabel: string - activeRegisteredInTargetDays: IActiveState - activeTotalRegistered: IActiveState - activeTotalEstimate: IActiveState -} - const CustomLegendContainer = styled.div<{ marginLeft: number marginTop: number @@ -123,6 +104,26 @@ interface ILineDataPoint { registrationPercentage: string } +interface IActiveState { + value: number + stroke: string +} + +interface IState { + legendMarginTop: number + legendMarginLeft: number + chartTop: number + chartRight: number + chartBottom: number + chartLeft: number + maximizeXAxisInterval?: boolean + legendLayout: LegendProps['layout'] + activeLabel: string + activeRegisteredInTargetDays: IActiveState + activeTotalRegistered: IActiveState + activeTotalEstimate: IActiveState +} + function LegendDot(props: React.HTMLAttributes) { return ( @@ -146,6 +147,7 @@ const RegRatesLineChartComponent = (props: IProps) => { legendLayout: 'horizontal' as const } } + const getStatePropertiesForLargeWindowChart = () => { return { legendMarginTop: -16, @@ -292,6 +294,7 @@ const RegRatesLineChartComponent = (props: IProps) => { ) } + // eslint-disable-next-line @typescript-eslint/no-explicit-any const customizedTooltip = (dataPoint: any) => { const wrapperPayload = dataPoint.payload[0] @@ -304,6 +307,7 @@ const RegRatesLineChartComponent = (props: IProps) => { ) } + // eslint-disable-next-line @typescript-eslint/no-explicit-any const mouseMoveHandler = (data: any) => { if (data && data.activePayload) { setState({ @@ -466,6 +470,7 @@ const RegRatesLineChartComponent = (props: IProps) => { /> ) } + return getLoadingIndicator() } diff --git a/packages/client/src/components/form/Redirect.tsx b/packages/client/src/components/form/Redirect.tsx index 050196e9c42..10af834fe22 100644 --- a/packages/client/src/components/form/Redirect.tsx +++ b/packages/client/src/components/form/Redirect.tsx @@ -14,7 +14,7 @@ import { getOfflineData } from '@client/offline/selectors' import { getUserDetails } from '@client/profile/profileSelectors' import React from 'react' import { useSelector } from 'react-redux' -import { Redirect } from 'react-router' +import { Redirect } from 'react-router-dom' export const RedirectField = ({ to, diff --git a/packages/client/src/components/interface/Navigation.tsx b/packages/client/src/components/interface/Navigation.tsx index 8bcb7d04a5e..5e6c286848c 100644 --- a/packages/client/src/components/interface/Navigation.tsx +++ b/packages/client/src/components/interface/Navigation.tsx @@ -62,7 +62,7 @@ import { omit } from 'lodash' import * as React from 'react' import { injectIntl, WrappedComponentProps as IntlShapeProps } from 'react-intl' import { connect } from 'react-redux' -import { RouteComponentProps, withRouter } from 'react-router' +import { RouteComponentProps, withRouter } from 'react-router-dom' import { IS_PROD_ENVIRONMENT } from '@client/utils/constants' const SCREEN_LOCK = 'screenLock' diff --git a/packages/client/src/setupTests.ts b/packages/client/src/setupTests.ts index bc635072148..5050f0985f2 100644 --- a/packages/client/src/setupTests.ts +++ b/packages/client/src/setupTests.ts @@ -279,8 +279,8 @@ vi.mock('./utils', async () => ({ getUserRole: vi.fn().mockImplementation((lang, role) => 'ENTREPENEUR') })) -vi.mock('react-router', async () => ({ - ...((await vi.importActual('react-router')) as any), +vi.mock('react-router-dom', async () => ({ + ...((await vi.importActual('react-router-dom')) as any), useParams: vi.fn().mockImplementation(() => ({ event: 'birth', section: 'child' diff --git a/packages/client/src/tests/util.tsx b/packages/client/src/tests/util.tsx index 916ee33f292..ef2df7cb08f 100644 --- a/packages/client/src/tests/util.tsx +++ b/packages/client/src/tests/util.tsx @@ -48,7 +48,7 @@ import { waitForElement } from './wait-for-element' import { setUserDetails } from '@client/profile/profileActions' import { createLocation, createMemoryHistory, History } from 'history' import { stringify } from 'query-string' -import { match as Match } from 'react-router' +import { match as Match } from 'react-router-dom' import { ConnectedRouter } from 'connected-react-router' import { mockOfflineData } from './mock-offline-data' import { Section, SubmissionAction } from '@client/forms' diff --git a/packages/client/src/views/AdvancedSearch/AdvancedSearchResult.tsx b/packages/client/src/views/AdvancedSearch/AdvancedSearchResult.tsx index 07c29364093..ece4847b3d7 100644 --- a/packages/client/src/views/AdvancedSearch/AdvancedSearchResult.tsx +++ b/packages/client/src/views/AdvancedSearch/AdvancedSearchResult.tsx @@ -43,7 +43,7 @@ import { messages as advancedSearchResultMessages } from '@client/i18n/messages/ import { SearchEventsQuery } from '@client/utils/gateway' import { Frame } from '@opencrvs/components/lib/Frame' import { LoadingIndicator } from '@client/views/OfficeHome/LoadingIndicator' -import { Redirect, RouteComponentProps } from 'react-router' +import { Redirect, RouteComponentProps } from 'react-router-dom' import { ErrorText, Link, Pill } from '@client/../../components/lib' import { WQContentWrapper } from '@client/views/OfficeHome/WQContentWrapper' import { diff --git a/packages/client/src/views/CorrectionForm/CorrectionForm.tsx b/packages/client/src/views/CorrectionForm/CorrectionForm.tsx index 723b0a1b0bc..678985b73b3 100644 --- a/packages/client/src/views/CorrectionForm/CorrectionForm.tsx +++ b/packages/client/src/views/CorrectionForm/CorrectionForm.tsx @@ -11,7 +11,7 @@ import * as React from 'react' import { connect } from 'react-redux' import { IStoreState } from '@client/store' -import { Redirect, RouteComponentProps } from 'react-router' +import { Redirect, RouteComponentProps } from 'react-router-dom' import { IDeclaration, modifyDeclaration } from '@client/declarations' import { CorrectorForm, diff --git a/packages/client/src/views/CorrectionForm/ReviewForm.tsx b/packages/client/src/views/CorrectionForm/ReviewForm.tsx index 513ae5120e2..cdee3a2fa7b 100644 --- a/packages/client/src/views/CorrectionForm/ReviewForm.tsx +++ b/packages/client/src/views/CorrectionForm/ReviewForm.tsx @@ -20,7 +20,7 @@ import { CERTIFICATE_CORRECTION_REVIEW, HOME } from '@client/navigation/routes' import { connect } from 'react-redux' import { getEventReviewForm } from '@client/forms/register/review-selectors' import { Event } from '@client/utils/gateway' -import { Redirect } from 'react-router' +import { Redirect } from 'react-router-dom' type IStateProps = { declaration: IDeclaration | undefined diff --git a/packages/client/src/views/CorrectionForm/VerifyCorrector.tsx b/packages/client/src/views/CorrectionForm/VerifyCorrector.tsx index 43c0a176822..db2ff5e8ca3 100644 --- a/packages/client/src/views/CorrectionForm/VerifyCorrector.tsx +++ b/packages/client/src/views/CorrectionForm/VerifyCorrector.tsx @@ -31,7 +31,7 @@ import { import * as React from 'react' import { WrappedComponentProps as IntlShapeProps, injectIntl } from 'react-intl' import { connect } from 'react-redux' -import { Redirect, RouteComponentProps } from 'react-router' +import { Redirect, RouteComponentProps } from 'react-router-dom' import { CERTIFICATE_CORRECTION_REVIEW, REGISTRAR_HOME_TAB diff --git a/packages/client/src/views/IssueCertificate/IssueCertificate.tsx b/packages/client/src/views/IssueCertificate/IssueCertificate.tsx index 0149f442dc8..c36c2356d52 100644 --- a/packages/client/src/views/IssueCertificate/IssueCertificate.tsx +++ b/packages/client/src/views/IssueCertificate/IssueCertificate.tsx @@ -14,7 +14,7 @@ import { Button } from '@opencrvs/components/lib/Button' import { constantsMessages } from '@client/i18n/messages' import { useIntl } from 'react-intl' import { HistoryNavigator } from '@client/components/Header/HistoryNavigator' -import { Redirect, useParams } from 'react-router' +import { Redirect, useParams } from 'react-router-dom' import { IPrintableDeclaration } from '@client/declarations' import { useDispatch } from 'react-redux' import { useDeclaration } from '@client/declarations/selectors' diff --git a/packages/client/src/views/IssueCertificate/IssueCollectorForm/IssueCollectorForm.tsx b/packages/client/src/views/IssueCertificate/IssueCollectorForm/IssueCollectorForm.tsx index 90492339a88..65a9f84a9e8 100644 --- a/packages/client/src/views/IssueCertificate/IssueCollectorForm/IssueCollectorForm.tsx +++ b/packages/client/src/views/IssueCertificate/IssueCollectorForm/IssueCollectorForm.tsx @@ -29,7 +29,7 @@ import { import { replaceInitialValues } from '@client/views/RegisterForm/RegisterForm' import { issueMessages } from '@client/i18n/messages/issueCertificate' import { getIssueCertCollectorGroupForEvent } from '@client/forms/certificate/fieldDefinitions/collectorSection' -import { Redirect } from 'react-router' +import { Redirect } from 'react-router-dom' import { REGISTRAR_HOME_TAB } from '@client/navigation/routes' import { WORKQUEUE_TABS } from '@client/components/interface/Navigation' import { getOfflineData } from '@client/offline/selectors' diff --git a/packages/client/src/views/IssueCertificate/IssueCollectorForm/IssueFormForOthers.tsx b/packages/client/src/views/IssueCertificate/IssueCollectorForm/IssueFormForOthers.tsx index bf8a07d5f38..d09e62d6db4 100644 --- a/packages/client/src/views/IssueCertificate/IssueCollectorForm/IssueFormForOthers.tsx +++ b/packages/client/src/views/IssueCertificate/IssueCollectorForm/IssueFormForOthers.tsx @@ -31,7 +31,7 @@ import { collectMarriageCertificateFormSection } from '@client/forms/certificate/fieldDefinitions/collectorSection' import { Event } from '@client/utils/gateway' -import { Redirect } from 'react-router' +import { Redirect } from 'react-router-dom' import { REGISTRAR_HOME_TAB } from '@client/navigation/routes' import { WORKQUEUE_TABS } from '@client/components/interface/Navigation' import { getOfflineData } from '@client/offline/selectors' diff --git a/packages/client/src/views/IssueCertificate/IssueCollectorForm/IssuePayment.test.tsx b/packages/client/src/views/IssueCertificate/IssueCollectorForm/IssuePayment.test.tsx index 1a8c4a62b94..8a0ce5963e7 100644 --- a/packages/client/src/views/IssueCertificate/IssueCollectorForm/IssuePayment.test.tsx +++ b/packages/client/src/views/IssueCertificate/IssueCollectorForm/IssuePayment.test.tsx @@ -25,7 +25,7 @@ import * as React from 'react' import { Mock, vi } from 'vitest' import { IssuePayment } from './IssuePayment' import { storeDeclaration } from '@client/declarations' -import { useParams } from 'react-router' +import { useParams } from 'react-router-dom' const getItem = window.localStorage.getItem as Mock ;(queries.fetchUserDetails as Mock).mockReturnValue(mockUserResponse) diff --git a/packages/client/src/views/IssueCertificate/IssueCollectorForm/IssuePayment.tsx b/packages/client/src/views/IssueCertificate/IssueCollectorForm/IssuePayment.tsx index ffd2b09c28f..74d2775a127 100644 --- a/packages/client/src/views/IssueCertificate/IssueCollectorForm/IssuePayment.tsx +++ b/packages/client/src/views/IssueCertificate/IssueCollectorForm/IssuePayment.tsx @@ -19,7 +19,7 @@ import { import { formatUrl, goBack, goToHomeTab } from '@client/navigation' import { useIntl } from 'react-intl' import * as React from 'react' -import { Redirect, useParams } from 'react-router' +import { Redirect, useParams } from 'react-router-dom' import { REGISTRAR_HOME_TAB } from '@client/navigation/routes' import { WORKQUEUE_TABS } from '@client/components/interface/Navigation' import { diff --git a/packages/client/src/views/OfficeHome/Home.tsx b/packages/client/src/views/OfficeHome/Home.tsx index 99da593c40a..5a58afb3a88 100644 --- a/packages/client/src/views/OfficeHome/Home.tsx +++ b/packages/client/src/views/OfficeHome/Home.tsx @@ -12,7 +12,7 @@ import { getUserDetails } from '@client/profile/profileSelectors' import * as React from 'react' import { useSelector } from 'react-redux' import { PERFORMANCE_HOME, REGISTRAR_HOME } from '@client/navigation/routes' -import { Redirect } from 'react-router' +import { Redirect } from 'react-router-dom' import { getDefaultPerformanceLocationId } from '@client/navigation' import { UserDetails } from '@client/utils/userUtils' import { diff --git a/packages/client/src/views/OfficeHome/OfficeHome.tsx b/packages/client/src/views/OfficeHome/OfficeHome.tsx index 595e5b83fc0..64795cb6ce2 100644 --- a/packages/client/src/views/OfficeHome/OfficeHome.tsx +++ b/packages/client/src/views/OfficeHome/OfficeHome.tsx @@ -40,7 +40,7 @@ import { Toast } from '@opencrvs/components/lib/Toast' import * as React from 'react' import { injectIntl, WrappedComponentProps as IntlShapeProps } from 'react-intl' import { connect } from 'react-redux' -import { RouteComponentProps } from 'react-router' +import { Link, RouteComponentProps } from 'react-router-dom' import { SentForReview } from './sentForReview/SentForReview' import { InProgress, SELECTOR_ID } from './inProgress/InProgress' import { ReadyToPrint } from './readyToPrint/ReadyToPrint' @@ -61,6 +61,7 @@ import { ReadyToIssue } from './readyToIssue/ReadyToIssue' import { getOfflineData } from '@client/offline/selectors' import { IOfflineData } from '@client/offline/reducer' import { Event } from '@client/utils/gateway' +import { SELECT_VITAL_EVENT } from '@client/navigation/routes' const FABContainer = styled.div` position: fixed; @@ -456,11 +457,12 @@ class OfficeHomeView extends React.Component< )} - } - /> + + } + /> + {this.state.showCertificateToast && ( diff --git a/packages/client/src/views/OfficeHome/inProgress/InProgress.tsx b/packages/client/src/views/OfficeHome/inProgress/InProgress.tsx index bccebf39128..e8949fe824a 100644 --- a/packages/client/src/views/OfficeHome/inProgress/InProgress.tsx +++ b/packages/client/src/views/OfficeHome/inProgress/InProgress.tsx @@ -84,6 +84,8 @@ import { isBirthEvent, isDeathEvent } from '@client/search/transformer' +import { useState } from 'react' +import { useWindowSize } from '@opencrvs/components/lib/hooks' interface IQueryData { inProgressData: GQLEventSearchResultSet @@ -109,12 +111,6 @@ interface IBaseRegistrarHomeProps { pageSize: number } -interface IRegistrarHomeState { - width: number - sortedCol: COLUMNS - sortOrder: SORT_ORDER -} - interface IProps { offlineCountryConfig: IOfflineData loading?: boolean @@ -129,52 +125,32 @@ export const SELECTOR_ID = { hospitalDrafts: 'hospitals' } -class InProgressComponent extends React.Component< - IRegistrarHomeProps, - IRegistrarHomeState -> { - constructor(props: IRegistrarHomeProps) { - super(props) - this.state = { - width: window.innerWidth, - sortedCol: - this.props.selectorId && this.props.selectorId !== SELECTOR_ID.ownDrafts - ? COLUMNS.NOTIFICATION_SENT - : COLUMNS.LAST_UPDATED, - sortOrder: SORT_ORDER.DESCENDING - } - } - - componentDidMount() { - window.addEventListener('resize', this.recordWindowWidth) - } +function InProgressComponent(props: IRegistrarHomeProps) { + const { width } = useWindowSize() - componentWillUnmount() { - window.removeEventListener('resize', this.recordWindowWidth) - } + const [sortedCol, setSortedCol] = useState( + props.selectorId && props.selectorId !== SELECTOR_ID.ownDrafts + ? COLUMNS.NOTIFICATION_SENT + : COLUMNS.LAST_UPDATED + ) + const [sortOrder, setSortOrder] = useState(SORT_ORDER.DESCENDING) - recordWindowWidth = () => { - this.setState({ width: window.innerWidth }) - } - - onColumnClick = (columnName: string) => { + const onColumnClick = (columnName: string) => { const { newSortedCol, newSortOrder } = changeSortedColumn( columnName, - this.state.sortedCol, - this.state.sortOrder + sortedCol, + sortOrder ) - this.setState({ - sortOrder: newSortOrder, - sortedCol: newSortedCol - }) + setSortOrder(newSortOrder) + setSortedCol(newSortedCol) } - transformRemoteDraftsContent = (data: GQLEventSearchResultSet) => { + const transformRemoteDraftsContent = (data: GQLEventSearchResultSet) => { if (!data || !data.results) { return [] } - const { intl } = this.props + const { intl } = props const { locale } = intl const items = data.results.map((reg, index) => { @@ -228,17 +204,17 @@ class InProgressComponent extends React.Component< ? plainDateToLocalDate(eventDate) : '' const actions: IAction[] = [] - const foundDeclaration = this.props.outboxDeclarations.find( + const foundDeclaration = props.outboxDeclarations.find( (declaration) => declaration.id === reg.id ) const downloadStatus = foundDeclaration?.downloadStatus - if (this.state.width > this.props.theme.grid.breakpoints.lg) { + if (width > props.theme.grid.breakpoints.lg) { actions.push({ label: intl.formatMessage(buttonMessages.update), handler: () => { if (downloadStatus === DOWNLOAD_STATUS.DOWNLOADED) { - this.props.goToPage( + props.goToPage( pageRoute, regId, 'review', @@ -267,8 +243,8 @@ class InProgressComponent extends React.Component< - this.props.goToDeclarationRecordAudit( - this.props.selectorId === SELECTOR_ID.hospitalDrafts + props.goToDeclarationRecordAudit( + props.selectorId === SELECTOR_ID.hospitalDrafts ? 'notificationTab' : 'inProgressTab', regId @@ -281,7 +257,7 @@ class InProgressComponent extends React.Component< - this.props.goToDeclarationRecordAudit('inProgressTab', regId) + props.goToDeclarationRecordAudit('inProgressTab', regId) } > {intl.formatMessage(constantsMessages.noNameProvided)} @@ -318,11 +294,7 @@ class InProgressComponent extends React.Component< actions } }) - const sortedItems = getSortedItems( - items, - this.state.sortedCol, - this.state.sortOrder - ) + const sortedItems = getSortedItems(items, sortedCol, sortOrder) return sortedItems.map((item) => { return { ...item, @@ -335,22 +307,19 @@ class InProgressComponent extends React.Component< }) } - getDraftsPaginatedData = (drafts: IDeclaration[], pageId: number) => { - return drafts.slice( - (pageId - 1) * this.props.pageSize, - pageId * this.props.pageSize - ) + const getDraftsPaginatedData = (drafts: IDeclaration[], pageId: number) => { + return drafts.slice((pageId - 1) * props.pageSize, pageId * props.pageSize) } - transformDraftContent = () => { - const { intl } = this.props + const transformDraftContent = () => { + const { intl } = props const { locale } = intl - if (!this.props.drafts || this.props.drafts.length <= 0) { + if (!props.drafts || props.drafts.length <= 0) { return [] } - const paginatedDrafts = this.getDraftsPaginatedData( - this.props.drafts, - this.props.paginationId.draftId + const paginatedDrafts = getDraftsPaginatedData( + props.drafts, + props.paginationId.draftId ) const items = paginatedDrafts.map((draft: IDeclaration, index) => { let pageRoute: string @@ -365,14 +334,14 @@ class InProgressComponent extends React.Component< const lastModificationDate = draft.modifiedOn || draft.savedOn const actions: IAction[] = [] - if (this.state.width > this.props.theme.grid.breakpoints.lg) { + if (width > props.theme.grid.breakpoints.lg) { actions.push({ - label: this.props.intl.formatMessage(buttonMessages.update), + label: props.intl.formatMessage(buttonMessages.update), handler: ( e: React.MouseEvent | undefined ) => { e && e.stopPropagation() - this.props.goToPage( + props.goToPage( pageRoute, draft.id, 'preview', @@ -405,7 +374,7 @@ class InProgressComponent extends React.Component< - this.props.goToDeclarationRecordAudit('inProgressTab', draft.id) + props.goToDeclarationRecordAudit('inProgressTab', draft.id) } > {name} @@ -414,7 +383,7 @@ class InProgressComponent extends React.Component< - this.props.goToDeclarationRecordAudit('inProgressTab', draft.id) + props.goToDeclarationRecordAudit('inProgressTab', draft.id) } > {intl.formatMessage(constantsMessages.noNameProvided)} @@ -446,11 +415,7 @@ class InProgressComponent extends React.Component< actions } }) - const sortedItems = getSortedItems( - items, - this.state.sortedCol, - this.state.sortOrder - ) + const sortedItems = getSortedItems(items, sortedCol, sortOrder) return sortedItems.map((item) => { return { @@ -463,50 +428,45 @@ class InProgressComponent extends React.Component< }) } - getColumns = () => { - if (this.state.width > this.props.theme.grid.breakpoints.lg) { + const getColumns = () => { + if (width > props.theme.grid.breakpoints.lg) { return [ { - label: this.props.intl.formatMessage(constantsMessages.name), + label: props.intl.formatMessage(constantsMessages.name), width: 30, key: COLUMNS.ICON_WITH_NAME, - isSorted: this.state.sortedCol === COLUMNS.NAME, - sortFunction: this.onColumnClick + isSorted: sortedCol === COLUMNS.NAME, + sortFunction: onColumnClick }, { - label: this.props.intl.formatMessage(constantsMessages.event), + label: props.intl.formatMessage(constantsMessages.event), width: 16, key: COLUMNS.EVENT, - isSorted: this.state.sortedCol === COLUMNS.EVENT, - sortFunction: this.onColumnClick + isSorted: sortedCol === COLUMNS.EVENT, + sortFunction: onColumnClick }, { - label: this.props.intl.formatMessage(constantsMessages.eventDate), + label: props.intl.formatMessage(constantsMessages.eventDate), width: 18, key: COLUMNS.DATE_OF_EVENT, - isSorted: this.state.sortedCol === COLUMNS.DATE_OF_EVENT, - sortFunction: this.onColumnClick + isSorted: sortedCol === COLUMNS.DATE_OF_EVENT, + sortFunction: onColumnClick }, { label: - this.props.selectorId && - this.props.selectorId !== SELECTOR_ID.ownDrafts - ? this.props.intl.formatMessage( - constantsMessages.notificationSent - ) - : this.props.intl.formatMessage(constantsMessages.lastUpdated), + props.selectorId && props.selectorId !== SELECTOR_ID.ownDrafts + ? props.intl.formatMessage(constantsMessages.notificationSent) + : props.intl.formatMessage(constantsMessages.lastUpdated), width: 18, key: - this.props.selectorId && - this.props.selectorId !== SELECTOR_ID.ownDrafts + props.selectorId && props.selectorId !== SELECTOR_ID.ownDrafts ? COLUMNS.NOTIFICATION_SENT : COLUMNS.LAST_UPDATED, isSorted: - this.props.selectorId && - this.props.selectorId !== SELECTOR_ID.ownDrafts - ? this.state.sortedCol === COLUMNS.NOTIFICATION_SENT - : this.state.sortedCol === COLUMNS.LAST_UPDATED, - sortFunction: this.onColumnClick + props.selectorId && props.selectorId !== SELECTOR_ID.ownDrafts + ? sortedCol === COLUMNS.NOTIFICATION_SENT + : sortedCol === COLUMNS.LAST_UPDATED, + sortFunction: onColumnClick }, { width: 18, @@ -518,7 +478,7 @@ class InProgressComponent extends React.Component< } else { return [ { - label: this.props.intl.formatMessage(constantsMessages.name), + label: props.intl.formatMessage(constantsMessages.name), width: 70, key: COLUMNS.ICON_WITH_NAME_EVENT }, @@ -532,38 +492,38 @@ class InProgressComponent extends React.Component< } } - getTabs( + const getTabs = ( selectorId: string, drafts: IDeclaration[], fieldAgentCount: number, hospitalCount: number - ) { - if (this.props.isFieldAgent) { + ) => { + if (props.isFieldAgent) { return undefined } const tabs = { activeTabId: selectorId || SELECTOR_ID.ownDrafts, onTabClick: (tabId: string) => { - this.props.goToHomeTab(WORKQUEUE_TABS.inProgress, tabId) + props.goToHomeTab(WORKQUEUE_TABS.inProgress, tabId) }, sections: [ { id: SELECTOR_ID.ownDrafts, - title: `${this.props.intl.formatMessage( - messages.inProgressOwnDrafts - )} (${drafts && drafts.length})`, + title: `${props.intl.formatMessage(messages.inProgressOwnDrafts)} (${ + drafts && drafts.length + })`, disabled: false }, { id: SELECTOR_ID.fieldAgentDrafts, - title: `${this.props.intl.formatMessage( + title: `${props.intl.formatMessage( messages.inProgressFieldAgents )} (${fieldAgentCount})`, disabled: false }, { id: SELECTOR_ID.hospitalDrafts, - title: `${this.props.intl.formatMessage( + title: `${props.intl.formatMessage( messages.hospitalDrafts )} (${hospitalCount})`, disabled: false @@ -580,141 +540,132 @@ class InProgressComponent extends React.Component< ) } - renderFieldAgentTable = ( + const renderFieldAgentTable = ( data: GQLEventSearchResultSet, isShowPagination: boolean ) => { return ( ) } - renderHospitalTable = ( + const renderHospitalTable = ( data: GQLEventSearchResultSet, isShowPagination: boolean ) => { return ( ) } - render() { - const { intl, selectorId, drafts, queryData, onPageChange, isFieldAgent } = - this.props - - const isShowPagination = - !this.props.selectorId || this.props.selectorId === SELECTOR_ID.ownDrafts - ? this.props.drafts.length > this.props.pageSize - ? true - : false - : this.props.selectorId === SELECTOR_ID.fieldAgentDrafts - ? this.props.queryData.inProgressData && - this.props.queryData.inProgressData.totalItems && - this.props.queryData.inProgressData.totalItems > this.props.pageSize - ? true - : false - : this.props.queryData.notificationData && - this.props.queryData.notificationData.totalItems && - this.props.queryData.notificationData.totalItems > this.props.pageSize + const { intl, selectorId, drafts, queryData, onPageChange, isFieldAgent } = + props + + const isShowPagination = + !props.selectorId || props.selectorId === SELECTOR_ID.ownDrafts + ? props.drafts.length > props.pageSize ? true : false - - const { inProgressData, notificationData } = queryData - const paginationId = - !selectorId || selectorId === SELECTOR_ID.ownDrafts - ? this.props.paginationId.draftId - : selectorId === SELECTOR_ID.fieldAgentDrafts - ? this.props.paginationId.fieldAgentId - : this.props.paginationId.healthSystemId - - const totalPages = - !selectorId || selectorId === SELECTOR_ID.ownDrafts - ? Math.ceil(this.props.drafts.length / this.props.pageSize) - : selectorId === SELECTOR_ID.fieldAgentDrafts - ? this.props.queryData.inProgressData && - this.props.queryData.inProgressData.totalItems && - Math.ceil( - this.props.queryData.inProgressData.totalItems / this.props.pageSize - ) - : this.props.queryData.notificationData && - this.props.queryData.notificationData.totalItems && - Math.ceil( - this.props.queryData.notificationData.totalItems / - this.props.pageSize - ) - - const noContent = - !selectorId || selectorId === SELECTOR_ID.ownDrafts - ? this.transformDraftContent().length <= 0 - : selectorId === SELECTOR_ID.fieldAgentDrafts - ? this.transformRemoteDraftsContent(inProgressData).length <= 0 - : this.transformRemoteDraftsContent(notificationData).length <= 0 - - const noResultMessage = - !selectorId || selectorId === SELECTOR_ID.ownDrafts - ? intl.formatMessage(wqMessages.noRecordsDraft) - : selectorId === SELECTOR_ID.fieldAgentDrafts - ? intl.formatMessage(wqMessages.noRecordsFieldAgents) - : intl.formatMessage(wqMessages.noRecordsHealthSystem) - - return ( - - {(!selectorId || selectorId === SELECTOR_ID.ownDrafts) && ( - - )} - {selectorId === SELECTOR_ID.fieldAgentDrafts && - !isFieldAgent && - this.renderFieldAgentTable(inProgressData, isShowPagination)} - {selectorId === SELECTOR_ID.hospitalDrafts && - !isFieldAgent && - this.renderHospitalTable(notificationData, isShowPagination)} - - ) - } + : props.selectorId === SELECTOR_ID.fieldAgentDrafts + ? props.queryData.inProgressData && + props.queryData.inProgressData.totalItems && + props.queryData.inProgressData.totalItems > props.pageSize + ? true + : false + : props.queryData.notificationData && + props.queryData.notificationData.totalItems && + props.queryData.notificationData.totalItems > props.pageSize + ? true + : false + + const { inProgressData, notificationData } = queryData + const paginationId = + !selectorId || selectorId === SELECTOR_ID.ownDrafts + ? props.paginationId.draftId + : selectorId === SELECTOR_ID.fieldAgentDrafts + ? props.paginationId.fieldAgentId + : props.paginationId.healthSystemId + + const totalPages = + !selectorId || selectorId === SELECTOR_ID.ownDrafts + ? Math.ceil(props.drafts.length / props.pageSize) + : selectorId === SELECTOR_ID.fieldAgentDrafts + ? props.queryData.inProgressData && + props.queryData.inProgressData.totalItems && + Math.ceil(props.queryData.inProgressData.totalItems / props.pageSize) + : props.queryData.notificationData && + props.queryData.notificationData.totalItems && + Math.ceil(props.queryData.notificationData.totalItems / props.pageSize) + + const noContent = + !selectorId || selectorId === SELECTOR_ID.ownDrafts + ? transformDraftContent().length <= 0 + : selectorId === SELECTOR_ID.fieldAgentDrafts + ? transformRemoteDraftsContent(inProgressData).length <= 0 + : transformRemoteDraftsContent(notificationData).length <= 0 + + const noResultMessage = + !selectorId || selectorId === SELECTOR_ID.ownDrafts + ? intl.formatMessage(wqMessages.noRecordsDraft) + : selectorId === SELECTOR_ID.fieldAgentDrafts + ? intl.formatMessage(wqMessages.noRecordsFieldAgents) + : intl.formatMessage(wqMessages.noRecordsHealthSystem) + + return ( + + {(!selectorId || selectorId === SELECTOR_ID.ownDrafts) && ( + + )} + {selectorId === SELECTOR_ID.fieldAgentDrafts && + !isFieldAgent && + renderFieldAgentTable(inProgressData, isShowPagination)} + {selectorId === SELECTOR_ID.hospitalDrafts && + !isFieldAgent && + renderHospitalTable(notificationData, isShowPagination)} + + ) } function mapStateToProps(state: IStoreState) { diff --git a/packages/client/src/views/OfficeHome/inProgress/inProgress.test.tsx b/packages/client/src/views/OfficeHome/inProgress/inProgress.test.tsx index 33b8b91e638..6232b2049b2 100644 --- a/packages/client/src/views/OfficeHome/inProgress/inProgress.test.tsx +++ b/packages/client/src/views/OfficeHome/inProgress/inProgress.test.tsx @@ -76,9 +76,9 @@ storage.getItem = vi.fn() storage.setItem = vi.fn() const { store, history } = createStore() -beforeAll(async () => { +beforeAll(() => { getItem.mockReturnValue(registerScopeToken) - await store.dispatch(checkAuth()) + store.dispatch(checkAuth()) }) describe('In Progress tab', () => { diff --git a/packages/client/src/views/OfficeHome/readyToPrint/ReadyToPrint.tsx b/packages/client/src/views/OfficeHome/readyToPrint/ReadyToPrint.tsx index fc488cce619..b6cdac6b39e 100644 --- a/packages/client/src/views/OfficeHome/readyToPrint/ReadyToPrint.tsx +++ b/packages/client/src/views/OfficeHome/readyToPrint/ReadyToPrint.tsx @@ -59,6 +59,8 @@ import { } from '@client/views/OfficeHome/components' import { WQContentWrapper } from '@client/views/OfficeHome/WQContentWrapper' import { RegStatus } from '@client/utils/gateway' +import { useWindowSize } from '@opencrvs/components/lib/hooks' +import { useState } from 'react' interface IBasePrintTabProps { theme: ITheme @@ -76,87 +78,53 @@ interface IBasePrintTabProps { pageSize: number } -interface IPrintTabState { - width: number - sortedCol: COLUMNS - sortOrder: SORT_ORDER -} - type IPrintTabProps = IntlShapeProps & IBasePrintTabProps -class ReadyToPrintComponent extends React.Component< - IPrintTabProps, - IPrintTabState -> { - constructor(props: IPrintTabProps) { - super(props) - this.state = { - width: window.innerWidth, - sortedCol: COLUMNS.REGISTERED, - sortOrder: SORT_ORDER.DESCENDING - } - } - - componentDidMount() { - window.addEventListener('resize', this.recordWindowWidth) - } - - componentWillUnmount() { - window.removeEventListener('resize', this.recordWindowWidth) - } - - recordWindowWidth = () => { - this.setState({ width: window.innerWidth }) - } - - getExpandable = () => { - return this.state.width > this.props.theme.grid.breakpoints.lg - ? true - : false - } +function ReadyToPrintComponent(props: IPrintTabProps) { + const { width } = useWindowSize() + const [sortedCol, setSortedCol] = useState(COLUMNS.REGISTERED) + const [sortOrder, setSortOrder] = useState(SORT_ORDER.DESCENDING) - onColumnClick = (columnName: string) => { + const onColumnClick = (columnName: string) => { const { newSortedCol, newSortOrder } = changeSortedColumn( columnName, - this.state.sortedCol, - this.state.sortOrder + sortedCol, + sortOrder ) - this.setState({ - sortOrder: newSortOrder, - sortedCol: newSortedCol - }) + setSortOrder(newSortOrder) + setSortedCol(newSortedCol) } - getColumns = () => { - if (this.state.width > this.props.theme.grid.breakpoints.lg) { + const getColumns = () => { + if (width > props.theme.grid.breakpoints.lg) { return [ { width: 30, - label: this.props.intl.formatMessage(constantsMessages.name), + label: props.intl.formatMessage(constantsMessages.name), key: COLUMNS.ICON_WITH_NAME, - isSorted: this.state.sortedCol === COLUMNS.NAME, - sortFunction: this.onColumnClick + isSorted: sortedCol === COLUMNS.NAME, + sortFunction: onColumnClick }, { - label: this.props.intl.formatMessage(constantsMessages.event), + label: props.intl.formatMessage(constantsMessages.event), width: 16, key: COLUMNS.EVENT, - isSorted: this.state.sortedCol === COLUMNS.EVENT, - sortFunction: this.onColumnClick + isSorted: sortedCol === COLUMNS.EVENT, + sortFunction: onColumnClick }, { - label: this.props.intl.formatMessage(constantsMessages.eventDate), + label: props.intl.formatMessage(constantsMessages.eventDate), width: 18, key: COLUMNS.DATE_OF_EVENT, - isSorted: this.state.sortedCol === COLUMNS.DATE_OF_EVENT, - sortFunction: this.onColumnClick + isSorted: sortedCol === COLUMNS.DATE_OF_EVENT, + sortFunction: onColumnClick }, { - label: this.props.intl.formatMessage(constantsMessages.registered), + label: props.intl.formatMessage(constantsMessages.registered), width: 18, key: COLUMNS.REGISTERED, - isSorted: this.state.sortedCol === COLUMNS.REGISTERED, - sortFunction: this.onColumnClick + isSorted: sortedCol === COLUMNS.REGISTERED, + sortFunction: onColumnClick }, { width: 18, @@ -168,7 +136,7 @@ class ReadyToPrintComponent extends React.Component< } else { return [ { - label: this.props.intl.formatMessage(constantsMessages.name), + label: props.intl.formatMessage(constantsMessages.name), width: 70, key: COLUMNS.ICON_WITH_NAME_EVENT }, @@ -182,31 +150,31 @@ class ReadyToPrintComponent extends React.Component< } } - transformRegisteredContent = (data: GQLEventSearchResultSet) => { - const { intl } = this.props + const transformRegisteredContent = (data: GQLEventSearchResultSet) => { + const { intl } = props if (!data || !data.results) { return [] } - const transformedData = transformData(data, this.props.intl) + const transformedData = transformData(data, props.intl) const items = transformedData.map((reg, index) => { - const foundDeclaration = this.props.outboxDeclarations.find( + const foundDeclaration = props.outboxDeclarations.find( (declaration) => declaration.id === reg.id ) const actions: IAction[] = [] const downloadStatus = foundDeclaration?.downloadStatus - if (this.state.width > this.props.theme.grid.breakpoints.lg) { + if (width > props.theme.grid.breakpoints.lg) { actions.push({ - label: this.props.intl.formatMessage(buttonMessages.print), + label: props.intl.formatMessage(buttonMessages.print), disabled: downloadStatus !== DOWNLOAD_STATUS.DOWNLOADED, handler: ( e: React.MouseEvent | undefined ) => { e && e.stopPropagation() if (downloadStatus === DOWNLOAD_STATUS.DOWNLOADED) { - this.props.clearCorrectionAndPrintChanges(reg.id) - this.props.goToPrintCertificate( + props.clearCorrectionAndPrintChanges(reg.id) + props.goToPrintCertificate( reg.id, reg.event.toLocaleLowerCase() || '' ) @@ -221,7 +189,7 @@ class ReadyToPrintComponent extends React.Component< event: reg.event, compositionId: reg.id, action: DownloadAction.LOAD_REVIEW_DECLARATION, - assignment: reg.assignment ?? undefined + assignment: reg.assignment || undefined }} key={`DownloadButton-${index}`} status={downloadStatus} @@ -247,18 +215,14 @@ class ReadyToPrintComponent extends React.Component< const NameComponent = reg.name ? ( - this.props.goToDeclarationRecordAudit('printTab', reg.id) - } + onClick={() => props.goToDeclarationRecordAudit('printTab', reg.id)} > {reg.name} ) : ( - this.props.goToDeclarationRecordAudit('printTab', reg.id) - } + onClick={() => props.goToDeclarationRecordAudit('printTab', reg.id)} > {intl.formatMessage(constantsMessages.noNameProvided)} @@ -282,11 +246,7 @@ class ReadyToPrintComponent extends React.Component< actions } }) - const sortedItems = getSortedItems( - items, - this.state.sortedCol, - this.state.sortOrder - ) + const sortedItems = getSortedItems(items, sortedCol, sortOrder) return sortedItems.map((item) => { return { @@ -299,40 +259,38 @@ class ReadyToPrintComponent extends React.Component< }) } - render() { - const { intl, queryData, paginationId, onPageChange, pageSize } = this.props - const { data } = queryData - const totalPages = this.props.queryData.data.totalItems - ? Math.ceil(this.props.queryData.data.totalItems / pageSize) - : 0 - const isShowPagination = - this.props.queryData.data.totalItems && - this.props.queryData.data.totalItems > pageSize - ? true - : false - return ( - - - - ) - } + const { intl, queryData, paginationId, onPageChange, pageSize } = props + const { data } = queryData + const totalPages = props.queryData.data.totalItems + ? Math.ceil(props.queryData.data.totalItems / pageSize) + : 0 + const isShowPagination = + props.queryData.data.totalItems && + props.queryData.data.totalItems > pageSize + ? true + : false + return ( + + + + ) } function mapStateToProps(state: IStoreState) { diff --git a/packages/client/src/views/OfficeHome/requiresUpdate/RequiresUpdate.tsx b/packages/client/src/views/OfficeHome/requiresUpdate/RequiresUpdate.tsx index 9d17a988e04..0f8179e54dc 100644 --- a/packages/client/src/views/OfficeHome/requiresUpdate/RequiresUpdate.tsx +++ b/packages/client/src/views/OfficeHome/requiresUpdate/RequiresUpdate.tsx @@ -54,6 +54,8 @@ import { } from '@client/views/OfficeHome/components' import { WQContentWrapper } from '@client/views/OfficeHome/WQContentWrapper' import { RegStatus } from '@client/utils/gateway' +import { useState } from 'react' +import { useWindowSize } from '@opencrvs/components/lib/hooks' interface IBaseRejectTabProps { theme: ITheme @@ -71,83 +73,53 @@ interface IBaseRejectTabProps { error?: boolean } -interface IRejectTabState { - width: number - sortedCol: COLUMNS - sortOrder: SORT_ORDER -} - type IRejectTabProps = IntlShapeProps & IBaseRejectTabProps -class RequiresUpdateComponent extends React.Component< - IRejectTabProps, - IRejectTabState -> { - constructor(props: IRejectTabProps) { - super(props) - this.state = { - width: window.innerWidth, - sortedCol: COLUMNS.SENT_FOR_UPDATES, - sortOrder: SORT_ORDER.ASCENDING - } - } - - componentDidMount() { - window.addEventListener('resize', this.recordWindowWidth) - } - - componentWillUnmount() { - window.removeEventListener('resize', this.recordWindowWidth) - } - - recordWindowWidth = () => { - this.setState({ width: window.innerWidth }) - } +function RequiresUpdateComponent(props: IRejectTabProps) { + const { width } = useWindowSize() + const [sortedCol, setSortedCol] = useState(COLUMNS.SENT_FOR_UPDATES) + const [sortOrder, setSortOrder] = useState(SORT_ORDER.ASCENDING) - onColumnClick = (columnName: string) => { + const onColumnClick = (columnName: string) => { const { newSortedCol, newSortOrder } = changeSortedColumn( columnName, - this.state.sortedCol, - this.state.sortOrder + sortedCol, + sortOrder ) - this.setState({ - sortOrder: newSortOrder, - sortedCol: newSortedCol - }) + setSortedCol(newSortedCol) + setSortOrder(newSortOrder) } - getColumns = () => { - if (this.state.width > this.props.theme.grid.breakpoints.lg) { + const getColumns = () => { + if (width > props.theme.grid.breakpoints.lg) { return [ { width: 30, - label: this.props.intl.formatMessage(constantsMessages.name), + label: props.intl.formatMessage(constantsMessages.name), key: COLUMNS.ICON_WITH_NAME, - isSorted: this.state.sortedCol === COLUMNS.NAME, - sortFunction: this.onColumnClick + isSorted: sortedCol === COLUMNS.NAME, + sortFunction: onColumnClick }, { - label: this.props.intl.formatMessage(constantsMessages.event), + label: props.intl.formatMessage(constantsMessages.event), width: 16, key: COLUMNS.EVENT, - isSorted: this.state.sortedCol === COLUMNS.EVENT, - sortFunction: this.onColumnClick + isSorted: sortedCol === COLUMNS.EVENT, + sortFunction: onColumnClick }, { - label: this.props.intl.formatMessage(constantsMessages.eventDate), + label: props.intl.formatMessage(constantsMessages.eventDate), width: 18, key: COLUMNS.DATE_OF_EVENT, - isSorted: this.state.sortedCol === COLUMNS.DATE_OF_EVENT, - sortFunction: this.onColumnClick + isSorted: sortedCol === COLUMNS.DATE_OF_EVENT, + sortFunction: onColumnClick }, { - label: this.props.intl.formatMessage( - constantsMessages.sentForUpdates - ), + label: props.intl.formatMessage(constantsMessages.sentForUpdates), width: 18, key: COLUMNS.SENT_FOR_UPDATES, - isSorted: this.state.sortedCol === COLUMNS.SENT_FOR_UPDATES, - sortFunction: this.onColumnClick + isSorted: sortedCol === COLUMNS.SENT_FOR_UPDATES, + sortFunction: onColumnClick }, { width: 18, @@ -159,7 +131,7 @@ class RequiresUpdateComponent extends React.Component< } else { return [ { - label: this.props.intl.formatMessage(constantsMessages.name), + label: props.intl.formatMessage(constantsMessages.name), width: 70, key: COLUMNS.ICON_WITH_NAME_EVENT }, @@ -173,44 +145,38 @@ class RequiresUpdateComponent extends React.Component< } } - transformRejectedContent = (data: GQLEventSearchResultSet) => { - const { intl } = this.props + const transformRejectedContent = (data: GQLEventSearchResultSet) => { + const { intl } = props if (!data || !data.results) { return [] } - const isFieldAgent = this.props.scope?.includes('declare') ? true : false - const transformedData = transformData(data, this.props.intl) + const isFieldAgent = props.scope?.includes('declare') ? true : false + const transformedData = transformData(data, props.intl) const items = transformedData.map((reg, index) => { const actions = [] as IAction[] - const foundDeclaration = this.props.outboxDeclarations.find( + const foundDeclaration = props.outboxDeclarations.find( (declaration) => declaration.id === reg.id ) const downloadStatus = foundDeclaration?.downloadStatus const isDuplicate = reg.duplicates && reg.duplicates.length > 0 if (downloadStatus !== DOWNLOAD_STATUS.DOWNLOADED) { - if ( - this.state.width > this.props.theme.grid.breakpoints.lg && - !isFieldAgent - ) { + if (width > props.theme.grid.breakpoints.lg && !isFieldAgent) { actions.push({ - label: this.props.intl.formatMessage(buttonMessages.update), + label: props.intl.formatMessage(buttonMessages.update), handler: () => {}, disabled: true }) } } else { - if ( - this.state.width > this.props.theme.grid.breakpoints.lg && - !isFieldAgent - ) { + if (width > props.theme.grid.breakpoints.lg && !isFieldAgent) { actions.push({ - label: this.props.intl.formatMessage(buttonMessages.update), + label: props.intl.formatMessage(buttonMessages.update), handler: ( e: React.MouseEvent | undefined ) => { e && e.stopPropagation() - this.props.goToPage( + props.goToPage( REVIEW_EVENT_PARENT_FORM_PAGE, reg.id, 'review', @@ -253,18 +219,14 @@ class RequiresUpdateComponent extends React.Component< const NameComponent = reg.name ? ( - this.props.goToDeclarationRecordAudit('rejectTab', reg.id) - } + onClick={() => props.goToDeclarationRecordAudit('rejectTab', reg.id)} > {reg.name} ) : ( - this.props.goToDeclarationRecordAudit('rejectTab', reg.id) - } + onClick={() => props.goToDeclarationRecordAudit('rejectTab', reg.id)} > {intl.formatMessage(constantsMessages.noNameProvided)} @@ -293,11 +255,7 @@ class RequiresUpdateComponent extends React.Component< actions } }) - const sortedItems = getSortedItems( - items, - this.state.sortedCol, - this.state.sortOrder - ) + const sortedItems = getSortedItems(items, sortedCol, sortOrder) return sortedItems.map((item) => { return { ...item, @@ -309,40 +267,38 @@ class RequiresUpdateComponent extends React.Component< }) } - render() { - const { intl, queryData, paginationId, onPageChange, pageSize } = this.props - const { data } = queryData - const totalPages = this.props.queryData.data.totalItems - ? Math.ceil(this.props.queryData.data.totalItems / pageSize) - : 0 - const isShowPagination = - this.props.queryData.data.totalItems && - this.props.queryData.data.totalItems > pageSize - ? true - : false - return ( - - - - ) - } + const totalPages = props.queryData.data.totalItems + ? Math.ceil(props.queryData.data.totalItems / props.pageSize) + : 0 + const isShowPagination = + props.queryData.data.totalItems && + props.queryData.data.totalItems > props.pageSize + ? true + : false + return ( + + + + ) } function mapStateToProps(state: IStoreState) { diff --git a/packages/client/src/views/OfficeHome/sentForReview/SentForReview.tsx b/packages/client/src/views/OfficeHome/sentForReview/SentForReview.tsx index 56f0a0c1f16..d43722a3f61 100644 --- a/packages/client/src/views/OfficeHome/sentForReview/SentForReview.tsx +++ b/packages/client/src/views/OfficeHome/sentForReview/SentForReview.tsx @@ -55,6 +55,9 @@ import { DownloadButton } from '@client/components/interface/DownloadButton' import { DownloadAction } from '@client/forms' import { Downloaded } from '@opencrvs/components/lib/icons/Downloaded' import { RegStatus } from '@client/utils/gateway' +import { useState } from 'react' +import { useWindowSize } from '@opencrvs/components/src/hooks' + const ToolTipContainer = styled.span` text-align: center; ` @@ -74,86 +77,57 @@ interface IBaseApprovalTabProps { error?: boolean } -interface IApprovalTabState { - width: number - sortedCol: COLUMNS - sortOrder: SORT_ORDER -} - type IApprovalTabProps = IntlShapeProps & IBaseApprovalTabProps -class SentForReviewComponent extends React.Component< - IApprovalTabProps, - IApprovalTabState -> { - pageSize = 10 - isFieldAgent = this.props.scope?.includes('declare') - - constructor(props: IApprovalTabProps) { - super(props) - this.state = { - width: window.innerWidth, - sortedCol: COLUMNS.SENT_FOR_APPROVAL, - sortOrder: SORT_ORDER.DESCENDING - } - } +function SentForReviewComponent(props: IApprovalTabProps) { + const { width } = useWindowSize() + const [sortedCol, setSortedCol] = useState(COLUMNS.SENT_FOR_APPROVAL) + const [sortOrder, setSortOrder] = useState(SORT_ORDER.DESCENDING) - componentDidMount() { - window.addEventListener('resize', this.recordWindowWidth) - } + const isFieldAgent = props.scope?.includes('declare') - componentWillUnmount() { - window.removeEventListener('resize', this.recordWindowWidth) - } - - recordWindowWidth = () => { - this.setState({ width: window.innerWidth }) - } - - onColumnClick = (columnName: string) => { + const onColumnClick = (columnName: string) => { const { newSortedCol, newSortOrder } = changeSortedColumn( columnName, - this.state.sortedCol, - this.state.sortOrder + sortedCol, + sortOrder ) - this.setState({ - sortOrder: newSortOrder, - sortedCol: newSortedCol - }) + setSortOrder(newSortOrder) + setSortedCol(newSortedCol) } - getColumns = () => { - if (this.state.width > this.props.theme.grid.breakpoints.lg) { + const getColumns = () => { + if (width > props.theme.grid.breakpoints.lg) { return [ { width: 30, - label: this.props.intl.formatMessage(constantsMessages.name), + label: props.intl.formatMessage(constantsMessages.name), key: COLUMNS.ICON_WITH_NAME, - isSorted: this.state.sortedCol === COLUMNS.NAME, - sortFunction: this.onColumnClick + isSorted: sortedCol === COLUMNS.NAME, + sortFunction: onColumnClick }, { - label: this.props.intl.formatMessage(constantsMessages.event), + label: props.intl.formatMessage(constantsMessages.event), width: 16, key: COLUMNS.EVENT, - isSorted: this.state.sortedCol === COLUMNS.EVENT, - sortFunction: this.onColumnClick + isSorted: sortedCol === COLUMNS.EVENT, + sortFunction: onColumnClick }, { - label: this.props.intl.formatMessage(constantsMessages.eventDate), + label: props.intl.formatMessage(constantsMessages.eventDate), width: 18, key: COLUMNS.DATE_OF_EVENT, - isSorted: this.state.sortedCol === COLUMNS.DATE_OF_EVENT, - sortFunction: this.onColumnClick + isSorted: sortedCol === COLUMNS.DATE_OF_EVENT, + sortFunction: onColumnClick }, { - label: this.isFieldAgent - ? this.props.intl.formatMessage(navigationMessages.sentForReview) - : this.props.intl.formatMessage(navigationMessages.approvals), + label: isFieldAgent + ? props.intl.formatMessage(navigationMessages.sentForReview) + : props.intl.formatMessage(navigationMessages.approvals), width: 18, key: COLUMNS.SENT_FOR_APPROVAL, - isSorted: this.state.sortedCol === COLUMNS.SENT_FOR_APPROVAL, - sortFunction: this.onColumnClick + isSorted: sortedCol === COLUMNS.SENT_FOR_APPROVAL, + sortFunction: onColumnClick }, { width: 18, @@ -165,7 +139,7 @@ class SentForReviewComponent extends React.Component< } else { return [ { - label: this.props.intl.formatMessage(constantsMessages.name), + label: props.intl.formatMessage(constantsMessages.name), width: 70, key: COLUMNS.ICON_WITH_NAME_EVENT }, @@ -179,15 +153,15 @@ class SentForReviewComponent extends React.Component< } } - transformValidatedContent = (data: GQLEventSearchResultSet) => { - const { intl } = this.props + const transformValidatedContent = (data: GQLEventSearchResultSet) => { + const { intl } = props if (!data || !data.results) { return [] } - const transformedData = transformData(data, this.props.intl) + const transformedData = transformData(data, props.intl) const items = transformedData.map((reg, index) => { const actions = [] as IAction[] - const foundDeclaration = this.props.outboxDeclarations.find( + const foundDeclaration = props.outboxDeclarations.find( (declaration) => declaration.id === reg.id ) const downloadStatus = @@ -202,7 +176,7 @@ class SentForReviewComponent extends React.Component< compositionId: reg.id, action: DownloadAction.LOAD_REVIEW_DECLARATION, declarationStatus: reg.declarationStatus, - assignment: reg.assignment ?? undefined + assignment: reg.assignment || undefined }} key={`DownloadButton-${index}`} status={downloadStatus as DOWNLOAD_STATUS} @@ -222,7 +196,7 @@ class SentForReviewComponent extends React.Component< '' let sentForApproval - if (this.isFieldAgent) { + if (isFieldAgent) { sentForApproval = getPreviousOperationDateByOperationType( reg.operationHistories, @@ -258,9 +232,9 @@ class SentForReviewComponent extends React.Component< - this.isFieldAgent - ? this.props.goToDeclarationRecordAudit('reviewTab', reg.id) - : this.props.goToDeclarationRecordAudit('approvalTab', reg.id) + isFieldAgent + ? props.goToDeclarationRecordAudit('reviewTab', reg.id) + : props.goToDeclarationRecordAudit('approvalTab', reg.id) } > {reg.name} @@ -269,9 +243,9 @@ class SentForReviewComponent extends React.Component< - this.isFieldAgent - ? this.props.goToDeclarationRecordAudit('reviewTab', reg.id) - : this.props.goToDeclarationRecordAudit('approvalTab', reg.id) + isFieldAgent + ? props.goToDeclarationRecordAudit('reviewTab', reg.id) + : props.goToDeclarationRecordAudit('approvalTab', reg.id) } > {intl.formatMessage(constantsMessages.noNameProvided)} @@ -307,11 +281,7 @@ class SentForReviewComponent extends React.Component< actions } }) - const sortedItems = getSortedItems( - items, - this.state.sortedCol, - this.state.sortOrder - ) + const sortedItems = getSortedItems(items, sortedCol, sortOrder) return sortedItems.map((item) => { return { ...item, @@ -324,55 +294,53 @@ class SentForReviewComponent extends React.Component< }) } - render() { - // Approval tab for registration clerk and registrar - // Review tab for field agent - const { intl, queryData, paginationId, pageSize, onPageChange } = this.props - const { data } = queryData - const totalPages = this.props.queryData.data.totalItems - ? Math.ceil(this.props.queryData.data.totalItems / pageSize) - : 0 - const isShowPagination = - this.props.queryData.data.totalItems && - this.props.queryData.data.totalItems > pageSize - ? true - : false - const noResultText = this.isFieldAgent - ? intl.formatMessage(wqMessages.noRecordsSentForReview) - : intl.formatMessage(wqMessages.noRecordsSentForApproval) - const title = this.isFieldAgent - ? intl.formatMessage(navigationMessages.sentForReview) - : intl.formatMessage(navigationMessages.approvals) - return ( - - - - {this.props.intl.formatMessage( - messages.validatedDeclarationTooltipForRegistrationAgent - )} - - - - - ) - } + // Approval tab for registration clerk and registrar + // Review tab for field agent + const { intl, queryData, paginationId, pageSize, onPageChange } = props + const { data } = queryData + const totalPages = props.queryData.data.totalItems + ? Math.ceil(props.queryData.data.totalItems / pageSize) + : 0 + const isShowPagination = + props.queryData.data.totalItems && + props.queryData.data.totalItems > pageSize + ? true + : false + const noResultText = isFieldAgent + ? intl.formatMessage(wqMessages.noRecordsSentForReview) + : intl.formatMessage(wqMessages.noRecordsSentForApproval) + const title = isFieldAgent + ? intl.formatMessage(navigationMessages.sentForReview) + : intl.formatMessage(navigationMessages.approvals) + return ( + + + + {props.intl.formatMessage( + messages.validatedDeclarationTooltipForRegistrationAgent + )} + + + + + ) } function mapStateToProps(state: IStoreState) { diff --git a/packages/client/src/views/Organisation/AdministrativeLevels.tsx b/packages/client/src/views/Organisation/AdministrativeLevels.tsx index 5664448a836..d3906732394 100644 --- a/packages/client/src/views/Organisation/AdministrativeLevels.tsx +++ b/packages/client/src/views/Organisation/AdministrativeLevels.tsx @@ -29,7 +29,7 @@ import { IBreadCrumbData } from '@opencrvs/components/src/Breadcrumb' import { useDispatch, useSelector } from 'react-redux' import { IStoreState } from '@client/store' import { ILocation } from '@client/offline/reducer' -import { useParams } from 'react-router' +import { useParams } from 'react-router-dom' import { goToOrganizationList, goToPerformanceHome, diff --git a/packages/client/src/views/Performance/Dashboard.tsx b/packages/client/src/views/Performance/Dashboard.tsx index fbe99ecaef1..d48e6fd7cf9 100644 --- a/packages/client/src/views/Performance/Dashboard.tsx +++ b/packages/client/src/views/Performance/Dashboard.tsx @@ -20,7 +20,7 @@ import { Icon } from '@opencrvs/components/lib/Icon' import styled from 'styled-components' import IframeResizer from 'iframe-resizer-react' import { messages } from '@client/i18n/messages/views/dashboard' -import { useLocation } from 'react-router' +import { useLocation } from 'react-router-dom' import { constantsMessages } from '@client/i18n/messages' const StyledIFrame = styled(IframeResizer)` diff --git a/packages/client/src/views/Performance/FieldAgentList.tsx b/packages/client/src/views/Performance/FieldAgentList.tsx index d534ff358b9..374cc889f7e 100644 --- a/packages/client/src/views/Performance/FieldAgentList.tsx +++ b/packages/client/src/views/Performance/FieldAgentList.tsx @@ -32,7 +32,7 @@ import { parse } from 'query-string' import * as React from 'react' import { injectIntl, WrappedComponentProps } from 'react-intl' import { connect, useSelector } from 'react-redux' -import { RouteComponentProps } from 'react-router' +import { RouteComponentProps } from 'react-router-dom' import ReactTooltip from 'react-tooltip' import styled from 'styled-components' import { ILocation } from '@client/offline/reducer' diff --git a/packages/client/src/views/Performance/RegistrationsList.tsx b/packages/client/src/views/Performance/RegistrationsList.tsx index 9c48497965a..57b9a483339 100644 --- a/packages/client/src/views/Performance/RegistrationsList.tsx +++ b/packages/client/src/views/Performance/RegistrationsList.tsx @@ -54,7 +54,7 @@ import { parse } from 'query-string' import React from 'react' import { injectIntl, WrappedComponentProps } from 'react-intl' import { connect } from 'react-redux' -import { RouteComponentProps } from 'react-router' +import { RouteComponentProps } from 'react-router-dom' import ReactTooltip from 'react-tooltip' import styled from 'styled-components' import { Link } from '@opencrvs/components/lib/Link' diff --git a/packages/client/src/views/PrintCertificate/Payment.tsx b/packages/client/src/views/PrintCertificate/Payment.tsx index 296af04acc1..5ff027deec5 100644 --- a/packages/client/src/views/PrintCertificate/Payment.tsx +++ b/packages/client/src/views/PrintCertificate/Payment.tsx @@ -28,7 +28,7 @@ import { ITheme } from '@opencrvs/components/lib/theme' import React from 'react' import { WrappedComponentProps as IntlShapeProps, injectIntl } from 'react-intl' import { connect } from 'react-redux' -import { Redirect, RouteComponentProps } from 'react-router' +import { Redirect, RouteComponentProps } from 'react-router-dom' import { withTheme } from 'styled-components' import { calculatePrice, diff --git a/packages/client/src/views/PrintCertificate/ReviewCertificateAction.test.tsx b/packages/client/src/views/PrintCertificate/ReviewCertificateAction.test.tsx index dd723b7643d..3806ce0308c 100644 --- a/packages/client/src/views/PrintCertificate/ReviewCertificateAction.test.tsx +++ b/packages/client/src/views/PrintCertificate/ReviewCertificateAction.test.tsx @@ -28,7 +28,7 @@ import { Event } from '@client/utils/gateway' import { cloneDeep } from 'lodash' import { waitForElement } from '@client/tests/wait-for-element' import { push } from 'connected-react-router' -import { useParams } from 'react-router' +import { useParams } from 'react-router-dom' const deathDeclaration = { id: 'mockDeath1234', diff --git a/packages/client/src/views/PrintCertificate/ReviewCertificateAction.tsx b/packages/client/src/views/PrintCertificate/ReviewCertificateAction.tsx index ac5ee08d4b1..472f6e40c9b 100644 --- a/packages/client/src/views/PrintCertificate/ReviewCertificateAction.tsx +++ b/packages/client/src/views/PrintCertificate/ReviewCertificateAction.tsx @@ -21,7 +21,7 @@ import { } from '@opencrvs/components' import React from 'react' import { useDispatch } from 'react-redux' -import { useLocation, useParams } from 'react-router' +import { useLocation, useParams } from 'react-router-dom' import { messages as certificateMessages } from '@client/i18n/messages/views/certificate' import { useIntl } from 'react-intl' import { buttonMessages } from '@client/i18n/messages/buttons' diff --git a/packages/client/src/views/PrintCertificate/VerifyCollector.tsx b/packages/client/src/views/PrintCertificate/VerifyCollector.tsx index 5ae1f8aff0a..5c4910a4b89 100644 --- a/packages/client/src/views/PrintCertificate/VerifyCollector.tsx +++ b/packages/client/src/views/PrintCertificate/VerifyCollector.tsx @@ -32,7 +32,7 @@ import { import * as React from 'react' import { WrappedComponentProps as IntlShapeProps, injectIntl } from 'react-intl' import { connect } from 'react-redux' -import { Redirect, RouteComponentProps } from 'react-router' +import { Redirect, RouteComponentProps } from 'react-router-dom' import { getEventDate, getRegisteredDate, isFreeOfCost } from './utils' import { getOfflineData } from '@client/offline/selectors' import { IOfflineData } from '@client/offline/reducer' diff --git a/packages/client/src/views/PrintCertificate/collectorForm/CollectorForm.tsx b/packages/client/src/views/PrintCertificate/collectorForm/CollectorForm.tsx index 6a465b56984..5fd9ae54470 100644 --- a/packages/client/src/views/PrintCertificate/collectorForm/CollectorForm.tsx +++ b/packages/client/src/views/PrintCertificate/collectorForm/CollectorForm.tsx @@ -66,7 +66,7 @@ import { flatten } from 'lodash' import * as React from 'react' import { WrappedComponentProps as IntlShapeProps, injectIntl } from 'react-intl' import { connect } from 'react-redux' -import { Redirect, RouteComponentProps } from 'react-router' +import { Redirect, RouteComponentProps } from 'react-router-dom' import { IValidationResult } from '@client/utils/validate' import { getRegisterForm } from '@client/forms/register/declaration-selectors' import { getCertificateCollectorFormSection } from '@client/forms/certificate/fieldDefinitions/collectorSection' diff --git a/packages/client/src/views/PrintRecord/PrintRecord.tsx b/packages/client/src/views/PrintRecord/PrintRecord.tsx index 69f58fe5a9d..bf531df661c 100644 --- a/packages/client/src/views/PrintRecord/PrintRecord.tsx +++ b/packages/client/src/views/PrintRecord/PrintRecord.tsx @@ -15,7 +15,7 @@ import { formatUrl, goBack } from '@client/navigation' import { REGISTRAR_HOME_TAB } from '@client/navigation/routes' import { IStoreState } from '@client/store' import { useSelector, useDispatch } from 'react-redux' -import { useParams, Redirect } from 'react-router' +import { useParams, Redirect } from 'react-router-dom' import { IDeclaration } from '@client/declarations' import styled from 'styled-components' import { diff --git a/packages/client/src/views/RecordAudit/RecordAudit.tsx b/packages/client/src/views/RecordAudit/RecordAudit.tsx index 3b19ab2d0ae..3d5824402c5 100644 --- a/packages/client/src/views/RecordAudit/RecordAudit.tsx +++ b/packages/client/src/views/RecordAudit/RecordAudit.tsx @@ -22,7 +22,7 @@ import { Duplicate } from '@opencrvs/components/lib/icons' import { connect, useDispatch } from 'react-redux' -import { RouteComponentProps, Redirect, useParams } from 'react-router' +import { RouteComponentProps, Redirect, useParams } from 'react-router-dom' import { goToHomeTab, goToPage, diff --git a/packages/client/src/views/RegisterForm/DeclarationForm.tsx b/packages/client/src/views/RegisterForm/DeclarationForm.tsx index 9b5deb3c9f5..90b98257cf0 100644 --- a/packages/client/src/views/RegisterForm/DeclarationForm.tsx +++ b/packages/client/src/views/RegisterForm/DeclarationForm.tsx @@ -25,7 +25,7 @@ import { connect } from 'react-redux' import { IForm } from '@client/forms' import { Event } from '@client/utils/gateway' import { IDeclaration } from '@client/declarations' -import { Redirect } from 'react-router' +import { Redirect } from 'react-router-dom' interface IFormProps { declaration?: IDeclaration diff --git a/packages/client/src/views/RegisterForm/RegisterForm.tsx b/packages/client/src/views/RegisterForm/RegisterForm.tsx index 55f24c15477..f31ce5c3542 100644 --- a/packages/client/src/views/RegisterForm/RegisterForm.tsx +++ b/packages/client/src/views/RegisterForm/RegisterForm.tsx @@ -17,7 +17,7 @@ import { useIntl } from 'react-intl' import { connect, useDispatch } from 'react-redux' -import { RouteComponentProps } from 'react-router' +import { RouteComponentProps } from 'react-router-dom' import { isNull, isUndefined, merge, flatten, isEqual, get } from 'lodash' import debounce from 'lodash/debounce' import { diff --git a/packages/client/src/views/RegisterForm/ReviewForm.tsx b/packages/client/src/views/RegisterForm/ReviewForm.tsx index a87ac8636be..c15e236713e 100644 --- a/packages/client/src/views/RegisterForm/ReviewForm.tsx +++ b/packages/client/src/views/RegisterForm/ReviewForm.tsx @@ -9,7 +9,7 @@ * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ import * as React from 'react' -import { Redirect, RouteComponentProps } from 'react-router' +import { Redirect, RouteComponentProps } from 'react-router-dom' import { WrappedComponentProps as IntlShapeProps, injectIntl } from 'react-intl' import styled, { withTheme } from 'styled-components' import { ITheme } from '@opencrvs/components/lib/theme' diff --git a/packages/client/src/views/ReviewCorrection/ReviewCorrection.tsx b/packages/client/src/views/ReviewCorrection/ReviewCorrection.tsx index 127af4cf5e8..42e442c775c 100644 --- a/packages/client/src/views/ReviewCorrection/ReviewCorrection.tsx +++ b/packages/client/src/views/ReviewCorrection/ReviewCorrection.tsx @@ -18,7 +18,7 @@ import { useLocation, useParams, useRouteMatch -} from 'react-router' +} from 'react-router-dom' import { useRecord } from '@client/hooks/useRecord' import { formatUrl } from '@client/navigation' import { REGISTRAR_HOME_TAB } from '@client/navigation/routes' diff --git a/packages/client/src/views/SearchResult/SearchResult.tsx b/packages/client/src/views/SearchResult/SearchResult.tsx index a218871802f..995a654be98 100644 --- a/packages/client/src/views/SearchResult/SearchResult.tsx +++ b/packages/client/src/views/SearchResult/SearchResult.tsx @@ -60,7 +60,7 @@ import { Frame } from '@opencrvs/components/lib/Frame' import * as React from 'react' import { injectIntl, WrappedComponentProps as IntlShapeProps } from 'react-intl' import { connect } from 'react-redux' -import { RouteComponentProps } from 'react-router' +import { RouteComponentProps } from 'react-router-dom' import ReactTooltip from 'react-tooltip' import { convertToMSISDN } from '@client/forms/utils' import { formattedDuration } from '@client/utils/date-formatting' @@ -74,6 +74,7 @@ import { import { WQContentWrapper } from '@client/views/OfficeHome/WQContentWrapper' import { LoadingIndicator } from '@client/views/OfficeHome/LoadingIndicator' import { SearchCriteria } from '@client/utils/referenceApi' +import { useWindowSize } from '@opencrvs/components/src/hooks' const ErrorText = styled.div` color: ${({ theme }) => theme.colors.negative}; @@ -137,58 +138,26 @@ type ISearchResultProps = IntlShapeProps & IBaseSearchResultProps & RouteComponentProps -interface ISearchResultState { - width: number -} - type QueryData = SearchEventsQuery['searchEvents'] -class SearchResultView extends React.Component< - ISearchResultProps, - ISearchResultState -> { - pageSize = 10 - showPaginated = false - constructor(props: ISearchResultProps) { - super(props) - this.state = { - width: window.innerWidth - } - } - - componentDidMount() { - window.addEventListener('resize', this.recordWindowWidth) - } - - componentWillUnmount() { - window.removeEventListener('resize', this.recordWindowWidth) - } - - recordWindowWidth = () => { - this.setState({ width: window.innerWidth }) - } +function SearchResultView(props: ISearchResultProps) { + const { width } = useWindowSize() - getExpandable = () => { - return this.state.width > this.props.theme.grid.breakpoints.lg - ? true - : false - } - - getColumns = () => { - if (this.state.width > this.props.theme.grid.breakpoints.lg) { + const getColumns = () => { + if (width > props.theme.grid.breakpoints.lg) { return [ { width: 35, - label: this.props.intl.formatMessage(constantsMessages.name), + label: props.intl.formatMessage(constantsMessages.name), key: COLUMNS.ICON_WITH_NAME }, { - label: this.props.intl.formatMessage(constantsMessages.event), + label: props.intl.formatMessage(constantsMessages.event), width: 20, key: COLUMNS.EVENT }, { - label: this.props.intl.formatMessage(constantsMessages.eventDate), + label: props.intl.formatMessage(constantsMessages.eventDate), width: 20, key: COLUMNS.DATE_OF_EVENT }, @@ -202,7 +171,7 @@ class SearchResultView extends React.Component< } else { return [ { - label: this.props.intl.formatMessage(constantsMessages.name), + label: props.intl.formatMessage(constantsMessages.name), width: 70, key: COLUMNS.ICON_WITH_NAME_EVENT }, @@ -216,33 +185,33 @@ class SearchResultView extends React.Component< } } - userHasRegisterScope() { - return this.props.scope && this.props.scope.includes('register') + function userHasRegisterScope() { + return props.scope && props.scope.includes('register') } - userHasValidateScope() { - return this.props.scope && this.props.scope.includes('validate') + function userHasValidateScope() { + return props.scope && props.scope.includes('validate') } - userHasCertifyScope() { - return this.props.scope && this.props.scope.includes('certify') + function userHasCertifyScope() { + return props.scope && props.scope.includes('certify') } - transformSearchContent = (data: QueryData) => { + const transformSearchContent = (data: QueryData) => { if (!data || !data.results) { return [] } - const transformedData = transformData(data, this.props.intl) + const transformedData = transformData(data, props.intl) const processingDeclarationIds = getProcessingDeclarationIds( - this.props.outboxDeclarations + props.outboxDeclarations ) return transformedData .filter(({ id }) => !processingDeclarationIds.includes(id)) .map((reg, index) => { - const foundDeclaration = this.props.outboxDeclarations.find( + const foundDeclaration = props.outboxDeclarations.find( (declaration) => declaration.id === reg.id ) const actions: IAction[] = [] @@ -265,7 +234,7 @@ class SearchResultView extends React.Component< reg.duplicates.length > 0 && reg.declarationStatus !== SUBMISSION_STATUS.CERTIFIED && reg.declarationStatus !== SUBMISSION_STATUS.REGISTERED - const { intl, location, userDetails } = this.props + const { intl, location, userDetails } = props const search = location.search const params = new URLSearchParams(search) const [searchText, searchType] = [ @@ -283,31 +252,31 @@ class SearchResultView extends React.Component< isDeclared || declarationIsInProgress || declarationIsRejected const shouldShowReviewButton = - (this.userHasRegisterScope() && isDeclarationReviewableByRegistrar) || - (this.userHasValidateScope() && isDeclarationReviewableByRegAgent) - if (this.state.width > this.props.theme.grid.breakpoints.lg) { + (userHasRegisterScope() && isDeclarationReviewableByRegistrar) || + (userHasValidateScope() && isDeclarationReviewableByRegAgent) + if (width > props.theme.grid.breakpoints.lg) { if ( (declarationIsRegistered || declarationIsIssued) && - this.userHasCertifyScope() + userHasCertifyScope() ) { actions.push({ - label: this.props.intl.formatMessage(buttonMessages.print), + label: props.intl.formatMessage(buttonMessages.print), handler: ( e: React.MouseEvent | undefined ) => { e && e.stopPropagation() - this.props.goToPrintCertificate(reg.id, reg.event) + props.goToPrintCertificate(reg.id, reg.event) }, disabled: downloadStatus !== DOWNLOAD_STATUS.DOWNLOADED }) - } else if (declarationIsCertified && this.userHasCertifyScope()) { + } else if (declarationIsCertified && userHasCertifyScope()) { actions.push({ - label: this.props.intl.formatMessage(buttonMessages.issue), + label: props.intl.formatMessage(buttonMessages.issue), handler: ( e: React.MouseEvent | undefined ) => { e && e.stopPropagation() - this.props.goToIssueCertificate(reg.id) + props.goToIssueCertificate(reg.id) }, disabled: downloadStatus !== DOWNLOAD_STATUS.DOWNLOADED }) @@ -315,10 +284,10 @@ class SearchResultView extends React.Component< actions.push({ label: declarationIsRejected || declarationIsInProgress - ? this.props.intl.formatMessage(constantsMessages.update) - : this.props.intl.formatMessage(constantsMessages.review), + ? props.intl.formatMessage(constantsMessages.update) + : props.intl.formatMessage(constantsMessages.review), handler: () => - this.props.goToPage( + props.goToPage( reg.declarationStatus === 'CORRECTION_REQUESTED' ? REVIEW_CORRECTION : REVIEW_EVENT_PARENT_FORM_PAGE, @@ -399,25 +368,21 @@ class SearchResultView extends React.Component< reg.dateOfEvent && formattedDuration(new Date(reg.dateOfEvent)) const isValidatedOnReview = reg.declarationStatus === SUBMISSION_STATUS.VALIDATED && - this.userHasRegisterScope() + userHasRegisterScope() ? true : false const isArchived = reg.declarationStatus === SUBMISSION_STATUS.ARCHIVED const NameComponent = reg.name ? ( - this.props.goToDeclarationRecordAudit('search', reg.id) - } + onClick={() => props.goToDeclarationRecordAudit('search', reg.id)} > {reg.name} ) : ( - this.props.goToDeclarationRecordAudit('search', reg.id) - } + onClick={() => props.goToDeclarationRecordAudit('search', reg.id)} > {intl.formatMessage(constantsMessages.noNameProvided)} @@ -451,120 +416,114 @@ class SearchResultView extends React.Component< }) } - render() { - const { intl, location, userDetails } = this.props - const search = location.search - const params = new URLSearchParams(search) - const [searchText, searchType] = [ - params.get('searchText'), - params.get('searchType') - ] - return ( - - } - navigation={} - skipToContentText={intl.formatMessage( - constantsMessages.skipToMainContent - )} - > - {searchText && searchType && ( - - query={SEARCH_EVENTS} - variables={{ - advancedSearchParameters: { - declarationLocationId: - userDetails && - ![ - SystemRoleType.LocalRegistrar, - SystemRoleType.NationalRegistrar, - SystemRoleType.RegistrationAgent - ].includes(userDetails.systemRole) - ? getUserLocation(userDetails).id - : '', - trackingId: - searchType === SearchCriteria.TRACKING_ID ? searchText : '', - nationalId: - searchType === SearchCriteria.NATIONAL_ID ? searchText : '', - registrationNumber: - searchType === SearchCriteria.REGISTRATION_NUMBER - ? searchText - : '', - contactNumber: - searchType === SearchCriteria.PHONE_NUMBER - ? convertToMSISDN(searchText, window.config.COUNTRY) - : '', - contactEmail: - searchType === SearchCriteria.EMAIL ? searchText : '', - name: searchType === SearchCriteria.NAME ? searchText : '' - }, - sort: SEARCH_RESULT_SORT - }} - fetchPolicy="cache-and-network" - > - {({ loading, error, data }) => { - const total = loading - ? -1 - : data?.searchEvents?.results?.length || 0 - return ( - - {loading ? ( -
- -
- ) : error ? ( - - {intl.formatMessage(errorMessages.queryError)} - - ) : ( - data?.searchEvents && - total > 0 && ( - <> - - - {this.props.intl.formatMessage( - registrarHomeMessages.validatedDeclarationTooltipForRegistrar - )} - - - + } + navigation={} + skipToContentText={intl.formatMessage( + constantsMessages.skipToMainContent + )} + > + {searchText && searchType && ( + + query={SEARCH_EVENTS} + variables={{ + advancedSearchParameters: { + declarationLocationId: + userDetails && + ![ + SystemRoleType.LocalRegistrar, + SystemRoleType.NationalRegistrar, + SystemRoleType.RegistrationAgent + ].includes(userDetails.systemRole) + ? getUserLocation(userDetails).id + : '', + trackingId: + searchType === SearchCriteria.TRACKING_ID ? searchText : '', + nationalId: + searchType === SearchCriteria.NATIONAL_ID ? searchText : '', + registrationNumber: + searchType === SearchCriteria.REGISTRATION_NUMBER + ? searchText + : '', + contactNumber: + searchType === SearchCriteria.PHONE_NUMBER + ? convertToMSISDN(searchText, window.config.COUNTRY) + : '', + contactEmail: + searchType === SearchCriteria.EMAIL ? searchText : '', + name: searchType === SearchCriteria.NAME ? searchText : '' + }, + sort: SEARCH_RESULT_SORT + }} + fetchPolicy="cache-and-network" + > + {({ loading, error, data }) => { + const total = loading + ? -1 + : data?.searchEvents?.results?.length || 0 + return ( + + {loading ? ( +
+ +
+ ) : error ? ( + + {intl.formatMessage(errorMessages.queryError)} + + ) : ( + data?.searchEvents && + total > 0 && ( + <> + + + {props.intl.formatMessage( + registrarHomeMessages.validatedDeclarationTooltipForRegistrar )} - hideLastBorder={true} - /> - - ) - )} -
- ) - }} - - )} - - ) - } + + + + + ) + )} + + ) + }} + + )} + + ) } export const SearchResult = connect( (state: IStoreState) => ({ diff --git a/packages/client/src/views/SysAdmin/Config/Systems/Systems.test.tsx b/packages/client/src/views/SysAdmin/Config/Systems/Systems.test.tsx index 28783b890bf..1ed0956f920 100644 --- a/packages/client/src/views/SysAdmin/Config/Systems/Systems.test.tsx +++ b/packages/client/src/views/SysAdmin/Config/Systems/Systems.test.tsx @@ -19,7 +19,7 @@ import { selectOption } from '@client/tests/util' import { SystemList } from '@client/views/SysAdmin/Config/Systems/Systems' -import { useParams } from 'react-router' +import { useParams } from 'react-router-dom' import { describe, Mock } from 'vitest' import { activateSystem, diff --git a/packages/client/src/views/SysAdmin/Performance/CompletenessRates.tsx b/packages/client/src/views/SysAdmin/Performance/CompletenessRates.tsx index 3ba146ab060..f79605fc104 100644 --- a/packages/client/src/views/SysAdmin/Performance/CompletenessRates.tsx +++ b/packages/client/src/views/SysAdmin/Performance/CompletenessRates.tsx @@ -32,7 +32,7 @@ import { parse } from 'query-string' import * as React from 'react' import { injectIntl, useIntl, WrappedComponentProps } from 'react-intl' import { connect, useDispatch } from 'react-redux' -import { RouteComponentProps } from 'react-router' +import { RouteComponentProps } from 'react-router-dom' import { IPerformanceSelectOption, PerformanceSelect diff --git a/packages/client/src/views/SysAdmin/Performance/PerformanceHome.tsx b/packages/client/src/views/SysAdmin/Performance/PerformanceHome.tsx index 0b4b2007539..28740b2bb38 100644 --- a/packages/client/src/views/SysAdmin/Performance/PerformanceHome.tsx +++ b/packages/client/src/views/SysAdmin/Performance/PerformanceHome.tsx @@ -22,7 +22,7 @@ import { parse } from 'query-string' import { ITheme } from '@opencrvs/components/lib/theme' import { injectIntl, WrappedComponentProps, IntlShape } from 'react-intl' import { connect } from 'react-redux' -import { RouteComponentProps } from 'react-router' +import { RouteComponentProps } from 'react-router-dom' import styled, { withTheme } from 'styled-components' import { SysAdminContentWrapper } from '@client/views/SysAdmin/SysAdminContentWrapper' import { Content, ContentSize } from '@opencrvs/components/lib/Content' diff --git a/packages/client/src/views/SysAdmin/Performance/WorkflowStatus.test.tsx b/packages/client/src/views/SysAdmin/Performance/WorkflowStatus.test.tsx index c9f746fa265..5085ba7b434 100644 --- a/packages/client/src/views/SysAdmin/Performance/WorkflowStatus.test.tsx +++ b/packages/client/src/views/SysAdmin/Performance/WorkflowStatus.test.tsx @@ -21,7 +21,7 @@ import { stringify, parse } from 'query-string' import { waitForElement } from '@client/tests/wait-for-element' import { FETCH_EVENTS_WITH_PROGRESS } from './queries' import { GraphQLError } from 'graphql' -import { match } from 'react-router' +import { match } from 'react-router-dom' import { vi } from 'vitest' import { Event } from '@client/utils/gateway' diff --git a/packages/client/src/views/SysAdmin/Performance/WorkflowStatus.tsx b/packages/client/src/views/SysAdmin/Performance/WorkflowStatus.tsx index ebf209283f1..fa5c4ca0dc5 100644 --- a/packages/client/src/views/SysAdmin/Performance/WorkflowStatus.tsx +++ b/packages/client/src/views/SysAdmin/Performance/WorkflowStatus.tsx @@ -47,7 +47,7 @@ import { parse } from 'query-string' import * as React from 'react' import { injectIntl, WrappedComponentProps } from 'react-intl' import { connect, useSelector } from 'react-redux' -import { RouteComponentProps } from 'react-router' +import { RouteComponentProps } from 'react-router-dom' import ReactTooltip from 'react-tooltip' import styled from 'styled-components' import { checkExternalValidationStatus } from '@client/views/SysAdmin/Team/utils' diff --git a/packages/client/src/views/SysAdmin/Team/TeamSearch.tsx b/packages/client/src/views/SysAdmin/Team/TeamSearch.tsx index 6b0ffaed888..6f7b70f4922 100644 --- a/packages/client/src/views/SysAdmin/Team/TeamSearch.tsx +++ b/packages/client/src/views/SysAdmin/Team/TeamSearch.tsx @@ -23,7 +23,7 @@ import { import * as React from 'react' import { injectIntl, WrappedComponentProps } from 'react-intl' import { connect } from 'react-redux' -import { RouteComponentProps } from 'react-router' +import { RouteComponentProps } from 'react-router-dom' import styled from 'styled-components' import { NoWifi } from '@opencrvs/components/lib/icons' import { withOnlineStatus } from '@client/views/OfficeHome/LoadingIndicator' diff --git a/packages/client/src/views/SysAdmin/Team/user/UserList.tsx b/packages/client/src/views/SysAdmin/Team/user/UserList.tsx index 34a7659c9ab..b2dd58c1ff5 100644 --- a/packages/client/src/views/SysAdmin/Team/user/UserList.tsx +++ b/packages/client/src/views/SysAdmin/Team/user/UserList.tsx @@ -65,7 +65,7 @@ import { WrappedComponentProps as IntlShapeProps } from 'react-intl' import { connect } from 'react-redux' -import { Redirect, RouteComponentProps } from 'react-router' +import { Redirect, RouteComponentProps } from 'react-router-dom' import { UserAuditActionModal } from '@client/views/SysAdmin/Team/user/UserAuditActionModal' import { userMutations } from '@client/user/mutations' import { Pagination } from '@opencrvs/components/lib/Pagination' diff --git a/packages/client/src/views/SysAdmin/Team/user/userCreation/CreateNewUser.tsx b/packages/client/src/views/SysAdmin/Team/user/userCreation/CreateNewUser.tsx index 29d14151900..17b021c21b0 100644 --- a/packages/client/src/views/SysAdmin/Team/user/userCreation/CreateNewUser.tsx +++ b/packages/client/src/views/SysAdmin/Team/user/userCreation/CreateNewUser.tsx @@ -34,7 +34,7 @@ import { withApollo, WithApolloClient } from '@apollo/client/react/hoc' import * as React from 'react' import { injectIntl, WrappedComponentProps as IntlShapeProps } from 'react-intl' import { connect } from 'react-redux' -import { RouteComponentProps } from 'react-router' +import { RouteComponentProps } from 'react-router-dom' import { gqlToDraftTransformer } from '@client/transformer' import { messages as userFormMessages } from '@client/i18n/messages/views/userForm' import { CREATE_USER_ON_LOCATION } from '@client/navigation/routes' diff --git a/packages/client/src/views/SysAdmin/Team/user/userCreation/UserReviewForm.tsx b/packages/client/src/views/SysAdmin/Team/user/userCreation/UserReviewForm.tsx index aba6ec4601a..e30d4170cd2 100644 --- a/packages/client/src/views/SysAdmin/Team/user/userCreation/UserReviewForm.tsx +++ b/packages/client/src/views/SysAdmin/Team/user/userCreation/UserReviewForm.tsx @@ -55,7 +55,7 @@ import * as React from 'react' import { injectIntl, WrappedComponentProps as IntlShapeProps } from 'react-intl' import { connect } from 'react-redux' import { Dispatch } from 'redux' -import { RouteComponentProps } from 'react-router' +import { RouteComponentProps } from 'react-router-dom' import { messages as sysAdminMessages } from '@client/i18n/messages/views/sysAdmin' import { Check } from '@opencrvs/components/lib/icons' import { getUserDetails } from '@client/profile/profileSelectors' diff --git a/packages/client/src/views/UserAudit/UserAudit.test.tsx b/packages/client/src/views/UserAudit/UserAudit.test.tsx index ede3e4c2872..ddcab7460bc 100644 --- a/packages/client/src/views/UserAudit/UserAudit.test.tsx +++ b/packages/client/src/views/UserAudit/UserAudit.test.tsx @@ -23,7 +23,7 @@ import { GET_USER } from '@client/user/queries' import { UserAudit } from '@client/views/UserAudit/UserAudit' import { userMutations } from '@client/user/mutations' import { vi, Mock } from 'vitest' -import * as Router from 'react-router' +import * as Router from 'react-router-dom' import { getStorageUserDetailsSuccess } from '@client/profile/profileActions' import { SystemRoleType } from '@client/utils/gateway' import * as profileSelectors from '@client/profile/profileSelectors' diff --git a/packages/client/src/views/UserAudit/UserAudit.tsx b/packages/client/src/views/UserAudit/UserAudit.tsx index 680ed4cec5d..fa27aa7ab0c 100644 --- a/packages/client/src/views/UserAudit/UserAudit.tsx +++ b/packages/client/src/views/UserAudit/UserAudit.tsx @@ -16,7 +16,7 @@ import { messages as sysMessages } from '@client/i18n/messages/views/sysAdmin' import { Navigation } from '@client/components/interface/Navigation' import { Frame } from '@opencrvs/components/lib/Frame' import { IntlShape, useIntl } from 'react-intl' -import { useParams } from 'react-router' +import { useParams } from 'react-router-dom' import { GET_USER } from '@client/user/queries' import { createNamesMap } from '@client/utils/data-formatting' import { AvatarSmall } from '@client/components/Avatar' diff --git a/packages/client/src/views/UserAudit/UserAuditHistory.tsx b/packages/client/src/views/UserAudit/UserAuditHistory.tsx index 8e2584b2dac..6119e51bc00 100644 --- a/packages/client/src/views/UserAudit/UserAuditHistory.tsx +++ b/packages/client/src/views/UserAudit/UserAuditHistory.tsx @@ -24,7 +24,6 @@ import type { GQLUserAuditLogResultSet } from '@client/utils/gateway-deprecated-do-not-use' import { ArrowDownBlue } from '@opencrvs/components/lib/icons' -import { LoadingGrey } from '@opencrvs/components/lib/LoadingGrey' import { Table } from '@opencrvs/components/lib/Table' import { GenericErrorToast } from '@client/components/GenericErrorToast' import { DateRangePicker } from '@client/components/DateRangePicker' @@ -46,6 +45,8 @@ import { ResponsiveModal } from '@opencrvs/components/lib/ResponsiveModal' import format from '@client/utils/date-formatting' import { Link } from '@opencrvs/components' import { Text } from '@opencrvs/components/lib/Text' +import { useState } from 'react' +import { useWindowSize } from '@opencrvs/components/src/hooks' const DEFAULT_LIST_SIZE = 10 @@ -64,11 +65,6 @@ const RecentActionsHolder = styled.div` border-top: 1px solid ${({ theme }) => theme.colors.grey200}; ` -const SectionTitle = styled.div` - ${({ theme }) => theme.fonts.h2}; - margin-bottom: 10px; -` - const AuditContent = styled.div` color: ${({ theme }) => theme.colors.grey600}; ` @@ -121,59 +117,41 @@ const isUserAuditItemWithDeclarationDetials = ( return (item as any).data } -class UserAuditHistoryComponent extends React.Component { - constructor(props: Props) { - super(props) - window.__localeId__ = props.intl.locale - this.state = { - timeStart: subMonths(new Date(Date.now()), 1), - timeEnd: new Date(Date.now()), - viewportWidth: 0, - currentPageNumber: 1, - sortOrder: SORT_ORDER.DESCENDING, - sortedColumn: SORTED_COLUMN.DATE, - showModal: false, - actionDetailsData: null - } - this.updateViewPort = this.updateViewPort.bind(this) - } - - componentDidMount() { - this.updateViewPort() - window.addEventListener('resize', this.updateViewPort) - } - - componentWillUnmount() { - window.removeEventListener('resize', this.updateViewPort) - } - - setDateRangePickerValues(startDate: Date, endDate: Date) { - this.setState({ +function UserAuditHistoryComponent(props: Props) { + window.__localeId__ = props.intl.locale + + const [state, setState] = useState({ + timeStart: subMonths(new Date(Date.now()), 1), + timeEnd: new Date(Date.now()), + viewportWidth: useWindowSize().width, + currentPageNumber: 1, + sortOrder: SORT_ORDER.DESCENDING, + sortedColumn: SORTED_COLUMN.DATE, + showModal: false, + actionDetailsData: null + }) + + function setDateRangePickerValues(startDate: Date, endDate: Date) { + setState((prevState) => ({ + ...prevState, timeStart: startDate, timeEnd: endDate - }) + })) } - toggleSortOrder(columnName: SORTED_COLUMN) { - this.setState({ + function toggleSortOrder(columnName: SORTED_COLUMN) { + setState((prevState) => ({ + ...prevState, sortedColumn: columnName, sortOrder: - this.state.sortOrder === SORT_ORDER.DESCENDING + prevState.sortOrder === SORT_ORDER.DESCENDING ? SORT_ORDER.ASCENDING : SORT_ORDER.DESCENDING - }) - } - - setCurrentPage = (currentPage: number) => { - this.setState({ currentPageNumber: currentPage }) + })) } - updateViewPort() { - this.setState({ viewportWidth: window.innerWidth }) - } - - getAuditColumns() { - const { intl } = this.props + function getAuditColumns() { + const { intl } = props return [ { label: intl.formatMessage(messages.auditActionColumnTitle), @@ -181,7 +159,7 @@ class UserAuditHistoryComponent extends React.Component { isSortable: true, icon: , key: 'actionDescription', - sortFunction: () => this.toggleSortOrder(SORTED_COLUMN.ACTION) + sortFunction: () => toggleSortOrder(SORTED_COLUMN.ACTION) }, { label: intl.formatMessage(messages.auditTrackingIDColumnTitle), @@ -189,7 +167,7 @@ class UserAuditHistoryComponent extends React.Component { isSortable: true, icon: , key: 'trackingId', - sortFunction: () => this.toggleSortOrder(SORTED_COLUMN.RECORD) + sortFunction: () => toggleSortOrder(SORTED_COLUMN.RECORD) }, { label: intl.formatMessage(messages.auditDeviceIpAddressColumnTitle), @@ -198,7 +176,7 @@ class UserAuditHistoryComponent extends React.Component { icon: , key: 'deviceIpAddress', alignment: ColumnContentAlignment.LEFT, - sortFunction: () => this.toggleSortOrder(SORTED_COLUMN.DEVICE) + sortFunction: () => toggleSortOrder(SORTED_COLUMN.DEVICE) }, { label: intl.formatMessage(messages.auditDateColumnTitle), @@ -208,19 +186,20 @@ class UserAuditHistoryComponent extends React.Component { isSorted: true, icon: , alignment: ColumnContentAlignment.RIGHT, - sortFunction: () => this.toggleSortOrder(SORTED_COLUMN.DATE) + sortFunction: () => toggleSortOrder(SORTED_COLUMN.DATE) } ] } - toggleActionDetails = (actionItem: UserAuditLogResultItem | null) => { - this.setState({ + const toggleActionDetails = (actionItem: UserAuditLogResultItem | null) => { + setState((prevState) => ({ + ...prevState, actionDetailsData: actionItem, - showModal: !this.state.showModal - }) + showModal: !state.showModal + })) } - getIpAdress(auditLog: UserAuditLogResultItem) { + function getIpAdress(auditLog: UserAuditLogResultItem) { const device = Bowser.getParser(auditLog.userAgent).getResult() return ( @@ -236,23 +215,21 @@ class UserAuditHistoryComponent extends React.Component { ) } - getActionMessage(auditLog: UserAuditLogResultItem) { + function getActionMessage(auditLog: UserAuditLogResultItem) { const actionDescriptor = getUserAuditDescription(auditLog.action) - return actionDescriptor - ? this.props.intl.formatMessage(actionDescriptor) - : '' + return actionDescriptor ? props.intl.formatMessage(actionDescriptor) : '' } - getAuditData(data: GQLUserAuditLogResultSet) { + function getAuditData(data: GQLUserAuditLogResultSet) { const auditList = data.results.map((userAuditItem) => { if (userAuditItem === null) { return {} } - const actionMessage = this.getActionMessage(userAuditItem) + const actionMessage = getActionMessage(userAuditItem) const isSystemAdmin = - this.props.loggedInUserRole === 'NATIONAL_SYSTEM_ADMIN' || - this.props.loggedInUserRole === 'LOCAL_SYSTEM_ADMIN' + props.loggedInUserRole === 'NATIONAL_SYSTEM_ADMIN' || + props.loggedInUserRole === 'LOCAL_SYSTEM_ADMIN' return { actionDescription: @@ -261,7 +238,7 @@ class UserAuditHistoryComponent extends React.Component { { - this.toggleActionDetails(userAuditItem) + toggleActionDetails(userAuditItem) }} > {actionMessage} @@ -271,7 +248,7 @@ class UserAuditHistoryComponent extends React.Component { { - this.toggleActionDetails(userAuditItem) + toggleActionDetails(userAuditItem) }} > {actionMessage} @@ -285,7 +262,7 @@ class UserAuditHistoryComponent extends React.Component { isUserAuditItemWithDeclarationDetials(userAuditItem) === undefined ? ( { - this.toggleActionDetails(userAuditItem) + toggleActionDetails(userAuditItem) }} > {actionMessage} @@ -293,7 +270,7 @@ class UserAuditHistoryComponent extends React.Component { ) : !isSystemAdmin ? ( { - this.toggleActionDetails(userAuditItem) + toggleActionDetails(userAuditItem) }} > {actionMessage} @@ -307,7 +284,7 @@ class UserAuditHistoryComponent extends React.Component { - this.props.goToDeclarationRecordAudit( + props.goToDeclarationRecordAudit( 'printTab', userAuditItem.data.compositionId as string ) @@ -319,7 +296,7 @@ class UserAuditHistoryComponent extends React.Component { {userAuditItem.data.trackingId} ) : null, - deviceIpAddress: this.getIpAdress(userAuditItem), + deviceIpAddress: getIpAdress(userAuditItem), trackingIdString: isUserAuditItemWithDeclarationDetials(userAuditItem) ? userAuditItem.data.trackingId : null, @@ -332,38 +309,22 @@ class UserAuditHistoryComponent extends React.Component { }) return ( (auditList && - orderBy( - auditList, - [this.state.sortedColumn], - [this.state.sortOrder] - )) || + orderBy(auditList, [state.sortedColumn], [state.sortOrder])) || [] ) } - getLoadingView() { - return ( - <> - - - - - {this.getLoadingAuditListView()} - - ) - } - - getLoadingAuditListView(hasError?: boolean) { + function getLoadingAuditListView(hasError?: boolean) { return ( <> {hasError && } @@ -371,110 +332,106 @@ class UserAuditHistoryComponent extends React.Component { ) } - render() { - const { intl, practitionerId, theme } = this.props - const { timeStart, timeEnd, currentPageNumber } = this.state - const recordCount = DEFAULT_LIST_SIZE + const { intl, practitionerId, theme } = props + const { timeStart, timeEnd, currentPageNumber } = state + const recordCount = DEFAULT_LIST_SIZE - return ( - + return ( + + <> <> + + + {intl.formatMessage(messages.auditSectionTitle)} + + { + setDateRangePickerValues(startDate, endDate) + }} + /> + <> - - - {intl.formatMessage(messages.auditSectionTitle)} - - { - this.setDateRangePickerValues(startDate, endDate) - }} - /> - - <> - - query={GET_USER_AUDIT_LOG} - variables={{ - practitionerId: practitionerId, - count: recordCount, - skip: DEFAULT_LIST_SIZE * (currentPageNumber - 1), - timeStart: timeStart, - timeEnd: timeEnd - }} - fetchPolicy={'no-cache'} - > - {({ data, loading, error }) => { - if (error || !data || !data.getUserAuditLog) { - return this.getLoadingAuditListView(error ? true : false) - } else { - const totalItems = Number( - (data && - data.getUserAuditLog && - data.getUserAuditLog.total) || - 0 - ) - - return ( - -
- - this.setState({ currentPageNumber: page }) - } - /> - - {this.state.actionDetailsData && ( - this.toggleActionDetails(null)} - show={this.state.showModal} - responsive={true} - title={this.getActionMessage( - this.state.actionDetailsData - )} - width={1024} - autoHeight={true} - > - <> - - {this.props.practitionerName} -{' '} - {format( - new Date(this.state.actionDetailsData.time), - 'MMMM dd, yyyy hh:mm a' - )}{' '} - {' | '} - {this.getIpAdress(this.state.actionDetailsData)} - - - - )} - - ) - } - }} - - + + query={GET_USER_AUDIT_LOG} + variables={{ + practitionerId: practitionerId, + count: recordCount, + skip: DEFAULT_LIST_SIZE * (currentPageNumber - 1), + timeStart: timeStart, + timeEnd: timeEnd + }} + fetchPolicy={'no-cache'} + > + {({ data, loading, error }) => { + if (error || !data || !data.getUserAuditLog) { + return getLoadingAuditListView(error ? true : false) + } else { + const totalItems = Number( + (data && + data.getUserAuditLog && + data.getUserAuditLog.total) || + 0 + ) + + return ( + +
+ + setState((prevState) => ({ + ...prevState, + currentPageNumber: page + })) + } + /> + + {state.actionDetailsData && ( + toggleActionDetails(null)} + show={state.showModal} + responsive={true} + title={getActionMessage(state.actionDetailsData)} + width={1024} + autoHeight={true} + > + <> + + {props.practitionerName} -{' '} + {format( + new Date(state.actionDetailsData.time), + 'MMMM dd, yyyy hh:mm a' + )}{' '} + {' | '} + {getIpAdress(state.actionDetailsData)} + + + + )} + + ) + } + }} + - - ) - } + + + ) } export const UserAuditHistory = connect(null, { diff --git a/packages/client/src/views/VerifyCertificate/VerifyCertificatePage.tsx b/packages/client/src/views/VerifyCertificate/VerifyCertificatePage.tsx index 18d698121b4..7888820505e 100644 --- a/packages/client/src/views/VerifyCertificate/VerifyCertificatePage.tsx +++ b/packages/client/src/views/VerifyCertificate/VerifyCertificatePage.tsx @@ -33,7 +33,7 @@ import { import { CountryLogo } from '@opencrvs/components/lib/icons' import { Spinner, Stack } from '@opencrvs/components' import { Toast } from '@opencrvs/components/lib/Toast/Toast' -import { useParams } from 'react-router' +import { useParams } from 'react-router-dom' import formatDate, { formatPlainDate } from '@client/utils/date-formatting' import { BirthRegistration, diff --git a/packages/client/src/views/VerifyCertificate/useVerificationRecordDetails.ts b/packages/client/src/views/VerifyCertificate/useVerificationRecordDetails.ts index e43034c4503..4ee9a411ec1 100644 --- a/packages/client/src/views/VerifyCertificate/useVerificationRecordDetails.ts +++ b/packages/client/src/views/VerifyCertificate/useVerificationRecordDetails.ts @@ -11,7 +11,7 @@ import { gql, useQuery } from '@apollo/client' import { FetchRecordDetailsForVerificationQuery } from '@client/utils/gateway' -import { useParams } from 'react-router' +import { useParams } from 'react-router-dom' const FETCH_RECORD_DETAILS_FOR_VERIFICATION = gql` query fetchRecordDetailsForVerification($id: String!) { diff --git a/packages/client/src/views/ViewRecord/ViewRecord.test.tsx b/packages/client/src/views/ViewRecord/ViewRecord.test.tsx index 7a79eec43d6..f6e8df374ba 100644 --- a/packages/client/src/views/ViewRecord/ViewRecord.test.tsx +++ b/packages/client/src/views/ViewRecord/ViewRecord.test.tsx @@ -15,7 +15,7 @@ import { createStore } from '@client/store' import { createTestComponent } from '@client/tests/util' import { FETCH_VIEW_RECORD_BY_COMPOSITION } from '@client/views/ViewRecord/query' import { ViewRecord } from './ViewRecord' -import { useParams } from 'react-router' +import { useParams } from 'react-router-dom' import { Mock } from 'vitest' describe('View Record for loading and success state', () => { diff --git a/packages/client/src/views/ViewRecord/ViewRecord.tsx b/packages/client/src/views/ViewRecord/ViewRecord.tsx index ab7ccf6e6da..19328aa84de 100644 --- a/packages/client/src/views/ViewRecord/ViewRecord.tsx +++ b/packages/client/src/views/ViewRecord/ViewRecord.tsx @@ -17,7 +17,7 @@ import { SUBMISSION_STATUS } from '@client/declarations' import { useIntl } from 'react-intl' -import { useParams } from 'react-router' +import { useParams } from 'react-router-dom' import { useQuery } from '@apollo/client' import { IFormData } from '@client/forms' import { goBack } from '@client/navigation' diff --git a/packages/commons/package.json b/packages/commons/package.json index 801f8cc25ee..eb6da5b9eaf 100644 --- a/packages/commons/package.json +++ b/packages/commons/package.json @@ -13,7 +13,8 @@ "./http": "./build/dist/common/http.js", "./fixtures": "./build/dist/common/fixtures.js", "./assignment": "./build/dist/common/assignment.js", - "./client": "./build/dist/esm/client.js" + "./client": "./build/dist/esm/client.js", + "./events": "./build/dist/common/events/index.js" }, "scripts": { "start": "yarn build:watch", @@ -42,10 +43,11 @@ "jwt-decode": "^3.0.0", "lodash": "^4.17.10", "node-fetch": "^2.6.7", + "pino": "^7.0.0", "pkg-up": "^3.1.0", "typescript": "5.6.3", "uuid": "^9.0.0", - "pino": "^7.0.0" + "zod": "^3.23.8" }, "devDependencies": { "@typescript-eslint/eslint-plugin": "^4.5.0", diff --git a/packages/commons/src/events/Action.ts b/packages/commons/src/events/Action.ts new file mode 100644 index 00000000000..15902b29b43 --- /dev/null +++ b/packages/commons/src/events/Action.ts @@ -0,0 +1,87 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ +import { z } from 'zod' +import { GetValues, NonEmptyArray } from '../types' +import { Form } from './Form' +import { Label } from './utils' + +/** + * Actions recognized by the system + */ +export const ActionType = { + CREATE: 'CREATE', + ASSIGN: 'ASSIGN', + UNASSIGN: 'UNASSIGN', + REGISTER: 'REGISTER', + VALIDATE: 'VALIDATE', + CORRECT: 'CORRECT', + DETECT_DUPLICATE: 'DETECT_DUPLICATE', + NOTIFY: 'NOTIFY', + DECLARE: 'DECLARE' +} as const + +export const actionTypes = Object.values(ActionType) +export type ActionType = GetValues + +/** + * Configuration of action performed on an event. + * Includes roles that can perform the action, label and forms involved. + */ +export const ActionConfig = z.object({ + type: z.enum(actionTypes as NonEmptyArray), + label: Label, + forms: z.array(Form) +}) + +export const ActionInputBase = z.object({ + type: z.enum(actionTypes as NonEmptyArray), + fields: z.array( + z.object({ + id: z.string(), + value: z.union([ + z.string(), + z.number(), + z.array( + z.object({ + optionValues: z.array(z.string()), + type: z.string(), + data: z.string(), + fileSize: z.number() + }) + ) + ]) + }) + ) +}) + +export const ActionInput = z.union([ + ActionInputBase.extend({ + type: z.enum([ActionType.CREATE]) + }), + ActionInputBase.extend({ + type: z.enum([ActionType.REGISTER]), + identifiers: z.object({ + trackingId: z.string(), + registrationNumber: z.string() + }) + }) +]) + +export type ActionInput = z.infer + +export const Action = ActionInput.and( + z.object({ + createdAt: z.date(), + createdBy: z.string() + }) +) + +export type Action = z.infer diff --git a/packages/commons/src/events/Event.ts b/packages/commons/src/events/Event.ts new file mode 100644 index 00000000000..078e61a117f --- /dev/null +++ b/packages/commons/src/events/Event.ts @@ -0,0 +1,86 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ +import { z } from 'zod' +import { Action, ActionConfig, ActionType } from './Action' +import { Label, Summary } from './utils' + +/** + * A subset of an event. Describes fields that can be sent to the system with the intention of either creating or mutating a an event + */ +export const EventInput = z.object({ + type: z.string(), + fields: z.array( + z.object({ + id: z.string(), + value: z.union([ + z.string(), + z.number(), + z.array( + // @TODO: Check if we could make this stricter + z.object({ + optionValues: z.array(z.string()), + type: z.string(), + data: z.string(), + fileSize: z.number() + }) + ) + ]) + }) + ) +}) +export type EventInput = z.infer + +/** + * Description of event features defined by the country. Includes configuration for process steps and forms involved. + */ +export const EventConfig = z.object({ + id: z.string(), + label: Label, + summary: Summary, + actions: z.array(ActionConfig) +}) + +export type EventConfig = z.infer + +/** + * A subset of an event. Describes how the event is stored in the search index. Contains static fields shared by all event types and custom fields defined by event configuration + */ + +export const EventIndex = z.object({ + id: z.string(), + event: z.string(), + status: z.enum([ActionType.CREATE]), + createdAt: z.date(), + createdBy: z.string(), + createdAtLocation: z.string(), // uuid + modifiedAt: z.date(), + assignedTo: z.string(), + updatedBy: z.string(), + data: z.object({}) +}) + +export type EventIndex = z.infer + +export const Event = EventInput.extend({ + id: z.string(), + type: z.string(), // Should be replaced by a reference to a form version + createdAt: z.date(), + updatedAt: z.date(), + actions: z.array(Action) +}) + +export type Event = z.infer + +/** + * Builds a validated configuration for an event + * @param config - Event specific configuration + */ +export const defineConfig = (config: EventConfig) => EventConfig.parse(config) diff --git a/packages/commons/src/events/Form.ts b/packages/commons/src/events/Form.ts new file mode 100644 index 00000000000..1daff128aee --- /dev/null +++ b/packages/commons/src/events/Form.ts @@ -0,0 +1,34 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ +import { z } from 'zod' +import { Field, Label } from './utils' + +export const FormGroupField = Field.extend({ + id: z.string(), + type: z.string(), // @TODO: Get enums from somewhere, field types + required: z.boolean(), + searchable: z.boolean().optional(), + analytics: z.boolean().optional() +}) + +export const FormSection = z.object({ + title: Label, + groups: z.array(FormGroupField) +}) + +export const Form = z.object({ + active: z.boolean(), + version: z.object({ + id: z.string(), + label: Label + }), + form: z.array(FormSection) +}) diff --git a/packages/commons/src/events/fixtures/tennis-club-membership-event.ts b/packages/commons/src/events/fixtures/tennis-club-membership-event.ts new file mode 100644 index 00000000000..9d8765cf12f --- /dev/null +++ b/packages/commons/src/events/fixtures/tennis-club-membership-event.ts @@ -0,0 +1,132 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ +import { defineConfig } from '../Event' + +export const tennisClubMembershipEvent = defineConfig({ + id: 'TENNIS_CLUB_MEMBERSHIP', + summary: { + title: { + defaultMessage: 'Tennis club membership application', + description: 'This is the title of the form', + id: 'event.tennis-club-membership.summary.title' + }, + fields: [] + }, + label: { + defaultMessage: 'Tennis club membership application', + description: 'This is what this event is referred as in the system', + id: 'event.tennis-club-membership.label' + }, + actions: [ + { + type: 'DECLARE', + label: { + defaultMessage: 'Send an application', + description: + 'This is shown as the action name anywhere the user can trigger the action from', + id: 'event.tennis-club-membership.action.declare.label' + }, + forms: [ + { + active: true, + version: { + id: '1', + label: { + defaultMessage: 'Version 1', + description: 'This is the first version of the form', + id: 'event.tennis-club-membership.action.declare.form.version.1' + } + }, + form: [ + { + title: { + id: 'event.tennis-club-membership.action.declare.form.section.who.title', + defaultMessage: 'Who is applying for the membership?', + description: 'This is the title of the section' + }, + groups: [ + { + id: 'applicant.firstname', + type: 'TEXT', + required: true, + label: { + defaultMessage: "Applicant's first name", + description: 'This is the label for the field', + id: 'event.tennis-club-membership.action.declare.form.section.who.field.firstname.label' + } + }, + { + id: 'applicant.surname', + type: 'TEXT', + required: true, + label: { + defaultMessage: "Applicant's surname", + description: 'This is the label for the field', + id: 'event.tennis-club-membership.action.declare.form.section.who.field.surname.label' + } + }, + { + id: 'applicant.dob', + type: 'DATE', + required: true, + label: { + defaultMessage: "Applicant's date of birth", + description: 'This is the label for the field', + id: 'event.tennis-club-membership.action.declare.form.section.who.field.dob.label' + } + } + ] + }, + { + title: { + id: 'event.tennis-club-membership.action.declare.form.section.recommender.title', + defaultMessage: 'Who is recommending the applicant?', + description: 'This is the title of the section' + }, + groups: [ + { + id: 'recommender.firstname', + type: 'TEXT', + required: true, + label: { + defaultMessage: "Recommender's first name", + description: 'This is the label for the field', + id: 'event.tennis-club-membership.action.declare.form.section.recommender.field.firstname.label' + } + }, + { + id: 'recommender.surname', + type: 'TEXT', + required: true, + label: { + defaultMessage: "Recommender's surname", + description: 'This is the label for the field', + id: 'event.tennis-club-membership.action.declare.form.section.recommender.field.surname.label' + } + }, + { + id: 'recommender.id', + type: 'TEXT', + required: true, + label: { + defaultMessage: "Recommender's membership ID", + description: 'This is the label for the field', + id: 'event.tennis-club-membership.action.declare.form.section.recommender.field.id.label' + } + } + ] + } + ] + } + ] + } + ] +}) diff --git a/packages/commons/src/events/index.ts b/packages/commons/src/events/index.ts new file mode 100644 index 00000000000..729d13e300f --- /dev/null +++ b/packages/commons/src/events/index.ts @@ -0,0 +1,14 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ +export * from './Action' +export * from './Event' +export * from './Form' +export * from './utils' diff --git a/packages/commons/src/events/utils.ts b/packages/commons/src/events/utils.ts new file mode 100644 index 00000000000..61af9351fa2 --- /dev/null +++ b/packages/commons/src/events/utils.ts @@ -0,0 +1,53 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ +import { GetValues } from '../types' +import { z } from 'zod' + +export const Label = z.object({ + defaultMessage: z.string(), + description: z.string(), + id: z.string() +}) + +// Ask whether these are always together +export const Field = z.object({ + label: Label +}) + +export const Summary = z.object({ + title: Label, + fields: z.array(Field) +}) + +// @TODO +export const Query = z.object({ + requester: z.object({ + phone: z.object({ + check: z.string() + }), + name: z.object({ + check: z.string() + }) + }) +}) + +export const SystemRoleType = { + FieldAgent: 'FIELD_AGENT', + LocalRegistrar: 'LOCAL_REGISTRAR', + LocalSystemAdmin: 'LOCAL_SYSTEM_ADMIN', + NationalRegistrar: 'NATIONAL_REGISTRAR', + NationalSystemAdmin: 'NATIONAL_SYSTEM_ADMIN', + PerformanceManagement: 'PERFORMANCE_MANAGEMENT', + RegistrationAgent: 'REGISTRATION_AGENT' +} as const + +export type SystemRoleType = GetValues +export const systemRoleTypes = Object.values(SystemRoleType) diff --git a/packages/commons/src/index.ts b/packages/commons/src/index.ts index 4ef19fe8523..e2aefcffdd7 100644 --- a/packages/commons/src/index.ts +++ b/packages/commons/src/index.ts @@ -14,3 +14,4 @@ export * from './documents' export * from './http' export * from './logger' export * from './search' +export * from './events' diff --git a/packages/commons/src/types.ts b/packages/commons/src/types.ts index 530eb9871e5..5a943f4ce77 100644 --- a/packages/commons/src/types.ts +++ b/packages/commons/src/types.ts @@ -16,3 +16,6 @@ export * from './nominal' export * from './search' export type PartialBy = Omit & Partial> + +export type GetValues> = T[keyof T] +export type NonEmptyArray = [T, ...T[]] diff --git a/packages/commons/tsconfig.json b/packages/commons/tsconfig.json index 02246857644..2c9bd0b8555 100644 --- a/packages/commons/tsconfig.json +++ b/packages/commons/tsconfig.json @@ -8,7 +8,6 @@ "module": "commonjs", "outDir": "build/dist/common", "sourceMap": true, - "moduleResolution": "node", "rootDir": "src", "declaration": true, "lib": ["esnext.asynciterable", "es2015", "es6"], @@ -20,7 +19,8 @@ "noUnusedLocals": true, "types": ["jest", "fhir"], "skipLibCheck": true, - "declarationMap": true + "declarationMap": true, + "composite": true }, "include": ["src/**/*.ts", "typings"], "exclude": ["node_modules", "build"] diff --git a/packages/events/src/index.test.ts b/packages/events/src/index.test.ts index 2054466830e..b4404f50cd3 100644 --- a/packages/events/src/index.test.ts +++ b/packages/events/src/index.test.ts @@ -15,6 +15,7 @@ import { getClient, resetServer } from './storage/__mocks__/mongodb' +import { ActionType } from '@opencrvs/commons' const { createCallerFactory } = t @@ -38,7 +39,7 @@ const client = createClient() test('event can be created and fetched', async () => { const event = await client.event.create({ transactionId: '1', - event: { type: 'birth' } + event: { type: 'birth', fields: [] } }) const fetchedEvent = await client.event.get(event.id) @@ -51,12 +52,12 @@ test('creating an event is an idempotent operation', async () => { await client.event.create({ transactionId: '1', - event: { type: 'birth' } + event: { type: 'birth', fields: [] } }) await client.event.create({ transactionId: '1', - event: { type: 'birth' } + event: { type: 'birth', fields: [] } }) expect(await db.collection('events').find().toArray()).toHaveLength(1) @@ -65,12 +66,13 @@ test('creating an event is an idempotent operation', async () => { test('stored events can be modified', async () => { const originalEvent = await client.event.create({ transactionId: '1', - event: { type: 'birth' } + event: { type: 'birth', fields: [] } }) const event = await client.event.patch({ id: originalEvent.id, - type: 'death' + type: 'death', + fields: [] }) expect(event.updatedAt).not.toBe(originalEvent.updatedAt) @@ -80,13 +82,13 @@ test('stored events can be modified', async () => { test('actions can be added to created events', async () => { const originalEvent = await client.event.create({ transactionId: '1', - event: { type: 'birth' } + event: { type: 'birth', fields: [] } }) const event = await client.event.actions.create({ eventId: originalEvent.id, action: { - type: 'REGISTERED', + type: ActionType.REGISTER, fields: [], identifiers: { trackingId: '123', @@ -97,7 +99,7 @@ test('actions can be added to created events', async () => { expect(event.actions).toContainEqual( expect.objectContaining({ - type: 'REGISTERED', + type: ActionType.REGISTER, identifiers: { trackingId: '123', registrationNumber: '456' diff --git a/packages/events/src/router.ts b/packages/events/src/router.ts index 95a55b3f45c..2333c7891e6 100644 --- a/packages/events/src/router.ts +++ b/packages/events/src/router.ts @@ -12,15 +12,15 @@ import { initTRPC } from '@trpc/server' import superjson from 'superjson' import { z } from 'zod' + import { - ActionInput, addAction, createEvent, - EventInput, EventInputWithId, getEventById, patchEvent } from './service/events' +import { ActionInput, EventInput } from '@opencrvs/commons' export const t = initTRPC.create({ transformer: superjson diff --git a/packages/events/src/service/events.ts b/packages/events/src/service/events.ts index 40034353196..44c67083321 100644 --- a/packages/events/src/service/events.ts +++ b/packages/events/src/service/events.ts @@ -10,77 +10,19 @@ */ import { getClient } from '@events/storage/mongodb' -import { getUUID } from '@opencrvs/commons' +import { + getUUID, + EventInput, + Event, + ActionInput, + ActionType +} from '@opencrvs/commons' import { z } from 'zod' -export const EventInput = z.object({ - type: z.string() -}) - export const EventInputWithId = EventInput.extend({ id: z.string() }) -const ActionInputBase = z.object({ - type: z.enum([ - 'CREATED', - 'ASSIGNMENT', - 'UNASSIGNMENT', - 'REGISTERED', - 'VALIDATED', - 'CORRECTION', - 'DUPLICATES_DETECTED' - ]), - fields: z.array( - z.object({ - id: z.string(), - value: z.union([ - z.string(), - z.number(), - z.array( - z.object({ - optionValues: z.array(z.string()), - type: z.string(), - data: z.string(), - fileSize: z.number() - }) - ) - ]) - }) - ) -}) - -export const ActionInput = z.union([ - ActionInputBase.extend({ - type: z.enum(['CREATED']) - }), - ActionInputBase.extend({ - type: z.enum(['REGISTERED']), - identifiers: z.object({ - trackingId: z.string(), - registrationNumber: z.string() - }) - }) -]) - -const Action = ActionInput.and( - z.object({ - createdAt: z.date(), - createdBy: z.string() - }) -) - -type ActionInput = z.infer - -export const Event = EventInput.extend({ - id: z.string(), - type: z.string(), // Should be replaced by a reference to a form version - createdAt: z.date(), - updatedAt: z.date(), - actions: z.array(Action) -}) -export type Event = z.infer - const EventWithTransactionId = Event.extend({ transactionId: z.string() }) @@ -103,7 +45,7 @@ class EventNotFoundError extends Error { export async function getEventById(id: string) { const db = await getClient() - const collection = db.collection>('events') + const collection = db.collection('events') const event = await collection.findOne({ id: id }) if (!event) { throw new EventNotFoundError(id) @@ -135,7 +77,7 @@ export async function createEvent( updatedAt: now, actions: [ { - type: 'CREATED', + type: ActionType.CREATE, createdAt: now, createdBy: '123-123-123', fields: [] @@ -148,7 +90,7 @@ export async function createEvent( export async function addAction(eventId: string, action: ActionInput) { const db = await getClient() - const collection = db.collection>('events') + const collection = db.collection('events') const now = new Date() diff --git a/packages/login/package.json b/packages/login/package.json index 0a3d43a6e32..6864518cbf0 100644 --- a/packages/login/package.json +++ b/packages/login/package.json @@ -10,7 +10,7 @@ "@sentry/tracing": "^7.12.1", "@types/history": "^4.6.2", "@types/react-redux": "^7.1.5", - "@types/react-router": "^5.1.2", + "@types/react-router-dom": "^5.1.2", "@types/redux-sentry-middleware": "^0.2.0", "@types/styled-components": "^5.1.3", "@typescript-eslint/eslint-plugin": "^4.5.0", @@ -40,7 +40,7 @@ "react-final-form": "^6.5.9", "react-intl": "5.25.1", "react-redux": "^7.1.1", - "react-router": "^5.1.2", + "react-router-dom": "^5.1.2", "redux": "^4.0.4", "redux-loop": "^5.0.0", "redux-sentry-middleware": "^0.2.0", diff --git a/packages/login/src/App.tsx b/packages/login/src/App.tsx index 55f1a1b4a55..1af08539ed1 100644 --- a/packages/login/src/App.tsx +++ b/packages/login/src/App.tsx @@ -18,7 +18,7 @@ import { getTheme } from '@opencrvs/components/lib/theme' import * as React from 'react' import { History } from 'history' import { Provider } from 'react-redux' -import { Route, Switch } from 'react-router' +import { Route, Switch } from 'react-router-dom' import { ConnectedRouter } from 'connected-react-router' import { createGlobalStyle, ThemeProvider } from 'styled-components' import { ForgottenItem } from './views/ResetCredentialsForm/ForgottenItemForm' diff --git a/packages/login/src/i18n/components/LanguageSelect.tsx b/packages/login/src/i18n/components/LanguageSelect.tsx index d4beaed03bd..83c09bdecf0 100644 --- a/packages/login/src/i18n/components/LanguageSelect.tsx +++ b/packages/login/src/i18n/components/LanguageSelect.tsx @@ -18,7 +18,7 @@ import { import styled from 'styled-components' import { useSearchQuery } from '@login/i18n/utils' import { getLanguages, getLanguage } from '@login/i18n/selectors' -import { useHistory, useLocation } from 'react-router' +import { useHistory, useLocation } from 'react-router-dom' import { defineMessages, useIntl } from 'react-intl' const SelectContainer = styled.div` diff --git a/packages/login/src/i18n/utils.ts b/packages/login/src/i18n/utils.ts index 712c1d7645c..8f1f829cd88 100644 --- a/packages/login/src/i18n/utils.ts +++ b/packages/login/src/i18n/utils.ts @@ -9,7 +9,7 @@ * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ import { storage } from '@login/storage' -import { useLocation } from 'react-router' +import { useLocation } from 'react-router-dom' export function getAvailableLanguages() { return window.config.LANGUAGES.split(',') diff --git a/packages/login/src/tests/util.tsx b/packages/login/src/tests/util.tsx index 9bebe365963..30ba3c0787f 100644 --- a/packages/login/src/tests/util.tsx +++ b/packages/login/src/tests/util.tsx @@ -12,7 +12,7 @@ import * as React from 'react' import { mount, configure } from 'enzyme' import { Provider } from 'react-redux' import Adapter from '@wojtekmaj/enzyme-adapter-react-17' -import { Router } from 'react-router' +import { Router } from 'react-router-dom' import { ThemeProvider } from 'styled-components' import { getTheme } from '@opencrvs/components/lib/theme' diff --git a/packages/login/src/views/ResetCredentialsForm/AuthDetailsVerificationForm.tsx b/packages/login/src/views/ResetCredentialsForm/AuthDetailsVerificationForm.tsx index 1286f138f5d..6b60b57c767 100644 --- a/packages/login/src/views/ResetCredentialsForm/AuthDetailsVerificationForm.tsx +++ b/packages/login/src/views/ResetCredentialsForm/AuthDetailsVerificationForm.tsx @@ -24,10 +24,10 @@ import { AppBar } from '@opencrvs/components/lib/AppBar' import { Button } from '@opencrvs/components/lib/Button' import { Icon } from '@opencrvs/components/lib/Icon' -import * as React from 'react' +import React, { useState } from 'react' import { injectIntl, WrappedComponentProps } from 'react-intl' import { connect } from 'react-redux' -import { RouteComponentProps, withRouter } from 'react-router' +import { RouteComponentProps, withRouter } from 'react-router-dom' import styled from 'styled-components' import { messages } from '@login/i18n/messages/views/resetCredentialsForm' import { convertToMSISDN } from '@login/utils/dataCleanse' @@ -58,245 +58,223 @@ type Props = BaseProps & RouteComponentProps<{}, {}, { forgottenItem: FORGOTTEN_ITEMS }> & WrappedComponentProps -class AuthDetailsVerificationComponent extends React.Component { - constructor(props: Props) { - super(props) - this.state = { - phone: '', - email: '', - touched: false, - error: false, - errorMessage: '', - notificationMethod: window.config.USER_NOTIFICATION_DELIVERY_METHOD - } - } +const AuthDetailsVerificationComponent = ({ + intl, + goToForgottenItemForm, + goToRecoveryCodeEntryForm, + goToSecurityQuestionForm, + location +}: Props) => { + const [phone, setPhone] = useState('') + const [email, setEmail] = useState('') + const [touched, setTouched] = useState(false) + const [error, setError] = useState(false) + const [errorMessage, setErrorMessage] = useState('') + const [notificationMethod] = useState( + window.config.USER_NOTIFICATION_DELIVERY_METHOD + ) - handleMobileChange = (value: string) => { - this.setState({ - error: phoneNumberFormat(value) ? true : false, - phone: value, - touched: true - }) + const handleMobileChange = (value: string) => { + setPhone(value) + setTouched(true) + setError(phoneNumberFormat(value) ? true : false) } - handleEmailChange = (value: string) => { - this.setState({ - error: emailAddressFormat(value) ? true : false, - email: value, - touched: true - }) + const handleEmailChange = (value: string) => { + setEmail(value) + setTouched(true) + setError(emailAddressFormat(value) ? true : false) } - handleContinue = async (event: React.FormEvent) => { + const handleContinue = async (event: React.FormEvent) => { event.preventDefault() - if ( - this.state.notificationMethod === 'sms' && - (!this.state.phone || this.state.error) - ) { - this.setState((prevState) => ({ - touched: true, - error: true, - errorMessage: !prevState.phone - ? this.props.intl.formatMessage(validationMessages.phoneNumberFormat) - : this.props.intl.formatMessage(messages.errorPhoneNumberNotFound) - })) + + if (notificationMethod === 'sms' && (!phone || error)) { + setError(true) + setTouched(true) + setErrorMessage( + !phone + ? intl.formatMessage(validationMessages.phoneNumberFormat) + : intl.formatMessage(messages.errorPhoneNumberNotFound) + ) return - } else if ( - this.state.notificationMethod === 'email' && - (!this.state.email || this.state.error) - ) { - this.setState((prevState) => ({ - touched: true, - error: true, - errorMessage: !prevState.email - ? this.props.intl.formatMessage(validationMessages.emailAddressFormat) - : this.props.intl.formatMessage(messages.errorEmailAddressNotFound) - })) + } + if (notificationMethod === 'email' && (!email || error)) { + setError(true) + setTouched(true) + setErrorMessage( + !email + ? intl.formatMessage(validationMessages.emailAddressFormat) + : intl.formatMessage(messages.errorEmailAddressNotFound) + ) return } + try { const { nonce, securityQuestionKey } = await authApi.verifyUser({ mobile: - this.state.notificationMethod === 'sms' - ? convertToMSISDN(this.state.phone, window.config.COUNTRY) - : undefined, - email: - this.state.notificationMethod === 'email' - ? this.state.email + notificationMethod === 'sms' + ? convertToMSISDN(phone, window.config.COUNTRY) : undefined, - retrieveFlow: this.props.location.state.forgottenItem + email: notificationMethod === 'email' ? email : undefined, + retrieveFlow: location.state.forgottenItem }) if (securityQuestionKey) { - this.props.goToSecurityQuestionForm( + return goToSecurityQuestionForm( nonce, securityQuestionKey, - this.props.location.state.forgottenItem - ) - } else { - this.props.goToRecoveryCodeEntryForm( - nonce, - this.props.location.state.forgottenItem, - this.state.phone, - this.state.email + location.state.forgottenItem ) } + + goToRecoveryCodeEntryForm( + nonce, + location.state.forgottenItem, + phone, + email + ) } catch (err) { - this.setState({ - error: true, - errorMessage: this.props.intl.formatMessage( - this.state.notificationMethod === 'sms' + setError(true) + setErrorMessage( + intl.formatMessage( + notificationMethod === 'sms' ? messages.errorPhoneNumberNotFound : messages.errorEmailAddressNotFound ) - }) + ) } } - render() { - const { - error: responseError, - errorMessage, - notificationMethod - } = this.state - const { intl, goToForgottenItemForm } = this.props - const validationError = - this.state.error && - (notificationMethod === 'sms' - ? phoneNumberFormat(this.state.phone) - : emailAddressFormat(this.state.email)) - return ( - <> - - - + const validationError = + error && + (notificationMethod === 'sms' + ? phoneNumberFormat(phone) + : emailAddressFormat(email)) + return ( + <> + + + + } + mobileLeft={ + + } + mobileTitle={intl.formatMessage( + messages.credentialsResetFormTitle, + { + forgottenItem: location.state.forgottenItem } - mobileLeft={ - + )} + desktopTitle={intl.formatMessage( + messages.credentialsResetFormTitle, + { + forgottenItem: location.state.forgottenItem } - mobileTitle={intl.formatMessage( - messages.credentialsResetFormTitle, - { - forgottenItem: this.props.location.state.forgottenItem + )} + /> + } + skipToContentText={intl.formatMessage( + constantsMessages.skipToMainContent + )} + > +
+ + {intl.formatMessage(messages.continueButtonLabel)} + + ]} + showTitleOnMobile + > + + - } - skipToContentText={intl.formatMessage( - constantsMessages.skipToMainContent - )} - > - - - {intl.formatMessage(messages.continueButtonLabel)} - - ]} - showTitleOnMobile - > - - - {notificationMethod === 'sms' && ( - this.handleMobileChange(e.target.value)} - touched={this.state.touched} - error={responseError} - /> - )} - {notificationMethod === 'email' && ( - this.handleEmailChange(e.target.value)} - touched={this.state.touched} - error={responseError} - /> - )} - - - - - - - ) - } + hideAsterisk={true} + > + {notificationMethod === 'sms' && ( + handleMobileChange(e.target.value)} + touched={touched} + error={error} + /> + )} + {notificationMethod === 'email' && ( + handleEmailChange(e.target.value)} + touched={touched} + error={error} + /> + )} + + + + + + + ) } export const AuthDetailsVerification = connect(null, { diff --git a/packages/login/src/views/ResetCredentialsForm/RecoveryCodeEntryForm.tsx b/packages/login/src/views/ResetCredentialsForm/RecoveryCodeEntryForm.tsx index bfc60c8e069..b1166e25e3b 100644 --- a/packages/login/src/views/ResetCredentialsForm/RecoveryCodeEntryForm.tsx +++ b/packages/login/src/views/ResetCredentialsForm/RecoveryCodeEntryForm.tsx @@ -19,7 +19,7 @@ import { TextInput } from '@opencrvs/components/lib/TextInput' import * as React from 'react' import { injectIntl, WrappedComponentProps } from 'react-intl' import { connect } from 'react-redux' -import { RouteComponentProps, withRouter } from 'react-router' +import { RouteComponentProps, withRouter } from 'react-router-dom' import styled from 'styled-components' import { Frame } from '@opencrvs/components/lib/Frame' import { Content, ContentSize } from '@opencrvs/components/lib/Content' diff --git a/packages/login/src/views/ResetCredentialsForm/ResetCredentialsSuccessPage.tsx b/packages/login/src/views/ResetCredentialsForm/ResetCredentialsSuccessPage.tsx index 918ea1f1fad..49670147dda 100644 --- a/packages/login/src/views/ResetCredentialsForm/ResetCredentialsSuccessPage.tsx +++ b/packages/login/src/views/ResetCredentialsForm/ResetCredentialsSuccessPage.tsx @@ -23,7 +23,7 @@ import { injectIntl, WrappedComponentProps as IntlShapeProps } from 'react-intl' import { connect } from 'react-redux' import styled from 'styled-components' import { messages } from '@login/i18n/messages/views/resetCredentialsForm' -import { RouteComponentProps } from 'react-router' +import { RouteComponentProps } from 'react-router-dom' import { selectCountryLogo } from '@login/login/selectors' import { IStoreState } from '@login/store' import { constantsMessages } from '@login/i18n/messages/constants' diff --git a/packages/login/src/views/ResetCredentialsForm/SecurityQuestionForm.tsx b/packages/login/src/views/ResetCredentialsForm/SecurityQuestionForm.tsx index 003dcd430fd..8eb87d65ccb 100644 --- a/packages/login/src/views/ResetCredentialsForm/SecurityQuestionForm.tsx +++ b/packages/login/src/views/ResetCredentialsForm/SecurityQuestionForm.tsx @@ -24,7 +24,7 @@ import { TextInput } from '@opencrvs/components/lib/TextInput' import React, { useState } from 'react' import { injectIntl, WrappedComponentProps } from 'react-intl' import { connect } from 'react-redux' -import { RouteComponentProps, withRouter } from 'react-router' +import { RouteComponentProps, withRouter } from 'react-router-dom' import styled from 'styled-components' import { messages as sharedMessages } from '@login/i18n/messages/views/resetCredentialsForm' import { Frame } from '@opencrvs/components/lib/Frame' diff --git a/packages/login/src/views/ResetCredentialsForm/UpdatePasswordForm.tsx b/packages/login/src/views/ResetCredentialsForm/UpdatePasswordForm.tsx index b600fce4812..88e1cb50014 100644 --- a/packages/login/src/views/ResetCredentialsForm/UpdatePasswordForm.tsx +++ b/packages/login/src/views/ResetCredentialsForm/UpdatePasswordForm.tsx @@ -26,7 +26,7 @@ import { Icon } from '@opencrvs/components/lib/Icon' import * as React from 'react' import { injectIntl, WrappedComponentProps as IntlShapeProps } from 'react-intl' import { connect } from 'react-redux' -import { RouteComponentProps, withRouter } from 'react-router' +import { RouteComponentProps, withRouter } from 'react-router-dom' import styled from 'styled-components' import { messages } from '@login/i18n/messages/views/resetCredentialsForm' import { constantsMessages } from '@login/i18n/messages/constants' diff --git a/packages/login/src/views/StepOne/StepOneContainer.tsx b/packages/login/src/views/StepOne/StepOneContainer.tsx index 782325e314c..955d5f09ba4 100644 --- a/packages/login/src/views/StepOne/StepOneContainer.tsx +++ b/packages/login/src/views/StepOne/StepOneContainer.tsx @@ -32,18 +32,15 @@ import { } from '@login/utils/authUtils' import { IAuthenticationData } from '@login/utils/authApi' import * as actions from '@login/login/actions' -import { - goToForgottenItemForm, - resetSubmissionError -} from '@login/login/actions' +import { resetSubmissionError } from '@login/login/actions' import { Button } from '@opencrvs/components/lib/Button' import { Toast } from '@opencrvs/components/lib/Toast/Toast' import { usePersistentCountryLogo } from '@login/common/LoginBackgroundWrapper' import { Container, FormWrapper, LogoContainer } from '@login/views/Common' -import { LanguageSelect } from '@login/i18n/components/LanguageSelect' import { Text } from '@opencrvs/components/lib/Text/Text' -import { Link } from '@opencrvs/components/lib/Link/Link' import { Stack } from '@opencrvs/components/lib/Stack/Stack' +import { Link } from 'react-router-dom' +import { FORGOTTEN_ITEM } from '@login/navigation/routes' const userNameField = stepOneFields.username const passwordField = stepOneFields.password @@ -148,14 +145,19 @@ export function StepOneContainer() { > {intl.formatMessage(messages.submit)} - + + )} diff --git a/packages/toolkit/.gitignore b/packages/toolkit/.gitignore index 5f8bbc7003a..4c0e589ce22 100644 --- a/packages/toolkit/.gitignore +++ b/packages/toolkit/.gitignore @@ -8,6 +8,7 @@ # production /lib +/dist # misc .DS_Store @@ -19,3 +20,5 @@ npm-debug.log* yarn-debug.log* yarn-error.log* + +tsconfig.tsbuildinfo diff --git a/packages/toolkit/.npmignore b/packages/toolkit/.npmignore index ebf4281dc07..cbdb9611d16 100644 --- a/packages/toolkit/.npmignore +++ b/packages/toolkit/.npmignore @@ -1 +1 @@ -!lib +!dist diff --git a/packages/toolkit/README.md b/packages/toolkit/README.md index dea66ff47fc..5f281d9bb81 100644 --- a/packages/toolkit/README.md +++ b/packages/toolkit/README.md @@ -1,3 +1,42 @@ # OpenCRVS toolkit OpenCRVS toolkit for building country configurations. + +## File structure + +``` +src/ + events/ # re-exports events module from commons package + lib.ts # standard library. currently empty +``` + +## Getting started + +### Development using yarn link + +``` +# tsconfig.json references commons. when `tsc --build` is run, both are built. +> yarn build +# If you miss this part you might face issues with types. +> cd dist +> yarn link +``` + +### Releasing and buiding + +#### While developing + +1. Update version number in `package.json` +2. `yarn build` +3. `npm publish` + +#### Through version control + +1. Update version number in `package.json` +2. Create a pull request +3. Once merged, github action will get triggered. + +#### Gotchas, good to know + +- Package is published and should be used without knowledge of rest of the monorepo +- Package exposes `/events` directory, with types, from `packages/commons` through the library, others are excluded. diff --git a/packages/toolkit/package.json b/packages/toolkit/package.json index 40e1c9f53ac..bef4ed517b6 100644 --- a/packages/toolkit/package.json +++ b/packages/toolkit/package.json @@ -1,15 +1,23 @@ { "name": "@opencrvs/toolkit", - "version": "0.0.2", + "version": "0.0.4", "description": "OpenCRVS toolkit for building country configurations", "license": "MPL-2.0", - "main": "./lib/index.js", - "exports": "./lib/index.js", - "types": "./lib/index.d.ts", + "exports": { + "./lib": "./dist/lib.js", + "./events": "./dist/events/index.js" + }, "scripts": { - "build": "rimraf lib && tsc" + "build": "rimraf dist && tsc --build && yarn build-common-events:js && yarn copy-common-events:ts", + "build-common-events:js": "esbuild src/events/index.ts --bundle --outdir=./dist/events --allow-overwrite --packages=external", + "copy-common-events:ts": "cp -r ../commons/build/dist/common/events/*.d.ts ./dist/events" }, + "dependencies": {}, "devDependencies": { + "esbuild": "^0.24.0", "typescript": "^5.6.3" + }, + "moduleNameMapper": { + "@opencrvs/commons/events": "@opencrvs/commons/build/dist/common/events/index.js" } } diff --git a/packages/toolkit/src/events/index.ts b/packages/toolkit/src/events/index.ts new file mode 100644 index 00000000000..352feb9b88a --- /dev/null +++ b/packages/toolkit/src/events/index.ts @@ -0,0 +1,11 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ +export * from '@opencrvs/commons/events' diff --git a/packages/toolkit/src/index.ts b/packages/toolkit/src/lib.ts similarity index 99% rename from packages/toolkit/src/index.ts rename to packages/toolkit/src/lib.ts index 0656f35a37d..41c8cc6b190 100644 --- a/packages/toolkit/src/index.ts +++ b/packages/toolkit/src/lib.ts @@ -8,6 +8,7 @@ * * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ + export type Noop = () => void export const noop: Noop = () => {} diff --git a/packages/toolkit/tsconfig.json b/packages/toolkit/tsconfig.json index 5824fbe6643..1039f6fb78a 100644 --- a/packages/toolkit/tsconfig.json +++ b/packages/toolkit/tsconfig.json @@ -9,9 +9,13 @@ "skipLibCheck": true, "declaration": true, "declarationMap": true, - "rootDir": "src", - "outDir": "lib" + "rootDir": "./src", + "outDir": "dist", + "paths": { + "@opencrvs/commons/events": ["../commons/src/events/index.ts"] + } }, + "references": [{ "path": "../commons" }], "include": ["src/**/*.ts"], - "exclude": ["**/node_modules/**"] + "exclude": ["**/node_modules/**", "dist"] } diff --git a/yarn.lock b/yarn.lock index 5f8c16e9961..fbf6531740e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3449,6 +3449,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== +"@esbuild/aix-ppc64@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz#b57697945b50e99007b4c2521507dc613d4a648c" + integrity sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw== + "@esbuild/android-arm64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622" @@ -3459,6 +3464,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== +"@esbuild/android-arm64@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz#1add7e0af67acefd556e407f8497e81fddad79c0" + integrity sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w== + "@esbuild/android-arm@0.15.18": version "0.15.18" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.15.18.tgz#266d40b8fdcf87962df8af05b76219bc786b4f80" @@ -3474,6 +3484,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== +"@esbuild/android-arm@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.24.0.tgz#ab7263045fa8e090833a8e3c393b60d59a789810" + integrity sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew== + "@esbuild/android-x64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.20.tgz#35cf419c4cfc8babe8893d296cd990e9e9f756f2" @@ -3484,6 +3499,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== +"@esbuild/android-x64@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.24.0.tgz#e8f8b196cfdfdd5aeaebbdb0110983460440e705" + integrity sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ== + "@esbuild/darwin-arm64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz#08172cbeccf95fbc383399a7f39cfbddaeb0d7c1" @@ -3494,6 +3514,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== +"@esbuild/darwin-arm64@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz#2d0d9414f2acbffd2d86e98253914fca603a53dd" + integrity sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw== + "@esbuild/darwin-x64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz#d70d5790d8bf475556b67d0f8b7c5bdff053d85d" @@ -3504,6 +3529,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== +"@esbuild/darwin-x64@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz#33087aab31a1eb64c89daf3d2cf8ce1775656107" + integrity sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA== + "@esbuild/freebsd-arm64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz#98755cd12707f93f210e2494d6a4b51b96977f54" @@ -3514,6 +3544,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== +"@esbuild/freebsd-arm64@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz#bb76e5ea9e97fa3c753472f19421075d3a33e8a7" + integrity sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA== + "@esbuild/freebsd-x64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz#c1eb2bff03915f87c29cece4c1a7fa1f423b066e" @@ -3524,6 +3559,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== +"@esbuild/freebsd-x64@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz#e0e2ce9249fdf6ee29e5dc3d420c7007fa579b93" + integrity sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ== + "@esbuild/linux-arm64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz#bad4238bd8f4fc25b5a021280c770ab5fc3a02a0" @@ -3534,6 +3574,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== +"@esbuild/linux-arm64@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz#d1b2aa58085f73ecf45533c07c82d81235388e75" + integrity sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g== + "@esbuild/linux-arm@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz#3e617c61f33508a27150ee417543c8ab5acc73b0" @@ -3544,6 +3589,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== +"@esbuild/linux-arm@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz#8e4915df8ea3e12b690a057e77a47b1d5935ef6d" + integrity sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw== + "@esbuild/linux-ia32@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz#699391cccba9aee6019b7f9892eb99219f1570a7" @@ -3554,6 +3604,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== +"@esbuild/linux-ia32@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz#8200b1110666c39ab316572324b7af63d82013fb" + integrity sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA== + "@esbuild/linux-loong64@0.15.18": version "0.15.18" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz#128b76ecb9be48b60cf5cfc1c63a4f00691a3239" @@ -3569,6 +3624,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== +"@esbuild/linux-loong64@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz#6ff0c99cf647504df321d0640f0d32e557da745c" + integrity sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g== + "@esbuild/linux-mips64el@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz#eeff3a937de9c2310de30622a957ad1bd9183231" @@ -3579,6 +3639,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== +"@esbuild/linux-mips64el@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz#3f720ccd4d59bfeb4c2ce276a46b77ad380fa1f3" + integrity sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA== + "@esbuild/linux-ppc64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz#2f7156bde20b01527993e6881435ad79ba9599fb" @@ -3589,6 +3654,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== +"@esbuild/linux-ppc64@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz#9d6b188b15c25afd2e213474bf5f31e42e3aa09e" + integrity sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ== + "@esbuild/linux-riscv64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz#6628389f210123d8b4743045af8caa7d4ddfc7a6" @@ -3599,6 +3669,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== +"@esbuild/linux-riscv64@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz#f989fdc9752dfda286c9cd87c46248e4dfecbc25" + integrity sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw== + "@esbuild/linux-s390x@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz#255e81fb289b101026131858ab99fba63dcf0071" @@ -3609,6 +3684,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== +"@esbuild/linux-s390x@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz#29ebf87e4132ea659c1489fce63cd8509d1c7319" + integrity sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g== + "@esbuild/linux-x64@0.18.20": version "0.18.20" resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz" @@ -3619,6 +3699,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== +"@esbuild/linux-x64@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz#4af48c5c0479569b1f359ffbce22d15f261c0cef" + integrity sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA== + "@esbuild/netbsd-x64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz#30e8cd8a3dded63975e2df2438ca109601ebe0d1" @@ -3629,6 +3714,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== +"@esbuild/netbsd-x64@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz#1ae73d23cc044a0ebd4f198334416fb26c31366c" + integrity sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg== + +"@esbuild/openbsd-arm64@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz#5d904a4f5158c89859fd902c427f96d6a9e632e2" + integrity sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg== + "@esbuild/openbsd-x64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz#7812af31b205055874c8082ea9cf9ab0da6217ae" @@ -3639,6 +3734,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== +"@esbuild/openbsd-x64@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz#4c8aa88c49187c601bae2971e71c6dc5e0ad1cdf" + integrity sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q== + "@esbuild/sunos-x64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz#d5c275c3b4e73c9b0ecd38d1ca62c020f887ab9d" @@ -3649,6 +3749,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== +"@esbuild/sunos-x64@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz#8ddc35a0ea38575fa44eda30a5ee01ae2fa54dd4" + integrity sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA== + "@esbuild/win32-arm64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz#73bc7f5a9f8a77805f357fab97f290d0e4820ac9" @@ -3659,6 +3764,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== +"@esbuild/win32-arm64@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz#6e79c8543f282c4539db684a207ae0e174a9007b" + integrity sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA== + "@esbuild/win32-ia32@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz#ec93cbf0ef1085cc12e71e0d661d20569ff42102" @@ -3669,6 +3779,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== +"@esbuild/win32-ia32@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz#057af345da256b7192d18b676a02e95d0fa39103" + integrity sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw== + "@esbuild/win32-x64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d" @@ -3679,6 +3794,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== +"@esbuild/win32-x64@0.24.0": + version "0.24.0" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz#168ab1c7e1c318b922637fad8f339d48b01e1244" + integrity sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA== + "@eslint/eslintrc@^0.4.3": version "0.4.3" resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz" @@ -8810,7 +8930,16 @@ hoist-non-react-statics "^3.3.0" redux "^4.0.0" -"@types/react-router@^5.1.2": +"@types/react-router-dom@^5.1.2": + version "5.3.3" + resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.3.tgz#e9d6b4a66fcdbd651a5f106c2656a30088cc1e83" + integrity sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw== + dependencies: + "@types/history" "^4.7.11" + "@types/react" "*" + "@types/react-router" "*" + +"@types/react-router@*": version "5.1.20" resolved "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz" integrity sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q== @@ -13809,6 +13938,36 @@ esbuild@^0.21.3, esbuild@~0.21.5: "@esbuild/win32-ia32" "0.21.5" "@esbuild/win32-x64" "0.21.5" +esbuild@^0.24.0: + version "0.24.0" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.24.0.tgz#f2d470596885fcb2e91c21eb3da3b3c89c0b55e7" + integrity sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ== + optionalDependencies: + "@esbuild/aix-ppc64" "0.24.0" + "@esbuild/android-arm" "0.24.0" + "@esbuild/android-arm64" "0.24.0" + "@esbuild/android-x64" "0.24.0" + "@esbuild/darwin-arm64" "0.24.0" + "@esbuild/darwin-x64" "0.24.0" + "@esbuild/freebsd-arm64" "0.24.0" + "@esbuild/freebsd-x64" "0.24.0" + "@esbuild/linux-arm" "0.24.0" + "@esbuild/linux-arm64" "0.24.0" + "@esbuild/linux-ia32" "0.24.0" + "@esbuild/linux-loong64" "0.24.0" + "@esbuild/linux-mips64el" "0.24.0" + "@esbuild/linux-ppc64" "0.24.0" + "@esbuild/linux-riscv64" "0.24.0" + "@esbuild/linux-s390x" "0.24.0" + "@esbuild/linux-x64" "0.24.0" + "@esbuild/netbsd-x64" "0.24.0" + "@esbuild/openbsd-arm64" "0.24.0" + "@esbuild/openbsd-x64" "0.24.0" + "@esbuild/sunos-x64" "0.24.0" + "@esbuild/win32-arm64" "0.24.0" + "@esbuild/win32-ia32" "0.24.0" + "@esbuild/win32-x64" "0.24.0" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" @@ -21274,7 +21433,20 @@ react-remove-scroll@2.5.5: use-callback-ref "^1.3.0" use-sidecar "^1.1.2" -react-router@^5.1.2: +react-router-dom@^5.1.2: + version "5.3.4" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.3.4.tgz#2ed62ffd88cae6db134445f4a0c0ae8b91d2e5e6" + integrity sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ== + dependencies: + "@babel/runtime" "^7.12.13" + history "^4.9.0" + loose-envify "^1.3.1" + prop-types "^15.6.2" + react-router "5.3.4" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + +react-router@5.3.4: version "5.3.4" resolved "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz" integrity sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==