Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

110 add email confirmation #212

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
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
12 changes: 6 additions & 6 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Amplify } from "aws-amplify";
import React from "react";

// import DonorScheduleDropoffPickupPage from "components/donor/DonorScheduleDropoffPickupPage/DonorScheduleDropoffPickupPage";
// import ActiveDonationPage from "components/admin/ActiveDonationsPage/ActiveDonationsPage";
import ActiveDonationPage from "components/admin/ActiveDonationsPage/ActiveDonationsPage";

/* Authentication Screens */
// import CreateAccountPage from './components/authentication/CreateAccountPage/CreateAccountPage';
Expand Down Expand Up @@ -51,7 +51,7 @@ Amplify.configure(awsExports);
function App(): JSX.Element {
return (
// <SuccessPage />
<LoginPage />
// <LoginPage />
// <DonatorHomePage />
// <CreateAccountPage />
// <DonorSchedulePickUp/>
Expand All @@ -66,7 +66,7 @@ function App(): JSX.Element {
// <DonatorNextStepsPage />
// <DonorScheduleDropoffPickupPage />
// <DonationInfoPage />
// <ActiveDonationPage />
<ActiveDonationPage />
);
}

Expand Down
52 changes: 52 additions & 0 deletions frontend/src/api/email.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const emailURL = "http://localhost:3001/api/email/";
/* ----------------------POST Requests---------------------------*/

// Email data model
export interface Email {
recipientEmail: string;
subject: string;
body: string | HTMLElement;
isHTML: boolean;
}

// Send an email without attachment
export const sendEmail = async (email: Email) =>
fetch(emailURL, {
headers: {
"Content-Type": "application/json",
},
method: "POST",
body: JSON.stringify({
recipientEmail: email.recipientEmail,
subject: email.subject,
body: email.body,
isHTML: email.isHTML,
}),
})
.then(async (res) => {
const response = await res.json();
if (!res.ok) {
// check server response
throw new Error(`${res.status}-${res.statusText}`);
}
return response;
})
.catch((error) => console.error("Error: ", error)); // handle error

/* --------------Functions for Creating HTMLElements-------------- */
export const createParagraph = (text: string) => {
const paragraph = document.createElement("p");
paragraph.style.fontFamily = "Rubik, sans-serif";
paragraph.innerText = text;
return paragraph;
};

export const createListItem = (label: string, value: any) => {
const listItem = document.createElement("li");
const strong = document.createElement("strong");
strong.style.fontFamily = "Rubik, sans-serif";
strong.innerText = label;
listItem.appendChild(strong);
listItem.appendChild(document.createTextNode(String(value)));
return listItem;
};
109 changes: 109 additions & 0 deletions frontend/src/components/admin/DonationInfoPage/DonationInfoPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import PropTypes from "prop-types";
import Box from "@mui/material/Box";
import { getUserByID, User } from "api/user";
import { getItemByID, Item, updateItem } from "api/item";
import { Email, sendEmail, createParagraph, createListItem } from "api/email";
import Tab from "@mui/material/Tab";
import Tabs from "@mui/material/Tabs";
import Typography from "@mui/material/Typography";
Expand Down Expand Up @@ -128,6 +129,7 @@ function DonationInfoPage(): JSX.Element {
} else if (e.currentTarget.value === "reject") {
updateItem({ ...item, status: "Rejected" });
sendUpdatedItemToDB("Rejected", false);
sendDonationStatusEmail(false);
navigate(backPath);
navigate(0); // Reload page after navigating back to fetch changes
} else if (e.currentTarget.value === "approve") {
Expand All @@ -137,6 +139,7 @@ function DonationInfoPage(): JSX.Element {
)) &&
(await sendUpdatedItemToDB("Approved and Scheduled", true))
) {
sendDonationStatusEmail(true);
console.log("Success submitting events!");
clearTimeSlots(); // Clear time slots from redux
navigate(nextPath);
Expand Down Expand Up @@ -179,9 +182,115 @@ function DonationInfoPage(): JSX.Element {
// "There was an error updating the item. Please try again later."
// TODO: add error message to user
}
setItem(updatedItem); // Update state of current item
return response;
};

const sendDonationStatusEmail = async (isApproved: boolean) => {
try {
const subject = isApproved
? "Donation Request Approval"
: "Donation Request Rejection";

const status = isApproved ? "Approved" : "Rejected";

const container = document.createElement("div");

container.appendChild(
createParagraph(
isApproved
? "Your donation request has been approved."
: "Your donation request has been rejected."
)
);
container.appendChild(
createParagraph("Here are the details of the item:")
);

const list = document.createElement("ul");
list.style.listStyleType = "none";
container.appendChild(list);

const stateValue = item.state || ""; // Check for undefined state

// TODO: Include time for pickup if
const listItems = [
{ label: "Name: ", value: item.name },
{ label: "Size: ", value: item.size },
{ label: "Address: ", value: item.address },
{ label: "City: ", value: item.city },
{ label: "State: ", value: stateValue },
{ label: "Zip Code: ", value: item.zipCode },
{ label: "Scheduling: ", value: item.scheduling },
];

listItems.forEach((item) => {
const listItem = createListItem(item.label, item.value);
list.appendChild(listItem);
});

// TODO: Include which time was approved
if (item.timeAvailability) {
const timeAvailability = document.createElement("li");
const strong = document.createElement("strong");
strong.style.fontFamily = "Rubik, sans-serif";
strong.innerText = "Time Availability: ";
timeAvailability.appendChild(strong);

const ul = document.createElement("ul");

item.timeAvailability.forEach((time, index) => {
const li = document.createElement("li");
const start = document.createElement("span");
start.innerText = `Start: ${new Date(time.start).toLocaleString(
"en-US",
{
timeZone: "America/Los_Angeles",
}
)}`;
li.appendChild(start);
li.appendChild(document.createElement("br"));
const end = document.createElement("span");
end.innerText = `End: ${new Date(time.end).toLocaleString("en-US", {
timeZone: "America/Los_Angeles",
})}`;
li.appendChild(end);
ul.appendChild(li);
});

timeAvailability.appendChild(ul);
list.appendChild(timeAvailability);
}

const stat = createListItem(
"Status: ",
isApproved ? "Approved" : "Rejected"
);
list.appendChild(stat);

container.appendChild(
createParagraph("Thank you for supporting our cause!")
);

const email = {
recipientEmail: donor.email,
subject,
body: container.outerHTML,
isHTML: true,
};

const response = await sendEmail(email);
if (!response) {
throw new Error("There was an error sending the donation email.");
}

return response;
} catch (error) {
console.error("Error sending donation email:", error);
throw error;
}
};

