Skip to content

Commit 4054e64

Browse files
authored
Merge pull request #271 from IFRCGo/feature/redirects
Implement redirects from old URLs
2 parents bb67917 + 3174548 commit 4054e64

File tree

19 files changed

+446
-199
lines changed

19 files changed

+446
-199
lines changed

src/App/Auth.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { type ReactElement, Fragment } from 'react';
2-
import { Navigate, useParams } from 'react-router-dom';
2+
import {
3+
Navigate,
4+
useParams,
5+
} from 'react-router-dom';
36

47
import useAuth from '#hooks/domain/useAuth';
58
import FourHundredThree from '#components/FourHundredThree';

src/App/routes/SmartNavigate.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import {
2+
Navigate,
3+
useLocation,
4+
type NavigateProps,
5+
} from 'react-router-dom';
6+
import { isDefined, isTruthyString } from '@togglecorp/fujs';
7+
8+
type RouteKey = string;
9+
10+
interface Props extends NavigateProps {
11+
hashToRouteMap: Record<string, RouteKey>;
12+
}
13+
14+
function SmartNavigate(props: Props) {
15+
const {
16+
hashToRouteMap,
17+
...navigateProps
18+
} = props;
19+
const location = useLocation();
20+
const newRoute = isTruthyString(location.hash)
21+
? hashToRouteMap[location.hash]
22+
: undefined;
23+
24+
if (isDefined(newRoute)) {
25+
return (
26+
<Navigate
27+
to={newRoute}
28+
replace
29+
/>
30+
);
31+
}
32+
33+
return (
34+
<Navigate
35+
// eslint-disable-next-line react/jsx-props-no-spreading
36+
{...navigateProps}
37+
/>
38+
);
39+
}
40+
41+
export default SmartNavigate;

src/App/routes/index.tsx

