From 5045fcd41b6c279dcc923cdc3c6a5cc6bddccb54 Mon Sep 17 00:00:00 2001 From: eddieyw <110384392+eddieyw@users.noreply.github.com> Date: Sun, 21 Jul 2024 15:23:38 +1200 Subject: [PATCH 01/13] Update README.md (#666) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cf8477284..0c12cccf8 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Project initiated by WDCC in 2023. **2023:** This project is focused on developing a new website for the University of Auckand's Snowsports Club (UASC) from their existing website, with the purpose of improving bookings for both users and admins. -**2024:** We are focused on creating a functional website the University of Auckand's Snowsports Club (UASC) for their existing website, by the clients proposed mid-year deadline. The functional website will comprise a sign-up process, membership management system and booking process. The follow-up focus after the proposed mid-year deadline is to improve the website through user experience updates, quality of life updates, implementing new features on the website and improving the website user inferface. +**2024:** We are focused on creating a functional website for the University of Auckand's Snowsports Club (UASC) by the clients proposed mid-year deadline. The functional website will comprise a sign-up process, membership management system and booking process. The follow-up focus after the mid-year deadline is to improve the website through implementing an events page (to replace the Facebook events page process), user experience updates, implementing new features on the website and improving the website user inferface. ## Get started From ab712c439d3c89e2c2c40f5a42cc58dba8d3cea0 Mon Sep 17 00:00:00 2001 From: Jeffery <61447509+jeffplays2005@users.noreply.github.com> Date: Mon, 22 Jul 2024 11:34:56 +1200 Subject: [PATCH 02/13] 287 Remove payment intent check in membership payment (#672) Removed `hasProcessingPaymentIntent` from StripeService.ts Removed corresponding check in the endpoint. --- .../business-layer/services/StripeService.ts | 19 ------------------- .../controllers/PaymentController.ts | 7 +++---- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/server/src/business-layer/services/StripeService.ts b/server/src/business-layer/services/StripeService.ts index 01cba323e..8537962c1 100644 --- a/server/src/business-layer/services/StripeService.ts +++ b/server/src/business-layer/services/StripeService.ts @@ -104,25 +104,6 @@ export default class StripeService { }) } - /** - * Checks for processing payments from a user. Used to avoid - * users from double paying after completing a checkout session for membership payments. - * - * @warning This assumes that users won't be paying for membership right after booking - * - * @param customerId `stripe_id` from the firebase document - */ - public async hasProcessingPaymentIntent(customerId: string) { - const { data } = await stripe.paymentIntents.list({ - customer: customerId - }) - const hasProcessingPaymentIntent = !!data.find( - (intent) => intent.status === "processing" - ) - - return hasProcessingPaymentIntent - } - /** * * Checks for processing payments from a user. Used to avoid diff --git a/server/src/service-layer/controllers/PaymentController.ts b/server/src/service-layer/controllers/PaymentController.ts index 4ec9e0589..2a75a088a 100644 --- a/server/src/service-layer/controllers/PaymentController.ts +++ b/server/src/service-layer/controllers/PaymentController.ts @@ -173,14 +173,13 @@ export class PaymentController extends Controller { } } /** - * See if user has pending payment or has recently paid -> if user didn't have a stripe id that means + * See if user has recently paid -> if user didn't have a stripe id that means * they couldn't have a completed session */ if ( - (await stripeService.hasRecentlyCompletedCheckoutSession( + await stripeService.hasRecentlyCompletedCheckoutSession( stripeCustomerId - )) || - (await stripeService.hasProcessingPaymentIntent(stripeCustomerId)) + ) ) { this.setStatus(409) return { From 927323ff771171ca70311ff70270585412eaa1cf Mon Sep 17 00:00:00 2001 From: AzizP786 <60585478+AzizPatel786@users.noreply.github.com> Date: Mon, 22 Jul 2024 15:09:57 +1200 Subject: [PATCH 03/13] 650 exec img comp (#664) * Added ExecImage component with hover and touch functionality. - Created ExecImage component to display executive images with hover and touch effects. - Integrated Next.js Image component for optimized image loading. - Added props for executive title and name, displayed on hover and touch. - Implemented CSS for hover effect and text overlay. - Created Storybook stories for visual testing and documentation. - Added ExecImageList component to render multiple ExecImage components using an array. * prettier and codegen * Refactored ExecImage component and its CSS to avoid using JavaScript for hover effects, using Tailwind CSS. * adjusted colour css * adjusted colour css --- .../generic/ExecImage/ExecImage.story.tsx | 23 +++++++++++++ .../generic/ExecImage/ExecImage.tsx | 32 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 client/src/components/generic/ExecImage/ExecImage.story.tsx create mode 100644 client/src/components/generic/ExecImage/ExecImage.tsx diff --git a/client/src/components/generic/ExecImage/ExecImage.story.tsx b/client/src/components/generic/ExecImage/ExecImage.story.tsx new file mode 100644 index 000000000..995c1276a --- /dev/null +++ b/client/src/components/generic/ExecImage/ExecImage.story.tsx @@ -0,0 +1,23 @@ +import React from "react" +import type { Meta, StoryObj } from "@storybook/react" +import ExecImage, { ExecImageProps } from "./ExecImage" + +const meta: Meta = { + component: ExecImage, + title: "Components/generic/ExecImage" +} + +type Story = StoryObj + +export default meta + +export const DefaultExecImage: Story = (args: ExecImageProps) => ( + +) + +DefaultExecImage.args = { + src: "https://static01.nyt.com/images/2022/12/30/multimedia/30soccer-ronaldo-1-76fd/30soccer-ronaldo-1-76fd-mediumSquareAt3X.jpg", + alt: "Placeholder Image", + title: "Admin suii", + name: "Ronaldo" +} diff --git a/client/src/components/generic/ExecImage/ExecImage.tsx b/client/src/components/generic/ExecImage/ExecImage.tsx new file mode 100644 index 000000000..f62b97a57 --- /dev/null +++ b/client/src/components/generic/ExecImage/ExecImage.tsx @@ -0,0 +1,32 @@ +import React from "react" +import Image from "next/image" + +export interface ExecImageProps { + src: string + alt: string + title: string + name: string +} + +const ExecImage: React.FC = ({ src, alt, title, name }) => { + return ( +
+ {alt} +
+
+

+ {title} +

+

{name}

+
+
+ ) +} + +export default ExecImage From 35524c0fbc62fedde444652b613cd972bffdd2a9 Mon Sep 17 00:00:00 2001 From: Jeffery <61447509+jeffplays2005@users.noreply.github.com> Date: Mon, 22 Jul 2024 16:28:44 +1200 Subject: [PATCH 04/13] 651 Document all backend endpoints (#673) * Add jsdoc to UserController * Tsoa generation and jsdoc for StripeWebhook endpoint * Add jsdoc for SignupController * Jsdoc for PaymentController and add fullstops * Add jsdoc for BookingController --- client/src/models/__generated__/schema.d.ts | 49 +++++++++++++++++-- .../src/middleware/__generated__/swagger.json | 48 ++++++++++++++---- .../controllers/BookingController.ts | 18 +++++++ .../controllers/PaymentController.ts | 18 +++++++ .../controllers/SignupController.ts | 6 ++- .../controllers/StripeWebhook.ts | 6 +++ .../controllers/UserController.ts | 16 ++++++ 7 files changed, 147 insertions(+), 14 deletions(-) diff --git a/client/src/models/__generated__/schema.d.ts b/client/src/models/__generated__/schema.d.ts index 657ea80a6..f4e296213 100644 --- a/client/src/models/__generated__/schema.d.ts +++ b/client/src/models/__generated__/schema.d.ts @@ -6,27 +6,38 @@ export interface paths { "/users/self": { + /** @description Fetches users additional info based on their uid. */ get: operations["GetSelf"]; }; "/users/edit-self": { + /** @description Edits the user's additional info based on their uid. */ patch: operations["EditSelf"]; }; "/users/delete-user": { + /** @description Deletes a user based on their uid. This requires an admin JWT token. */ delete: operations["DeleteUser"]; }; "/webhook": { + /** + * @description Webhook endpoint for Stripe events. + * This single endpoint is setup in the Stripe developer config to handle various events. + */ post: operations["ReceiveWebhook"]; }; "/signup": { + /** @description Signs up a user and creates a user record in the database. Also creates a JWT token for the user in AuthService. */ post: operations["Signup"]; }; "/payment/membership_prices": { + /** @description Fetches the prices of the membership products from Stripe. */ get: operations["GetMembershipPrices"]; }; "/payment/checkout_status": { + /** @description Fetches the details of a checkout session based on a stripe checkout session id. */ get: operations["GetCheckoutSessionDetails"]; }; "/payment/membership": { + /** @description Creates a checkout session for membership payment. */ post: operations["GetMembershipPayment"]; }; "/payment/booking": { @@ -38,16 +49,22 @@ export interface paths { post: operations["GetBookingPayment"]; }; "/bookings/create-bookings": { + /** @description An admin method to create bookings for a list of users within a date range. */ post: operations["CreateBookings"]; }; "/bookings": { + /** @description Fetches all bookings for a user based on their UID. */ get: operations["GetAllBookings"]; }; "/bookings/available-dates": { + /** @description Fetches all available booking dates within a date range. */ post: operations["GetAvailableDates"]; }; "/bookings/fetch-users": { - /** @description This method fetches users based on a booking date range. */ + /** + * @description This method fetches users based on a booking date range. + * This method requires an admin JWT token. + */ post: operations["FetchUsersByBookingDateRange"]; }; "/admin/bookings/make-dates-available": { @@ -469,6 +486,7 @@ export type external = Record; export interface operations { + /** @description Fetches users additional info based on their uid. */ GetSelf: { responses: { /** @description Fetched self data */ @@ -498,7 +516,9 @@ export interface operations { }; }; }; + /** @description Edits the user's additional info based on their uid. */ EditSelf: { + /** @description - The updated user additional info, note that the stripe_id is omitted. */ requestBody: { content: { "application/json": components["schemas"]["EditSelfRequestBody"]; @@ -511,7 +531,9 @@ export interface operations { }; }; }; + /** @description Deletes a user based on their uid. This requires an admin JWT token. */ DeleteUser: { + /** @description - The uid of the user to be deleted. */ requestBody: { content: { "application/json": components["schemas"]["DeleteUserRequestBody"]; @@ -526,6 +548,10 @@ export interface operations { }; }; }; + /** + * @description Webhook endpoint for Stripe events. + * This single endpoint is setup in the Stripe developer config to handle various events. + */ ReceiveWebhook: { responses: { /** @description Webhook post received */ @@ -534,7 +560,9 @@ export interface operations { }; }; }; + /** @description Signs up a user and creates a user record in the database. Also creates a JWT token for the user in AuthService. */ Signup: { + /** @description - The user's email and their user additional info. */ requestBody: { content: { "application/json": components["schemas"]["UserSignupBody"]; @@ -549,9 +577,10 @@ export interface operations { }; }; }; + /** @description Fetches the prices of the membership products from Stripe. */ GetMembershipPrices: { responses: { - /** @description Ok */ + /** @description The prices of the membership products. */ 200: { content: { "application/json": components["schemas"]["MembershipStripeProductResponse"]; @@ -559,9 +588,11 @@ export interface operations { }; }; }; + /** @description Fetches the details of a checkout session based on a stripe checkout session id. */ GetCheckoutSessionDetails: { parameters: { query: { + /** @description The id of the stripe checkout session to fetch. */ sessionId: string; }; }; @@ -580,7 +611,9 @@ export interface operations { }; }; }; + /** @description Creates a checkout session for membership payment. */ GetMembershipPayment: { + /** @description The request body containing the membership type. */ requestBody: { content: { "application/json": components["schemas"]["UserPaymentRequestModel"]; @@ -601,6 +634,7 @@ export interface operations { * the last 30 minutes (the minimum period stripe has to persist a session for) */ GetBookingPayment: { + /** @description The request body containing the date ranges for the booking. */ requestBody: { content: { "application/json": components["schemas"]["UserBookingRequestingModel"]; @@ -615,7 +649,9 @@ export interface operations { }; }; }; + /** @description An admin method to create bookings for a list of users within a date range. */ CreateBookings: { + /** @description - The date range and list of user ids to create bookings for. */ requestBody: { content: { "application/json": components["schemas"]["CreateBookingsRequestModel"]; @@ -630,6 +666,7 @@ export interface operations { }; }; }; + /** @description Fetches all bookings for a user based on their UID. */ GetAllBookings: { responses: { /** @description Found bookings */ @@ -640,7 +677,9 @@ export interface operations { }; }; }; + /** @description Fetches all available booking dates within a date range. */ GetAvailableDates: { + /** @description - The date range to check for available booking slots. */ requestBody: { content: { "application/json": components["schemas"]["AvailableDatesRequestModel"]; @@ -655,8 +694,12 @@ export interface operations { }; }; }; - /** @description This method fetches users based on a booking date range. */ + /** + * @description This method fetches users based on a booking date range. + * This method requires an admin JWT token. + */ FetchUsersByBookingDateRange: { + /** @description - The date range to check for user bookings. */ requestBody: { content: { "application/json": components["schemas"]["BookingsByDateRangeRequestModel"]; diff --git a/server/src/middleware/__generated__/swagger.json b/server/src/middleware/__generated__/swagger.json index 406ef662e..caa5e6c45 100644 --- a/server/src/middleware/__generated__/swagger.json +++ b/server/src/middleware/__generated__/swagger.json @@ -1146,6 +1146,7 @@ } } }, + "description": "Fetches users additional info based on their uid.", "security": [ { "jwt": [] @@ -1162,6 +1163,7 @@ "description": "Successful edit" } }, + "description": "Edits the user's additional info based on their uid.", "security": [ { "jwt": [] @@ -1169,11 +1171,13 @@ ], "parameters": [], "requestBody": { + "description": "- The updated user additional info, note that the stripe_id is omitted.", "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EditSelfRequestBody" + "$ref": "#/components/schemas/EditSelfRequestBody", + "description": "- The updated user additional info, note that the stripe_id is omitted." } } } @@ -1200,6 +1204,7 @@ } } }, + "description": "Deletes a user based on their uid. This requires an admin JWT token.", "security": [ { "jwt": [ @@ -1209,11 +1214,13 @@ ], "parameters": [], "requestBody": { + "description": "- The uid of the user to be deleted.", "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeleteUserRequestBody" + "$ref": "#/components/schemas/DeleteUserRequestBody", + "description": "- The uid of the user to be deleted." } } } @@ -1228,6 +1235,7 @@ "description": "Webhook post received" } }, + "description": "Webhook endpoint for Stripe events.\nThis single endpoint is setup in the Stripe developer config to handle various events.", "security": [], "parameters": [] } @@ -1247,14 +1255,17 @@ } } }, + "description": "Signs up a user and creates a user record in the database. Also creates a JWT token for the user in AuthService.", "security": [], "parameters": [], "requestBody": { + "description": "- The user's email and their user additional info.", "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UserSignupBody" + "$ref": "#/components/schemas/UserSignupBody", + "description": "- The user's email and their user additional info." } } } @@ -1266,7 +1277,7 @@ "operationId": "GetMembershipPrices", "responses": { "200": { - "description": "Ok", + "description": "The prices of the membership products.", "content": { "application/json": { "schema": { @@ -1276,6 +1287,7 @@ } } }, + "description": "Fetches the prices of the membership products from Stripe.", "security": [], "parameters": [] } @@ -1316,6 +1328,7 @@ } } }, + "description": "Fetches the details of a checkout session based on a stripe checkout session id.", "security": [ { "jwt": [] @@ -1323,6 +1336,7 @@ ], "parameters": [ { + "description": "The id of the stripe checkout session to fetch.", "in": "query", "name": "sessionId", "required": true, @@ -1348,6 +1362,7 @@ } } }, + "description": "Creates a checkout session for membership payment.", "security": [ { "jwt": [] @@ -1355,11 +1370,13 @@ ], "parameters": [], "requestBody": { + "description": "The request body containing the membership type.", "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UserPaymentRequestModel" + "$ref": "#/components/schemas/UserPaymentRequestModel", + "description": "The request body containing the membership type." } } } @@ -1391,11 +1408,13 @@ ], "parameters": [], "requestBody": { + "description": "The request body containing the date ranges for the booking.", "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UserBookingRequestingModel" + "$ref": "#/components/schemas/UserBookingRequestingModel", + "description": "The request body containing the date ranges for the booking." } } } @@ -1417,6 +1436,7 @@ } } }, + "description": "An admin method to create bookings for a list of users within a date range.", "security": [ { "jwt": [ @@ -1426,11 +1446,13 @@ ], "parameters": [], "requestBody": { + "description": "- The date range and list of user ids to create bookings for.", "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateBookingsRequestModel" + "$ref": "#/components/schemas/CreateBookingsRequestModel", + "description": "- The date range and list of user ids to create bookings for." } } } @@ -1452,6 +1474,7 @@ } } }, + "description": "Fetches all bookings for a user based on their UID.", "security": [ { "jwt": [ @@ -1477,6 +1500,7 @@ } } }, + "description": "Fetches all available booking dates within a date range.", "security": [ { "jwt": [ @@ -1486,11 +1510,13 @@ ], "parameters": [], "requestBody": { + "description": "- The date range to check for available booking slots.", "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AvailableDatesRequestModel" + "$ref": "#/components/schemas/AvailableDatesRequestModel", + "description": "- The date range to check for available booking slots." } } } @@ -1512,7 +1538,7 @@ } } }, - "description": "This method fetches users based on a booking date range.", + "description": "This method fetches users based on a booking date range.\nThis method requires an admin JWT token.", "security": [ { "jwt": [ @@ -1522,11 +1548,13 @@ ], "parameters": [], "requestBody": { + "description": "- The date range to check for user bookings.", "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BookingsByDateRangeRequestModel" + "$ref": "#/components/schemas/BookingsByDateRangeRequestModel", + "description": "- The date range to check for user bookings." } } } diff --git a/server/src/service-layer/controllers/BookingController.ts b/server/src/service-layer/controllers/BookingController.ts index 69a92fc93..0bf8e03ad 100644 --- a/server/src/service-layer/controllers/BookingController.ts +++ b/server/src/service-layer/controllers/BookingController.ts @@ -42,6 +42,11 @@ import BookingUtils from "business-layer/utils/BookingUtils" @Route("bookings") export class BookingController extends Controller { + /** + * An admin method to create bookings for a list of users within a date range. + * @param requestBody - The date range and list of user ids to create bookings for. + * @returns A list of users and timestamps that were successfully added to the booking slots. + */ @SuccessResponse("200", "Bookings successfully created") @Security("jwt", ["admin"]) @Post("create-bookings") @@ -114,6 +119,11 @@ export class BookingController extends Controller { } } + /** + * Fetches all bookings for a user based on their UID. + * @param request - The request object that includes the UserRecord. + * @returns A list of booking string dates. + */ @SuccessResponse("200", "Found bookings") @Security("jwt", ["member"]) @Get() @@ -151,6 +161,11 @@ export class BookingController extends Controller { } } + /** + * Fetches all available booking dates within a date range. + * @param requestBody - The date range to check for available booking slots. + * @returns A list of available booking dates in an array of strings. + */ @SuccessResponse("200", "Availabilities found") @Security("jwt", ["member"]) @Post("available-dates") @@ -252,6 +267,9 @@ export class BookingController extends Controller { /** * This method fetches users based on a booking date range. + * This method requires an admin JWT token. + * @param requestBody - The date range to check for user bookings. + * @returns A list of users data, booking ids and booking timestamps. */ @SuccessResponse("200", "Users found") @Security("jwt", ["admin"]) diff --git a/server/src/service-layer/controllers/PaymentController.ts b/server/src/service-layer/controllers/PaymentController.ts index 2a75a088a..9b81acdf8 100644 --- a/server/src/service-layer/controllers/PaymentController.ts +++ b/server/src/service-layer/controllers/PaymentController.ts @@ -46,6 +46,10 @@ import BookingUtils from "business-layer/utils/BookingUtils" @Route("payment") export class PaymentController extends Controller { + /** + * Fetches the prices of the membership products from Stripe. + * @returns The prices of the membership products. + */ @Get("membership_prices") public async getMembershipPrices(): Promise { const stripeService = new StripeService() @@ -103,6 +107,11 @@ export class PaymentController extends Controller { } } + /** + * Fetches the details of a checkout session based on a stripe checkout session id. + * @param sessionId The id of the stripe checkout session to fetch. + * @returns The details of the checkout session. + */ @SuccessResponse("200", "Session Fetched") @Security("jwt") @Get("checkout_status") @@ -124,6 +133,12 @@ export class PaymentController extends Controller { } } + /** + * Creates a checkout session for membership payment. + * @param request The user's record, this endpoint extracts the uid and customClaims to check for membership status. + * @param requestBody The request body containing the membership type. + * @returns The client secret of the checkout session and membership type. + */ @SuccessResponse("200", "Session created") @Security("jwt") @Post("membership") @@ -249,6 +264,9 @@ export class PaymentController extends Controller { * Creates a new booking session for the date ranges passed in, * will return any existing sessions if they have been started in * the last 30 minutes (the minimum period stripe has to persist a session for) + * @param request The user's record, the uid is used from this to identify the user. + * @param requestBody The request body containing the date ranges for the booking. + * @returns The client secret of the checkout session. */ @SuccessResponse("200", "Created booking checkout session") @Security("jwt", ["member"]) diff --git a/server/src/service-layer/controllers/SignupController.ts b/server/src/service-layer/controllers/SignupController.ts index 148e63eeb..c93c6ea1e 100644 --- a/server/src/service-layer/controllers/SignupController.ts +++ b/server/src/service-layer/controllers/SignupController.ts @@ -8,9 +8,13 @@ import { parseFirebaseError } from "business-layer/utils/FirebaseErrorParser" @Route("signup") export class UserSignup extends Controller { + /** + * Signs up a user and creates a user record in the database. Also creates a JWT token for the user in AuthService. + * @param requestBody - The user's email and their user additional info. + * @returns The JWT token and the user's UID. + */ @Post() @SuccessResponse(200, "Signup successful") - // return a JWT token at the end public async signup( @Body() requestBody: UserSignupBody ): Promise { diff --git a/server/src/service-layer/controllers/StripeWebhook.ts b/server/src/service-layer/controllers/StripeWebhook.ts index 31a1b015b..29c08debc 100644 --- a/server/src/service-layer/controllers/StripeWebhook.ts +++ b/server/src/service-layer/controllers/StripeWebhook.ts @@ -11,6 +11,12 @@ import { @Route("webhook") export class StripeWebhook extends Controller { + /** + * Webhook endpoint for Stripe events. + * This single endpoint is setup in the Stripe developer config to handle various events. + * @param request - The raw request that's passed from Stripe. + * @returns void. + */ @Post() @SuccessResponse(200, "Webhook post received") public async receiveWebhook(@Request() request: any): Promise { diff --git a/server/src/service-layer/controllers/UserController.ts b/server/src/service-layer/controllers/UserController.ts index 0ce4d464c..018e74d2c 100644 --- a/server/src/service-layer/controllers/UserController.ts +++ b/server/src/service-layer/controllers/UserController.ts @@ -22,6 +22,11 @@ import { AuthServiceClaims } from "business-layer/utils/AuthServiceClaims" @Route("users") export class UsersController extends Controller { + /** + * Fetches users additional info based on their uid. + * @param request - Takes a UserRecord and uses the UID to fetch the user's additional info. + * @returns The additionalInfo of the user. + */ @SuccessResponse("200", "Fetched self data") @Security("jwt") @Get("self") @@ -40,6 +45,12 @@ export class UsersController extends Controller { return data } + /** + * Edits the user's additional info based on their uid. + * @param request - Takes a UserRecord and uses the UID to edit the user's additional info. + * @param requestBody - The updated user additional info, note that the stripe_id is omitted. + * @returns void. + */ @SuccessResponse("200", "Successful edit") @Security("jwt") @Patch("edit-self") @@ -59,6 +70,11 @@ export class UsersController extends Controller { } } + /** + * Deletes a user based on their uid. This requires an admin JWT token. + * @param requestBody - The uid of the user to be deleted. + * @returns void. + */ @SuccessResponse("200", "Deleted user") @Security("jwt", ["admin"]) @Delete("delete-user") From dd6896aa9fa32b40e0ce18978a08d47c43efd2d8 Mon Sep 17 00:00:00 2001 From: Jeffery <61447509+jeffplays2005@users.noreply.github.com> Date: Mon, 22 Jul 2024 16:43:48 +1200 Subject: [PATCH 05/13] 651 Add jsdoc for AdminController (#674) * 651 Add jsdoc for AdminController --- client/src/models/__generated__/schema.d.ts | 71 +++++++++++++++++++ .../src/middleware/__generated__/swagger.json | 44 +++++++++--- .../controllers/AdminController.ts | 66 ++++++++++++++++- 3 files changed, 172 insertions(+), 9 deletions(-) diff --git a/client/src/models/__generated__/schema.d.ts b/client/src/models/__generated__/schema.d.ts index f4e296213..796d500e8 100644 --- a/client/src/models/__generated__/schema.d.ts +++ b/client/src/models/__generated__/schema.d.ts @@ -72,9 +72,11 @@ export interface paths { post: operations["MakeDateAvailable"]; }; "/admin/bookings/make-dates-unavailable": { + /** @description Decreases availability count to 0 for all booking slots in a date range. */ post: operations["MakeDateUnavailable"]; }; "/admin/bookings/delete": { + /** @description Delete a users booking by booking ID. */ post: operations["RemoveBooking"]; }; "/admin/users": { @@ -82,24 +84,52 @@ export interface paths { get: operations["GetAllUsers"]; }; "/admin/users/{uid}": { + /** + * @description Get a user by their UID. + * Requires an admin JWT token. + */ get: operations["GetUser"]; }; "/admin/users/create": { + /** + * @description Adds a new user to the database with their UID and user data. + * Requires an admin JWT token. + */ put: operations["CreateUser"]; }; "/admin/users/bulk-edit": { + /** + * @description Edits a list of users with updated user additional info. + * Requires an admin JWT token. + */ patch: operations["EditUsers"]; }; "/admin/users/promote": { + /** + * @description Promotes a user to a member. This returns a conflict when the user is already a member. + * Requires an admin JWT token. + */ put: operations["PromoteUser"]; }; "/admin/users/demote": { + /** + * @description Demotes a member to a guest. This returns a conflict when the user is already a guest. + * Requires an admin JWT token. + */ put: operations["DemoteUser"]; }; "/admin/users/demote-all": { + /** + * @description Demotes all non-admin users to guests. This is used to purge all membership statuses at the end of a billing cycle. + * Requires an admin JWT token. + */ patch: operations["DemoteAllUsers"]; }; "/admin/users/add-coupon": { + /** + * @description Adds a coupon to a user's stripe id. + * Requires an admin JWT token. + */ post: operations["AddCoupon"]; }; } @@ -716,6 +746,7 @@ export interface operations { }; /** @description Booking Operations */ MakeDateAvailable: { + /** @description - The start and end date of the range and the number of slots to add. */ requestBody: { content: { "application/json": components["schemas"]["MakeDatesAvailableRequestBody"]; @@ -730,7 +761,9 @@ export interface operations { }; }; }; + /** @description Decreases availability count to 0 for all booking slots in a date range. */ MakeDateUnavailable: { + /** @description - The start and end date of the range, the number of slots is omitted as we're decreases all slots to 0. */ requestBody: { content: { "application/json": components["schemas"]["Omit_MakeDatesAvailableRequestBody.slots_"]; @@ -745,7 +778,9 @@ export interface operations { }; }; }; + /** @description Delete a users booking by booking ID. */ RemoveBooking: { + /** @description - The booking ID to delete. */ requestBody: { content: { "application/json": components["schemas"]["DeleteBookingRequest"]; @@ -764,7 +799,9 @@ export interface operations { GetAllUsers: { parameters: { query?: { + /** @description - The cursor to start fetching users from. Essentially a pagination token. */ cursor?: string; + /** @description - The number of users to fetch. Defaults to 100. Is also a maximum of 100 users per fetch */ toFetch?: number; }; }; @@ -777,9 +814,14 @@ export interface operations { }; }; }; + /** + * @description Get a user by their UID. + * Requires an admin JWT token. + */ GetUser: { parameters: { path: { + /** @description - The UID of the user to fetch. */ uid: string; }; }; @@ -792,7 +834,12 @@ export interface operations { }; }; }; + /** + * @description Adds a new user to the database with their UID and user data. + * Requires an admin JWT token. + */ CreateUser: { + /** @description - The user data to create and their UID. */ requestBody: { content: { "application/json": components["schemas"]["CreateUserRequestBody"]; @@ -805,7 +852,12 @@ export interface operations { }; }; }; + /** + * @description Edits a list of users with updated user additional info. + * Requires an admin JWT token. + */ EditUsers: { + /** @description - The list of users to edit and their updated information. */ requestBody: { content: { "application/json": components["schemas"]["EditUsersRequestBody"]; @@ -818,7 +870,12 @@ export interface operations { }; }; }; + /** + * @description Promotes a user to a member. This returns a conflict when the user is already a member. + * Requires an admin JWT token. + */ PromoteUser: { + /** @description - The UID of the user to promote. */ requestBody: { content: { "application/json": components["schemas"]["PromoteUserRequestBody"]; @@ -831,7 +888,12 @@ export interface operations { }; }; }; + /** + * @description Demotes a member to a guest. This returns a conflict when the user is already a guest. + * Requires an admin JWT token. + */ DemoteUser: { + /** @description - The UID of the user to demote. */ requestBody: { content: { "application/json": components["schemas"]["DemoteUserRequestBody"]; @@ -844,6 +906,10 @@ export interface operations { }; }; }; + /** + * @description Demotes all non-admin users to guests. This is used to purge all membership statuses at the end of a billing cycle. + * Requires an admin JWT token. + */ DemoteAllUsers: { responses: { /** @description Demoted all non-admin users */ @@ -852,7 +918,12 @@ export interface operations { }; }; }; + /** + * @description Adds a coupon to a user's stripe id. + * Requires an admin JWT token. + */ AddCoupon: { + /** @description - The UID of the user to add the coupon to and the quantity of coupons to add. */ requestBody: { content: { "application/json": components["schemas"]["AddCouponRequestBody"]; diff --git a/server/src/middleware/__generated__/swagger.json b/server/src/middleware/__generated__/swagger.json index caa5e6c45..cf4498544 100644 --- a/server/src/middleware/__generated__/swagger.json +++ b/server/src/middleware/__generated__/swagger.json @@ -1586,11 +1586,13 @@ ], "parameters": [], "requestBody": { + "description": "- The start and end date of the range and the number of slots to add.", "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MakeDatesAvailableRequestBody" + "$ref": "#/components/schemas/MakeDatesAvailableRequestBody", + "description": "- The start and end date of the range and the number of slots to add." } } } @@ -1612,6 +1614,7 @@ } } }, + "description": "Decreases availability count to 0 for all booking slots in a date range.", "security": [ { "jwt": [ @@ -1621,11 +1624,13 @@ ], "parameters": [], "requestBody": { + "description": "- The start and end date of the range, the number of slots is omitted as we're decreases all slots to 0.", "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Omit_MakeDatesAvailableRequestBody.slots_" + "$ref": "#/components/schemas/Omit_MakeDatesAvailableRequestBody.slots_", + "description": "- The start and end date of the range, the number of slots is omitted as we're decreases all slots to 0." } } } @@ -1647,6 +1652,7 @@ } } }, + "description": "Delete a users booking by booking ID.", "security": [ { "jwt": [ @@ -1656,11 +1662,13 @@ ], "parameters": [], "requestBody": { + "description": "- The booking ID to delete.", "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeleteBookingRequest" + "$ref": "#/components/schemas/DeleteBookingRequest", + "description": "- The booking ID to delete." } } } @@ -1692,6 +1700,7 @@ ], "parameters": [ { + "description": "- The cursor to start fetching users from. Essentially a pagination token.", "in": "query", "name": "cursor", "required": false, @@ -1700,6 +1709,7 @@ } }, { + "description": "- The number of users to fetch. Defaults to 100. Is also a maximum of 100 users per fetch", "in": "query", "name": "toFetch", "required": false, @@ -1726,6 +1736,7 @@ } } }, + "description": "Get a user by their UID.\nRequires an admin JWT token.", "security": [ { "jwt": [ @@ -1735,6 +1746,7 @@ ], "parameters": [ { + "description": "- The UID of the user to fetch.", "in": "path", "name": "uid", "required": true, @@ -1753,6 +1765,7 @@ "description": "Created" } }, + "description": "Adds a new user to the database with their UID and user data.\nRequires an admin JWT token.", "security": [ { "jwt": [ @@ -1762,11 +1775,13 @@ ], "parameters": [], "requestBody": { + "description": "- The user data to create and their UID.", "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateUserRequestBody" + "$ref": "#/components/schemas/CreateUserRequestBody", + "description": "- The user data to create and their UID." } } } @@ -1781,6 +1796,7 @@ "description": "Edited" } }, + "description": "Edits a list of users with updated user additional info.\nRequires an admin JWT token.", "security": [ { "jwt": [ @@ -1790,11 +1806,13 @@ ], "parameters": [], "requestBody": { + "description": "- The list of users to edit and their updated information.", "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EditUsersRequestBody" + "$ref": "#/components/schemas/EditUsersRequestBody", + "description": "- The list of users to edit and their updated information." } } } @@ -1809,6 +1827,7 @@ "description": "Promoted user" } }, + "description": "Promotes a user to a member. This returns a conflict when the user is already a member.\nRequires an admin JWT token.", "security": [ { "jwt": [ @@ -1818,11 +1837,13 @@ ], "parameters": [], "requestBody": { + "description": "- The UID of the user to promote.", "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PromoteUserRequestBody" + "$ref": "#/components/schemas/PromoteUserRequestBody", + "description": "- The UID of the user to promote." } } } @@ -1837,6 +1858,7 @@ "description": "Demoted user" } }, + "description": "Demotes a member to a guest. This returns a conflict when the user is already a guest.\nRequires an admin JWT token.", "security": [ { "jwt": [ @@ -1846,11 +1868,13 @@ ], "parameters": [], "requestBody": { + "description": "- The UID of the user to demote.", "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DemoteUserRequestBody" + "$ref": "#/components/schemas/DemoteUserRequestBody", + "description": "- The UID of the user to demote." } } } @@ -1865,6 +1889,7 @@ "description": "Demoted all non-admin users" } }, + "description": "Demotes all non-admin users to guests. This is used to purge all membership statuses at the end of a billing cycle.\nRequires an admin JWT token.", "security": [ { "jwt": [ @@ -1883,6 +1908,7 @@ "description": "Coupon Added" } }, + "description": "Adds a coupon to a user's stripe id.\nRequires an admin JWT token.", "security": [ { "jwt": [ @@ -1892,11 +1918,13 @@ ], "parameters": [], "requestBody": { + "description": "- The UID of the user to add the coupon to and the quantity of coupons to add.", "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AddCouponRequestBody" + "$ref": "#/components/schemas/AddCouponRequestBody", + "description": "- The UID of the user to add the coupon to and the quantity of coupons to add." } } } diff --git a/server/src/service-layer/controllers/AdminController.ts b/server/src/service-layer/controllers/AdminController.ts index f78924d4b..db395ea83 100644 --- a/server/src/service-layer/controllers/AdminController.ts +++ b/server/src/service-layer/controllers/AdminController.ts @@ -54,6 +54,12 @@ export class AdminController extends Controller { /** * Booking Operations */ + + /** + * Increases availability count for bookings slots in a date range. + * @param requestBody - The start and end date of the range and the number of slots to add. + * @returns An updated list of booking timestamps and their corresponding booking slot IDs. + */ @SuccessResponse("201", "Slot made available") @Post("/bookings/make-dates-available") public async makeDateAvailable( @@ -103,6 +109,11 @@ export class AdminController extends Controller { } } + /** + * Decreases availability count to 0 for all booking slots in a date range. + * @param requestBody - The start and end date of the range, the number of slots is omitted as we're decreases all slots to 0. + * @returns An updated list of booking timestamps and their corresponding booking slot IDs. + */ @SuccessResponse("201", "Slot made unavailable") @Post("/bookings/make-dates-unavailable") public async makeDateUnavailable( @@ -153,6 +164,11 @@ export class AdminController extends Controller { } } + /** + * Delete a users booking by booking ID. + * @param requestBody - The booking ID to delete. + * @returns The user ID of the user who made the booking. + */ @SuccessResponse("200", "Booking deleted successfuly") // TODO: Refactor this to be a DELETE request @Post("/bookings/delete") @@ -178,6 +194,14 @@ export class AdminController extends Controller { /** * User Operations */ + + /** + * Get all users in the system. + * Requires an admin JWT token. + * @param cursor - The cursor to start fetching users from. Essentially a pagination token. + * @param toFetch - The number of users to fetch. Defaults to 100. Is also a maximum of 100 users per fetch + * @returns The list of users that were fetched. + */ @SuccessResponse("200", "Users found") @Security("jwt", ["admin"]) @Get("/users") @@ -244,6 +268,12 @@ export class AdminController extends Controller { } } + /** + * Get a user by their UID. + * Requires an admin JWT token. + * @param uid - The UID of the user to fetch. + * @returns The user data of the user with the given UID. + */ @SuccessResponse("200", "User found") @Get("/users/{uid}") public async getUser(@Path() uid: string): Promise { @@ -277,6 +307,12 @@ export class AdminController extends Controller { } } + /** + * Adds a new user to the database with their UID and user data. + * Requires an admin JWT token. + * @param requestBody - The user data to create and their UID. + * @returns void. + */ @SuccessResponse("200", "Created") @Put("/users/create") public async createUser( @@ -291,6 +327,12 @@ export class AdminController extends Controller { this.setStatus(200) } + /** + * Edits a list of users with updated user additional info. + * Requires an admin JWT token. + * @param requestBody - The list of users to edit and their updated information. + * @returns void. + */ @SuccessResponse("200", "Edited") @Patch("/users/bulk-edit") public async editUsers( @@ -306,7 +348,12 @@ export class AdminController extends Controller { this.setStatus(200) } - // ticket 202 - endpoint to demote/promote users + /** + * Promotes a user to a member. This returns a conflict when the user is already a member. + * Requires an admin JWT token. + * @param requestBody - The UID of the user to promote. + * @returns void. + */ @SuccessResponse("200", "Promoted user") @Put("/users/promote") // set user membership to "member" @@ -335,6 +382,12 @@ export class AdminController extends Controller { } } + /** + * Demotes a member to a guest. This returns a conflict when the user is already a guest. + * Requires an admin JWT token. + * @param requestBody - The UID of the user to demote. + * @returns void. + */ @SuccessResponse("200", "Demoted user") @Put("/users/demote") // set user membership type to `undefined` @@ -362,6 +415,11 @@ export class AdminController extends Controller { } } + /** + * Demotes all non-admin users to guests. This is used to purge all membership statuses at the end of a billing cycle. + * Requires an admin JWT token. + * @returns void. + */ @SuccessResponse("200", "Demoted all non-admin users") @Patch("/users/demote-all") public async demoteAllUsers(): Promise { @@ -382,6 +440,12 @@ export class AdminController extends Controller { } } + /** + * Adds a coupon to a user's stripe id. + * Requires an admin JWT token. + * @param requestBody - The UID of the user to add the coupon to and the quantity of coupons to add. + * @returns void. + */ @SuccessResponse("200", "Coupon Added") @Post("users/add-coupon") public async addCoupon( From 9fcb92c00639c78b7442e41d19bd53c2cc9223fd Mon Sep 17 00:00:00 2001 From: Benson Cho <100653148+bcho892@users.noreply.github.com> Date: Mon, 22 Jul 2024 19:04:42 +1200 Subject: [PATCH 06/13] enforce dietary requirements on booking creation (#676) --- .../BookingCreation/BookingCreation.test.tsx | 17 ++++ .../BookingCreation/BookingCreation.tsx | 78 +++++++++++++------ 2 files changed, 70 insertions(+), 25 deletions(-) diff --git a/client/src/components/composite/Booking/BookingCreation/BookingCreation.test.tsx b/client/src/components/composite/Booking/BookingCreation/BookingCreation.test.tsx index e0244beee..4c25b44df 100644 --- a/client/src/components/composite/Booking/BookingCreation/BookingCreation.test.tsx +++ b/client/src/components/composite/Booking/BookingCreation/BookingCreation.test.tsx @@ -50,8 +50,19 @@ describe("RequirementCheckBoxes", () => { "agreed-to-general-policy-checkbox" ) + const dietaryRequirementsInput = getByTestId("dietary-requirements-input") + fireEvent.click(nightPolicyCheckbox) fireEvent.click(bookingPolicyCheckbox) + fireEvent.change(dietaryRequirementsInput, { + target: { value: "i" } + }) + + expect(mockOnValidityChange).toHaveBeenCalledWith(false) + + fireEvent.change(dietaryRequirementsInput, { + target: { value: "i3" } + }) expect(mockOnValidityChange).toHaveBeenCalledWith(true) }) @@ -67,8 +78,14 @@ describe("RequirementCheckBoxes", () => { "agreed-to-general-policy-checkbox" ) + const dietaryRequirementsInput = getByTestId("dietary-requirements-input") + fireEvent.click(nightPolicyCheckbox) + fireEvent.change(dietaryRequirementsInput, { + target: { value: "ii" } + }) + expect(mockOnValidityChange).toHaveBeenCalledWith(false) fireEvent.click(bookingPolicyCheckbox) diff --git a/client/src/components/composite/Booking/BookingCreation/BookingCreation.tsx b/client/src/components/composite/Booking/BookingCreation/BookingCreation.tsx index 8f77a2701..adfd782c7 100644 --- a/client/src/components/composite/Booking/BookingCreation/BookingCreation.tsx +++ b/client/src/components/composite/Booking/BookingCreation/BookingCreation.tsx @@ -165,7 +165,9 @@ export const CreateBookingSection = ({ variant="default" onClick={() => { if (!isValidForCreation) { - alert("Please check all the required acknowledgements") + alert( + "Please check all the required acknowledgements and enter your dietary requirements" + ) return } if ( @@ -312,12 +314,7 @@ export const CreateBookingSection = ({ onValidityChange={(newValid) => { setIsValidForCreation(newValid) }} - /> - - handleAllergyChange?.(e.target.value)} - label="Please describe your dietary requirements" - placeholder="Enter dietary requirements here" + handleAllergyChange={handleAllergyChange} /> {hasExistingSession ? ( @@ -338,47 +335,78 @@ interface IRequirementCheckBoxes { * @param newValid if the current state of the checkboxes is valid */ onValidityChange: (newValid: boolean) => void + + /** + * @param newAllergies + */ + handleAllergyChange?: (newAllergies: string) => void } +/** + * To allow users to enter "no" + */ +const DIETARY_REQUIREMENTS_MIN_LENGTH = 2 as const + /** * Provides a way to see if the user has agreed to all required policy * @deprecated only for internal use in `BookingCreation`, exported for testing purposes */ export const RequirementCheckBoxes = ({ - onValidityChange + onValidityChange, + handleAllergyChange }: IRequirementCheckBoxes) => { const [acceptedRequirements, setAcceptedRequirements] = useState<{ nightPolicy?: boolean bookingPolicy?: boolean + dietaryRequirements?: boolean }>({}) useEffect(() => { onValidityChange( - !!acceptedRequirements.nightPolicy && !!acceptedRequirements.bookingPolicy + !!acceptedRequirements.nightPolicy && + !!acceptedRequirements.bookingPolicy && + !!acceptedRequirements.dietaryRequirements ) }, [acceptedRequirements, onValidityChange]) return ( - - { - setAcceptedRequirements({ - ...acceptedRequirements, - nightPolicy: e.target.checked - }) - }} - label="I understand that each date corresponds to one night's stay" - /> - + + { + setAcceptedRequirements({ + ...acceptedRequirements, + nightPolicy: e.target.checked + }) + }} + label="I understand that each date corresponds to one night's stay" + /> + { + setAcceptedRequirements({ + ...acceptedRequirements, + bookingPolicy: e.target.checked + }) + }} + /> + + + { + handleAllergyChange?.(e.target.value) + setAcceptedRequirements({ ...acceptedRequirements, - bookingPolicy: e.target.checked + dietaryRequirements: + e.target.value.length >= DIETARY_REQUIREMENTS_MIN_LENGTH }) }} + data-testid="dietary-requirements-input" + label="Please describe your dietary requirements" + placeholder="Enter dietary requirements here" /> - + ) } From 47c2652c13a222c9878927eb591968b8a40ee530 Mon Sep 17 00:00:00 2001 From: Jeffery <61447509+jeffplays2005@users.noreply.github.com> Date: Wed, 24 Jul 2024 09:17:31 +1200 Subject: [PATCH 07/13] 671-readme-for-server (#675) I hope I didn't misunderstand this ticket... Created a README.md file with the Backend-Architecture part of the wiki. --- server/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 server/README.md diff --git a/server/README.md b/server/README.md new file mode 100644 index 000000000..6a2799a6c --- /dev/null +++ b/server/README.md @@ -0,0 +1,3 @@ +# Backend architecture information + +https://github.com/UoaWDCC/uasc-web/wiki/Backend-Architecture From 4541bc0f16461d59df48f62615dc5d57f6ef3081 Mon Sep 17 00:00:00 2001 From: Jeffery <61447509+jeffplays2005@users.noreply.github.com> Date: Wed, 24 Jul 2024 10:43:32 +1200 Subject: [PATCH 08/13] 669 Delete docs folder (#679) Removed --- docs/ARCHITECTURE.md | 105 ------------------------------------------- 1 file changed, 105 deletions(-) delete mode 100644 docs/ARCHITECTURE.md diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md deleted file mode 100644 index ea5e3a66f..000000000 --- a/docs/ARCHITECTURE.md +++ /dev/null @@ -1,105 +0,0 @@ -# Firestore Architecture - -We use [Firestore](https://firebase.google.com/docs/firestore) to manage the database infrastructure. Firestore index rules are currently not set up. - -## `users` collection - -After a user signs up through the web app, extra information will be populated through this collection on top of the Firebase [User](https://firebase.google.com/docs/auth/users) object. - -| **Field** | **Type** | **Example Value** | -| ------------------ | --------- | -------------------------- | -| date_of_birth | timestamp | 1 January 1970 at 00:00:00 | -| does_freestyle | boolean | true | -| does_racing | boolean | false | -| does_ski | boolean | true | -| emergency_name | string | Jane Doe | -| emergency_phone | string | 123 456 789 | -| emergency_relation | string | Friend | -| first_name | string | John | -| last_name | string | Doe | -| membership | string | "admin" or "member" | - -## `demographics` collection - -Users may contain metadata which is not strictly necessary and may be optionally entered by them. This collection tracks this information. - -| **Field** | **Type** | **Example Value** | -| --------------- | -------- | ----------------- | -| faculty | string | Engineering | -| gender | string | female | -| second_faculty | string | Software | -| student_id | string | 123456789 | -| university_year | string | 3rd | - -## `requests` collection - -This manages all requests by users to the executive team at UASC. - -| **Field** | **Type** | **Example Value** | -| ------------- | --------- | ----------------------------------- | -| user_id | reference | /users/lVsOjAp06AfD6atT8bnrVEpcdcg2 | -| booking_id | reference | /bookings/8mYj7rWOMH6hGy4FzMed | -| query | string | Hello, when is your next event? | -| query_type | string | cancellation | -| status | string | unresolved | -| creation_time | timestamp | 1970-01-01T00:00:00Z | - -- `query_type` allows for future different query types. Possible query types - are - - `cancellation` - - `dateChange` -- `status` indicates the status of the query for the executive team to see (can - be `unresolved` or `resolved`) - -Additional fields may be specified, depending on the `query_type`, as follows: - -### `cancellation` request - -Has no additional fields specified. - -### `dateChange` request - -The following additional fields are specified: - -| **Field** | **Type** | **Example Value** | -| ------------- | --------- | ----------------- | -| old_check_in | timestamp | 25-07-2023 | -| old_check_out | timestamp | 27-07-2023 | -| new_check_in | timestamp | 26-07-2023 | -| new_check_out | timestamp | 28-07-2023 | - -Implementors should ensure that the range between -`old_check_out - old_check_in` and `new_check_out` - `new_check_in` do not -differ (i.e., the amount of days in the booking does not change). - -Because Firebase does not have a timestamp without time, ensure that time is set to midnight. - -## `bookings` collection - -This manages the link between users and booking IDs, and references the current -check-in and check-out dates of the booking. - -| **Field** | **Type** | **Example Value** | -| --------- | --------- | ----------------------------------- | -| user_id | reference | /users/lVsOjAp06AfD6atT8bnrVEpcdcg2 | -| check_in | timestamp | 26-07-2023 | -| check_out | timestamp | 26-07-2023 | - -Times of timestamps should be set to midnight. - -## `booking_changes` collection - -This manages any changes that have happened to a specific booking. - -This collection primarily exists to have a documented record of any changes -applied to a booking. - -| **Field** | **Type** | **Example Value** | -| ------------- | --------- | ------------------------------ | -| booking_id | reference | /bookings/8mYj7rWOMH6hGy4FzMed | -| old_check_in | timestamp | 26-07-2023 | -| old_check_out | timestamp | 26-07-2023 | -| new_check_in | timestamp | 27-07-2023 | -| new_check_out | timestamp | 27-07-2023 | - -Times of timestamps should be set to midnight. From 9af22622d506a6b231a262646693205fabe7845c Mon Sep 17 00:00:00 2001 From: Jeffery <61447509+jeffplays2005@users.noreply.github.com> Date: Thu, 25 Jul 2024 10:21:03 +1200 Subject: [PATCH 09/13] 670 Create readme including frontend wiki page (#678) There was already a README.md file so instead, I just added another heading with a link redirecting to the wiki. --- client/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/README.md b/client/README.md index c4033664f..0ab378544 100644 --- a/client/README.md +++ b/client/README.md @@ -1,3 +1,7 @@ +# Frotend architecture information + +https://github.com/UoaWDCC/uasc-web/wiki/Frontend-Architecture + This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). ## Getting Started From ccc7fc8d7aa2ea9215c7508ae99b5af9b18a560d Mon Sep 17 00:00:00 2001 From: Jeffery <61447509+jeffplays2005@users.noreply.github.com> Date: Thu, 25 Jul 2024 14:54:38 +1200 Subject: [PATCH 10/13] 566 Delete preview channel upon merge (#680) * 566 Delete preview channel upon merge Have added the feature to the pre-existing workflow action as per [this pull request on action-hosting-deploy](https://github.com/FirebaseExtended/action-hosting-deploy/pull/65) * Add new workflow and revert changes Added a new delete firebase channel upon PR close action. Reverted the not implemented changes from the firebase deployment on PR. --- .../delete-firebase-client-on-close.yml | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/delete-firebase-client-on-close.yml diff --git a/.github/workflows/delete-firebase-client-on-close.yml b/.github/workflows/delete-firebase-client-on-close.yml new file mode 100644 index 000000000..90d481b69 --- /dev/null +++ b/.github/workflows/delete-firebase-client-on-close.yml @@ -0,0 +1,20 @@ +name: Delete firebase channel upon PR close + +on: + pull_request: + types: [closed] + +jobs: + delete_firebase_channel: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v3 + + - uses: w9jds/firebase-action@v12.4.0 + with: + args: hosting:channel:delete ${{ github.head_ref }} --force + env: + # The Firebase service account key + GCP_SA_KEY: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_UASC_CEEBC }} + PROJECT_ID: uasc-ceebc From 94b8ca887c01b0dc77bcad379d621662b1705b22 Mon Sep 17 00:00:00 2001 From: Jeffery <61447509+jeffplays2005@users.noreply.github.com> Date: Thu, 25 Jul 2024 16:35:58 +1200 Subject: [PATCH 11/13] 566 delete preview channel on merge (#682) * 566 Delete preview channel upon merge Have added the feature to the pre-existing workflow action as per [this pull request on action-hosting-deploy](https://github.com/FirebaseExtended/action-hosting-deploy/pull/65) * Add new workflow and revert changes Added a new delete firebase channel upon PR close action. Reverted the not implemented changes from the firebase deployment on PR. * Changed the preview channel to delete Instead of just using the `github.head_ref` as the preview channel id, will need to make some changes as the preview channel created uses a different preview id. * Substring shell workaround for PR title --- .github/workflows/delete-firebase-client-on-close.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/delete-firebase-client-on-close.yml b/.github/workflows/delete-firebase-client-on-close.yml index 90d481b69..dc392cde5 100644 --- a/.github/workflows/delete-firebase-client-on-close.yml +++ b/.github/workflows/delete-firebase-client-on-close.yml @@ -9,11 +9,19 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: + # Substring workaround by using shell method + - name: Get short pull request title + id: get-short-pr + run: | + echo "pr=$(echo ${{github.head_ref}} | cut -c 1-20)" >> $GITHUB_OUTPUT" + - uses: actions/checkout@v3 - uses: w9jds/firebase-action@v12.4.0 with: - args: hosting:channel:delete ${{ github.head_ref }} --force + # as we didn't specify a specific channel id, firebase-action defaults + # with using pr- + args: hosting:channel:delete pr${{ github.event.pull_request.number }}-${{ steps.get-short-pr.outputs.pr }} --force env: # The Firebase service account key GCP_SA_KEY: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_UASC_CEEBC }} From 7571f2962d90400b5bccaa2c69fe4de9725a0554 Mon Sep 17 00:00:00 2001 From: Jeffery <61447509+jeffplays2005@users.noreply.github.com> Date: Thu, 25 Jul 2024 16:45:27 +1200 Subject: [PATCH 12/13] 566 delete preview channel on merge (#683) * 566 Delete preview channel upon merge Have added the feature to the pre-existing workflow action as per [this pull request on action-hosting-deploy](https://github.com/FirebaseExtended/action-hosting-deploy/pull/65) * Add new workflow and revert changes Added a new delete firebase channel upon PR close action. Reverted the not implemented changes from the firebase deployment on PR. * Changed the preview channel to delete Instead of just using the `github.head_ref` as the preview channel id, will need to make some changes as the preview channel created uses a different preview id. * Substring shell workaround for PR title * Remove extra " at the end --- .github/workflows/delete-firebase-client-on-close.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/delete-firebase-client-on-close.yml b/.github/workflows/delete-firebase-client-on-close.yml index dc392cde5..5dfc27a5d 100644 --- a/.github/workflows/delete-firebase-client-on-close.yml +++ b/.github/workflows/delete-firebase-client-on-close.yml @@ -13,7 +13,7 @@ jobs: - name: Get short pull request title id: get-short-pr run: | - echo "pr=$(echo ${{github.head_ref}} | cut -c 1-20)" >> $GITHUB_OUTPUT" + echo "pr=$(echo ${{github.head_ref}} | cut -c 1-20)" >> $GITHUB_OUTPUT - uses: actions/checkout@v3 From 4d2a901e4cce3c904fe85522100303398f00a501 Mon Sep 17 00:00:00 2001 From: Benson Cho <100653148+bcho892@users.noreply.github.com> Date: Fri, 26 Jul 2024 09:53:52 +1200 Subject: [PATCH 13/13] use the wrapped of the booking creation popup (#686) --- .../composite/Admin/AdminBookingView/AdminBookingView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/components/composite/Admin/AdminBookingView/AdminBookingView.tsx b/client/src/components/composite/Admin/AdminBookingView/AdminBookingView.tsx index 067b742cf..24f8092fb 100644 --- a/client/src/components/composite/Admin/AdminBookingView/AdminBookingView.tsx +++ b/client/src/components/composite/Admin/AdminBookingView/AdminBookingView.tsx @@ -9,7 +9,7 @@ import { import { useState, useRef } from "react" import { useClickOutside } from "@/components/utils/Utils" import ModalContainer from "@/components/generic/ModalContainer/ModalContainer" -import AdminBookingCreationPopUp from "./AdminBookingCreationPopUp" +import WrappedAdminBookingCreationPopUp from "./WrappedAdminBookingCreationPopUp" /** * The format of the columns in the admin booking view. @@ -167,7 +167,7 @@ export const AdminBookingView = ({ /> - setOpenAddBookingPopup(false)} />