Skip to content
This repository was archived by the owner on May 12, 2026. It is now read-only.

update staging - #61

Merged
oduwoleeyinojuoluwa44 merged 3 commits into
stagingfrom
dev
May 12, 2026
Merged

update staging #61
oduwoleeyinojuoluwa44 merged 3 commits into
stagingfrom
dev

Conversation

@oduwoleeyinojuoluwa44

Copy link
Copy Markdown
Collaborator

Description

Related Issue (Link to Github issue)

Motivation and Context

How Has This Been Tested?

Screenshots (if appropriate - Postman, etc):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

oduwoleeyinojuoluwa44 and others added 3 commits May 12, 2026 09:12
New endpoint replaces the legacy onboarding flow for the FE-ONB-EMP-001 UI:

Fields accepted:
- employerType: Founder | Recruiter | Agency (422 on invalid)
- companyName: required string
- companySize: 1-10 | 11-50 | 51-200 | 201-500 | 500+ (422 on invalid)
- companyWebsite: optional, URL-validated (422 on bad format)
- hiringRoles: string[] min 1, custom values accepted (422 if empty)
- hiringLocations: string[] min 1, validated enum (422 on invalid/empty)

Returns 201 { status: "success", message: "Profile saved" }.
Upserts employer_profiles and sets onboarding_complete = true.
Validation uses endpoint-level pipe with errorHttpStatusCode 422.

Migration adds employer_type, hiring_roles, hiring_locations columns.
Legacy POST /employer/onboarding kept for backward compatibility.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New endpoints (all role-guarded to TALENT, 422 for validation errors):

POST /talent/onboarding/goal       — Step 1: save single goal; 422 on missing/invalid
POST /talent/onboarding/track      — Step 2: save single track; 422 on missing/invalid
POST /talent/profile               — Step 3: multipart photo (required) + optional
                                     region/educationLevel/linkedinProfile; returns 201
                                     Sets onboarding_complete=true; profile_verified=true
                                     only when all optional fields are also provided
POST /talent/onboarding/personalise — Step 4: reads saved data, logs personalisation
                                     event (userId, timestamp, available data points);
                                     422 if track missing; assessments generated from
                                     track; recommendations skipped if goal/region/
                                     education missing

Migration adds track (varchar) and profile_verified (bool) to talent_profiles.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rding

Feat/talent progressive onboarding
Copilot AI review requested due to automatic review settings May 12, 2026 09:46
@oduwoleeyinojuoluwa44
oduwoleeyinojuoluwa44 merged commit 0d98189 into staging May 12, 2026
6 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces new multi-step onboarding endpoints for talent (goal → track → profile upload → personalisation) and a new employer profile onboarding endpoint, alongside the required message additions and database schema changes.

Changes:

  • Added new talent onboarding step endpoints and corresponding service logic, including a profile-photo upload flow and a personalisation trigger.
  • Added a new employer POST /employer/profile onboarding completion endpoint with new profile fields/constants.
  • Added DB migrations + entity updates for talent_profiles.track/profile_verified and new employer_profiles fields, plus updated shared success/error messages and adjusted an e2e mock.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
