diff --git a/app/src/pages/adminPage/AdminPage.test.tsx b/app/src/pages/adminPage/AdminPage.test.tsx
new file mode 100644
index 000000000..7043854a3
--- /dev/null
+++ b/app/src/pages/adminPage/AdminPage.test.tsx
@@ -0,0 +1,46 @@
+import { render, screen } from '@testing-library/react';
+import { AdminPage } from './AdminPage';
+import { runAxeTest } from '../../helpers/test/axeTestHelper';
+import { describe, expect, it, vi } from 'vitest';
+import { routeChildren } from '../../types/generic/routes';
+
+vi.mock('../../../helpers/hooks/useTitle');
+
+describe('AdminPage', (): void => {
+ describe('Rendering', (): void => {
+ it('renders the admin console heading', (): void => {
+ render();
+ expect(screen.getByRole('heading', { name: 'Admin console' })).toBeInTheDocument();
+ });
+
+ it('renders the Reviews card', (): void => {
+ render();
+ const reviewsLink = screen.getByTestId('admin-reviews-btn');
+ expect(reviewsLink).toBeInTheDocument();
+ expect(reviewsLink).toHaveTextContent('Reviews');
+ });
+
+ it('renders the Reviews card with correct href', (): void => {
+ render();
+ const reviewsLink = screen.getByTestId('admin-reviews-btn');
+ expect(reviewsLink).toHaveAttribute('href', routeChildren.ADMIN_REVIEW);
+ });
+
+ it('renders the Reviews card description', (): void => {
+ render();
+ expect(
+ screen.getByText(
+ 'Review documents from practice to practice transfers and rejections from bulk transfer into this service.',
+ ),
+ ).toBeInTheDocument();
+ });
+ });
+
+ describe('Accessibility', (): void => {
+ it('passes accessibility checks', async (): Promise => {
+ render();
+ const results = await runAxeTest(document.body);
+ expect(results).toHaveNoViolations();
+ });
+ });
+});
diff --git a/app/src/pages/adminPage/AdminPage.tsx b/app/src/pages/adminPage/AdminPage.tsx
new file mode 100644
index 000000000..5a5fd365a
--- /dev/null
+++ b/app/src/pages/adminPage/AdminPage.tsx
@@ -0,0 +1,37 @@
+import { Card } from 'nhsuk-react-components';
+import { JSX } from 'react';
+import useTitle from '../../helpers/hooks/useTitle';
+import { routeChildren } from '../../types/generic/routes';
+import { ReactComponent as RightCircleIcon } from '../../styles/right-chevron-circle.svg';
+
+export const AdminPage = (): JSX.Element => {
+ useTitle({ pageTitle: 'Admin console' });
+
+ return (
+ <>
+ Admin console
+
+
+ {/* Reviews */}
+
+
+
+
+ Reviews
+
+
+
+ Review documents from practice to practice transfers and rejections
+ from bulk transfer into this service.
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/app/src/pages/adminRoutesPage/AdminRoutesPage.tsx b/app/src/pages/adminRoutesPage/AdminRoutesPage.tsx
new file mode 100644
index 000000000..7ab069038
--- /dev/null
+++ b/app/src/pages/adminRoutesPage/AdminRoutesPage.tsx
@@ -0,0 +1,14 @@
+import { JSX } from 'react';
+import { Routes, Route } from 'react-router';
+import { AdminPage } from '../adminPage/AdminPage';
+import { routeChildren } from '../../types/generic/routes';
+import { getLastURLPath } from '../../helpers/utils/urlManipulations';
+
+export const AdminRoutesPage = (): JSX.Element => {
+ return (
+
+ >} />
+ } />
+
+ );
+};
diff --git a/app/src/pages/homePage/HomePage.test.tsx b/app/src/pages/homePage/HomePage.test.tsx
index 37f7f7d93..f61c22c85 100644
--- a/app/src/pages/homePage/HomePage.test.tsx
+++ b/app/src/pages/homePage/HomePage.test.tsx
@@ -1,9 +1,8 @@
import { render, screen } from '@testing-library/react';
import HomePage from './HomePage';
-import useRole from '../../helpers/hooks/useRole';
-import { REPOSITORY_ROLE } from '../../types/generic/authRole';
import { buildConfig } from '../../helpers/test/testBuilders';
import useConfig from '../../helpers/hooks/useConfig';
+import { routes } from '../../types/generic/routes';
import { afterEach, beforeEach, describe, expect, it, vi, Mock } from 'vitest';
const mockedUseNavigate = vi.fn();
@@ -15,9 +14,7 @@ vi.mock('react-router-dom', async () => {
};
});
-vi.mock('../../helpers/hooks/useRole');
vi.mock('../../helpers/hooks/useConfig');
-const mockUseRole = useRole as Mock;
const mockUseConfig = useConfig as Mock;
describe('HomePage', () => {
@@ -27,33 +24,40 @@ describe('HomePage', () => {
afterEach(() => {
vi.clearAllMocks();
});
- const gpRoles = [REPOSITORY_ROLE.GP_ADMIN, REPOSITORY_ROLE.GP_CLINICAL];
- const validateHomePageRendered = () => {
- render();
+ describe('Rendering', () => {
+ it('should render home page with patient search and download report', async () => {
+ render();
- const searchPatientButton = screen.getByTestId('search-patient-btn') as HTMLAnchorElement;
- const downloadReportButton = screen.getByTestId('download-report-btn') as HTMLAnchorElement;
- expect(searchPatientButton).toBeInTheDocument();
- expect(downloadReportButton).toBeInTheDocument();
- };
+ const searchPatientButton = screen.getByTestId('search-patient-btn') as HTMLAnchorElement;
+ const downloadReportButton = screen.getByTestId('download-report-btn') as HTMLAnchorElement;
+ expect(searchPatientButton).toBeInTheDocument();
+ expect(downloadReportButton).toBeInTheDocument();
+ });
+ });
- describe('Rendering for GP roles', () => {
- it.each(gpRoles)(
- '[%s] render home page with patient search and download report',
- async (role) => {
- mockUseRole.mockReturnValue(role);
+ describe('Admin Console button', () => {
+ it('renders admin console button when feature flag is enabled', () => {
+ mockUseConfig.mockReturnValue(
+ buildConfig(undefined, { uploadDocumentIteration3Enabled: true }),
+ );
- validateHomePageRendered();
- },
- );
- });
+ render();
- describe('PCSE Rendering', () => {
- it('should render home page with patient search and download report', async () => {
- mockUseRole.mockReturnValue(REPOSITORY_ROLE.PCSE);
+ const adminConsoleButton = screen.getByTestId('admin-console-btn') as HTMLAnchorElement;
+ expect(adminConsoleButton).toBeInTheDocument();
+ expect(adminConsoleButton).toHaveTextContent('Admin console');
+ expect(adminConsoleButton).toHaveAttribute('href', routes.ADMIN_ROUTE);
+ });
+
+ it('does not render admin console button when feature flag is disabled', () => {
+ mockUseConfig.mockReturnValue(
+ buildConfig(undefined, { uploadDocumentIteration3Enabled: false }),
+ );
+
+ render();
- validateHomePageRendered();
+ expect(screen.queryByTestId('admin-console-btn')).not.toBeInTheDocument();
});
});
});
diff --git a/app/src/pages/homePage/HomePage.tsx b/app/src/pages/homePage/HomePage.tsx
index a6568909d..9418decd7 100644
--- a/app/src/pages/homePage/HomePage.tsx
+++ b/app/src/pages/homePage/HomePage.tsx
@@ -74,6 +74,26 @@ const HomePage = (): React.JSX.Element => {
+ {config.featureFlags.uploadDocumentIteration3Enabled &&
+
+
+
+
+
+ Admin console
+
+
+
+ Review records and actions for incoming patients
+
+
+
+
+
+ }
>
);
diff --git a/app/src/router/AppRouter.tsx b/app/src/router/AppRouter.tsx
index 4e0e73adc..aa9e261ed 100644
--- a/app/src/router/AppRouter.tsx
+++ b/app/src/router/AppRouter.tsx
@@ -27,6 +27,7 @@ import NonAuthGuard from './guards/notAuthGuard/NonAuthGuard';
import PatientAccessAuditPage from '../pages/patientAccessAuditPage/PatientAccessAuditPage';
import MockLoginPage from '../pages/mockLoginPage/MockLoginPage';
import DocumentUploadPage from '../pages/documentUploadPage/DocumentUploadPage';
+import { AdminRoutesPage } from '../pages/adminRoutesPage/AdminRoutesPage';
const {
START,
@@ -55,6 +56,8 @@ const {
MOCK_LOGIN,
DOCUMENT_UPLOAD,
DOCUMENT_UPLOAD_WILDCARD,
+ ADMIN_ROUTE,
+ ADMIN_ROUTE_WILDCARD,
} = routes;
type Routes = {
@@ -146,6 +149,10 @@ export const childRoutes = [
route: routeChildren.DOCUMENT_UPLOAD_FILE_ERRORS,
parent: DOCUMENT_UPLOAD,
},
+ {
+ route: routeChildren.ADMIN_REVIEW,
+ parent: ADMIN_ROUTE,
+ },
];
export const routeMap: Routes = {
@@ -227,6 +234,14 @@ export const routeMap: Routes = {
page: ,
type: ROUTE_TYPE.PRIVATE,
},
+ [ADMIN_ROUTE]: {
+ page: ,
+ type: ROUTE_TYPE.PRIVATE,
+ },
+ [ADMIN_ROUTE_WILDCARD]: {
+ page: ,
+ type: ROUTE_TYPE.PRIVATE,
+ },
// App guard routes
[VERIFY_PATIENT]: {
diff --git a/app/src/types/generic/routes.ts b/app/src/types/generic/routes.ts
index 157916363..efcc05ee3 100644
--- a/app/src/types/generic/routes.ts
+++ b/app/src/types/generic/routes.ts
@@ -29,6 +29,9 @@ export enum routes {
DOCUMENT_UPLOAD_WILDCARD = '/patient/document-upload/*',
MOCK_LOGIN = 'Auth/MockLogin',
+
+ ADMIN_ROUTE = '/admin',
+ ADMIN_ROUTE_WILDCARD = '/admin/*',
}
export enum routeChildren {
@@ -55,6 +58,8 @@ export enum routeChildren {
DOCUMENT_DELETE = '/patient/documents/delete',
DOCUMENT_DELETE_CONFIRMATION = '/patient/documents/delete/confirmation',
DOCUMENT_DELETE_COMPLETE = '/patient/documents/delete/complete',
+
+ ADMIN_REVIEW = '/admin/reviews',
}
export enum ROUTE_TYPE {