Skip to content

Commit

Permalink
Download informed consent as pdf
Browse files Browse the repository at this point in the history
  • Loading branch information
jakdan99 committed Nov 20, 2024
1 parent 88284b8 commit 775b7ab
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 43 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"@mui/system": "^5.15.20",
"@mui/x-charts": "^7.7.0",
"@mui/x-date-pickers": "^7.6.2",
"@react-pdf/renderer": "^4.1.4",
"@tanstack/react-query": "^5.45.0",
"@tanstack/react-query-devtools": "^5.45.0",
"big.js": "^6.2.1",
Expand Down
7 changes: 3 additions & 4 deletions src/pages/Deployment/InformedConsentCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
import { useEffect, useState } from "react";
import { InformedConsent } from "@carp-dk/client/models/InputDataTypes";
import FileDownloadOutlinedIcon from "@mui/icons-material/FileDownloadOutlined";
import { formatDateTime } from "@Utils/utility";
import { convertICToReactPdf, formatDateTime } from "@Utils/utility";
import {
DownloadButton,
LastUploadText,
Expand Down Expand Up @@ -158,9 +158,8 @@ const InformedConsentCard = () => {
</LastUploadText>
</i>
<DownloadButton
onClick={(e) =>
exportToJson(e, participant.participantId, consent)
}
document={convertICToReactPdf(JSON.parse(consent.consent))}
fileName="informedConsent.pdf"
>
<FileDownloadOutlinedIcon />
<Typography variant="h6">
Expand Down
8 changes: 5 additions & 3 deletions src/pages/Deployment/InformedConsentCard/styles.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {
Button,
Divider,
Typography,
Accordion,
AccordionSummary,
Stack,
} from "@mui/material";
import { PDFDownloadLink } from "@react-pdf/renderer";
import { styled } from "@Utils/theme";

export const StyledAccordion = styled(Accordion)(({ expanded }) => ({
Expand Down Expand Up @@ -66,15 +66,17 @@ export const NameContainer = styled("div")({
gap: 6,
});

export const DownloadButton = styled(Button)(({ theme }) => ({
export const DownloadButton = styled(PDFDownloadLink)(({ theme }) => ({
display: "flex",
alignItems: "center",
height: "36px",
color: theme.palette.primary.main,
backgroundColor: "transparent",
border: "1px solid",
borderColor: theme.palette.grey[700],
borderRadius: 16,
cursor: "pointer",
textTransform: "none",
textDecoration: "none",
padding: "8px 16px",
gap: 8,
}));
Expand Down
38 changes: 6 additions & 32 deletions src/pages/Participant/InformedConsent/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable no-underscore-dangle */
import CarpErrorCardComponent from "@Components/CarpErrorCardComponent";
import { formatDateTime } from "@Utils/utility";
import { convertICToReactPdf, formatDateTime } from "@Utils/utility";
import FileDownloadOutlinedIcon from "@mui/icons-material/FileDownloadOutlined";
import { Typography } from "@mui/material";
import { useEffect, useMemo, useState } from "react";
Expand All @@ -19,12 +19,6 @@ import {
Title,
} from "./styles";

interface FileInfo {
data: string;
fileName: string;
fileType: string;
}

const InformedConsent = () => {
const { participantId, deploymentId, id: studyId } = useParams();

Expand All @@ -40,29 +34,6 @@ const InformedConsent = () => {
error: participantGroupStatusError,
} = useParticipantGroupsAccountsAndStatus(studyId);

const downloadFile = ({ data, fileName, fileType }: FileInfo) => {
const blob = new Blob([data], { type: fileType });
const a = document.createElement("a");
a.download = fileName;
a.href = window.URL.createObjectURL(blob);
const clickEvt = new MouseEvent("click", {
view: window,
bubbles: true,
cancelable: true,
});
a.dispatchEvent(clickEvt);
a.remove();
};

const exportToJson = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
e.preventDefault();
downloadFile({
data: JSON.stringify(consent),
fileName: "informedConsent.json",
fileType: "text/json",
});
};

useEffect(() => {
if (!isLoading && !participantGroupStatusLoading) {
const participant = participantGroupStatus.groups
Expand Down Expand Up @@ -93,7 +64,7 @@ const InformedConsent = () => {
setConsent(roleConsent);
}
}
}, [participantData]);
}, [participantGroupStatus, participantData]);

const dateOfLastUpdate = useMemo(() => {
if (consent) {
Expand Down Expand Up @@ -125,7 +96,10 @@ const InformedConsent = () => {
{consent && (
<>
<StyledDivider />
<DownloadButton onClick={(e) => exportToJson(e)}>
<DownloadButton
document={convertICToReactPdf(JSON.parse(consent.consent))}
fileName="informedConsent.pdf"
>
<Typography variant="h6">Export</Typography>
<FileDownloadOutlinedIcon fontSize="small" />
</DownloadButton>
Expand Down
11 changes: 7 additions & 4 deletions src/pages/Participant/InformedConsent/styles.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Button, Card, Divider, Typography } from "@mui/material";
import { Card, Divider, Typography } from "@mui/material";
import { PDFDownloadLink } from "@react-pdf/renderer";
import { styled } from "@Utils/theme";

export const StyledCard = styled(Card)({
Expand All @@ -22,17 +23,19 @@ export const StyledDivider = styled(Divider)(({ theme }) => ({
borderColor: theme.palette.grey[500],
borderWidth: 1,
width: 1,
marginRight: 8,
marginRight: 16,
marginLeft: 16,
height: 20,
}));

export const DownloadButton = styled(Button)(({ theme }) => ({
export const DownloadButton = styled(PDFDownloadLink)(({ theme }) => ({
display: "flex",
alignItems: "center",
color: theme.palette.primary.main,
backgroundColor: "transparent",
border: "none",
cursor: "pointer",
textTransform: "none",
textDecoration: "none",
gap: 4,
}));

Expand Down
108 changes: 108 additions & 0 deletions src/utils/utility.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import RadioButtonCheckedIcon from "@mui/icons-material/RadioButtonChecked";
import SmartphoneIcon from "@mui/icons-material/Smartphone";
import TimelineRoundedIcon from "@mui/icons-material/TimelineRounded";
import WatchRoundedIcon from "@mui/icons-material/WatchRounded";
import { Document, Page, Text, StyleSheet } from "@react-pdf/renderer";

import getSerializer = kotlinx.serialization.getSerializer;
import DefaultSerializer = carpCommon.dk.cachet.carp.common.infrastructure.serialization.JSON;
Expand Down Expand Up @@ -308,3 +309,110 @@ export const getParticipantDataName = (dataType: string) => {
return "";
}
};

interface ConsentObject {
__type: string;
identifier: string;
endDate: string;
consentDocument: ConsentDocument;
signature: Signature;
}

interface Signature {
__type: string;
firstName: string;
lastName: string;
signatureImage: string;
}

interface SignatureMetaData {
__type: string;
identifier: string;
requiresName: boolean;
requiresSignatureImage: boolean;
}

interface ConsentDocument {
__type: string;
title: string;
signatures: SignatureMetaData[];
sections: Section[];
}

interface Section {
__type: string;
type: string;
title: string;
summary: string;
content: string;
}

const styles = StyleSheet.create({
body: {
paddingTop: 35,
paddingBottom: 65,
paddingHorizontal: 35,
},
h1: {
fontSize: 24,
textAlign: "center",
fontWeight: 700,
margin: 12,
},
h2: {
fontSize: 18,
fontWeight: 500,
margin: "12 12 0 12",
},
text: {
margin: "12 12 12 12",
fontSize: 12,
textAlign: "justify",
fontWeight: 300,
},
italics: {
fontStyle: "italic",
textAlign: "justify",
margin: "12 12 0 12",
fontSize: 12,
fontWeight: 300,
fontFamily: "Times-Italic",
},
pageNumber: {
position: "absolute",
fontSize: 12,
bottom: 30,
left: 0,
right: 0,
textAlign: "center",
color: "grey",
},
});

export const convertICToReactPdf = (consent: ConsentObject) => {
return (
<Document>
<Page style={styles.body}>
<Text style={styles.h1}>{consent.consentDocument.title}</Text>
{consent.consentDocument.sections.map((section) => {
return (
<div key={section.type}>
<Text style={styles.h2}>{section.title}</Text>
<Text style={styles.italics}>
{section.summary.replaceAll("\n", " ")}
</Text>
<Text style={styles.text}>{section.content}</Text>
</div>
);
})}
<Text
style={styles.pageNumber}
render={({ pageNumber, totalPages }) =>
`${pageNumber} / ${totalPages}`
}
fixed
/>
</Page>
</Document>
);
};

0 comments on commit 775b7ab

Please sign in to comment.