test/onboarding.e2e-spec.ts Updates UploadService mock to return a URL for avatar uploads.
src/shared/messages/success.messages.ts Adds new onboarding success message keys for the new endpoints.
src/shared/messages/error.messages.ts Adds new onboarding error message keys for photo validation and personalisation.
src/modules/talent/talent.service.ts Implements new talent onboarding step methods, photo/profile save, and personalisation logging.
src/modules/talent/talent.controller.ts Adds new talent onboarding routes (goal/track/profile/personalise) and multipart handling.
src/modules/talent/entities/talent-profile.entity.ts Adds track and profile_verified fields to the TalentProfile entity.
src/modules/talent/dto/save-goal.dto.ts Adds DTO + validation for single-goal selection.
src/modules/talent/dto/save-track.dto.ts Adds DTO + validation for single-track selection.
src/modules/talent/dto/save-talent-profile.dto.ts Adds DTO + validation for optional profile fields in the new step flow.
src/modules/employer/entities/employer-profile.entity.ts Adds new employer profile fields for the new onboarding endpoint.
src/modules/employer/employer.service.ts Implements employer profile save + onboarding completion transaction flow.
src/modules/employer/employer.controller.ts Adds POST /employer/profile endpoint with validation pipe configuration.
src/modules/employer/employer.constants.ts Adds constants for employer types, company sizes, and hiring locations.
src/modules/employer/dto/save-employer-profile.dto.ts Adds DTO + validation for the new employer profile onboarding endpoint.
src/database/migrations/1779100000000-AddTalentTrackAndProfileVerified.ts Adds track and profile_verified columns to talent_profiles.
src/database/migrations/1779000000000-AddEmployerProfileNewFields.ts Adds employer_type, hiring_roles, and hiring_locations columns to employer_profiles.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +168 to +173
FileInterceptor('photo', {
storage: memoryStorage(),
fileFilter: (_req, file, cb) => {
if (ALLOWED_MIME_TYPES.includes(file.mimetype)) {
cb(null, true);
} else {
Comment on lines +141 to +143
@ApiOperation({ summary: 'Step 1 — save career goal (BE-ONB-TAL-001)' })
@UsePipes(new ValidationPipe({ whitelist: true, transform: true, errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY }))
async saveGoalStep(
Comment on lines +153 to +155
@ApiOperation({ summary: 'Step 2 — save track (BE-ONB-TAL-002)' })
@UsePipes(new ValidationPipe({ whitelist: true, transform: true, errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY }))
async saveTrackStep(
},
}),
)
@UsePipes(new ValidationPipe({ whitelist: true, transform: true, errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY }))
Comment on lines +85 to +95
/** BE-ONB-TAL-001 — save single goal, 422 on invalid. */
async saveGoalStep(
userId: string,
dto: SaveGoalDto,
): Promise<{ status: string; message: string }> {
const profile = await this.findOrCreateProfile(userId);
profile.goal = dto.goal;
if (profile.onboarding_step < 1) profile.onboarding_step = 1;
await this.talentProfileRepository.save(profile);
return { status: 'success', message: SuccessMessages.ONBOARDING.GOAL_SAVED };
}
Comment on lines +39 to +45
@UsePipes(
new ValidationPipe({
whitelist: true,
transform: true,
errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY,
}),
)
Comment on lines +27 to +31
@ApiProperty({ example: 'Acme Labs' })
@IsString()
@MinLength(1, { message: 'companyName is required' })
@MaxLength(255)
companyName: string;
@Column({ type: 'varchar', length: 100, nullable: true })
track: string | null;

@ApiProperty({ default: false, description: 'True when all profile fields including optional ones are complete' })
Comment on lines +138 to +203
/** BE-ONB-TAL-001 — save goal (single select, 422 on missing/invalid). */
@Post('onboarding/goal')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'Step 1 — save career goal (BE-ONB-TAL-001)' })
@UsePipes(new ValidationPipe({ whitelist: true, transform: true, errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY }))
async saveGoalStep(
@CurrentUser('sub') userId: string,
@Body() dto: SaveGoalDto,
) {
return this.talentService.saveGoalStep(userId, dto);
}

/** BE-ONB-TAL-002 — save single track (422 on missing/invalid). */
@Post('onboarding/track')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'Step 2 — save track (BE-ONB-TAL-002)' })
@UsePipes(new ValidationPipe({ whitelist: true, transform: true, errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY }))
async saveTrackStep(
@CurrentUser('sub') userId: string,
@Body() dto: SaveTrackDto,
) {
return this.talentService.saveTrackStep(userId, dto);
}

/** BE-ONB-TAL-003 — save profile; photo required, optional fields optional. */
@Post('profile')
@HttpCode(HttpStatus.CREATED)
@ApiOperation({ summary: 'Step 3 — save talent profile with photo upload (BE-ONB-TAL-003)' })
@ApiConsumes('multipart/form-data')
@UseInterceptors(
FileInterceptor('photo', {
storage: memoryStorage(),
fileFilter: (_req, file, cb) => {
if (ALLOWED_MIME_TYPES.includes(file.mimetype)) {
cb(null, true);
} else {
cb(
new UnprocessableEntityException(ErrorMessages.ONBOARDING.INVALID_PHOTO_TYPE),
false,
);
}
},
}),
)
@UsePipes(new ValidationPipe({ whitelist: true, transform: true, errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY }))
async saveTalentProfile(
@CurrentUser('sub') userId: string,
@UploadedFile() photo: Express.Multer.File | undefined,
@Body() dto: SaveTalentProfileDto,
) {
if (!photo) {
throw new UnprocessableEntityException(ErrorMessages.ONBOARDING.PHOTO_REQUIRED);
}
if (photo.size > MAX_FILE_BYTES) {
throw new UnprocessableEntityException(ErrorMessages.ONBOARDING.PHOTO_TOO_LARGE);
}
return this.talentService.saveTalentProfile(userId, photo, dto);
}

/** BE-ONB-TAL-004 — trigger personalisation; no body needed. */
@Post('onboarding/personalise')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'Step 4 — personalise dashboard from saved onboarding data (BE-ONB-TAL-004)' })
async personalise(@CurrentUser('sub') userId: string) {
return this.talentService.personalise(userId);
}
Comment on lines +34 to +51
@Post('profile')
@HttpCode(HttpStatus.CREATED)
@ApiOperation({ summary: 'Save employer profile and complete onboarding (BE-ONB-EMP-001)' })
@ApiUnprocessableEntityResponse({ description: 'Validation failed — field-specific error messages' })
@ApiForbiddenResponse({ description: 'Onboarding already completed or wrong role' })
@UsePipes(
new ValidationPipe({
whitelist: true,
transform: true,
errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY,
}),
)
async saveProfile(
@CurrentUser('sub') userId: string,
@Body() dto: SaveEmployerProfileDto,
) {
return this.employerService.saveProfile(userId, dto);
}
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants