Skip to content

Commit 556ffd7

Browse files
authored
Merge pull request #2821 from bcgov/DEP-256-migrate-links-to-getpath
Migrate all links to use getPath()
2 parents 82ee69c + 07f8bf4 commit 556ffd7

67 files changed

Lines changed: 633 additions & 434 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.MD

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
## April 13, 2026
22

3+
- **Feature** Migrate all links to use getPath() [🎟️ DEP-255](https://citz-gdx.atlassian.net/browse/DEP-255)
4+
- Updated all hardcoded links in the codebase to use the new `getPath()` function from `routes/routes.ts`, which generates URLs based on a centralized ROUTES constant. This ensures that all links are consistent and automatically updated if route paths change in the future.
5+
- This includes links in components, API calls, navigation, and anywhere else URLs are generated or used in the application.
6+
- Also updated links to use RouterLinkRenderer where appropriate to take advantage of client-side routing and prevent full page reloads.
7+
- Leveraged new routing system to remove redundant routes in public view and restructure public engagement URLs to use the slug-based format
8+
- Fixed several bugs related to link construction
9+
10+
## April 13, 2026
11+
312
- **Feature** Add constant ROUTES list in routes/routes.ts [🎟️ DEP-254](https://citz-gdx.atlassian.net/browse/DEP-254)
413
- Created a new file `routes/routes.ts` that exports a constant `ROUTES` object containing all the route paths used in the application, organized by section (e.g. ENGAGEMENTS, SURVEYS, USERS, etc.).
514
- Also created a type-safe `getRoute` function based on generatePath from react-router that takes a route key and optional parameters, and returns the corresponding route path with parameters interpolated. This centralizes route management and makes it easier to update routes in the future without having to search through the entire codebase.

web/src/components/comments/admin/review/CommentReview.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import { faMessageCheck } from '@fortawesome/pro-solid-svg-icons/faMessageCheck'
3838
import { faMessageSlash } from '@fortawesome/pro-solid-svg-icons/faMessageSlash';
3939
import { LanguageState } from 'reduxSlices/languageSlice';
4040
import { TenantState } from 'reduxSlices/tenantSlice';
41+
import { ROUTES, getPath } from 'routes/routes';
4142

4243
const CommentReview = () => {
4344
const [submission, setSubmission] = useState<SurveySubmission>(createDefaultSubmission());
@@ -107,7 +108,7 @@ const CommentReview = () => {
107108
setIsLoading(false);
108109
} catch {
109110
dispatch(openNotification({ severity: 'error', text: 'Error occurred while fetching comments' }));
110-
navigate('/');
111+
navigate(getPath(ROUTES.PUBLIC_LANDING));
111112
}
112113
};
113114

@@ -169,7 +170,7 @@ const CommentReview = () => {
169170
});
170171
setIsSaving(false);
171172
dispatch(openNotification({ severity: 'success', text: 'Comments successfully reviewed.' }));
172-
navigate(`/surveys/${submission.survey_id}/comments`);
173+
navigate(getPath(ROUTES.SURVEY_COMMENTS, { surveyId: submission.survey_id }));
173174
} catch {
174175
dispatch(openNotification({ severity: 'error', text: 'Error occurred while sending comments review.' }));
175176
setIsSaving(false);

web/src/components/comments/admin/reviewListing/Submissions.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import React, { useState, useContext } from 'react';
22
import CustomTable from 'components/common/Table';
33
import Grid from '@mui/material/Grid2';
4-
import { Link, useLocation } from 'react-router';
4+
import { useLocation } from 'react-router';
55
import { ResponsiveContainer } from 'components/common/Layout';
66
import { HeadCell, PaginationOptions } from 'components/common/Table/types';
77
import { formatDate } from 'components/common/dateHelper';
8-
import { Collapse, Link as MuiLink } from '@mui/material';
8+
import { Collapse, Link } from '@mui/material';
99
import TextField from '@mui/material/TextField';
1010
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
1111
import { faMagnifyingGlass } from '@fortawesome/pro-regular-svg-icons/faMagnifyingGlass';
@@ -21,6 +21,7 @@ import { USER_COMPOSITE_ROLE } from 'models/user';
2121
import { Heading1 } from 'components/common/Typography';
2222
import { Button } from 'components/common/Input/Button';
2323
import { RouterLinkRenderer } from 'components/common/Navigation/Link';
24+
import { ROUTES, getPath } from 'routes/routes';
2425

2526
const Submissions = () => {
2627
const {
@@ -61,9 +62,15 @@ const Submissions = () => {
6162
userDetail.composite_roles?.includes('/' + USER_COMPOSITE_ROLE.TEAM_MEMBER.value))
6263
) {
6364
return (
64-
<MuiLink component={Link} to={`/surveys/${Number(row.survey_id)}/submissions/${row.id}/review`}>
65+
<Link
66+
component={RouterLinkRenderer}
67+
href={getPath(ROUTES.SURVEY_SUBMISSION_REVIEW, {
68+
surveyId: Number(row.survey_id),
69+
submissionId: row.id,
70+
})}
71+
>
6572
{row.id}
66-
</MuiLink>
73+
</Link>
6774
);
6875
}
6976
return row.id;
@@ -153,7 +160,7 @@ const Submissions = () => {
153160
size="small"
154161
variant="primary"
155162
component={RouterLinkRenderer}
156-
href={`/surveys/${survey.id}/comments/all`}
163+
href={getPath(ROUTES.SURVEY_COMMENTS_ALL, { surveyId: survey.id })}
157164
>
158165
Read All Comments
159166
</Button>

web/src/components/comments/admin/textListing/index.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import React, { useState, useEffect } from 'react';
22
import CustomTable from 'components/common/Table';
3-
import { Link, useLocation, useParams } from 'react-router';
3+
import { useLocation, useParams } from 'react-router';
44
import { ResponsiveContainer } from 'components/common/Layout';
55
import { HeadCell, PageInfo, PaginationOptions } from 'components/common/Table/types';
6-
import { Link as MuiLink, Grid2 as Grid, Stack, TextField, Menu, MenuItem, Tooltip } from '@mui/material';
6+
import { Link, Grid2 as Grid, Stack, TextField, Menu, MenuItem, Tooltip } from '@mui/material';
77
import { Button } from 'components/common/Input/Button';
88
import { RouterLinkRenderer } from 'components/common/Navigation/Link';
99
import { BodyText } from 'components/common/Typography/Body';
@@ -30,6 +30,7 @@ import { Survey, createDefaultSurvey } from 'models/survey';
3030
import { PermissionsGate } from 'components/permissionsGate';
3131
import { HTTP_STATUS_CODES } from 'constants/httpResponseCodes';
3232
import axios from 'axios';
33+
import { ROUTES, getPath } from 'routes/routes';
3334

3435
const CommentTextListing = () => {
3536
const { roles, userDetail, assignedEngagements } = useAppSelector((state) => state.user);
@@ -184,9 +185,15 @@ const CommentTextListing = () => {
184185
userDetail.composite_roles?.includes('/' + USER_COMPOSITE_ROLE.TEAM_MEMBER.value))
185186
) {
186187
return (
187-
<MuiLink component={Link} to={`/surveys/${Number(row.survey_id)}/submissions/${row.id}/review`}>
188+
<Link
189+
component={RouterLinkRenderer}
190+
href={getPath(ROUTES.SURVEY_SUBMISSION_REVIEW, {
191+
surveyId: Number(row.survey_id),
192+
submissionId: row.id,
193+
})}
194+
>
188195
{row.id}
189-
</MuiLink>
196+
</Link>
190197
);
191198
}
192199
return row.id;
@@ -363,7 +370,7 @@ const CommentTextListing = () => {
363370
<Button
364371
variant="primary"
365372
LinkComponent={RouterLinkRenderer}
366-
href={`/surveys/${submissions[0]?.survey_id || 0}/comments`}
373+
href={getPath(ROUTES.SURVEY_COMMENTS, { surveyId: submissions[0]?.survey_id || 0 })}
367374
>
368375
Return to Comments List
369376
</Button>

web/src/components/engagement/admin/config/EngagementCreateAction.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ActionFunction, redirect } from 'react-router';
22
import { postEngagement as createEngagement } from 'services/engagementService';
33
import { patchEngagementSlug } from 'services/engagementSlugService';
44
import { addTeamMemberToEngagement } from 'services/membershipService';
5+
import { ROUTES, getPath } from 'routes/routes';
56

67
export const engagementCreateAction: ActionFunction = async ({ request }) => {
78
const formData = (await request.formData()) as FormData;
@@ -25,7 +26,7 @@ export const engagementCreateAction: ActionFunction = async ({ request }) => {
2526
formData.getAll('users').forEach((user_id) => {
2627
addTeamMemberToEngagement({ user_id: user_id.toString(), engagement_id: engagement.id });
2728
});
28-
return redirect(`/engagements/${engagement.id}/details/config`);
29+
return redirect(getPath(ROUTES.ENGAGEMENT_DETAILS_CONFIG, { engagementId: engagement.id }));
2930
};
3031

3132
export default engagementCreateAction;

web/src/components/engagement/admin/config/EngagementUpdateAction.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
getTeamMembers,
1212
} from 'services/membershipService';
1313
import { store } from 'store';
14+
import { ROUTES, getPath } from 'routes/routes';
1415

1516
export const engagementUpdateAction: ActionFunction = async ({ request, params }) => {
1617
const formData = (await request.formData()) as FormData;
@@ -72,7 +73,7 @@ export const engagementUpdateAction: ActionFunction = async ({ request, params }
7273
console.error('Error updating team members', e);
7374
}
7475

75-
return redirect(`/engagements/${engagementId}/details/config`);
76+
return redirect(getPath(ROUTES.ENGAGEMENT_DETAILS_CONFIG, { engagementId }));
7677
};
7778

7879
export default engagementUpdateAction;

web/src/components/engagement/admin/config/LanguageManager.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import MultiSelect from './MultiSelect';
1313
import { SystemMessage } from 'components/common/Layout/SystemMessage';
1414
import { LanguageLoaderData } from './LanguageLoader';
1515
import { Awaited } from 'utils';
16+
import { ROUTES, getPath } from 'routes/routes';
1617

1718
export const LanguageManager = () => {
1819
const SINGLE_LANGUAGE = [{ code: 'en', name: 'English' }] as Language[];
@@ -39,7 +40,7 @@ export const LanguageManager = () => {
3940
const isSingleLanguage = determineSingleLanguage(selectedLanguages);
4041

4142
useEffect(() => {
42-
fetcher.load('/languages/');
43+
fetcher.load(`${getPath(ROUTES.LANGUAGES)}/`);
4344
}, []);
4445

4546
return (

web/src/components/engagement/admin/config/wizard/ConfigWizard.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { Heading1, Heading2 } from 'components/common/Typography';
1111
import dayjs from 'dayjs';
1212
import { Language } from 'models/language';
1313
import { Grid2 as Grid, Skeleton } from '@mui/material';
14+
import { ROUTES, getPath } from 'routes/routes';
1415

1516
const EngagementConfigurationWizard = () => {
1617
const { engagement, teamMembers, slug } = useRouteLoaderData('single-engagement') as EngagementLoaderAdminData;
@@ -84,7 +85,7 @@ const ConfigForm = ({
8485
}),
8586
{
8687
method: 'patch',
87-
action: `/engagements/${engagement.id}/details/config/edit`,
88+
action: getPath(ROUTES.ENGAGEMENT_DETAILS_CONFIG_EDIT, { engagementId: engagement.id }),
8889
},
8990
);
9091
};
@@ -104,7 +105,7 @@ const ConfigForm = ({
104105

105106
return (
106107
<FormProvider {...engagementConfigForm}>
107-
<EngagementForm onSubmit={onSubmit} />
108+
<EngagementForm engagement={engagement} onSubmit={onSubmit} />
108109
</FormProvider>
109110
);
110111
};

web/src/components/engagement/admin/config/wizard/CreationWizard.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import EngagementForm, { EngagementConfigurationData } from '.';
77
import { Heading1, Heading2 } from 'components/common/Typography';
88
import { SystemMessage } from 'components/common/Layout/SystemMessage';
99
import Grid from '@mui/material/Grid2';
10+
import { ROUTES, getPath } from 'routes/routes';
1011

1112
const EngagementCreationWizard = () => {
1213
const fetcher = useFetcher({ key: 'config-update' });
@@ -42,7 +43,7 @@ const EngagementCreationWizard = () => {
4243
}),
4344
{
4445
method: 'post',
45-
action: '/engagements/create/',
46+
action: `${getPath(ROUTES.ENGAGEMENT_CREATE)}/`,
4647
},
4748
);
4849
};
@@ -66,7 +67,7 @@ const EngagementCreationWizard = () => {
6667
</Grid>
6768
<Grid size={12} mt={5}>
6869
<FormProvider {...engagementCreationForm}>
69-
<EngagementForm isNewEngagement onSubmit={onSubmit} />
70+
<EngagementForm engagement={null} onSubmit={onSubmit} />
7071
</FormProvider>
7172
</Grid>
7273
</ResponsiveContainer>

web/src/components/engagement/admin/config/wizard/index.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ import { DateRangePickerWithCalculation } from '../DateRangePickerWithCalculatio
1111
import { LanguageManager } from '../LanguageManager';
1212
import { UserManager } from '../UserManager';
1313
import { User } from 'models/user';
14+
import { Engagement } from 'models/engagement';
1415
import { Language } from 'models/language';
1516
import { FormStep } from 'components/common/Layout/FormStep';
1617
import { Modal, CircularProgress } from '@mui/material';
1718
import { modalStyle } from 'components/common';
1819
import UnsavedWorkConfirmation from 'components/common/Navigation/UnsavedWorkConfirmation';
20+
import { ROUTES, getPath } from 'routes/routes';
1921

2022
export interface EngagementConfigurationData {
2123
// 1. Title
@@ -38,10 +40,10 @@ export interface EngagementConfigurationData {
3840

3941
const EngagementForm = ({
4042
onSubmit,
41-
isNewEngagement,
43+
engagement,
4244
}: {
4345
onSubmit: (data: EngagementConfigurationData) => void;
44-
isNewEngagement?: boolean;
46+
engagement: Engagement | null;
4547
}) => {
4648
const engagementForm = useFormContext<EngagementConfigurationData>();
4749

@@ -54,10 +56,14 @@ const EngagementForm = ({
5456

5557
const [nameHasBeenEdited, setNameHasBeenEdited] = useState(false);
5658

59+
const parentPageLink = engagement
60+
? getPath(ROUTES.ENGAGEMENT_DETAILS_CONFIG, { engagementId: engagement.id })
61+
: getPath(ROUTES.ENGAGEMENTS);
62+
5763
return (
5864
<Form onSubmit={handleSubmit(onSubmit)} id="engagement-config-form">
5965
<Grid container sx={{ maxWidth: '49.25rem' }}>
60-
<Heading2 decorated>{isNewEngagement ? 'Configure Engagement' : 'Edit Configuration'}</Heading2>
66+
<Heading2 decorated>{engagement ? 'Edit Configuration' : 'Configure Engagement'}</Heading2>
6167
<Controller
6268
control={control}
6369
name="name"
@@ -152,9 +158,9 @@ const EngagementForm = ({
152158
variant="primary"
153159
type="submit"
154160
>
155-
{isNewEngagement ? 'Create Engagement' : 'Save Changes'}
161+
{engagement ? 'Save Changes' : 'Create Engagement'}
156162
</Button>
157-
<Button href={isNewEngagement ? '/engagements' : '../'}>Cancel</Button>
163+
<Button href={parentPageLink}>Cancel</Button>
158164
</Grid>
159165
<UnsavedWorkConfirmation blockNavigationWhen={isDirty && !isSubmitting} />
160166
<Modal open={isSubmitting || isSubmitted}>

0 commit comments

Comments
 (0)