Skip to content

Commit 05e8f98

Browse files
[PRMP-788] Admin Console (#882)
Co-authored-by: adamwhitingnhs <[email protected]>
1 parent f1bb33c commit 05e8f98

File tree

7 files changed

+166
-25
lines changed

7 files changed

+166
-25
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { render, screen } from '@testing-library/react';
2+
import { AdminPage } from './AdminPage';
3+
import { runAxeTest } from '../../helpers/test/axeTestHelper';
4+
import { describe, expect, it, vi } from 'vitest';
5+
import { routeChildren } from '../../types/generic/routes';
6+
7+
vi.mock('../../../helpers/hooks/useTitle');
8+
9+
describe('AdminPage', (): void => {
10+
describe('Rendering', (): void => {
11+
it('renders the admin console heading', (): void => {
12+
render(<AdminPage />);
13+
expect(screen.getByRole('heading', { name: 'Admin console' })).toBeInTheDocument();
14+
});
15+
16+
it('renders the Reviews card', (): void => {
17+
render(<AdminPage />);
18+
const reviewsLink = screen.getByTestId('admin-reviews-btn');
19+
expect(reviewsLink).toBeInTheDocument();
20+
expect(reviewsLink).toHaveTextContent('Reviews');
21+
});
22+
23+
it('renders the Reviews card with correct href', (): void => {
24+
render(<AdminPage />);
25+
const reviewsLink = screen.getByTestId('admin-reviews-btn');
26+
expect(reviewsLink).toHaveAttribute('href', routeChildren.ADMIN_REVIEW);
27+
});
28+
29+
it('renders the Reviews card description', (): void => {
30+
render(<AdminPage />);
31+
expect(
32+
screen.getByText(
33+
'Review documents from practice to practice transfers and rejections from bulk transfer into this service.',
34+
),
35+
).toBeInTheDocument();
36+
});
37+
});
38+
39+
describe('Accessibility', (): void => {
40+
it('passes accessibility checks', async (): Promise<void> => {
41+
render(<AdminPage />);
42+
const results = await runAxeTest(document.body);
43+
expect(results).toHaveNoViolations();
44+
});
45+
});
46+
});
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Card } from 'nhsuk-react-components';
2+
import { JSX } from 'react';
3+
import useTitle from '../../helpers/hooks/useTitle';
4+
import { routeChildren } from '../../types/generic/routes';
5+
import { ReactComponent as RightCircleIcon } from '../../styles/right-chevron-circle.svg';
6+
7+
export const AdminPage = (): JSX.Element => {
8+
useTitle({ pageTitle: 'Admin console' });
9+
10+
return (
11+
<>
12+
<h1>Admin console</h1>
13+
<Card.Group>
14+
<Card.GroupItem width="one-half">
15+
{/* Reviews */}
16+
<Card clickable cardType="primary">
17+
<Card.Content>
18+
<Card.Heading className="nhsuk-heading-m">
19+
<Card.Link
20+
data-testid="admin-reviews-btn"
21+
href={routeChildren.ADMIN_REVIEW}
22+
>
23+
Reviews
24+
</Card.Link>
25+
</Card.Heading>
26+
<Card.Description>
27+
Review documents from practice to practice transfers and rejections
28+
from bulk transfer into this service.
29+
</Card.Description>
30+
<RightCircleIcon />
31+
</Card.Content>
32+
</Card>
33+
</Card.GroupItem>
34+
</Card.Group>
35+
</>
36+
);
37+
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { JSX } from 'react';
2+
import { Routes, Route } from 'react-router';
3+
import { AdminPage } from '../adminPage/AdminPage';
4+
import { routeChildren } from '../../types/generic/routes';
5+
import { getLastURLPath } from '../../helpers/utils/urlManipulations';
6+
7+
export const AdminRoutesPage = (): JSX.Element => {
8+
return (
9+
<Routes>
10+
<Route path={getLastURLPath(routeChildren.ADMIN_REVIEW)} element={<></>} />
11+
<Route path="*" element={<AdminPage />} />
12+
</Routes>
13+
);
14+
};
Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { render, screen } from '@testing-library/react';
22
import HomePage from './HomePage';
3-
import useRole from '../../helpers/hooks/useRole';
4-
import { REPOSITORY_ROLE } from '../../types/generic/authRole';
53
import { buildConfig } from '../../helpers/test/testBuilders';
64
import useConfig from '../../helpers/hooks/useConfig';
5+
import { routes } from '../../types/generic/routes';
76
import { afterEach, beforeEach, describe, expect, it, vi, Mock } from 'vitest';
87

98
const mockedUseNavigate = vi.fn();
@@ -15,9 +14,7 @@ vi.mock('react-router-dom', async () => {
1514
};
1615
});
1716

