From ccad805c801c4acac713feaee0c05ecf987c7baf Mon Sep 17 00:00:00 2001 From: Sim Shang En Date: Thu, 10 Jul 2025 23:07:52 +0100 Subject: [PATCH 1/9] feat: Add CHECKED_IN status to StatusType enum in application model --- services/registration/src/models/application.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/services/registration/src/models/application.ts b/services/registration/src/models/application.ts index f41cd73..01c9bdb 100644 --- a/services/registration/src/models/application.ts +++ b/services/registration/src/models/application.ts @@ -13,6 +13,7 @@ export enum StatusType { CONFIRMED = "CONFIRMED", DENIED = "DENIED", NOT_ATTENDING = "NOT_ATTENDING", + CHECKED_IN = "CHECKED_IN", } export interface Essay extends Types.Subdocument { From 4115355c51cb6b5c6e354e37bcedd95c1fc39d02 Mon Sep 17 00:00:00 2001 From: Sim Shang En Date: Thu, 10 Jul 2025 23:11:53 +0100 Subject: [PATCH 2/9] feat: Update application status handling and statistics to include CHECKED_IN --- services/registration/src/routes/application.ts | 5 +++++ services/registration/src/routes/statistics.ts | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/services/registration/src/routes/application.ts b/services/registration/src/routes/application.ts index eafe2d6..5414750 100644 --- a/services/registration/src/routes/application.ts +++ b/services/registration/src/routes/application.ts @@ -586,6 +586,11 @@ applicationRouter.route("/:id/actions/update-status").post( newStatus === StatusType.NOT_ATTENDING ) { // pass + } else if ( + existingApplication.status === StatusType.CONFIRMED && + newStatus === StatusType.CHECKED_IN + ) { + // Allow confirmed users to check themselves in } else { throw new BadRequestError( "You do not have permission to change this application to the new status provided." diff --git a/services/registration/src/routes/statistics.ts b/services/registration/src/routes/statistics.ts index 045dc1b..afe5506 100644 --- a/services/registration/src/routes/statistics.ts +++ b/services/registration/src/routes/statistics.ts @@ -281,8 +281,9 @@ statisticsRouter.route("/").get( acceptedUsers: (allUsersStatusCount.ACCEPTED || 0) + (allUsersStatusCount.CONFIRMED || 0) + + (allUsersStatusCount.CHECKED_IN || 0) + (allUsersStatusCount.NOT_ATTENDING || 0), - confirmedUsers: allUsersStatusCount.CONFIRMED || 0, + confirmedUsers: (allUsersStatusCount.CONFIRMED || 0) + (allUsersStatusCount.CHECKED_IN || 0), waitlistedUsers: allUsersStatusCount.WAITLISTED || 0, withdrawnUsers: allUsersStatusCount.NOT_ATTENDING || 0, checkedinUsers: checkinInteractions, @@ -351,7 +352,7 @@ statisticsRouter.route("/").get( const numTotal = branchAggregates.reduce((acc, aggregate) => acc + aggregate.count, 0); confirmationBranchStatistics[branch.name] = { - confirmed: statusCount[StatusType.CONFIRMED] || 0, + confirmed: (statusCount[StatusType.CONFIRMED] || 0) + (statusCount[StatusType.CHECKED_IN] || 0), notAttending: statusCount[StatusType.NOT_ATTENDING] || 0, total: numTotal, }; From 4bdd25990160a5cc54eae7d264e9643e694afdb0 Mon Sep 17 00:00:00 2001 From: Sim Shang En Date: Thu, 10 Jul 2025 23:28:22 +0100 Subject: [PATCH 3/9] feat: Implement check-in interaction to update application status to CHECKED_IN --- services/hexathons/src/routes/interaction.ts | 48 +++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/services/hexathons/src/routes/interaction.ts b/services/hexathons/src/routes/interaction.ts index b91aab9..593f13f 100644 --- a/services/hexathons/src/routes/interaction.ts +++ b/services/hexathons/src/routes/interaction.ts @@ -1,4 +1,5 @@ -import { asyncHandler, BadRequestError, checkAbility, ConfigError } from "@api/common"; +import { asyncHandler, BadRequestError, checkAbility, ConfigError, apiCall } from "@api/common"; +import { Service } from "@api/config"; import express from "express"; import { FilterQuery } from "mongoose"; @@ -105,6 +106,51 @@ interactionRoutes.route("/").post( }); } + // If this is a check-in interaction, update the application status to CHECKED_IN + if (req.body.type === InteractionType.CHECK_IN) { + try { + // Find the user's application for this hexathon + const applicationsResponse = await apiCall( + Service.REGISTRATION, + { + method: "GET", + url: "/applications", + params: { + hexathon: req.body.hexathon, + userId: req.body.userId, + }, + }, + req + ); + + if (applicationsResponse.applications && applicationsResponse.applications.length > 0) { + const application = applicationsResponse.applications[0]; + + // Update the application status to CHECKED_IN + await apiCall( + Service.REGISTRATION, + { + method: "POST", + url: `/applications/${application._id}/actions/update-status`, + data: { + status: "CHECKED_IN", + }, + }, + req + ); + } else { + console.warn( + `User ${req.body.userId} checked in but no application found for hexathon ${req.body.hexathon}` + ); + } + } catch (error) { + console.error( + `Failed to update application status for user ${req.body.userId} check-in:`, + error + ); + } + } + return res.send(interaction); }) ); From 85b6c478b93113c5c2e139bce6f04efa41b3477d Mon Sep 17 00:00:00 2001 From: Sim Shang En Date: Thu, 10 Jul 2025 23:28:38 +0100 Subject: [PATCH 4/9] feat: Add checkedInStatusUsers to statistics --- services/registration/src/routes/statistics.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/services/registration/src/routes/statistics.ts b/services/registration/src/routes/statistics.ts index afe5506..c4b1052 100644 --- a/services/registration/src/routes/statistics.ts +++ b/services/registration/src/routes/statistics.ts @@ -286,7 +286,8 @@ statisticsRouter.route("/").get( confirmedUsers: (allUsersStatusCount.CONFIRMED || 0) + (allUsersStatusCount.CHECKED_IN || 0), waitlistedUsers: allUsersStatusCount.WAITLISTED || 0, withdrawnUsers: allUsersStatusCount.NOT_ATTENDING || 0, - checkedinUsers: checkinInteractions, + checkedinUsers: checkinInteractions, // Physical check-in interactions + checkedInStatusUsers: allUsersStatusCount.CHECKED_IN || 0, // Application status count deniedUsers: allUsersStatusCount.DENIED || 0, }; @@ -352,7 +353,8 @@ statisticsRouter.route("/").get( const numTotal = branchAggregates.reduce((acc, aggregate) => acc + aggregate.count, 0); confirmationBranchStatistics[branch.name] = { - confirmed: (statusCount[StatusType.CONFIRMED] || 0) + (statusCount[StatusType.CHECKED_IN] || 0), + confirmed: + (statusCount[StatusType.CONFIRMED] || 0) + (statusCount[StatusType.CHECKED_IN] || 0), notAttending: statusCount[StatusType.NOT_ATTENDING] || 0, total: numTotal, }; From 5ea4b48c5c889f36233a5b81341863611e2a2d1d Mon Sep 17 00:00:00 2001 From: Shang En Sim Date: Thu, 10 Jul 2025 23:35:24 +0100 Subject: [PATCH 5/9] Potential fix for code scanning alert no. 138: Use of externally-controlled format string Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- services/hexathons/src/routes/interaction.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/hexathons/src/routes/interaction.ts b/services/hexathons/src/routes/interaction.ts index 593f13f..e1e8d5a 100644 --- a/services/hexathons/src/routes/interaction.ts +++ b/services/hexathons/src/routes/interaction.ts @@ -145,7 +145,8 @@ interactionRoutes.route("/").post( } } catch (error) { console.error( - `Failed to update application status for user ${req.body.userId} check-in:`, + "Failed to update application status for user %s check-in:", + req.body.userId, error ); } From b0937e578f195ea7789f2a22391863f12dddeea3 Mon Sep 17 00:00:00 2001 From: Shang En Sim Date: Sat, 12 Jul 2025 21:11:12 +0100 Subject: [PATCH 6/9] fix: address review comments on not grouping status --- services/registration/src/routes/statistics.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/services/registration/src/routes/statistics.ts b/services/registration/src/routes/statistics.ts index c4b1052..148ca4d 100644 --- a/services/registration/src/routes/statistics.ts +++ b/services/registration/src/routes/statistics.ts @@ -283,7 +283,7 @@ statisticsRouter.route("/").get( (allUsersStatusCount.CONFIRMED || 0) + (allUsersStatusCount.CHECKED_IN || 0) + (allUsersStatusCount.NOT_ATTENDING || 0), - confirmedUsers: (allUsersStatusCount.CONFIRMED || 0) + (allUsersStatusCount.CHECKED_IN || 0), + confirmedUsers: allUsersStatusCount.CONFIRMED || 0, waitlistedUsers: allUsersStatusCount.WAITLISTED || 0, withdrawnUsers: allUsersStatusCount.NOT_ATTENDING || 0, checkedinUsers: checkinInteractions, // Physical check-in interactions @@ -353,8 +353,7 @@ statisticsRouter.route("/").get( const numTotal = branchAggregates.reduce((acc, aggregate) => acc + aggregate.count, 0); confirmationBranchStatistics[branch.name] = { - confirmed: - (statusCount[StatusType.CONFIRMED] || 0) + (statusCount[StatusType.CHECKED_IN] || 0), + confirmed: statusCount[StatusType.CONFIRMED] || 0, notAttending: statusCount[StatusType.NOT_ATTENDING] || 0, total: numTotal, }; From d353c13b305c87dbbe9f561d2f3c931b1116f412 Mon Sep 17 00:00:00 2001 From: Shang En Sim Date: Sat, 12 Jul 2025 21:13:15 +0100 Subject: [PATCH 7/9] fix: only allow staff to check in --- services/registration/src/routes/application.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/services/registration/src/routes/application.ts b/services/registration/src/routes/application.ts index 5414750..eafe2d6 100644 --- a/services/registration/src/routes/application.ts +++ b/services/registration/src/routes/application.ts @@ -586,11 +586,6 @@ applicationRouter.route("/:id/actions/update-status").post( newStatus === StatusType.NOT_ATTENDING ) { // pass - } else if ( - existingApplication.status === StatusType.CONFIRMED && - newStatus === StatusType.CHECKED_IN - ) { - // Allow confirmed users to check themselves in } else { throw new BadRequestError( "You do not have permission to change this application to the new status provided." From 9563e3e3328b39cce1e7c038ab18a76618f05523 Mon Sep 17 00:00:00 2001 From: Shang En Sim Date: Sat, 12 Jul 2025 21:16:43 +0100 Subject: [PATCH 8/9] fix: restrict check-in interactions to members only --- services/hexathons/src/routes/interaction.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/hexathons/src/routes/interaction.ts b/services/hexathons/src/routes/interaction.ts index e1e8d5a..82c9218 100644 --- a/services/hexathons/src/routes/interaction.ts +++ b/services/hexathons/src/routes/interaction.ts @@ -45,6 +45,10 @@ interactionRoutes.route("/").post( throw new BadRequestError("Only members can create event interactions"); } + if (!req.user?.roles.member && req.body.type === InteractionType.CHECK_IN) { + throw new BadRequestError("Only members can create check-in interactions"); + } + // For event or scavenger hunt interactions, the identifier is required and must be unique if ([InteractionType.EVENT, InteractionType.SCAVENGER_HUNT].includes(req.body.type)) { const existingInteraction = await InteractionModel.findOne({ From 3f8f5e2e3163c14584df0bde95af8e4f3e4aa17f Mon Sep 17 00:00:00 2001 From: Shang En Sim Date: Sat, 12 Jul 2025 21:26:38 +0100 Subject: [PATCH 9/9] feat: Add endpoint to check application check-in status --- services/registration/src/routes/application.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/services/registration/src/routes/application.ts b/services/registration/src/routes/application.ts index eafe2d6..aeebb26 100644 --- a/services/registration/src/routes/application.ts +++ b/services/registration/src/routes/application.ts @@ -794,3 +794,20 @@ applicationRouter.route("/slack/confirmed-users").get( }); }) ); + +applicationRouter.route("/:id/check-in-status").get( + checkAbility("read", "Application"), + asyncHandler(async (req, res) => { + const application = await ApplicationModel.findById(req.params.id).accessibleBy(req.ability); + + if (!application) { + throw new BadRequestError("Application not found or you do not have permission to access."); + } + + const isCheckedInByStatus = application.status === StatusType.CHECKED_IN; + + return res.status(200).json({ + isCheckedIn: isCheckedInByStatus, + }); + }) +);