Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,45 +1,28 @@
"use client";

import { useMemo } from "react";
import { Grid, Link, Typography } from "@mui/material";
import { Grid, Typography } from "@mui/material";
import { useTranslations } from "next-intl";
import { useSearchParams } from "next/navigation";
import { templateRepeatFields } from "@/interfaces/Cms";
import Box from "@/components/Box";
import Chip from "@/components/Chip";
import Container from "@/components/Container";
import IndicateNhseSdeAccessButton from "@/components/IndicateNhseSdeAccessButton";
import { MarkDownSanitizedWithHtml } from "@/components/MarkDownSanitizedWithHTML";
import Paper from "@/components/Paper";
import RequestNhseSdeAccessButton from "@/components/RequestNhseSdeAccessButton";
import useAuth from "@/hooks/useAuth";
import { useCohortStatus } from "@/hooks/useCohortStatus";
import { colors } from "@/config/theme";
import { NHSSDEStatusMapping } from "@/consts/cohortDiscovery";
import { capitalise } from "@/utils/general";
import { useFeatures } from "@/providers/FeatureProvider";
import CohortAccessStepper from "../CohortAccessStepper";
import NhsSdeAccessStepper from "../NhsSdeAccessStepper";

export default function CohortDiscoveryCoverPage({
cmsContent,
}: {
cmsContent: templateRepeatFields;
}) {
const t = useTranslations("pages.account.profile.cohortDiscovery");
const { isNhsSdeApplicationsEnabled } = useFeatures();
const { user, isLoading: loadingUser } = useAuth();
const { nhseSdeRequestStatus, isLoading, refetch } = useCohortStatus(
user?.id
);

const searchParams = useSearchParams();

const autoOpen = useMemo(() => {
return searchParams?.get("open") === "true";
}, [searchParams]);

const loading = loadingUser || isLoading;

return (
<Container sx={{ display: "flex", flexDirection: "column" }}>
<Box sx={{ bgcolor: "white", mb: 3, px: 4, pb: 1, pt: 3 }}>
Expand All @@ -58,98 +41,8 @@ export default function CohortDiscoveryCoverPage({
autoOpen={autoOpen}
/>
</Grid>
<Grid size={{ mobile: 12, laptop: 8 }}>
<Paper
sx={{
bgcolor: "white",
px: { mobile: 3, laptop: 8 },
py: { mobile: 2, laptop: 6 },
}}>
<Typography variant="h1">
{t("nhseSdeTitle")}
</Typography>
<Box sx={{ display: "flex", px: 0, pt: 0, gap: 2 }}>
{nhseSdeRequestStatus && (
<>
<Chip
size="small"
label={capitalise(nhseSdeRequestStatus)}
color={
NHSSDEStatusMapping[
nhseSdeRequestStatus
]
}
/>

{nhseSdeRequestStatus === "APPROVED" && (
<>
<Typography
sx={{
color: colors.grey600,
alignContent: "center",
}}>
{t("nhsExpiry")}
</Typography>
</>
)}
</>
)}
</Box>
{isNhsSdeApplicationsEnabled && (
<>
<Typography
color={colors.grey700}
sx={{ pb: 2 }}>
{t("nhseSdeText1")}
</Typography>
{!loading && !nhseSdeRequestStatus && (
<MarkDownSanitizedWithHtml
sx={{ color: colors.red700 }}
content={t("nhseSdeText2")}
/>
)}
</>
)}
{!isNhsSdeApplicationsEnabled && (
<Typography color={colors.grey600}>
{t.rich("nhseSdeTemporaryText", {
mailto: chunks => (
<Link href={`mailto:${chunks}`}>
{chunks}
</Link>
),
})}
</Typography>
)}
</Paper>
</Grid>

<Grid size={{ mobile: 12, laptop: 4 }}>
<Paper
sx={{
bgcolor: "white",
height: "100%",
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
mb: 2,
gap: 2,
p: 2,
}}>
{!loading && nhseSdeRequestStatus !== "APPROVED" && (
<>
<RequestNhseSdeAccessButton
color="greyCustom"
refetchCohort={refetch}
/>
<IndicateNhseSdeAccessButton
sx={{ width: "100%" }}
refetchCohort={refetch}
/>
</>
)}
</Paper>
<Grid size={12}>
<NhsSdeAccessStepper />
</Grid>
</Grid>
</Container>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import userEvent from "@testing-library/user-event";
import NhsSdeAccessStepper from "./NhsSdeAccessStepper";
import { render, screen } from "@/utils/testUtils";

const mockUseCohortStatus = jest.fn();
const mockUseFeatures = jest.fn();

jest.mock("@/hooks/useAuth", () => ({
__esModule: true,
default: () => ({ user: { id: 1 }, isLoading: false }),
}));

jest.mock("@/hooks/useCohortStatus", () => ({
__esModule: true,
useCohortStatus: () => mockUseCohortStatus(),
}));

jest.mock("@/providers/FeatureProvider", () => ({
__esModule: true,
useFeatures: () => mockUseFeatures(),
}));

const baseStatus = {
requestStatus: null,
nhseSdeRequestStatus: null,
isLoading: false,
hasFetched: true,
refetch: jest.fn(),
};

const baseFeatures = { isNhsSdeApplicationsEnabled: true };

const renderStepper = () => render(<NhsSdeAccessStepper />);

describe("NhsSdeAccessStepper", () => {
beforeEach(() => {
mockUseCohortStatus.mockReturnValue(baseStatus);
mockUseFeatures.mockReturnValue(baseFeatures);
});

it("locks step 1 when Cohort Discovery access has not been approved", () => {
renderStepper();

expect(
screen.getByText("Existing Cohort Discovery Access")
).toBeInTheDocument();
expect(
screen.queryByRole("button", {
name: "Apply for NHS Research SDE Cohort Data Access",
})
).not.toBeInTheDocument();
});

it("shows step 1 as complete with an apply button when Cohort Discovery is approved", async () => {
mockUseCohortStatus.mockReturnValue({
...baseStatus,
requestStatus: "APPROVED",
});

renderStepper();

const applyButton = screen.getByRole("button", {
name: "Apply for NHS Research SDE Cohort Data Access",
});
expect(applyButton).toBeInTheDocument();

await userEvent.click(applyButton);

expect(
screen.queryByRole("button", {
name: "Apply for NHS Research SDE Cohort Data Access",
})
).not.toBeInTheDocument();
});

it("shows the pilot text and hides the steps when the feature flag is off", () => {
mockUseFeatures.mockReturnValue({ isNhsSdeApplicationsEnabled: false });

renderStepper();

expect(
screen.getByText(/currently in its pilot phase/i)
).toBeInTheDocument();
expect(
screen.queryByText("Existing Cohort Discovery Access")
).not.toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
"use client";

import { ReactNode, useState } from "react";
import { useTranslations } from "next-intl";
import Button from "@/components/Button";
import Link from "@/components/Link";
import Loading from "@/components/Loading";
import Paper from "@/components/Paper";
import Typography from "@/components/Typography";
import useAuth from "@/hooks/useAuth";
import { useCohortStatus } from "@/hooks/useCohortStatus";
import { colors } from "@/config/theme";
import { STEP_STATE } from "@/consts/cohortDiscovery";
import { RouteName } from "@/consts/routeName";
import { useFeatures } from "@/providers/FeatureProvider";
import { CircleState, StepNode, StepTitle } from "../Stepper";

const TRANSLATION_PATH = "pages.account.profile.cohortDiscovery.nhsStepper";
const ABOUT_HREF = `/${RouteName.ABOUT}/${RouteName.COHORT_DISCOVERY}`;
const MORE_INFO_HREF = `${ABOUT_HREF}?tab=nhs-sde-network`;

const NhsSdeAccessStepper = () => {
const t = useTranslations(TRANSLATION_PATH);
const tCd = useTranslations("pages.account.profile.cohortDiscovery");

const { user, isLoading: userLoading } = useAuth();
const { isNhsSdeApplicationsEnabled } = useFeatures();
const {
requestStatus,
isLoading: statusLoading,
hasFetched,
} = useCohortStatus(user?.id);

const [applied, setApplied] = useState(false);

const loading = userLoading || statusLoading || !hasFetched;
const cdsApproved = requestStatus === "APPROVED";

const steps: {
label: string;
state: CircleState;
titleKey: string;
muted: boolean;
extra?: ReactNode;
}[] = [
{
label: "1",
state: cdsApproved ? STEP_STATE.COMPLETE : STEP_STATE.LOCKED,
titleKey: "step1Title",
muted: false,
extra: cdsApproved
? applied
? (
<Typography color={colors.grey600} sx={{ mt: 1 }}>
{t("appliedText")}
</Typography>
)
: (
<Button
variant="outlined"
color="secondary"
sx={{ mt: 1 }}
onClick={() => setApplied(true)}>
{t("applyButton")}
</Button>
)
: undefined,
},
{
label: "2",
state: STEP_STATE.LOCKED,
titleKey: "step2Title",
muted: true,
},
{
label: "3",
state: STEP_STATE.LOCKED,
titleKey: "step3Title",
muted: true,
},
{
label: "4",
state: STEP_STATE.LOCKED,
titleKey: "step4Title",
muted: true,
},
{
label: "5",
state: STEP_STATE.LOCKED,
titleKey: "step5Title",
muted: true,
},
];

return (
<Paper sx={{ bgcolor: "white", p: { mobile: 3, laptop: 4 } }}>
<Typography variant="h2">{t("title")}</Typography>
<Typography sx={{ mb: 2 }} color={colors.grey700}>
{t.rich("moreInfo", {
link: chunks => <Link href={MORE_INFO_HREF}>{chunks}</Link>,
})}
</Typography>

{loading ? (
<Loading />
) : !isNhsSdeApplicationsEnabled ? (
<Typography component="div" color={colors.grey600}>
{tCd.rich("nhseSdeTemporaryText", {
mailto: chunks => (
<Link href={`mailto:${chunks}`}>{chunks}</Link>
),
})}
</Typography>
) : (
<>
{steps.map((step, i) => (
<StepNode
key={step.label}
circleState={step.state}
label={step.label}
isLast={i === steps.length - 1}>
<StepTitle muted={step.muted}>
{t(step.titleKey)}
</StepTitle>
{step.extra}
</StepNode>
))}
</>
)}
</Paper>
);
};

export default NhsSdeAccessStepper;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./NhsSdeAccessStepper";
13 changes: 12 additions & 1 deletion src/config/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,18 @@
"nhseSdeText1": "This service gives approved researchers, access to the additional NHS Research SDE Network datasets and subsequently those with approved projects secure access to NHS data. You must also have approved access to Cohort Discovery service to access the NHS Research SDE Network datasets.",
"nhseSdeText2": "By selecting ‘Confirm approval by the NHS Research SDE’, you are confirming that you have been approved by the NHS Research SDE validation service and you are consenting to HDR UK sharing your email address with the NHS Research SDE team solely for the purpose of verifying your approval status. \nPlease note: this step involves manual validation by NHS Research SDE validation team and may take up to 10 working days. You will receive an email notification once your access to the additional datasets in cohort discovery has been granted.",
"nhseSdeTemporaryText": "To access SDE Network data, users must first be validated by the SDE Network. This validation process is currently in its pilot phase, with the full service expected to be operational by October 2025. In the meantime, if you would like to be added to the list for validation once the service goes live, please email <mailto>england.data.healthresearch@nhs.net</mailto>",
"nhsExpiry": "SDE access is limited by access to Gateway Cohort Discovery"
"nhsExpiry": "SDE access is limited by access to Gateway Cohort Discovery",
"nhsStepper": {
"title": "Optional NHS Research SDE Cohort Data Access",
"moreInfo": "More information about this service can be found <link>here</link>",
"step1Title": "Existing Cohort Discovery Access",
"applyButton": "Apply for NHS Research SDE Cohort Data Access",
"appliedText": "Your application for NHS Research SDE Cohort Data Access has started.",
"step2Title": "Complete NHS SDE Validation Form",
"step3Title": "Submit NHS SDE Validation Status to HDRUK",
"step4Title": "Application Review",
"step5Title": "Access Decision"
}
},
"dataAccessRequests": {
"applications": {
Expand Down
Loading