diff --git a/api/controllers/UserController.ts b/api/controllers/UserController.ts index 1ecb88e5..2e2c6106 100644 --- a/api/controllers/UserController.ts +++ b/api/controllers/UserController.ts @@ -96,6 +96,12 @@ export class UserController { return { error: null, user: userProfile }; } + @Patch('/onboarding/collect') + async collectOnboarding(@AuthenticatedUser() user: UserModel): Promise { + const userProfile = await this.userAccountService.collectOnboarding(user); + return { error: null, user: userProfile }; + } + @Patch() async patchCurrentUser(@Body() patchUserRequest: PatchUserRequest, @AuthenticatedUser() user: UserModel): Promise { diff --git a/api/validators/UserControllerRequests.ts b/api/validators/UserControllerRequests.ts index f0b03f9b..f3ba77ce 100644 --- a/api/validators/UserControllerRequests.ts +++ b/api/validators/UserControllerRequests.ts @@ -47,6 +47,9 @@ export class UserPatches implements IUserPatches { @Allow() isAttendancePublic?: boolean; + @Allow() + onboardingSeen?: boolean; + @Type(() => PasswordUpdate) @ValidateNested() @HasMatchingPasswords() diff --git a/services/UserAccountService.ts b/services/UserAccountService.ts index 92d814f4..12e41af6 100644 --- a/services/UserAccountService.ts +++ b/services/UserAccountService.ts @@ -10,6 +10,7 @@ import { englishDataset, englishRecommendedTransformers, } from 'obscenity'; +import { UserError } from 'utils/Errors'; import Repositories, { TransactionsManager } from '../repositories'; import { Uuid, @@ -166,6 +167,23 @@ export default class UserAccountService { }); } + public async collectOnboarding(user: UserModel): Promise { + if (user.attendances.length < 5 + || user.resumes.length < 1 + || user.profilePicture == null + || user.bio == null) { + throw new UserError('Onboarding tasks not completed'); + } + if (user.onboardingCollected) { + throw new UserError('Onboarding reward already collected'); + } + return this.transactions.readWrite(async (txn) => { + const userRepository = Repositories.user(txn); + await userRepository.addPoints(user, 10); + return userRepository.upsertUser(user, { onboardingCollected: true }); + }); + } + public async grantBonusPoints(emails: string[], description: string, points: number) { return this.transactions.readWrite(async (txn) => { const userRepository = Repositories.user(txn); @@ -188,10 +206,6 @@ export default class UserAccountService { .getAllNamesAndEmails()); } - public async checkOnboarding(user: UserModel) : Promise { - - } - /** * UserAccountService::getFullUserProfile() differs from UserModel::getFullUserProfile() in that it also returns any * user data that needs to be joined from other tables (e.g. resumes and social media URLs) diff --git a/types/ApiRequests.ts b/types/ApiRequests.ts index 816b59f7..4af0f22f 100644 --- a/types/ApiRequests.ts +++ b/types/ApiRequests.ts @@ -68,6 +68,7 @@ export interface UserPatches { graduationYear?: number; bio?: string; isAttendancePublic?: boolean; + onboardingSeen?: boolean; passwordChange?: PasswordUpdate; }