Skip to content

Commit

Permalink
create waiver pdf registration email
Browse files Browse the repository at this point in the history
revert some changes
  • Loading branch information
bowenzhu1 committed Dec 22, 2022
1 parent 93fbdfa commit ea16a93
Showing 8 changed files with 91 additions and 7 deletions.
1 change: 1 addition & 0 deletions backend/typescript/package.json
Original file line number Diff line number Diff line change
@@ -32,6 +32,7 @@
"graphql-middleware": "^6.0.6",
"graphql-upload": "^12.0.0",
"json2csv": "^5.0.6",
"jspdf": "^2.5.1",
"lodash": "^4.17.21",
"mongoose": "^6.2.3",
"multer": "^1.4.2",
15 changes: 15 additions & 0 deletions backend/typescript/rest/camperRoutes.ts
Original file line number Diff line number Diff line change
@@ -14,11 +14,16 @@ import { getErrorMessage } from "../utilities/errorUtils";
import { sendResponseByMimeType } from "../utilities/responseUtil";
import { CamperDTO, CreateCampersDTO, WaitlistedCamperDTO } from "../types";
import { createWaitlistedCamperDtoValidator } from "../middlewares/validators/waitlistedCamperValidators";
import IEmailService from "../services/interfaces/emailService";
import EmailService from "../services/implementations/emailService";
import nodemailerConfig from "../nodemailer.config";

const camperRouter: Router = Router();

const camperService: ICamperService = new CamperService();

const emailService: IEmailService = new EmailService(nodemailerConfig);

