diff --git a/packages/tupaia-web-server/src/routes/EmailDashboardRoute.ts b/packages/tupaia-web-server/src/routes/EmailDashboardRoute.ts index fc90fc2217..e62296e9fd 100644 --- a/packages/tupaia-web-server/src/routes/EmailDashboardRoute.ts +++ b/packages/tupaia-web-server/src/routes/EmailDashboardRoute.ts @@ -41,12 +41,15 @@ export class EmailDashboardRoute extends Route { }, columns: ['id'], })) as Pick[]; + const projectEntity = (await this.req.ctx.services.entity.getEntity(projectCode, projectCode, { fields: ['name'], })) as Pick; + const entity = (await this.req.ctx.services.entity.getEntity(projectCode, entityCode, { fields: ['id', 'name', 'country_code'], })) as Pick; + const [dashboard] = (await this.req.ctx.services.central.fetchResources('dashboards', { filter: { code: dashboardCode }, columns: ['id', 'name'], @@ -102,7 +105,7 @@ export class EmailDashboardRoute extends Route { const buffer = await downloadDashboardAsPdf( projectCode, entityCode, - dashboard.name, + dashboardCode, baseUrl, cookie, cookieDomain, diff --git a/packages/tupaia-web-server/src/routes/export/ExportDashboardRoute.ts b/packages/tupaia-web-server/src/routes/export/ExportDashboardRoute.ts index 4d3279e897..2321d02534 100644 --- a/packages/tupaia-web-server/src/routes/export/ExportDashboardRoute.ts +++ b/packages/tupaia-web-server/src/routes/export/ExportDashboardRoute.ts @@ -30,8 +30,8 @@ export class ExportDashboardRoute extends Route { const [dashboard] = (await this.req.ctx.services.central.fetchResources('dashboards', { filter: { code: dashboardCode }, - columns: ['name'], - })) as Pick[]; + columns: ['code'], + })) as Pick[]; if (!dashboard) { throw new Error(`Cannot find dashboard with code: ${dashboardCode}`); @@ -40,7 +40,7 @@ export class ExportDashboardRoute extends Route { const buffer = await downloadDashboardAsPdf( projectCode, entityCode, - dashboard.name, + dashboardCode, baseUrl, cookie, cookieDomain, diff --git a/packages/tupaia-web-server/src/utils/downloadDashboardAsPdf.ts b/packages/tupaia-web-server/src/utils/downloadDashboardAsPdf.ts index 65f47c86e1..02116f4a0b 100644 --- a/packages/tupaia-web-server/src/utils/downloadDashboardAsPdf.ts +++ b/packages/tupaia-web-server/src/utils/downloadDashboardAsPdf.ts @@ -10,7 +10,7 @@ import { stringifyQuery } from '@tupaia/utils'; export const downloadDashboardAsPdf = ( projectCode: string, entityCode: string, - dashboardName: string, + dashboardCode: string, baseUrl: TupaiaWebExportDashboardRequest.ReqBody['baseUrl'], cookie: string, cookieDomain: TupaiaWebExportDashboardRequest.ReqBody['cookieDomain'], @@ -21,7 +21,7 @@ export const downloadDashboardAsPdf = ( separatePagePerItem: true, }, ) => { - const endpoint = `${projectCode}/${entityCode}/${dashboardName}/dashboard-pdf-export`; + const endpoint = `${projectCode}/${entityCode}/${dashboardCode}/dashboard-pdf-export`; const pdfPageUrl = stringifyQuery(baseUrl, endpoint, { selectedDashboardItems: selectedDashboardItems?.join(','), settings: JSON.stringify(settings), diff --git a/packages/tupaia-web/src/Routes.tsx b/packages/tupaia-web/src/Routes.tsx index 9c0c1a8375..cc6af5a4da 100644 --- a/packages/tupaia-web/src/Routes.tsx +++ b/packages/tupaia-web/src/Routes.tsx @@ -3,7 +3,7 @@ * Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd */ import React from 'react'; -import { Navigate, Route, Routes as RouterRoutes, useLocation } from 'react-router-dom'; +import { Navigate, Route, Routes as RouterRoutes, useLocation, useParams } from 'react-router-dom'; import { DashboardPDFExport, LandingPage, @@ -13,10 +13,10 @@ import { } from './views'; import { Dashboard } from './features'; import { MODAL_ROUTES, DEFAULT_URL, ROUTE_STRUCTURE, MAP_OVERLAY_EXPORT_ROUTE } from './constants'; -import { useUser } from './api/queries'; +import { useDashboards, useProject, useUser } from './api/queries'; import { MainLayout } from './layout'; import { LoadingScreen } from './components'; -import { gaEvent, useEntityLink } from './utils'; +import { gaEvent, getDefaultDashboard, useEntityLink } from './utils'; const HomeRedirect = () => { const { isLoggedIn, data } = useUser(); @@ -57,6 +57,48 @@ const UserPageRedirect = ({ modal }: { modal: MODAL_ROUTES }) => { ); }; +const DashboardRoute = () => { + const { dashboardCode, projectCode, entityCode } = useParams(); + const location = useLocation(); + + // if the code is not a valid code but is a valid name, redirect to the correct code + const { + data: dashboards = [], + isLoading: isLoadingDashboards, + isError, + } = useDashboards(projectCode, entityCode); + + const { data: project, isLoading: isLoadingProject } = useProject(projectCode); + + // if the project or dashboards are still loading, show the dashboard as this will handle loading state + if (isLoadingDashboards || isLoadingProject) { + return ; + } + + const uriDecodedDashboardCode = decodeURIComponent(dashboardCode!); + + // if the dashboard code is valid, render the dashboard with the code + if (dashboards.find(d => d.code === uriDecodedDashboardCode)) { + return ; + } + + const dashboardByName = dashboards.find(d => d.name === uriDecodedDashboardCode); + + // if the dashboard name is valid, redirect to the correct code + if (dashboardByName) { + const to = { + ...location, + pathname: `/${projectCode}/${entityCode}/${dashboardByName.code}`, + }; + return ; + } + + const defaultDashboard = getDefaultDashboard(project, dashboards, false, isError); + + // if the dashboard name is not valid, redirect to the default dashboard + return ; +}; + /** * This Router is using [version 6.3]{@link https://reactrouter.com/en/v6.3.0}, as later versions are not supported by our TS setup. See [this issue here]{@link https://github.com/remix-run/react-router/discussions/8364} * This means the newer 'createBrowserRouter' and 'RouterProvider' can't be used here. @@ -75,7 +117,7 @@ export const Routes = () => { return ( } /> } /> @@ -97,7 +139,7 @@ export const Routes = () => { } /> {/* The Dashboard has to be rendered below the Map, otherwise the map will re-mount on route changes */} - } /> + } /> diff --git a/packages/tupaia-web/src/api/mutations/useExportDashboard.tsx b/packages/tupaia-web/src/api/mutations/useExportDashboard.tsx index 27f1036bca..dc29c2b31b 100644 --- a/packages/tupaia-web/src/api/mutations/useExportDashboard.tsx +++ b/packages/tupaia-web/src/api/mutations/useExportDashboard.tsx @@ -5,13 +5,13 @@ import { useMutation } from '@tanstack/react-query'; import { TupaiaWebExportDashboardRequest } from '@tupaia/types'; import { API_URL, post } from '../api'; -import { DashboardName, EntityCode, ProjectCode } from '../../types'; +import { Dashboard, EntityCode, ProjectCode } from '../../types'; import { downloadPDF } from '../../utils'; type ExportDashboardBody = { projectCode?: ProjectCode; entityCode?: EntityCode; - dashboardCode?: DashboardName; + dashboardCode?: Dashboard['code']; selectedDashboardItems?: TupaiaWebExportDashboardRequest.ReqBody['selectedDashboardItems']; settings?: TupaiaWebExportDashboardRequest.ReqBody['settings']; }; diff --git a/packages/tupaia-web/src/constants/url.ts b/packages/tupaia-web/src/constants/url.ts index e2e36f78ca..50f1aafc3c 100644 --- a/packages/tupaia-web/src/constants/url.ts +++ b/packages/tupaia-web/src/constants/url.ts @@ -26,7 +26,7 @@ export enum MODAL_ROUTES { } export const DEFAULT_PROJECT_ENTITY = '/explore/explore'; -export const DEFAULT_URL = `${DEFAULT_PROJECT_ENTITY}/General`; +export const DEFAULT_URL = `${DEFAULT_PROJECT_ENTITY}/explore_General`; export enum TABS { MAP = 'map', @@ -36,6 +36,6 @@ export const DEFAULT_PERIOD_PARAM_STRING = 'DEFAULT_PERIOD'; export const DEFAULT_MAP_OVERLAY_ID = '126'; // 'Operational Facilities' -export const ROUTE_STRUCTURE = '/:projectCode/:entityCode/:dashboardName'; +export const ROUTE_STRUCTURE = '/:projectCode/:entityCode/:dashboardCode'; export const MAP_OVERLAY_EXPORT_ROUTE = '/:projectCode/:entityCode/map-overlay-pdf-export'; diff --git a/packages/tupaia-web/src/features/Dashboard/Dashboard.tsx b/packages/tupaia-web/src/features/Dashboard/Dashboard.tsx index 09bf480255..58d5055606 100644 --- a/packages/tupaia-web/src/features/Dashboard/Dashboard.tsx +++ b/packages/tupaia-web/src/features/Dashboard/Dashboard.tsx @@ -3,9 +3,9 @@ * Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd */ -import React, { useEffect, useState } from 'react'; +import React, { useState, useEffect } from 'react'; import styled from 'styled-components'; -import { useLocation, useNavigate, useParams } from 'react-router-dom'; +import { useParams } from 'react-router-dom'; import { Typography } from '@material-ui/core'; import { DEFAULT_BOUNDS } from '@tupaia/ui-map-components'; import { ErrorBoundary, SpinningLoader } from '@tupaia/ui-components'; @@ -13,7 +13,7 @@ import { MatrixConfig } from '@tupaia/types'; import { MOBILE_BREAKPOINT } from '../../constants'; import { useDashboards, useEntity, useProject, useUser } from '../../api/queries'; import { DashboardItem as DashboardItemType } from '../../types'; -import { gaEvent, getDefaultDashboard } from '../../utils'; +import { gaEvent } from '../../utils'; import { DashboardItem } from '../DashboardItem'; import { EnlargedDashboardItem } from '../EnlargedDashboardItem'; import { ExpandButton } from './ExpandButton'; @@ -109,10 +109,8 @@ const DashboardItemsWrapper = styled.div<{ `; export const Dashboard = () => { - const navigate = useNavigate(); - const location = useLocation(); const { projectCode, entityCode } = useParams(); - const { data: project, isLoading: isLoadingProject } = useProject(projectCode); + const { data: project } = useProject(projectCode); const { data: user } = useUser(); const { mutate: updateUser } = useEditUser(); @@ -123,48 +121,17 @@ export const Dashboard = () => { }, [project?.code, user?.project?.code]); const { activeDashboard } = useDashboard(); - const { - data: dashboards, - isLoading: isLoadingDashboards, - isError, - isFetched, - } = useDashboards(projectCode, entityCode); + const { isLoading: isLoadingDashboards } = useDashboards(projectCode, entityCode); const [isExpanded, setIsExpanded] = useState(false); const { data: entity } = useEntity(projectCode, entityCode); const bounds = entity?.bounds || DEFAULT_BOUNDS; - // we don't want useEntityLink to take care of this because useEntityLink gets called for all child entities on the map, meaning lots of extra queries when we don't need them. Instead the redirect will be taken care of in the useEffect below, as needed - const defaultDashboardName = getDefaultDashboard( - project, - dashboards, - isLoadingDashboards, - isError, - ); - const toggleExpanded = () => { setIsExpanded(!isExpanded); gaEvent('Pages', 'Toggle Info Panel'); }; - // check for valid dashboard name, and if not valid and not still loading, redirect to default dashboard - const dashboardNotFound = - isFetched && - !isError && - !isLoadingDashboards && - !isLoadingProject && - project?.code === projectCode && - !activeDashboard; - - useEffect(() => { - if (dashboardNotFound) { - navigate({ - ...location, - pathname: `/${projectCode}/${entityCode}/${defaultDashboardName}`, - }); - } - }, [dashboardNotFound, defaultDashboardName]); - // Filter out drill down items from the dashboard items const visibleDashboards = (activeDashboard?.items as DashboardItemType[])?.reduce( diff --git a/packages/tupaia-web/src/features/Dashboard/DashboardMenu/DashboardMenu.tsx b/packages/tupaia-web/src/features/Dashboard/DashboardMenu/DashboardMenu.tsx index 38be89ca0b..016cab8828 100644 --- a/packages/tupaia-web/src/features/Dashboard/DashboardMenu/DashboardMenu.tsx +++ b/packages/tupaia-web/src/features/Dashboard/DashboardMenu/DashboardMenu.tsx @@ -56,16 +56,17 @@ const StyledPaper = styled(Paper)` interface DashboardMenuItemProps { dashboardName: Dashboard['name']; onClose: () => void; + code: Dashboard['code']; } -const DashboardMenuItem = ({ dashboardName, onClose }: DashboardMenuItemProps) => { +const DashboardMenuItem = ({ dashboardName, code, onClose }: DashboardMenuItemProps) => { const location = useLocation(); - const { projectCode, entityCode, dashboardName: selectedDashboardName } = useParams(); + const { projectCode, entityCode, dashboardCode: selectedDashboardCode } = useParams(); - const encodedDashboardName = encodeURIComponent(dashboardName); + const encodedDashboardCode = encodeURIComponent(code); const link = { ...location, - pathname: `/${projectCode}/${entityCode}/${encodedDashboardName}`, + pathname: `/${projectCode}/${entityCode}/${encodedDashboardCode}`, }; return ( @@ -73,7 +74,7 @@ const DashboardMenuItem = ({ dashboardName, onClose }: DashboardMenuItemProps) = to={link} onClick={onClose} component={Link} - selected={dashboardName === selectedDashboardName} + selected={code === selectedDashboardCode} > {dashboardName} @@ -116,7 +117,7 @@ export const DashboardMenu = () => { PaperProps={{ component: StyledPaper }} > {dashboards.map(({ name, code }) => ( - + ))} diff --git a/packages/tupaia-web/src/features/Dashboard/ExportDashboard/ExportConfig.tsx b/packages/tupaia-web/src/features/Dashboard/ExportDashboard/ExportConfig.tsx index 95edf412f4..c16a152e25 100644 --- a/packages/tupaia-web/src/features/Dashboard/ExportDashboard/ExportConfig.tsx +++ b/packages/tupaia-web/src/features/Dashboard/ExportDashboard/ExportConfig.tsx @@ -120,13 +120,13 @@ interface ExportDashboardProps { } export const ExportConfig = ({ onClose, selectedDashboardItems }: ExportDashboardProps) => { - const { projectCode, entityCode, dashboardName } = useParams(); + const { projectCode, entityCode, dashboardCode } = useParams(); const { data: project } = useProject(projectCode); const { data: entity } = useEntity(projectCode, entityCode); const { activeDashboard } = useDashboard(); const { exportWithLabels, exportWithTable, separatePagePerItem } = useExportSettings(); - const exportFileName = `${project?.name}-${entity?.name}-${dashboardName}-dashboard-export`; + const exportFileName = `${project?.name}-${entity?.name}-${dashboardCode}-dashboard-export`; const { mutate: requestPdfExport, error, isLoading, reset } = useExportDashboard(exportFileName); diff --git a/packages/tupaia-web/src/features/Dashboard/utils/useDashboard.ts b/packages/tupaia-web/src/features/Dashboard/utils/useDashboard.ts index a838de6ef1..64f30b8c9e 100644 --- a/packages/tupaia-web/src/features/Dashboard/utils/useDashboard.ts +++ b/packages/tupaia-web/src/features/Dashboard/utils/useDashboard.ts @@ -8,7 +8,7 @@ import { DashboardContext } from './DashboardContext'; // Contains the dashboards, active dashboard, and export and subscribe state export const useDashboard = () => { - const { dashboardName, entityCode, projectCode } = useParams(); + const { dashboardCode, entityCode, projectCode } = useParams(); const { data: dashboards = [] } = useDashboards(projectCode, entityCode); const { exportModalOpen, subscribeModalOpen, setExportModalOpen, setSubscribeModalOpen } = useContext(DashboardContext); @@ -22,7 +22,7 @@ export const useDashboard = () => { }; // trim dashboard name to avoid issues with trailing or leading spaces const activeDashboard = - dashboards?.find(dashboard => dashboard.name.trim() === dashboardName?.trim()) ?? undefined; + dashboards?.find(dashboard => dashboard.code.trim() === dashboardCode?.trim()) ?? undefined; useGAEffect('Dashboard', 'Change Tab', activeDashboard?.name); diff --git a/packages/tupaia-web/src/features/Map/utils/useNavigateToEntity.ts b/packages/tupaia-web/src/features/Map/utils/useNavigateToEntity.ts index dfcccf498a..27fceb13fb 100644 --- a/packages/tupaia-web/src/features/Map/utils/useNavigateToEntity.ts +++ b/packages/tupaia-web/src/features/Map/utils/useNavigateToEntity.ts @@ -16,7 +16,7 @@ export const useNavigateToEntity = () => { const navigate = useNavigate(); const { data: project } = useProject(projectCode); - const dashboardNameParam = project?.dashboardGroupName + const projectDashboardName = project?.dashboardGroupName ? encodeURIComponent(project.dashboardGroupName) : ''; @@ -29,7 +29,7 @@ export const useNavigateToEntity = () => { const link = { ...location, search: urlSearchParams.toString(), - pathname: `/${projectCode}/${entityCode}/${dashboardNameParam}`, + pathname: `/${projectCode}/${entityCode}/${projectDashboardName}`, }; navigate(link); }; diff --git a/packages/tupaia-web/src/utils/getDefaultDashboard.ts b/packages/tupaia-web/src/utils/getDefaultDashboard.ts index 5d647900c8..e0fdf5b043 100644 --- a/packages/tupaia-web/src/utils/getDefaultDashboard.ts +++ b/packages/tupaia-web/src/utils/getDefaultDashboard.ts @@ -13,19 +13,14 @@ export const getDefaultDashboard = ( ) => { // when loading, return '' so that the user doesn't get redirected more than once, e.g. if there is a dashboardGroupName but it ends up not being valid after loading if (isLoadingDashboards || (!dashboards && !hasDashboardError)) return ''; - let defaultDashboardName = project?.dashboardGroupName || ''; + const defaultDashboardName = project?.dashboardGroupName || ''; - if ( - !defaultDashboardName || - (dashboards && - dashboards?.length > 0 && - !dashboards?.find( - (dashboard: Dashboard) => - dashboard.name.trim() === decodeURIComponent(defaultDashboardName), - )) - ) { - defaultDashboardName = dashboards?.[0]?.name || ''; - } + const dashboard = + dashboards?.find( + (dashboard: Dashboard) => dashboard.name.trim() === decodeURIComponent(defaultDashboardName), + ) || + dashboards?.[0] || + null; - return encodeURIComponent(defaultDashboardName); + return encodeURIComponent(dashboard?.code || ''); }; diff --git a/packages/tupaia-web/src/views/RequestProjectAccessModal/RequestProjectAccessModal.tsx b/packages/tupaia-web/src/views/RequestProjectAccessModal/RequestProjectAccessModal.tsx new file mode 100644 index 0000000000..b115b0e198 --- /dev/null +++ b/packages/tupaia-web/src/views/RequestProjectAccessModal/RequestProjectAccessModal.tsx @@ -0,0 +1,218 @@ +/* + * Tupaia + * Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd + */ +import React, { useEffect, useState } from 'react'; +import { + Location, + generatePath, + useLocation, + useNavigate, + useParams, + useSearchParams, +} from 'react-router-dom'; +import styled from 'styled-components'; +import { Alert, SpinningLoader } from '@tupaia/ui-components'; +import { CountryAccessListItem } from '../../types'; +import { DEFAULT_URL, MODAL_ROUTES, ROUTE_STRUCTURE, URL_SEARCH_PARAMS } from '../../constants'; +import { LoadingScreen, Modal } from '../../components'; +import { gaEvent, getDefaultDashboard, removeUrlSearchParams } from '../../utils'; +import { + useProjectCountryAccessList, + useLandingPage, + useProject, + useUser, + useEntity, + useDashboards, +} from '../../api/queries'; +import { ModalHeader } from './ModalHeader'; +import { ProjectHero } from './ProjectHero'; +import { ProjectDetails } from './ProjectDetails'; +import { ProjectAccessForm } from './ProjectAccessForm'; +import { RequestedCountries } from './RequestedCountries'; + +const ModalBody = styled.div` + display: flex; + flex-direction: column; + padding: 0.2rem 0 0; + width: 30rem; + max-width: 100%; + + .MuiAlert-root + .MuiAlert-root { + margin-block-start: 1rem; + } +`; + +export const RequestProjectAccessModal = () => { + const [urlSearchParams] = useSearchParams(); + const params = useParams(); + const [requestAdditionalCountries, setRequestAdditionalCountries] = useState(false); + const { hash, ...location } = useLocation(); + const navigate = useNavigate(); + const { isLandingPage } = useLandingPage(); + const { isLoggedIn, isLoading: isLoadingUser, isFetching } = useUser(); + + // Get the project code from the URL search params, or from the URL params if not otherwise set + const altProjectCode = urlSearchParams.get(URL_SEARCH_PARAMS.PROJECT); + const requestingProjectCode = altProjectCode || params?.projectCode; + + const { data: requestingProject, isLoading } = useProject(requestingProjectCode!); + + const { + data: dashboards = [], + isLoading: isLoadingDashboards, + isError: isDashboardError, + } = useDashboards(requestingProjectCode!, requestingProject?.homeEntityCode); + + const defaultDashboard = getDefaultDashboard( + requestingProject, + dashboards, + isLoadingDashboards, + isDashboardError, + ); + // the project loaded behind the modal, based on the url project code + const { data: backgroundProject } = useProject(params?.projectCode); + + useEffect(() => { + // if user is not already logged in, redirect to login page first, and then redirect back to this page + const checkLogin = () => { + if (isLoadingUser || isLoggedIn || isFetching) return; + navigate( + { + ...location, + hash: MODAL_ROUTES.LOGIN, + }, + { + state: { + referrer: location, + }, + }, + ); + }; + checkLogin(); + }, [isLoggedIn, isLoadingUser, isFetching, requestingProject]); + + const { data: countries = [], isFetching: isLoadingCountryAccessList } = + useProjectCountryAccessList(requestingProjectCode!); + + const countriesWithAccess = countries?.filter((c: CountryAccessListItem) => c.hasAccess); + + // the countries that have already got a request + const requestedCountries = countries?.filter((c: CountryAccessListItem) => c.hasPendingAccess); + + // the countries that are available to request + const availableCountries = countries?.filter( + (c: CountryAccessListItem) => !c.hasAccess && !c.hasPendingAccess, + ); + + // Show the form if there are available countries, or if there are requested countries and the user has opted to request additional countries + const showForm = requestedCountries?.length + ? requestAdditionalCountries && availableCountries?.length > 0 + : availableCountries?.length > 0; + + // Show the requested countries if there are any, and the user has not opted to request additional countries + const showRequestedCountries = requestedCountries?.length > 0 && !requestAdditionalCountries; + + // if the project code is the same as the selected project and the user has access, then we are not returning to the projects page, we just want to close the modal + const isReturningToProjects = !( + (!altProjectCode || altProjectCode === params?.projectCode) && + requestingProject?.hasAccess + ); + + // only request the entity if the project code is the same as the selected project, or if the alt project code is not set, so that we don't get any false errors. This query is just to check if the user has been directed here from the useEntity hook because of a 403 error + const showCheckForEntityError = !altProjectCode || params.projectCode === altProjectCode; + const { error, isError } = useEntity( + params.projectCode, + params.entityCode, + showCheckForEntityError, + ); + + // show the error if the user is getting a 403 error when trying to access an entity, as this means they have been redirected here from the useEntity hook + const showError = isError && error.code === 403; + + // show the no countries message if the country access list has loaded and there are no countries available + const showNoCountriesMessage = !isLoadingCountryAccessList && !availableCountries?.length; + + const getBaseCloseLocation = () => { + if (isLandingPage) return location; + // if the user has accessed the request access modal and does have access to that project in some way, then return to the project. This would happen if the user went directly to the project with the request access modal details in the url + if ( + (!altProjectCode || altProjectCode === params?.projectCode) && + requestingProject?.hasAccess + ) { + return { + ...location, + pathname: generatePath(ROUTE_STRUCTURE, { + projectCode: requestingProject?.code, + entityCode: requestingProject?.homeEntityCode, + dashboardCode: defaultDashboard, + }), + }; + } + return { + ...location, + // if the user has access to the project in the background, then return to the project with the project modal open, otherwise return to the default url with the project modal open + pathname: backgroundProject?.hasAccess ? location.pathname : DEFAULT_URL, + hash: MODAL_ROUTES.PROJECTS, + }; + }; + + const getCloseLocation = () => { + const baseCloseLocation = getBaseCloseLocation(); + // return the base close location with the project search param removed + return { + ...baseCloseLocation, + search: removeUrlSearchParams([URL_SEARCH_PARAMS.PROJECT]), + } as Location; + }; + + const closeLocation = getCloseLocation(); + + const onCloseModal = () => { + gaEvent('User', 'Close Dialog'); + navigate(closeLocation); + }; + return ( + + + + + + + {isLoadingCountryAccessList ? ( + + ) : ( + <> + {showError && {error.message}} + {showNoCountriesMessage && ( + + There are no countries available to request access to for this project. This means + you already have access to all countries in this project. If you need to change your + permissions, please contact your system administrator. + + )} + {showRequestedCountries && ( + 0} + onShowForm={() => setRequestAdditionalCountries(true)} + isLandingPage={isLandingPage} + baseCloseLocation={closeLocation} + /> + )} + {showForm && ( + + )} + + )} + + + ); +};