diff --git a/src/_enums/StorageBuckets.ts b/src/_enums/StorageBuckets.ts index 796a5fd..ba21d1b 100644 --- a/src/_enums/StorageBuckets.ts +++ b/src/_enums/StorageBuckets.ts @@ -1,6 +1,6 @@ export enum StorageBucket { - RESUME = "2023-resumes", - PROFILE_PICTURES = "2023-profile-pictures", + RESUME = "2024-resumes", + PROFILE_PICTURES = "2024-profile-pictures", } /** diff --git a/src/_types/Settings.d.ts b/src/_types/Settings.d.ts index dd9636a..8b4c92f 100644 --- a/src/_types/Settings.d.ts +++ b/src/_types/Settings.d.ts @@ -12,6 +12,8 @@ export interface ISettings extends Document { timeConfirm?: string; enableWhitelist?: number; whitelistedEmails?: string[]; + maxParticipants?: number; + autoWaitlist?: boolean; waitlistText?: string; acceptanceText?: string; confirmationText?: string; diff --git a/src/controllers/SettingsController.ts b/src/controllers/SettingsController.ts index ea6f549..76f48cd 100644 --- a/src/controllers/SettingsController.ts +++ b/src/controllers/SettingsController.ts @@ -19,6 +19,19 @@ export const handleGetSettings = async ( res.json(settings.toJSON()); }; +/** + * Express handler for getting the waitlist status + */ +export const handleGetWaitlistStatus = async ( + req: Request, + res: Response +): Promise => { + const settings = await getSettings(); + res.json({ + waitlist: settings.autoWaitlist, + }); +}; + /** * Express handler for updating settings */ diff --git a/src/controllers/TestAccount/accountCreator.ts b/src/controllers/TestAccount/accountCreator.ts index 052b4bd..bbce8d2 100644 --- a/src/controllers/TestAccount/accountCreator.ts +++ b/src/controllers/TestAccount/accountCreator.ts @@ -55,7 +55,10 @@ export async function createTestAccountWithStatus( await user.save(); let profile: IProfile | undefined = undefined; - if (doesStatusImply(status, Status.COMPLETED_PROFILE)) { + if ( + doesStatusImply(status, Status.COMPLETED_PROFILE) || + status === Status.WAITLISTED + ) { profile = await createTestProfileForUser(user); if (doesStatusImply(status, Status.CONFIRMED)) { diff --git a/src/controllers/UsersController.ts b/src/controllers/UsersController.ts index cc3d09d..14ef16d 100644 --- a/src/controllers/UsersController.ts +++ b/src/controllers/UsersController.ts @@ -114,7 +114,10 @@ export const admitUser = async (req: Request, res: Response): Promise => { if (user == null) { return bad(res, "User not found"); } - if (!user.hasStatus(Status.COMPLETED_PROFILE)) { + if ( + !user.hasStatus(Status.COMPLETED_PROFILE) && + !user.hasStatus(Status.WAITLISTED) + ) { return bad(res, "User has not completed their profile yet!"); } @@ -169,7 +172,14 @@ export const admitAllUsers = async ( const tartanhacks = await getTartanHacks(); const users = await User.find({ - status: Status.COMPLETED_PROFILE, + $or: [ + { + status: Status.COMPLETED_PROFILE, + }, + { + status: Status.WAITLISTED, + }, + ], }); const promises = []; @@ -200,7 +210,14 @@ export const rejectAllUsers = async ( const tartanhacks = await getTartanHacks(); const users = await User.find({ - status: Status.COMPLETED_PROFILE, + $or: [ + { + status: Status.COMPLETED_PROFILE, + }, + { + status: Status.WAITLISTED, + }, + ], }); const promises = []; @@ -234,7 +251,10 @@ export const rejectUser = async ( if (user == null) { return bad(res, "User not found"); } - if (!user.hasStatus(Status.COMPLETED_PROFILE)) { + if ( + !user.hasStatus(Status.COMPLETED_PROFILE) && + !user.hasStatus(Status.WAITLISTED) + ) { return bad(res, "User has not completed their profile yet!"); } const profile = await Profile.findOne({ diff --git a/src/models/Settings.ts b/src/models/Settings.ts index 2cf1742..1042eb6 100644 --- a/src/models/Settings.ts +++ b/src/models/Settings.ts @@ -17,6 +17,8 @@ const Settings: Schema = new Schema( timeConfirm: Date, enableWhitelist: Boolean, whitelistedEmails: [String], + maxParticipants: Number, + autoWaitlist: Boolean, waitlistText: String, acceptanceText: String, confirmationText: String, diff --git a/src/routes/settings.ts b/src/routes/settings.ts index baf6e28..ddf751a 100644 --- a/src/routes/settings.ts +++ b/src/routes/settings.ts @@ -4,6 +4,7 @@ import { getConfirmTime, getOpenTime, handleGetSettings, + handleGetWaitlistStatus, updateSettings, } from "../controllers/SettingsController"; import { asyncCatch } from "../util/asyncCatch"; @@ -149,4 +150,25 @@ router.get("/time/close", asyncCatch(getCloseTime)); */ router.get("/time/confirm", asyncCatch(getConfirmTime)); +/** + * @swagger + * /settings/waitlist: + * get: + * summary: Check whether the waitlist is open + * tags: [Settings Module] + * description: Returns whether newly registered users should be put on a waitlist. Access - Public + * security: + * - apiKeyAuth: [] + * responses: + * 200: + * description: Success. + * 400: + * description: Bad request + * 404: + * description: User does not exist. + * 500: + * description: Internal Server Error. + */ +router.get("/waitlist", asyncCatch(handleGetWaitlistStatus)); + export default router; diff --git a/src/services/analytics.ts b/src/services/analytics.ts index ec27189..cf20090 100644 --- a/src/services/analytics.ts +++ b/src/services/analytics.ts @@ -110,14 +110,14 @@ export const computeAnalytics = async (): Promise => { } // Count verified - const isVerified = doesStatusImply(status, Status.VERIFIED); + const isVerified = + status == Status.WAITLISTED || doesStatusImply(status, Status.VERIFIED); stats.verified += isVerified ? 1 : 0; // Count submitted - const isProfileComplete = doesStatusImply( - status, - Status.COMPLETED_PROFILE - ); + const isProfileComplete = + status == Status.WAITLISTED || + doesStatusImply(status, Status.COMPLETED_PROFILE); stats.submitted += isProfileComplete ? 1 : 0; if (isProfileComplete && !isCMU) { @@ -125,11 +125,14 @@ export const computeAnalytics = async (): Promise => { } // Count accepted - const isAdmitted = doesStatusImply(status, Status.ADMITTED); + const isAdmitted = + status == Status.WAITLISTED || doesStatusImply(status, Status.ADMITTED); stats.admitted += isAdmitted ? 1 : 0; // Count confirmed - const isConfirmed = doesStatusImply(status, Status.CONFIRMED); + const isConfirmed = + status == Status.WAITLISTED || + doesStatusImply(status, Status.CONFIRMED); stats.confirmed += isConfirmed ? 1 : 0; // Count declined diff --git a/src/settings.json b/src/settings.json index c381d0d..ef9cee9 100644 --- a/src/settings.json +++ b/src/settings.json @@ -22,6 +22,14 @@ "value": [], "description": "List of enabled email domains. Ignored if enableWhitelist is false." }, + "maxParticipants": { + "value": 450, + "description": "Number of participants that need to confirm before opening the waitlist" + }, + "autoWaitlist": { + "value": false, + "description": "Whether or not to automatically place new participants on the waitlist" + }, "waitlistText": { "value": "You have been placed on the waitlist!", "description": "Email sent to participants who were placed on the waitlist"