From f5cb95fadef9ac37f0405c9a52e1859407eadafc Mon Sep 17 00:00:00 2001 From: Benson Cho <100653148+bcho892@users.noreply.github.com> Date: Sun, 4 Aug 2024 23:32:55 +1200 Subject: [PATCH] display the booking history to admin on front end (#740) * set up components * working implementation * fix tests --- client/src/app/admin/booking-history/page.tsx | 13 +++ .../AdminBookingHistoryItem.story.tsx | 55 +++++++++ .../AdminBookingHistoryItem.tsx | 107 ++++++++++++++++++ .../AdminBookingHistoryView.story.tsx | 74 ++++++++++++ .../AdminBookingHistoryView.tsx | 72 ++++++++++++ .../WrappedAdminBookingHistoryView.tsx | 43 +++++++ .../WrappedAdminBookingCreationPopUp.tsx | 23 +--- .../Admin/AdminNavbar/AdminNavbar.tsx | 3 + client/src/hooks/useAllUsers.ts | 35 ++++++ client/src/models/History.ts | 3 + client/src/models/__generated__/schema.d.ts | 16 +-- client/src/services/Admin/AdminMutations.ts | 19 +++- client/src/services/Admin/AdminQueries.ts | 11 ++ client/src/services/Admin/AdminService.ts | 25 ++++ server/src/middleware/__generated__/routes.ts | 14 +-- .../src/middleware/__generated__/swagger.json | 49 +++----- .../middleware/tests/AdminController.test.ts | 23 ++-- .../controllers/AdminController.ts | 9 +- 18 files changed, 504 insertions(+), 90 deletions(-) create mode 100644 client/src/app/admin/booking-history/page.tsx create mode 100644 client/src/components/composite/Admin/AdminBookingHistoryView/AdminBookingHistoryItem.story.tsx create mode 100644 client/src/components/composite/Admin/AdminBookingHistoryView/AdminBookingHistoryItem.tsx create mode 100644 client/src/components/composite/Admin/AdminBookingHistoryView/AdminBookingHistoryView.story.tsx create mode 100644 client/src/components/composite/Admin/AdminBookingHistoryView/AdminBookingHistoryView.tsx create mode 100644 client/src/components/composite/Admin/AdminBookingHistoryView/WrappedAdminBookingHistoryView.tsx create mode 100644 client/src/hooks/useAllUsers.ts create mode 100644 client/src/models/History.ts diff --git a/client/src/app/admin/booking-history/page.tsx b/client/src/app/admin/booking-history/page.tsx new file mode 100644 index 000000000..e9db764db --- /dev/null +++ b/client/src/app/admin/booking-history/page.tsx @@ -0,0 +1,13 @@ +"use client" + +import WrappedAdminBookingHistoryView from "@/components/composite/Admin/AdminBookingHistoryView/WrappedAdminBookingHistoryView" +import { AdminHeading } from "../AdminHeading" + +export default function AdminBookingHistoryPage() { + return ( + <> + + + + ) +} diff --git a/client/src/components/composite/Admin/AdminBookingHistoryView/AdminBookingHistoryItem.story.tsx b/client/src/components/composite/Admin/AdminBookingHistoryView/AdminBookingHistoryItem.story.tsx new file mode 100644 index 000000000..0ea69d6fa --- /dev/null +++ b/client/src/components/composite/Admin/AdminBookingHistoryView/AdminBookingHistoryItem.story.tsx @@ -0,0 +1,55 @@ +import type { Meta, StoryObj } from "@storybook/react" +import { BookingHistoryEvent } from "@/models/History" +import { Timestamp } from "firebase/firestore" +import AdminBookingHistoryItem from "./AdminBookingHistoryItem" + +const meta: Meta = { + component: AdminBookingHistoryItem +} + +export default meta +type Story = StoryObj + +const bookingAdditionItem: BookingHistoryEvent = { + event_type: "added_user_to_booking", + timestamp: Timestamp.fromMillis(69), + start_date: Timestamp.now(), + end_date: Timestamp.now(), + uid: "jack sun" +} + +const bookingDeletionItem: BookingHistoryEvent = { + event_type: "removed_user_from_booking", + timestamp: Timestamp.fromMillis(69), + start_date: Timestamp.now(), + end_date: Timestamp.now(), + uid: "stephen zhang" +} + +const availabilityChangeItem: BookingHistoryEvent = { + event_type: "changed_date_availability", + timestamp: Timestamp.fromMillis(69), + start_date: Timestamp.now(), + end_date: Timestamp.now(), + change: -32 +} + +export const AdditionItem: Story = { + args: { + item: bookingAdditionItem, + name: "stephen zhang" + } +} + +export const DeletionItem: Story = { + args: { + item: bookingDeletionItem, + name: "Jack sun" + } +} + +export const AvailabilityChangeItem: Story = { + args: { + item: availabilityChangeItem + } +} diff --git a/client/src/components/composite/Admin/AdminBookingHistoryView/AdminBookingHistoryItem.tsx b/client/src/components/composite/Admin/AdminBookingHistoryView/AdminBookingHistoryItem.tsx new file mode 100644 index 000000000..6be6153ef --- /dev/null +++ b/client/src/components/composite/Admin/AdminBookingHistoryView/AdminBookingHistoryItem.tsx @@ -0,0 +1,107 @@ +import { DateUtils } from "@/components/utils/DateUtils" +import { BookingHistoryEvent } from "@/models/History" +import { useMemo } from "react" + +interface IAdminBookingHistoryItem { + /** + * The event that is to be parsed and displayed in list view + */ + item: BookingHistoryEvent + /** + * The name of the user associated with the event (**NOT** the admin performing it) + */ + name?: string + /** + * The email of the user associated with the event (**NOT** the admin performing it) + */ + email?: string +} + +const AdminBookingHistoryItem = ({ + item, + name, + email +}: IAdminBookingHistoryItem) => { + /** + * Used for parsing the history event and presenting it + */ + const InnerContent = useMemo(() => { + const UserInformation = () => { + return ( +
+ User: {name} | Email: {email} +
+ ) + } + const SharedContent = () => { + return ( + <> +

+ At{" "} + + {new Date( + DateUtils.timestampMilliseconds(item.timestamp) + ).toLocaleString()} + {" "} + for the date range{" "} + + {DateUtils.formattedNzDate( + new Date(DateUtils.timestampMilliseconds(item.start_date)) + )} + {" "} + to{" "} + + {DateUtils.formattedNzDate( + new Date(DateUtils.timestampMilliseconds(item.end_date)) + )} + +

+ + ) + } + switch (item.event_type) { + case "added_user_to_booking": + return ( + <> +
Added to booking
+ + + + ) + case "removed_user_from_booking": + return ( + <> +
+ Removed from booking +
+ + + + ) + case "changed_date_availability": + return ( + <> +
+ Availability Changed +
+
+ +

+ for {item.change} slots +

+
+ + ) + } + }, [item, name, email]) + + return ( + <> +
+ {InnerContent} +
+ + ) +} + +export default AdminBookingHistoryItem diff --git a/client/src/components/composite/Admin/AdminBookingHistoryView/AdminBookingHistoryView.story.tsx b/client/src/components/composite/Admin/AdminBookingHistoryView/AdminBookingHistoryView.story.tsx new file mode 100644 index 000000000..2f3b55ef2 --- /dev/null +++ b/client/src/components/composite/Admin/AdminBookingHistoryView/AdminBookingHistoryView.story.tsx @@ -0,0 +1,74 @@ +import type { Meta, StoryObj } from "@storybook/react" +import { BookingHistoryEvent } from "@/models/History" +import AdminBookingHistoryView from "./AdminBookingHistoryView" +import { Timestamp } from "firebase/firestore" +import { CombinedUserData } from "@/models/User" + +const meta: Meta = { + component: AdminBookingHistoryView +} + +export default meta +type Story = StoryObj + +const bookingAdditionItem: BookingHistoryEvent = { + event_type: "added_user_to_booking", + timestamp: Timestamp.fromMillis(69), + start_date: Timestamp.now(), + end_date: Timestamp.now(), + uid: "jack sun" +} + +const bookingDeletionItem: BookingHistoryEvent = { + event_type: "removed_user_from_booking", + timestamp: Timestamp.fromMillis(69), + start_date: Timestamp.now(), + end_date: Timestamp.now(), + uid: "stephen zhang" +} + +const availabilityChangeItem: BookingHistoryEvent = { + event_type: "changed_date_availability", + timestamp: Timestamp.fromMillis(69), + start_date: Timestamp.now(), + end_date: Timestamp.now(), + change: -32 +} + +const users: CombinedUserData[] = [ + { + uid: "stephen zhang", + first_name: "Stephen", + last_name: "Zhang", + dietary_requirements: "nothing", + email: "love@gmail.com", + phone_number: 696969, + date_of_birth: Timestamp.now(), + membership: "guest" + }, + { + uid: "jack sun", + first_name: "Jack", + last_name: "Sun", + dietary_requirements: "nothing", + email: "love@gmail.com", + phone_number: 696969, + date_of_birth: Timestamp.now(), + membership: "guest" + } +] + +const mockDataArray: BookingHistoryEvent[] = [] + +for (let i = 0; i < 100; ++i) { + mockDataArray.push(bookingAdditionItem) + mockDataArray.push(bookingDeletionItem) + mockDataArray.push(availabilityChangeItem) +} + +export const DefaultAdminBookingHistoryView: Story = { + args: { + historyItems: mockDataArray, + users + } +} diff --git a/client/src/components/composite/Admin/AdminBookingHistoryView/AdminBookingHistoryView.tsx b/client/src/components/composite/Admin/AdminBookingHistoryView/AdminBookingHistoryView.tsx new file mode 100644 index 000000000..de5c2add8 --- /dev/null +++ b/client/src/components/composite/Admin/AdminBookingHistoryView/AdminBookingHistoryView.tsx @@ -0,0 +1,72 @@ +import { BookingHistoryEvent } from "@/models/History" +import AdminBookingHistoryItem from "./AdminBookingHistoryItem" +import { CombinedUserData } from "@/models/User" +import { useMemo } from "react" +import Button from "@/components/generic/FigmaButtons/FigmaButton" + +interface IAdminBookingHistoryView { + /** + * The list of history items to display + */ + historyItems?: BookingHistoryEvent[] + + /** + * The list of all users for querying names and emails + */ + users?: CombinedUserData[] + + /** + * If not all history items have been loaded + */ + hasMore?: boolean + + /** + * callback to fetch more history items (if there *is* more) + */ + loadMore?: () => void +} + +/** + * @deprecated do not use directly, use {@link WrappedAdminBookingHistoryView} instead + */ +const AdminBookingHistoryView = ({ + historyItems = [], + users = [], + hasMore, + loadMore +}: IAdminBookingHistoryView) => { + const content = useMemo(() => { + return ( + <> + {historyItems.map((item) => { + let matchingUser + if ( + item.event_type === "added_user_to_booking" || + item.event_type === "removed_user_from_booking" + ) { + matchingUser = users.find((user) => user.uid === item.uid) + } + return ( + + ) + })} + + ) + }, [historyItems, users]) + + return ( +
+
Newest
+ {content} +
Oldest
+ +
+ ) +} + +export default AdminBookingHistoryView diff --git a/client/src/components/composite/Admin/AdminBookingHistoryView/WrappedAdminBookingHistoryView.tsx b/client/src/components/composite/Admin/AdminBookingHistoryView/WrappedAdminBookingHistoryView.tsx new file mode 100644 index 000000000..a723336c9 --- /dev/null +++ b/client/src/components/composite/Admin/AdminBookingHistoryView/WrappedAdminBookingHistoryView.tsx @@ -0,0 +1,43 @@ +import { useBookingHistoryQuery } from "@/services/Admin/AdminQueries" +import AdminBookingHistoryView from "./AdminBookingHistoryView" +import { useCallback, useMemo } from "react" +import useAllUsers from "@/hooks/useAllUsers" + +/** + * To be used for consumption of {@link AdminBookingHistoryView} + */ +const WrappedAdminBookingHistoryView = () => { + const { + data: bookingHistoryData, + fetchNextPage, + hasNextPage, + isFetchingNextPage + } = useBookingHistoryQuery() + + const historyItems = useMemo(() => { + const history = bookingHistoryData?.pages.flatMap( + (page) => page.historyEvents || [] + ) + return history?.reverse() + }, [bookingHistoryData]) + + const loadMoreHistory = useCallback(() => { + if (hasNextPage && !isFetchingNextPage) { + fetchNextPage() + } + }, [fetchNextPage, hasNextPage, isFetchingNextPage]) + + const { users } = useAllUsers() + + return ( + <> + + + ) +} + +export default WrappedAdminBookingHistoryView diff --git a/client/src/components/composite/Admin/AdminBookingView/WrappedAdminBookingCreationPopUp.tsx b/client/src/components/composite/Admin/AdminBookingView/WrappedAdminBookingCreationPopUp.tsx index 7e8bd9911..cdd23f25a 100644 --- a/client/src/components/composite/Admin/AdminBookingView/WrappedAdminBookingCreationPopUp.tsx +++ b/client/src/components/composite/Admin/AdminBookingView/WrappedAdminBookingCreationPopUp.tsx @@ -1,8 +1,7 @@ -import { useUsersQuery } from "@/services/Admin/AdminQueries" import AdminBookingCreationPopUp from "./AdminBookingCreationPopUp" -import { useEffect, useMemo } from "react" import { useAvailableBookingsQuery } from "@/services/Booking/BookingQueries" import { useAddUserToBookingMutation } from "@/services/Admin/AdminMutations" +import useAllUsers from "@/hooks/useAllUsers" interface IWrappedAdminBookingCreationPopUp { handleClose: () => void @@ -11,28 +10,12 @@ interface IWrappedAdminBookingCreationPopUp { const WrappedAdminBookingCreationPopUp = ({ handleClose }: IWrappedAdminBookingCreationPopUp) => { - const { - data: userPages, - fetchNextPage, - isFetchingNextPage, - hasNextPage - } = useUsersQuery() - const { data: bookingSlots } = useAvailableBookingsQuery() const { mutateAsync: handleAddUserToBooking, isPending } = useAddUserToBookingMutation() - useEffect(() => { - if (hasNextPage && !isFetchingNextPage) { - fetchNextPage() - } - }, [fetchNextPage, isFetchingNextPage, hasNextPage]) - - const users = useMemo( - () => userPages?.pages.flatMap((page) => page.data || []), - [userPages] - ) + const { users, stillLoadingUsers } = useAllUsers() return ( @@ -41,7 +24,7 @@ const WrappedAdminBookingCreationPopUp = ({ bookingSlots={bookingSlots} handleClose={handleClose} isPending={isPending} - isLoading={hasNextPage} + isLoading={stillLoadingUsers} bookingCreationHandler={async (startDate, endDate, uid) => { await handleAddUserToBooking({ startDate, endDate, userId: uid }) }} diff --git a/client/src/components/composite/Admin/AdminNavbar/AdminNavbar.tsx b/client/src/components/composite/Admin/AdminNavbar/AdminNavbar.tsx index 24569dfd9..d167527b5 100644 --- a/client/src/components/composite/Admin/AdminNavbar/AdminNavbar.tsx +++ b/client/src/components/composite/Admin/AdminNavbar/AdminNavbar.tsx @@ -30,6 +30,9 @@ const AdminNavbar = () => { availability + + history +
diff --git a/client/src/hooks/useAllUsers.ts b/client/src/hooks/useAllUsers.ts new file mode 100644 index 000000000..0b21f6ddd --- /dev/null +++ b/client/src/hooks/useAllUsers.ts @@ -0,0 +1,35 @@ +import { useUsersQuery } from "@/services/Admin/AdminQueries" +import { useEffect, useMemo } from "react" + +/** + * A wrapper hook for {@link useUsersQuery} to be used by consumers which + * require all of the user data, and do not care about manually fetching + * + * @example + * const {users, stillLoadingUsers} = useAllUsers() + * + * if(!stillLoadingUsers){ + * performFunctionRequiringAllUsers(users) + * } + */ +export default function useAllUsers() { + const { + data: userPages, + fetchNextPage, + isFetchingNextPage, + hasNextPage + } = useUsersQuery() + + useEffect(() => { + if (hasNextPage && !isFetchingNextPage) { + fetchNextPage() + } + }, [fetchNextPage, isFetchingNextPage, hasNextPage]) + + const users = useMemo( + () => userPages?.pages.flatMap((page) => page.data || []), + [userPages] + ) + + return { users, stillLoadingUsers: hasNextPage } +} diff --git a/client/src/models/History.ts b/client/src/models/History.ts new file mode 100644 index 000000000..b3e532904 --- /dev/null +++ b/client/src/models/History.ts @@ -0,0 +1,3 @@ +import { components } from "./__generated__/schema" + +export type BookingHistoryEvent = components["schemas"]["BookingHistoryEvent"] diff --git a/client/src/models/__generated__/schema.d.ts b/client/src/models/__generated__/schema.d.ts index 1008eacb9..00518f901 100644 --- a/client/src/models/__generated__/schema.d.ts +++ b/client/src/models/__generated__/schema.d.ts @@ -134,7 +134,7 @@ export interface paths { }; "/admin/bookings/history": { /** @description Fetches the **latest** booking history events (uses cursor-based pagination) */ - post: operations["GetLatestHistory"]; + get: operations["GetLatestHistory"]; }; } @@ -591,12 +591,6 @@ export interface components { message?: string; historyEvents?: components["schemas"]["BookingHistoryEvent"][]; }; - FetchLatestBookingEventRequest: { - /** Format: double */ - limit: number; - /** @description The id of the cursor to continue paginating from */ - cursor?: string; - }; }; responses: { }; @@ -1037,10 +1031,10 @@ export interface operations { }; /** @description Fetches the **latest** booking history events (uses cursor-based pagination) */ GetLatestHistory: { - /** @description - contains the pagination variables */ - requestBody: { - content: { - "application/json": components["schemas"]["FetchLatestBookingEventRequest"]; + parameters: { + query: { + limit: number; + cursor?: string; }; }; responses: { diff --git a/client/src/services/Admin/AdminMutations.ts b/client/src/services/Admin/AdminMutations.ts index 4c8f9e75f..e608fde8a 100644 --- a/client/src/services/Admin/AdminMutations.ts +++ b/client/src/services/Admin/AdminMutations.ts @@ -5,7 +5,8 @@ import queryClient from "@/services/QueryClient" import { BOOKING_AVAILABLITY_KEY } from "@/services/Booking/BookingQueries" import { ALL_BOOKINGS_BETWEEN_RANGE_QUERY, - ALL_USERS_QUERY + ALL_USERS_QUERY, + BOOKING_HISTORY_QUERY } from "./AdminQueries" import { CombinedUserData } from "@/models/User" import { replaceUserInPage } from "./AdminUtils" @@ -93,6 +94,10 @@ export function useMakeDatesAvailableMutation( retry: false, onSuccess: () => { queryClient.invalidateQueries({ queryKey: [BOOKING_AVAILABLITY_KEY] }) + + queryClient.invalidateQueries({ + queryKey: [BOOKING_HISTORY_QUERY] + }) } }) } @@ -116,6 +121,10 @@ export function useMakeDatesUnavailableMutation( }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: [BOOKING_AVAILABLITY_KEY] }) + + queryClient.invalidateQueries({ + queryKey: [BOOKING_HISTORY_QUERY] + }) } }) } @@ -130,6 +139,10 @@ export function useAddUserToBookingMutation() { queryKey: [ALL_BOOKINGS_BETWEEN_RANGE_QUERY] }) + queryClient.invalidateQueries({ + queryKey: [BOOKING_HISTORY_QUERY] + }) + queryClient.invalidateQueries({ queryKey: [BOOKING_AVAILABLITY_KEY] }) @@ -147,6 +160,10 @@ export function useDeleteBookingMutation() { queryKey: [ALL_BOOKINGS_BETWEEN_RANGE_QUERY] }) + queryClient.invalidateQueries({ + queryKey: [BOOKING_HISTORY_QUERY] + }) + queryClient.invalidateQueries({ queryKey: [BOOKING_AVAILABLITY_KEY] }) diff --git a/client/src/services/Admin/AdminQueries.ts b/client/src/services/Admin/AdminQueries.ts index 4882f6499..143c8f229 100644 --- a/client/src/services/Admin/AdminQueries.ts +++ b/client/src/services/Admin/AdminQueries.ts @@ -4,6 +4,7 @@ import AdminService from "./AdminService" export const ALL_USERS_QUERY = "allUsers" export const ALL_BOOKINGS_BETWEEN_RANGE_QUERY = "bookings-between-range" +export const BOOKING_HISTORY_QUERY = "latest-booking-history" export function useUsersQuery() { return useInfiniteQuery({ @@ -30,3 +31,13 @@ export function useAdminBookingsQuery( staleTime: 30000 // 30 sec }) } + +export function useBookingHistoryQuery() { + return useInfiniteQuery({ + queryKey: [BOOKING_HISTORY_QUERY], + queryFn: AdminService.getBookingHistory, + retry: 0, + initialPageParam: undefined, + getNextPageParam: (lastPage) => lastPage.nextCursor + }) +} diff --git a/client/src/services/Admin/AdminService.ts b/client/src/services/Admin/AdminService.ts index 24bb3935b..2f4c41525 100644 --- a/client/src/services/Admin/AdminService.ts +++ b/client/src/services/Admin/AdminService.ts @@ -167,6 +167,31 @@ const AdminService = { } return data?.data + }, + getBookingHistory: async function ({ + pageParam, + limit = 200 + }: { + pageParam?: string + limit?: number + }) { + const { response, data } = await fetchClient.GET( + "/admin/bookings/history", + { + params: { + query: { + limit, + cursor: pageParam + } + } + } + ) + + if (!response.ok || !data) { + throw new Error(`Failed to fetch ${limit} of the latest booking items`) + } + + return data } } as const diff --git a/server/src/middleware/__generated__/routes.ts b/server/src/middleware/__generated__/routes.ts index de9ba4320..049aee8eb 100644 --- a/server/src/middleware/__generated__/routes.ts +++ b/server/src/middleware/__generated__/routes.ts @@ -485,15 +485,6 @@ const models: TsoaRoute.Models = { "additionalProperties": false, }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - "FetchLatestBookingEventRequest": { - "dataType": "refObject", - "properties": { - "limit": {"dataType":"double","required":true,"validators":{"maximum":{"errorMsg":"please select a smaller limit (max 500)","value":500},"minimum":{"errorMsg":"please choose a positive, non-zero limit","value":1}}}, - "cursor": {"dataType":"string"}, - }, - "additionalProperties": false, - }, - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa }; const templateService = new ExpressTemplateService(models, {"noImplicitAdditionalProperties":"throw-on-extras","bodyCoercion":true}); @@ -1247,14 +1238,15 @@ export function RegisterRoutes(app: Router) { } }); // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - app.post('/admin/bookings/history', + app.get('/admin/bookings/history', authenticateMiddleware([{"jwt":["admin"]}]), ...(fetchMiddlewares(AdminController)), ...(fetchMiddlewares(AdminController.prototype.getLatestHistory)), function AdminController_getLatestHistory(request: ExRequest, response: ExResponse, next: any) { const args: Record = { - requestBody: {"in":"body","name":"requestBody","required":true,"ref":"FetchLatestBookingEventRequest"}, + limit: {"in":"query","name":"limit","required":true,"dataType":"double"}, + cursor: {"in":"query","name":"cursor","dataType":"string"}, }; // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa diff --git a/server/src/middleware/__generated__/swagger.json b/server/src/middleware/__generated__/swagger.json index 4371cadab..61a737b8c 100644 --- a/server/src/middleware/__generated__/swagger.json +++ b/server/src/middleware/__generated__/swagger.json @@ -1201,25 +1201,6 @@ }, "type": "object", "additionalProperties": false - }, - "FetchLatestBookingEventRequest": { - "properties": { - "limit": { - "type": "number", - "format": "double", - "maximum": 500, - "minimum": 1 - }, - "cursor": { - "type": "string", - "description": "The id of the cursor to continue paginating from" - } - }, - "required": [ - "limit" - ], - "type": "object", - "additionalProperties": false } }, "securitySchemes": { @@ -2099,7 +2080,7 @@ } }, "/admin/bookings/history": { - "post": { + "get": { "operationId": "GetLatestHistory", "responses": { "200": { @@ -2121,19 +2102,25 @@ ] } ], - "parameters": [], - "requestBody": { - "description": "- contains the pagination variables", - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FetchLatestBookingEventRequest", - "description": "- contains the pagination variables" - } + "parameters": [ + { + "in": "query", + "name": "limit", + "required": true, + "schema": { + "format": "double", + "type": "number" + } + }, + { + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" } } - } + ] } } }, diff --git a/server/src/middleware/tests/AdminController.test.ts b/server/src/middleware/tests/AdminController.test.ts index 9def29548..829e76c3c 100644 --- a/server/src/middleware/tests/AdminController.test.ts +++ b/server/src/middleware/tests/AdminController.test.ts @@ -741,18 +741,19 @@ describe("AdminController endpoint tests", () => { describe("/admin/bookings/history", () => { it("should be scoped to admins only", async () => { let res = await request - .post("/admin/bookings/history") + .get(`/admin/bookings/history?limit=100`) .set("Authorization", `Bearer ${memberToken}`) - .send({ limit: 1 }) + .send({}) expect(res.status).toEqual(401) res = await request - .post("/admin/bookings/history") + .get(`/admin/bookings/history?limit=100`) .set("Authorization", `Bearer ${guestToken}`) - .send({ limit: 1 }) + .send({}) expect(res.status).toEqual(401) - res = await request.post("/admin/bookings/history").send({ limit: 1 }) + res = await request.get(`/admin/bookings/history?limit=100`).send({}) + expect(res.status).toEqual(401) }) @@ -779,9 +780,9 @@ describe("AdminController endpoint tests", () => { }) let res = await request - .post("/admin/bookings/history") + .get(`/admin/bookings/history?limit=1`) .set("Authorization", `Bearer ${adminToken}`) - .send({ limit: 1 }) + .send({}) expect(res.status).toEqual(200) expect(res.body.historyEvents).toHaveLength(1) @@ -790,17 +791,17 @@ describe("AdminController endpoint tests", () => { * Pagination Test */ res = await request - .post("/admin/bookings/history") + .get(`/admin/bookings/history?limit=100&cursor=${res.body.nextCursor}`) .set("Authorization", `Bearer ${adminToken}`) - .send({ limit: 100, cursor: res.body.nextCursor }) + .send({}) expect(res.status).toEqual(200) expect(res.body.historyEvents).toHaveLength(1) res = await request - .post("/admin/bookings/history") + .get(`/admin/bookings/history?limit=2`) .set("Authorization", `Bearer ${adminToken}`) - .send({ limit: 2 }) + .send({}) expect(res.body.historyEvents).toHaveLength(2) }) diff --git a/server/src/service-layer/controllers/AdminController.ts b/server/src/service-layer/controllers/AdminController.ts index 6b8f0588d..045aee21f 100644 --- a/server/src/service-layer/controllers/AdminController.ts +++ b/server/src/service-layer/controllers/AdminController.ts @@ -161,7 +161,7 @@ export class AdminController extends Controller { // Was available if (bookingSlotForDate.max_bookings > EMPTY_BOOKING_SLOTS) { // TODO: change to proper functionality (i.e not completely make it empty) - change = bookingSlotForDate.max_bookings - EMPTY_BOOKING_SLOTS + change = EMPTY_BOOKING_SLOTS - bookingSlotForDate.max_bookings await bookingSlotService.updateBookingSlot(bookingSlotForDate.id, { max_bookings: EMPTY_BOOKING_SLOTS }) @@ -660,12 +660,11 @@ export class AdminController extends Controller { * @returns the list of latest history events */ @SuccessResponse("200", "History Events Fetched") - @Post("bookings/history") + @Get("bookings/history") public async getLatestHistory( - @Body() requestBody: FetchLatestBookingEventRequest + @Query() limit: FetchLatestBookingEventRequest["limit"], + @Query() cursor?: FetchLatestBookingEventRequest["cursor"] ): Promise { - const { limit, cursor } = requestBody - try { const bookingHistoryService = new BookingHistoryService()