18-
vi.mock('../../helpers/hooks/useRole');
1917
vi.mock('../../helpers/hooks/useConfig');
20-
const mockUseRole = useRole as Mock;
2118
const mockUseConfig = useConfig as Mock;
2219

2320
describe('HomePage', () => {
@@ -27,33 +24,40 @@ describe('HomePage', () => {
2724
afterEach(() => {
2825
vi.clearAllMocks();
2926
});
30-
const gpRoles = [REPOSITORY_ROLE.GP_ADMIN, REPOSITORY_ROLE.GP_CLINICAL];
3127

32-
const validateHomePageRendered = () => {
33-
render(<HomePage />);
28+
describe('Rendering', () => {
29+
it('should render home page with patient search and download report', async () => {
30+
render(<HomePage />);
3431

35-
const searchPatientButton = screen.getByTestId('search-patient-btn') as HTMLAnchorElement;
36-
const downloadReportButton = screen.getByTestId('download-report-btn') as HTMLAnchorElement;
37-
expect(searchPatientButton).toBeInTheDocument();
38-
expect(downloadReportButton).toBeInTheDocument();
39-
};
32+
const searchPatientButton = screen.getByTestId('search-patient-btn') as HTMLAnchorElement;
33+
const downloadReportButton = screen.getByTestId('download-report-btn') as HTMLAnchorElement;
34+
expect(searchPatientButton).toBeInTheDocument();
35+
expect(downloadReportButton).toBeInTheDocument();
36+
});
37+
});
4038

41-
describe('Rendering for GP roles', () => {
42-
it.each(gpRoles)(
43-
'[%s] render home page with patient search and download report',
44-
async (role) => {
45-
mockUseRole.mockReturnValue(role);
39+
describe('Admin Console button', () => {
40+
it('renders admin console button when feature flag is enabled', () => {
41+
mockUseConfig.mockReturnValue(
42+
buildConfig(undefined, { uploadDocumentIteration3Enabled: true }),
43+
);
4644

47-
validateHomePageRendered();
48-
},
49-
);
50-
});
45+
render(<HomePage />);
5146

52-
describe('PCSE Rendering', () => {
53-
it('should render home page with patient search and download report', async () => {
54-
mockUseRole.mockReturnValue(REPOSITORY_ROLE.PCSE);
47+
const adminConsoleButton = screen.getByTestId('admin-console-btn') as HTMLAnchorElement;
48+
expect(adminConsoleButton).toBeInTheDocument();
49+
expect(adminConsoleButton).toHaveTextContent('Admin console');
50+
expect(adminConsoleButton).toHaveAttribute('href', routes.ADMIN_ROUTE);
51+
});
52+
53+
it('does not render admin console button when feature flag is disabled', () => {
54+
mockUseConfig.mockReturnValue(
55+
buildConfig(undefined, { uploadDocumentIteration3Enabled: false }),
56+
);
57+
58+
render(<HomePage />);
5559

56-
validateHomePageRendered();
60+
expect(screen.queryByTestId('admin-console-btn')).not.toBeInTheDocument();
5761
});
5862
});
5963
});

app/src/pages/homePage/HomePage.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,26 @@ const HomePage = (): React.JSX.Element => {
7474
</Card.Content>
7575
</Card>
7676
</Card.GroupItem>
77+
{config.featureFlags.uploadDocumentIteration3Enabled &&
78+
<Card.GroupItem width="one-half">
79+
<Card clickable cardType="primary">
80+
<Card.Content>
81+
<Card.Heading className="nhsuk-heading-m">
82+
<Card.Link
83+
data-testid="admin-console-btn"
84+
href={routes.ADMIN_ROUTE}
85+
>
86+
Admin console
87+
</Card.Link>
88+
</Card.Heading>
89+
<Card.Description>
90+
Review records and actions for incoming patients
91+
</Card.Description>
92+
<RightCircleIcon />
93+
</Card.Content>
94+
</Card>
95+
</Card.GroupItem>
96+
}
7797
</Card.Group>
7898
</>
7999
);

app/src/router/AppRouter.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import NonAuthGuard from './guards/notAuthGuard/NonAuthGuard';
2727
import PatientAccessAuditPage from '../pages/patientAccessAuditPage/PatientAccessAuditPage';
2828
import MockLoginPage from '../pages/mockLoginPage/MockLoginPage';
2929
import DocumentUploadPage from '../pages/documentUploadPage/DocumentUploadPage';
30+
import { AdminRoutesPage } from '../pages/adminRoutesPage/AdminRoutesPage';
3031

3132
const {
3233
START,
@@ -55,6 +56,8 @@ const {
5556
MOCK_LOGIN,
5657
DOCUMENT_UPLOAD,
5758
DOCUMENT_UPLOAD_WILDCARD,
59+
ADMIN_ROUTE,
60+
ADMIN_ROUTE_WILDCARD,
5861
} = routes;
5962

6063
type Routes = {
@@ -146,6 +149,10 @@ export const childRoutes = [
146149
route: routeChildren.DOCUMENT_UPLOAD_FILE_ERRORS,
147150
parent: DOCUMENT_UPLOAD,
148151
},
152+
{
153+
route: routeChildren.ADMIN_REVIEW,
154+
parent: ADMIN_ROUTE,
155+
},
149156
];
150157

151158
export const routeMap: Routes = {
@@ -227,6 +234,14 @@ export const routeMap: Routes = {
227234
page: <ReportDownloadPage />,
228235
type: ROUTE_TYPE.PRIVATE,
229236
},
237+
[ADMIN_ROUTE]: {
238+
page: <AdminRoutesPage />,
239+
type: ROUTE_TYPE.PRIVATE,
240+
},
241+
[ADMIN_ROUTE_WILDCARD]: {
242+
page: <AdminRoutesPage />,
243+
type: ROUTE_TYPE.PRIVATE,
244+
},
230245

231246
// App guard routes
232247
[VERIFY_PATIENT]: {

app/src/types/generic/routes.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ export enum routes {
2929
DOCUMENT_UPLOAD_WILDCARD = '/patient/document-upload/*',
3030

3131
MOCK_LOGIN = 'Auth/MockLogin',
32+
33+
ADMIN_ROUTE = '/admin',
34+
ADMIN_ROUTE_WILDCARD = '/admin/*',
3235
}
3336

3437
export enum routeChildren {
@@ -55,6 +58,8 @@ export enum routeChildren {
5558
DOCUMENT_DELETE = '/patient/documents/delete',
5659
DOCUMENT_DELETE_CONFIRMATION = '/patient/documents/delete/confirmation',
5760
DOCUMENT_DELETE_COMPLETE = '/patient/documents/delete/complete',
61+
62+
ADMIN_REVIEW = '/admin/reviews',
5863
}
5964

6065
export enum ROUTE_TYPE {

0 commit comments

Comments
 (0)