Lines changed: 187 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import { Navigate } from 'react-router-dom';
1+
import { Navigate, Outlet, useParams } from 'react-router-dom';
2+
import { isDefined } from '@togglecorp/fujs';
3+
24
import {
35
wrapRoute,
46
unwrapRoute,
@@ -15,6 +17,8 @@ import { Component as RootLayout } from '#views/RootLayout';
1517
import Auth from '../Auth';
1618
import PageError from '../PageError';
1719

20+
import SmartNavigate from './SmartNavigate';
21+
1822
interface Perms {
1923
isDrefRegionalCoordinator: (regionId: number | undefined) => boolean,
2024
isRegionAdmin: (regionId: number | undefined) => boolean,
@@ -179,10 +183,18 @@ const regionIndex = customWrapRoute({
179183
index: true,
180184
component: {
181185
eagerLoad: true,
182-
render: Navigate,
186+
render: SmartNavigate,
183187
props: {
184188
to: 'operations' satisfies DefaultRegionsChild,
185189
replace: true,
190+
hashToRouteMap: {
191+
'#operations': 'operations',
192+
'#3w': 'three-w',
193+
'#risk-watch': 'risk-watch',
194+
'#regional-profile': 'profile',
195+
'#preparedness': 'preparedness',
196+
'#additional-info': 'additional-info',
197+
},
186198
},
187199
},
188200
context: {
@@ -335,10 +347,18 @@ const countryIndex = customWrapRoute({
335347
index: true,
336348
component: {
337349
eagerLoad: true,
338-
render: Navigate,
350+
render: SmartNavigate,
339351
props: {
340352
to: 'operations' satisfies DefaultCountriesChild,
341353
replace: true,
354+
hashToRouteMap: {
355+
'#3w': 'three-w',
356+
'#operations': 'operations',
357+
'#risk-watch': 'risk-watch',
358+
'#preparedness': 'preparedness',
359+
'#country-plan': 'plan',
360+
'#additional': 'additional-info',
361+
},
342362
},
343363
},
344364
context: {
@@ -533,10 +553,16 @@ const emergencyIndex = customWrapRoute({
533553
index: true,
534554
component: {
535555
eagerLoad: true,
536-
render: Navigate,
556+
render: SmartNavigate,
537557
props: {
538558
to: 'details' satisfies DefaultEmergenciesChild,
539559
replace: true,
560+
hashToRouteMap: {
561+
'#details': 'details',
562+
'#reports': 'reports',
563+
'#activities': 'activities',
564+
'#surge': 'surge',
565+
},
540566
},
541567
},
542568
context: {
@@ -872,7 +898,7 @@ const surgeCatalogueCashRapidResponse = customWrapRoute({
872898

873899
const surgeCatalogueCommunityEngagement = customWrapRoute({
874900
parent: surgeCatalogueLayout,
875-
path: 'community',
901+
path: 'community-engagement',
876902
component: {
877903
render: () => import('#views/SurgeCatalogueCommunityEngagement'),
878904
props: {},
@@ -1788,10 +1814,16 @@ const preparednessIndex = customWrapRoute({
17881814
index: true,
17891815
component: {
17901816
eagerLoad: true,
1791-
render: Navigate,
1817+
render: SmartNavigate,
17921818
props: {
17931819
to: 'global-summary' satisfies DefaultPreparednessChild,
17941820
replace: true,
1821+
hashToRouteMap: {
1822+
'#global-summary': 'global-summary',
1823+
'#global-performance': 'global-performance',
1824+
'#resources-catalogue': 'resources-catalogue',
1825+
'#operational-learning': 'operational-learning',
1826+
},
17951827
},
17961828
},
17971829
context: {
@@ -2034,10 +2066,17 @@ const accountIndex = customWrapRoute({
20342066
index: true,
20352067
component: {
20362068
eagerLoad: true,
2037-
render: Navigate,
2069+
render: SmartNavigate,
20382070
props: {
20392071
to: 'details' satisfies DefaultAccountChild,
20402072
replace: true,
2073+
hashToRouteMap: {
2074+
'#account-information': 'details',
2075+
'#notifications': 'notifications',
2076+
'#per-forms': 'my-forms/per',
2077+
'#my-dref-applications': 'my-forms/dref',
2078+
'#three-w-forms': 'my-forms/three-w',
2079+
},
20412080
},
20422081
},
20432082
context: {
@@ -2331,7 +2370,7 @@ const allSurgeAlerts = customWrapRoute({
23312370

23322371
const allDeployedPersonnel = customWrapRoute({
23332372
parent: rootLayout,
2334-
path: 'personnel/all',
2373+
path: 'deployed-personnels/all',
23352374
component: {
23362375
render: () => import('#views/AllDeployedPersonnel'),
23372376
props: {},
@@ -2345,7 +2384,7 @@ const allDeployedPersonnel = customWrapRoute({
23452384

23462385
const allDeployedEmergencyResponseUnits = customWrapRoute({
23472386
parent: rootLayout,
2348-
path: 'eru/all',
2387+
path: 'deployed-erus/all',
23492388
component: {
23502389
render: () => import('#views/AllDeployedEmergencyResponseUnits'),
23512390
props: {},
@@ -2603,6 +2642,140 @@ const perWorkPlanForm = customWrapRoute({
26032642
},
26042643
});
26052644

2645+
// Redirect Routes
2646+
2647+
// eslint-disable-next-line react-refresh/only-export-components
2648+
function DeploymentNavigate() {
2649+
const params = useParams<{ surgeId: string }>();
2650+
2651+
const deploymentRouteMap: Record<string, MyOutputNonIndexRouteObject<ExtendedProps>> = {
2652+
overview: surgeOverview,
2653+
'operational-toolbox': surgeOperationalToolbox,
2654+
personnel: allDeployedPersonnel,
2655+
erus: allDeployedEmergencyResponseUnits,
2656+
};
2657+
2658+
const newRoute = isDefined(params.surgeId)
2659+
? deploymentRouteMap[params.surgeId]
2660+
: undefined;
2661+
2662+
const path = isDefined(newRoute)
2663+
? newRoute.absoluteForwardPath
2664+
: surgeOverview.absoluteForwardPath;
2665+
2666+
return (
2667+
<Navigate
2668+
to={path}
2669+
replace
2670+
/>
2671+
);
2672+
}
2673+
2674+
const deploymentOthers = customWrapRoute({
2675+
parent: rootLayout,
2676+
path: 'deployments/:surgeId/*',
2677+
component: {
2678+
eagerLoad: true,
2679+
render: DeploymentNavigate,
2680+
props: {},
2681+
},
2682+
wrapperComponent: Auth,
2683+
context: {
2684+
title: 'Catalogue of surge services',
2685+
visibility: 'anything',
2686+
},
2687+
});
2688+
2689+
const deploymentCatalogueLayout = customWrapRoute({
2690+
parent: rootLayout,
2691+
path: 'deployments/catalogue',
2692+
component: {
2693+
eagerLoad: true,
2694+
render: () => <Outlet />,
2695+
props: {},
2696+
},
2697+
wrapperComponent: Auth,
2698+
context: {
2699+
title: 'Catalogue of surge services',
2700+
visibility: 'anything',
2701+
},
2702+
});
2703+
2704+
// eslint-disable-next-line react-refresh/only-export-components
2705+
function DeploymentCatalogueNavigate() {
2706+
const params = useParams<{ catalogueId: string }>();
2707+
2708+
const catalogueRouteMap: Record<string, MyOutputNonIndexRouteObject<ExtendedProps>> = {
2709+
overview: surgeCatalogueOverview,
2710+
emergency: surgeCatalogueEmergencyNeedsAssessment,
2711+
basecamp: surgeCatalogueBasecamp,
2712+
cash: surgeCatalogueCash,
2713+
community: surgeCatalogueCommunityEngagement,
2714+
communications: surgeCatalogueCommunication,
2715+
health: surgeCatalogueHealth,
2716+
infoMgt: surgeCatalogueInformationManagement,
2717+
informationTech: surgeCatalogueInformationTechnology,
2718+
livelihoods: surgeCatalogueLivelihood,
2719+
logistics: surgeCatalogueLogistics,
2720+
operations: surgeCatalogueOperationsManagement,
2721+
protection: surgeCataloguePgi,
2722+
planning: surgeCataloguePmer,
2723+
relief: surgeCatalogueRelief,
2724+
security: surgeCatalogueSecurity,
2725+
shelter: surgeCatalogueShelter,
2726+
water: surgeCatalogueWash,
2727+
other: surgeCatalogueOther,
2728+
};
2729+
2730+
const newRoute = isDefined(params.catalogueId)
2731+
? catalogueRouteMap[params.catalogueId]
2732+
: undefined;
2733+
2734+
const path = isDefined(newRoute)
2735+
? newRoute.absoluteForwardPath
2736+
: surgeCatalogueOverview.absoluteForwardPath;
2737+
2738+
return (
2739+
<Navigate
2740+
to={path}
2741+
replace
2742+
/>
2743+
);
2744+
}
2745+
2746+
const deploymentCatalogueIndex = customWrapRoute({
2747+
parent: deploymentCatalogueLayout,
2748+
index: true,
2749+
component: {
2750+
eagerLoad: true,
2751+
render: Navigate,
2752+
props: {
2753+
to: '/surge/catalogue',
2754+
replace: true,
2755+
},
2756+
},
2757+
wrapperComponent: Auth,
2758+
context: {
2759+
title: 'Catalogue of surge services',
2760+
visibility: 'anything',
2761+
},
2762+
});
2763+
2764+
const deploymentCatalogueChildren = customWrapRoute({
2765+
parent: deploymentCatalogueLayout,
2766+
path: ':catalogueId',
2767+
component: {
2768+
eagerLoad: true,
2769+
render: DeploymentCatalogueNavigate,
2770+
props: {},
2771+
},
2772+
wrapperComponent: Auth,
2773+
context: {
2774+
title: 'Catalogue of surge services',
2775+
visibility: 'anything',
2776+
},
2777+
});
2778+
26062779
const wrappedRoutes = {
26072780
fourHundredFour,
26082781
rootLayout,
@@ -2785,6 +2958,11 @@ const wrappedRoutes = {
27852958
surgeCatalogueOtherRecovery,
27862959
surgeCatalogueOtherGreenResponse,
27872960

2961+
// Redirect routes
2962+
deploymentCatalogueLayout,
2963+
deploymentCatalogueIndex,
2964+
deploymentCatalogueChildren,
2965+
deploymentOthers,
27882966
};
27892967

27902968
export const unwrappedRoutes = unwrapRoute(Object.values(wrappedRoutes));

src/components/Page/index.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,11 @@ function Page(props: Props) {
100100
)}
101101
</div>
102102
)}
103-
<PageContainer>
104-
{beforeHeaderContent}
105-
</PageContainer>
103+
{beforeHeaderContent && (
104+
<PageContainer>
105+
{beforeHeaderContent}
106+
</PageContainer>
107+
)}
106108
{isNotDefined(blockingContent) && showPageContainer && (
107109
<PageHeader
108110
className={_cs(

0 commit comments

Comments
 (0)