Skip to content

Commit

Permalink
Merge pull request #152 from UoaWDCC/feat/request-hooks
Browse files Browse the repository at this point in the history
Feat/request hooks
  • Loading branch information
gmat224 authored Dec 22, 2024
2 parents 9c5c5a3 + f538e08 commit e43100d
Show file tree
Hide file tree
Showing 25 changed files with 411 additions and 201 deletions.
11 changes: 6 additions & 5 deletions api/controller/stripeController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const createCheckout = asyncHandler(
} else {
let ticketAvailable = await isTicketAvailableByPriceId(priceId);
if (!ticketAvailable) {
return res.send({
return res.status(400).send({
error:
"There are no tickets available for this event. Please come back later to see if more tickets become available.",
});
Expand Down Expand Up @@ -121,11 +121,12 @@ export const handleWebhook = asyncHandler(

if (event.type === "checkout.session.completed") {
const session: Stripe.Checkout.Session = event.data.object;

if (
!session.metadata &&
!session.metadata!["priceId"] &&
!session.metadata!["isEventTicket"]
!(
!session.metadata &&
!session.metadata!["priceId"] &&
!session.metadata!["isEventTicket"]
)
) {
if (session.metadata!["isEventTicket"] === "y") {
completeTicketPurchase(session.id);
Expand Down
1 change: 0 additions & 1 deletion api/controller/userController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { insertUserBySuperToken } from "../gateway/userGateway";

export const updateUserTicketInfo = asyncHandler(
async (req: Request, res: Response) => {
console.log(process.env.DOMAIN_DB);
try {
const { name, email, phoneNumber, ticketId, answers } = req.body;

Expand Down
1 change: 1 addition & 0 deletions api/gateway/userGateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ export async function insertUserTicket(data: {
return newUserTicket[0];
}

// TODO: Add check to make sure server does not crash if an expiry date already exists
export async function updateUserMembershipExpiryDate(
sessionId: string
): Promise<void> {
Expand Down
1 change: 1 addition & 0 deletions api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ app.use(
cors({
origin: [
`${process.env.DOMAIN_FRONTEND}`, //FE
`${process.env.DOMAIN_FRONTEND_AAAA}`,
`${process.env.DOMAIN_STRAPI}`, //Strapi
`${process.env.DOMAIN_SUPERTOKENS}`, //ST user Dashboard
`${process.env.DATABASE_HOST}:${process.env.DATABASE_PORT}`, //DB
Expand Down
11 changes: 8 additions & 3 deletions web/__test__/screens/CheckoutScreen.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import CheckoutScreen from "../../src/screens/CheckoutScreen";
import React from "react";
import "@testing-library/jest-dom";
import { MemoryRouter } from "react-router";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const mockedUseNavigate = vi.fn();
vi.mock("react-router-dom", async () => {
Expand All @@ -24,13 +25,17 @@ vi.mock("@components/checkout-page/CheckoutError", () => ({
},
}));

const queryClient = new QueryClient();

describe("AboutUsScreen", () => {
it("renders error screen properly", () => {
render(
<MockedProvider addTypename={false}>
<MemoryRouter>
<CheckoutScreen />
</MemoryRouter>
<QueryClientProvider client={queryClient}>
<MemoryRouter>
<CheckoutScreen />
</MemoryRouter>
</QueryClientProvider>
</MockedProvider>
);

Expand Down
26 changes: 17 additions & 9 deletions web/__test__/screens/SignUpInformationScreen.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import SignUpInformationScreen from "../../src/screens/SignUpInformationScreen";
import React from "react";
import "@testing-library/jest-dom";
import { MemoryRouter } from "react-router";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const mockedUseNavigate = vi.fn();
vi.mock("react-router-dom", async () => {
Expand All @@ -17,14 +18,17 @@ vi.mock("react-router-dom", async () => {
useNavigate: () => mockedUseNavigate,
};
});
const queryClient = new QueryClient();

describe("AboutUsScreen", () => {
it("renders questions correctly", async () => {
render(
<MockedProvider addTypename={false}>
<MemoryRouter>
<SignUpInformationScreen navbar={<></>} />
</MemoryRouter>
<QueryClientProvider client={queryClient}>
<MemoryRouter>
<SignUpInformationScreen navbar={<></>} />
</MemoryRouter>
</QueryClientProvider>
</MockedProvider>
);

Expand All @@ -50,9 +54,11 @@ describe("AboutUsScreen", () => {
it("does not render errors on load up", async () => {
render(
<MockedProvider addTypename={false}>
<MemoryRouter>
<SignUpInformationScreen navbar={<></>} />
</MemoryRouter>
<QueryClientProvider client={queryClient}>
<MemoryRouter>
<SignUpInformationScreen navbar={<></>} />
</MemoryRouter>
</QueryClientProvider>
</MockedProvider>
);
expect(await screen.queryByText("Please enter your full name")).toBeNull();
Expand All @@ -73,9 +79,11 @@ describe("AboutUsScreen", () => {
it("renders error correctly", async () => {
render(
<MockedProvider addTypename={false}>
<MemoryRouter>
<SignUpInformationScreen navbar={<></>} />
</MemoryRouter>
<QueryClientProvider client={queryClient}>
<MemoryRouter>
<SignUpInformationScreen navbar={<></>} />
</MemoryRouter>
</QueryClientProvider>
</MockedProvider>
);

Expand Down
93 changes: 47 additions & 46 deletions web/src/api/apiRequests.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import axios, { AxiosResponse } from "axios";
import {
AnswerList,
AttendanceList,
EventOrMembershipReturn,
MembershipExpiryDate as MembershipExpiryDate,
stripeSessionStatus,
UpdateUserInfoOrNewUser,
} from "../types/types";

const apiClient = axios.create({
Expand All @@ -10,37 +14,64 @@ const apiClient = axios.create({
});

// Get user metadata
export const getUserMetadaData = async (): Promise<AxiosResponse> => {
export const getUserMetaData = async (): Promise<AxiosResponse> => {
const response = await apiClient.get("/api/user/get-metadata", {
headers: {
"Content-Type": "application/json",
},
});

return response;
};

// Update user info
export const updateUserInfo = async (data: object): Promise<AxiosResponse> => {
export const updateUserInfo = async (
name: string,
universityId: string,
upi: string,
yearOfStudy: string,
fieldOfStudy: string,
isDomestic: string,
institution: string
): Promise<AxiosResponse> => {
const data = {
name,
universityId,
upi,
yearOfStudy,
fieldOfStudy,
isDomestic,
institution,
};

const response = await apiClient.post("/api/user/update-user-info", data, {
headers: {
"Content-Type": "application/json",
},
data: { data },
data,
});
return response;
};

export const updateUserTicketInfo = async (
data: object
): Promise<AxiosResponse> => {
ticketId: number,
name: string,
email: string,
phoneNumber: string,
answers: AnswerList[]
): Promise<UpdateUserInfoOrNewUser> => {
const data = {
ticketId,
name,
email,
phoneNumber,
answers,
};
const response = await apiClient.post("/api/user/user-ticket-info", data, {
headers: {
"Content-Type": "application/json",
},
});

return response;
return response.data;
};

// User membership expiry
Expand Down Expand Up @@ -85,22 +116,22 @@ export const postAttendanceUpdate = async (
// Get session status
export const getSessionStatus = async (
sessionId: string
): Promise<AxiosResponse> => {
): Promise<stripeSessionStatus> => {
const response = await apiClient.get(
`/api/stripe/session-status?session_id=${sessionId}`,
{
headers: { "Content-Type": "application/json" },
}
);

return response;
return response.data;
};

//Use this one to automatically create an Event or Membership checkout. Event checkout will decrement a ticket.
export const fetchEventOrMembershipCheckoutSecret = async (payload: {
priceId: string;
userTicketId: number;
}): Promise<string> => {
export const fetchEventOrMembershipCheckoutSecret = async (
priceId: string,
userTicketId: number
): Promise<EventOrMembershipReturn> => {
const payload = { priceId, userTicketId };
const response = await apiClient.post(
"/api/stripe/create-checkout",
payload,
Expand All @@ -109,35 +140,5 @@ export const fetchEventOrMembershipCheckoutSecret = async (payload: {
headers: { "Content-Type": "application/json" },
}
);

return response.data.clientSecret;
};

// TODO: Delete these? ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

// the ones below are for a separate approach. Safe to use.
export const fetchEventCheckoutSecret = async (payload: {
stripeKey: string;
}): Promise<string> => {
return await fetch("/api/stripe/create-event-checkout", {
method: "POST",
headers: { "Content-Type": "application/json" },
// add our own priceId here later for different products
body: JSON.stringify(payload),
})
.then((res) => res.json())
.then((data) => data.clientSecret);
};

export const fetchMembershipCheckoutSecret = async (payload: {
stripeKey: string;
}): Promise<string> => {
return await fetch("/api/stripe/create-membership-checkout", {
method: "POST",
headers: { "Content-Type": "application/json" },
// add our own priceId here later for different products
body: JSON.stringify(payload),
})
.then((res) => res.json())
.then((data) => data.clientSecret);
return response.data;
};
48 changes: 26 additions & 22 deletions web/src/components/forms/CheckoutInformation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Mapper } from "@utils/Mapper";
import LoadingSpinner from "@components/navigation/LoadingSpinner";
import { useNavigate } from "react-router";
import CheckoutInformationForm from "./CheckoutInformationForm";
import { updateUserTicketInfo } from "../../api/apiRequests";
import { useUpdateUserTicketInfo } from "../../hooks/api/useUpdateUserTicketInfo";

interface CheckoutInformationProps {
ticketId: number;
Expand Down Expand Up @@ -65,28 +65,32 @@ export default function CheckoutInformation({
const [submitError, setSubmitError] = useState(false);
const [submitLoading, setSubmitLoading] = useState(false);

// Submit ticket information to backend
const onSubmit = async (data: any) => {
try {
const response = await updateUserTicketInfo(data);
if (response.status === 200) {
// Form Submission Successful
setSubmitLoading(false);
// Move user to payment screen after the user ticket id is received
navigateToPaymentScreen(
response.data.updateUserInfoOrNewUser.userTicketId
);
} else {
setSubmitLoading(false);
setSubmitError(true);
}
} catch (error) {
const {
data: updateUserTicketInfoData,
mutateAsync,
status,
} = useUpdateUserTicketInfo();
// Update status text as it changes
useEffect(() => {
if (status === "success") {
setSubmitLoading(false);
navigateToPaymentScreen(
updateUserTicketInfoData.updateUserInfoOrNewUser.userTicketId
);
}

if (status == "pending") {
setSubmitLoading(true);
} else {
setSubmitLoading(false);
}

if (status == "error") {
setSubmitError(true);
}
};
}, [status]);

const handleSubmit = (
const onSubmit = async (
event: React.FormEvent<HTMLFormElement>,
name: string,
email: string,
Expand All @@ -101,8 +105,8 @@ export default function CheckoutInformation({
return rest;
});

// call post request
onSubmit({
// Mutation hook
mutateAsync({
ticketId: ticketId,
name: name,
email: email,
Expand All @@ -125,7 +129,7 @@ export default function CheckoutInformation({
return ticketAndQuestions ? (
<div className="drop-shadow-all mb-20 w-full rounded-lg bg-white px-2 py-12 sm:px-12">
<CheckoutInformationForm
handleSubmit={handleSubmit}
handleSubmit={onSubmit}
questions={ticketAndQuestions}
submitError={submitError}
/>
Expand Down
3 changes: 2 additions & 1 deletion web/src/components/forms/CheckoutInformationForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,8 @@ export default function CheckoutInformationForm({
<br />
If this is a double ticket, both ticket holders must be members.
<br />
If this is a Member ticket, make sure you haven't already purchased a Member ticket.
If this is a Member ticket, make sure you haven't already purchased
a Member ticket.
</p>
) : (
<></>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export default function PurchaseMembershipCard({
}

return (
<div className="drop-shadow-all w-[30rem] m-5 rounded-lg bg-white">
<div className="drop-shadow-all m-5 w-[30rem] rounded-lg bg-white">
<div>
<div className="flex items-center justify-center">
<img
Expand Down
Loading

0 comments on commit e43100d

Please sign in to comment.