// ROLES: Leaving unprotected as the registration flow probs needs this endpoint to register @dhruv
/* Create a camper */
camperRouter.post("/register", createCampersDtoValidator, async (req, res) => {
@@ -34,6 +39,16 @@ camperRouter.post("/register", createCampersDtoValidator, async (req, res) => {
}
});

camperRouter.post("/email", async (req, res) => {
try {
const waiverContent = req.body;
emailService.sendWaiverEmail(waiverContent);
res.status(201);
} catch (error: unknown) {
res.status(500).json({ error: getErrorMessage(error) });
}
});

// ROLES: Admin + CC
/* Get all campers, optionally filter by camp ID */
camperRouter.get(
2 changes: 1 addition & 1 deletion backend/typescript/server.ts
Original file line number Diff line number Diff line change
@@ -32,7 +32,7 @@ const swaggerDocument = YAML.load("swagger.yml");
const app = express();
app.use(cookieParser());
app.use(cors(CORS_OPTIONS));
app.use(express.json());
app.use(express.json({ limit: "50mb" }));
app.use(express.urlencoded({ extended: true }));

app.use("/auth", authRouter);
21 changes: 20 additions & 1 deletion backend/typescript/services/implementations/emailService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import nodemailer, { Transporter } from "nodemailer";
import { Attachment } from "nodemailer/lib/mailer";
import IEmailService from "../interfaces/emailService";
import { NodemailerConfig } from "../../types";
import { EmailDTO, NodemailerConfig } from "../../types";
import { getErrorMessage } from "../../utilities/errorUtils";
import logger from "../../utilities/logger";
import { Camper } from "../../models/camper.model";
@@ -34,6 +35,22 @@ class EmailService implements IEmailService {
}
}

async sendWaiverEmail(waiverContent: EmailDTO): Promise<void> {
const pdfAttachment: Attachment = {
filename: "waiver.pdf",
content: Buffer.from(waiverContent.pdf),
contentType: "application/pdf",
encoding: "base64",
};

await this.sendEmail(
"bowenzhu@uwblueprint.org",
"Focus on Nature Camp Registration - Waiver",
"A copy of your Focus on Nature Camp Registration is attached.",
[pdfAttachment],
);
}

async sendParentConfirmationEmail(
camp: Camp,
campers: Camper[],
@@ -267,12 +284,14 @@ class EmailService implements IEmailService {
to: string,
subject: string,
htmlBody: string,
attachments?: Attachment[],
): Promise<void> {
const mailOptions = {
from: this.sender,
to,
subject,
html: htmlBody,
attachments,
};

try {
2 changes: 2 additions & 0 deletions backend/typescript/services/interfaces/emailService.ts
Original file line number Diff line number Diff line change
@@ -2,8 +2,10 @@ import { Camp } from "../../models/camp.model";
import { Camper } from "../../models/camper.model";
import { CampSession } from "../../models/campSession.model";
import { WaitlistedCamper } from "../../models/waitlistedCamper.model";
import { EmailDTO } from "../../types";

interface IEmailService {
sendWaiverEmail(waiverContent: EmailDTO): Promise<void>;
/**
* Send camp registration confirmation email.
* @throws Error if email was not sent successfully
4 changes: 4 additions & 0 deletions backend/typescript/types.ts
Original file line number Diff line number Diff line change
@@ -239,6 +239,10 @@ export type NodemailerConfig = {
};
};

export type EmailDTO = {
pdf: string;
};

export type WaiverDTO = {
clauses: {
text: string;
25 changes: 25 additions & 0 deletions frontend/src/APIClients/CamperAPIClient.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import JsPDF from "jspdf";
import { getBearerToken } from "../constants/AuthConstants";
import {
WaitlistedCamper,
@@ -92,6 +93,29 @@ const deleteMultipleCampersById = async (ids: string[]): Promise<boolean> => {
}
};

const sendWaiverEmail = async (
waiverContent: HTMLElement,
): Promise<boolean> => {
try {
const waiverPdf = new JsPDF("portrait", "pt", "a4");
await waiverPdf.html(waiverContent, {
margin: 20,
html2canvas: { scale: 0.7 },
});
const waiverBuffer = waiverPdf.output();

await baseAPIClient.post(
`/campers/email`,
{ pdf: waiverBuffer },
{
headers: { Authorization: getBearerToken() },
});
return true;
} catch (error) {
return false;
}
}

const getCampersByChargeIdAndSessionId = async (
chargeId: string,
sessionId: string,
@@ -114,6 +138,7 @@ export default {
updateCamperRegistrationStatus,
deleteWaitlistedCamperById,
updateCampersById,
sendWaiverEmail,
getCampersByChargeIdAndSessionId,
deleteMultipleCampersById,
};
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@ import {
HStack,
Wrap,
} from "@chakra-ui/react";
import JsPDF from "jspdf";
import RequiredAsterisk from "../../../common/RequiredAsterisk";
import {
OptionalClauseResponse,
@@ -23,6 +24,7 @@ import {
WaiverInterface,
WaiverReducerDispatch,
} from "../../../../types/waiverTypes";
import CamperAPIClient from "../../../../APIClients/CamperAPIClient";

interface WaiverPageProps {
waiverInterface: WaiverInterface;
@@ -48,8 +50,24 @@ const WaiverPage = ({
// As a note, waiverDispatch is stable (should't change) but we need to pass it into dependency to make linter happy
}, [waiverDispatch]);

const sendEmail = async () => {
const waiver = document.getElementById("waiver") as HTMLElement;
await CamperAPIClient.sendWaiverEmail(waiver);
console.log("sent");
}
const generatePDF = () => {
const report = new JsPDF("portrait", "pt", "a4");
report
.html(document.getElementById("waiver") as HTMLElement, { margin: 20,
html2canvas: { scale: 0.7 },
})
.then(() => {
report.save("report.pdf");
});
};

return (
<Box>
<Box id="waiver">
<VStack spacing={3} pt={1} align="stretch">
<Text textStyle="displayXLarge">
{waiverInterface.campName} Registration
@@ -60,9 +78,8 @@ const WaiverPage = ({
<Text textStyle="buttonSemiBold" pt={6}>
In consideration of the participation of my child/children, (the
“child or children“), in the Focus on Nature photography camp/workshop
and all activities associated therewith, I, the undersigned
parent/guardian of the child/children, agree to the following terms
and conditions:
and all activities associated therewith, I, the parent/guardian of the
child/children, agree to the following terms and conditions:
</Text>
</VStack>

@@ -202,8 +219,9 @@ const WaiverPage = ({
/>
</FormControl>
</Wrap>
<button onClick={sendEmail} type="button">Send Email PDF</button>
</Box>
);
};

export default WaiverPage;
export default WaiverPage;

0 comments on commit ea16a93

Please sign in to comment.