// Fetch and set item on load
useEffect(() => {
const fetchedItem =
Expand Down
119 changes: 118 additions & 1 deletion frontend/src/components/donor/donation/SubmitInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { useNavigate } from "react-router-dom";
import DonatorNavbar from "components/donor/DonorNavbar/DonorNavbar";
import ProgressBar from "components/donor/donation/ProgressBar";
import { useSelector, useDispatch } from "react-redux";
import { getUserByID } from "api/user";
import { Email, sendEmail, createParagraph, createListItem } from "api/email";
import { Item, addItem } from "../../../api/item";
import { addImages, getImages, getImageByID } from "../../../api/image";
import { RootState } from "../../../redux/store";
Expand Down Expand Up @@ -143,6 +145,121 @@ const SubmitInfo: React.FC<DummyComponentProps> = ({
return response;
};

const sendDonationRequestEmail = async () => {
try {
const donorUser = await getUserByID(storedDonation.donorID);

const container = document.createElement("div");
container.appendChild(
createParagraph(
"Thank you for submitting your donation request. Here are the details:"
)
);

const list = document.createElement("ul");
list.style.listStyleType = "none";
container.appendChild(list);

const listItems = [
{ label: "Name: ", value: storedDonation.name },
{ label: "Size: ", value: storedDonation.dimensions },
{ label: "Address: ", value: storedDonation.address },
{ label: "City: ", value: storedDonation.city },
{ label: "State: ", value: storedDonation.state },
{ label: "Zip Code: ", value: storedDonation.zipCode },
{
label: "Scheduling: ",
value: storedDonation.dropoff ? "Dropoff" : "Pickup",
},
];

listItems.forEach((item) => {
const listItem = createListItem(item.label, item.value);
list.appendChild(listItem);
});

if (!storedDonation.dropoff) {
const timeAvailability = document.createElement("li");
const strong = document.createElement("strong");
strong.style.fontFamily = "Rubik, sans-serif";
strong.innerText = "Time Availability: ";
timeAvailability.appendChild(strong);

const ul = document.createElement("ul");

storedDonation.pickupTimes.forEach((time, index) => {
const li = document.createElement("li");
const start = document.createElement("span");
start.innerText = `Start: ${new Date(time.start).toLocaleString(
"en-US",
{
timeZone: "America/Los_Angeles",
}
)}`;
li.appendChild(start);
li.appendChild(document.createElement("br"));
const end = document.createElement("span");
end.innerText = `End: ${new Date(time.end).toLocaleString("en-US", {
timeZone: "America/Los_Angeles",
})}`;
li.appendChild(end);
ul.appendChild(li);
});

timeAvailability.appendChild(ul);
list.appendChild(timeAvailability);
}

const timeSubmitted = createListItem(
"Time Submitted: ",
new Date().toLocaleString("en-US", {
timeZone: "America/Los_Angeles",
weekday: "long",
month: "long",
day: "numeric",
year: "numeric",
hour: "numeric",
minute: "numeric",
hour12: true,
second: undefined,
})
);
list.appendChild(timeSubmitted);

const status = createListItem("Status: ", "Needs Approval");
list.appendChild(status);

container.appendChild(
createParagraph(
"We appreciate your generous donation. Our team will review your request and get back to you soon."
)
);
container.appendChild(
createParagraph("Thank you for supporting our cause!")
);

const body = container.outerHTML;

const email = {
recipientEmail: donorUser.email,
subject: "Donation Request Submission Confirmation",
body,
isHTML: true,
};

const response = await sendEmail(email);
if (!response) {
setServerError(
"There was an error sending your donation submission confirmation email. Your donation request has been tracked."
);
}
return response;
} catch (error) {
console.error("Error sending donation request email:", error);
throw error;
}
};

const buttonNavigation = async (
e: React.MouseEvent<HTMLButtonElement>
): Promise<void> => {
Expand All @@ -152,7 +269,7 @@ const SubmitInfo: React.FC<DummyComponentProps> = ({
if (e.currentTarget.value === "backButton") {
navigate(backPath);
} else if (e.currentTarget.value === "nextButton") {
if (await sendToDB()) {
if ((await sendToDB()) && (await sendDonationRequestEmail())) {
navigate(nextPath);
}
}
Expand Down