Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AA-82: Assign Recruiters #99

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
13 changes: 13 additions & 0 deletions apps/backend/src/applications/application.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,23 @@ export class Application {
@IsObject({ each: true })
response: Response[];

@Column('jsonb', { nullable: true, default: [] })
@IsArray()
@IsObject({ each: true })
@OneToMany(() => User, (user) => user.firstName + user.lastName)
recruiters: User[];

@Column('varchar', { array: true, default: {} })
@IsArray()
@IsObject({ each: true })
@OneToMany(() => Review, (review) => review.application)
reviews: Review[];

@Column({ nullable: false, default: 0 })
@IsPositive()
@Min(0)
numApps: number;

toGetAllApplicationResponseDTO(
meanRatingAllReviews,
meanRatingResume,
Expand All @@ -80,6 +91,7 @@ export class Application {
step: applicationStep,
position: this.position,
createdAt: this.createdAt,
recruiters: this.recruiters,
meanRatingAllReviews,
meanRatingResume,
meanRatingChallenge,
Expand All @@ -101,6 +113,7 @@ export class Application {
stage: this.stage,
step: applicationStep,
response: this.response,
recruiters: this.recruiters,
reviews: this.reviews,
numApps,
};
Expand Down
26 changes: 25 additions & 1 deletion apps/backend/src/applications/applications.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
UseInterceptors,
UseGuards,
Post,
Patch,
Body,
BadRequestException,
NotFoundException,
Expand All @@ -17,11 +18,12 @@ import { ApplicationsService } from './applications.service';
import { CurrentUserInterceptor } from '../interceptors/current-user.interceptor';
import { AuthGuard } from '@nestjs/passport';
import { GetApplicationResponseDTO } from './dto/get-application.response.dto';
import { getAppForCurrentCycle } from './utils';
import { getAppForCurrentCycle, toGetApplicationResponseDTO } from './utils';
import { UserStatus } from '../users/types';
import { Application } from './application.entity';
import { GetAllApplicationResponseDTO } from './dto/get-all-application.response.dto';
import { ApplicationStep } from './types';
import { UpdateApplicationRequestDTO } from './dto/update-application.request.dto';

@Controller('apps')
@UseInterceptors(CurrentUserInterceptor)
Expand Down Expand Up @@ -115,4 +117,26 @@ export class ApplicationsController {

return app.toGetApplicationResponseDTO(apps.length, applicationStep);
}

// TODO: Update DTOs
@Patch('/:applicantId')
async updateApplication(
@Body() updateApplicationDTO: UpdateApplicationRequestDTO,
@Param('applicantId', ParseIntPipe) applicantId: number,
@Request() req,
): Promise<GetApplicationResponseDTO> {
if (req.user.status !== UserStatus.ADMIN) {
throw new UnauthorizedException(
'Only admins can assign recruiters to applicants',
);
}

const newApplicant = await this.applicationsService.updateApplication(
req.application,
applicantId,
updateApplicationDTO,
);

return toGetApplicationResponseDTO(newApplicant);
}
}
38 changes: 38 additions & 0 deletions apps/backend/src/applications/applications.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
BadRequestException,
UnauthorizedException,
Injectable,
NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
Expand All @@ -19,6 +20,7 @@ import { User } from '../users/user.entity';
import { Position, ApplicationStage, ApplicationStep } from './types';
import { GetAllApplicationResponseDTO } from './dto/get-all-application.response.dto';
import { stagesMap } from './applications.constants';
import { UpdateApplicationRequestDTO } from './dto/update-application.request.dto';

@Injectable()
export class ApplicationsService {
Expand Down Expand Up @@ -237,4 +239,40 @@ export class ApplicationsService {

return currentApp;
}

async findOne(
currentApplication: Application,
applicationId: number,
): Promise<Application> {
const application = await this.applicationsRepository.findOne({
where: { id: applicationId },
});

if (!application) {
throw new NotFoundException(
`Application with ID ${applicationId} not found`,
);
}

return application;
}

async updateApplication(
currentApplication: Application,
applicationId: number,
updateApplicationDTO: UpdateApplicationRequestDTO,
): Promise<Application> {
await this.findOne(currentApplication, applicationId);

try {
await this.applicationsRepository.update(
{ id: applicationId },
updateApplicationDTO,
);
} catch (error) {
throw new BadRequestException('Cannot update application');
}

return await this.findOne(currentApplication, applicationId);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { IsDate, IsEnum, IsPositive, IsString } from 'class-validator';
import { IsArray, IsDate, IsEnum, IsPositive, IsString } from 'class-validator';
import { ApplicationStage, ApplicationStep, Position } from '../types';
import { User } from '../../users/user.entity';

export class GetAllApplicationResponseDTO {
@IsPositive()
Expand All @@ -23,6 +24,9 @@ export class GetAllApplicationResponseDTO {
@IsDate()
createdAt: Date;

@IsArray()
recruiters: User[];

@IsPositive()
meanRatingAllReviews: number;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Review } from '../../reviews/review.entity';
import { User } from '../../users/user.entity';
import {
ApplicationStage,
ApplicationStep,
Expand All @@ -24,6 +25,8 @@ export class GetApplicationResponseDTO {

response: Response[];

recruiters: User[];

reviews: Review[];

numApps: number;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { User } from '../../users/user.entity';
import { Application } from '../application.entity';

Check warning on line 2 in apps/backend/src/applications/dto/update-application.request.dto.ts

View workflow job for this annotation

GitHub Actions / pre-deploy

'Application' is defined but never used
import {
Semester,
Position,
ApplicationStage,
ApplicationStep,
} from '../types';
import { Review } from '../../reviews/review.entity';
import {
IsArray,
IsEnum,
IsObject,
IsOptional,
IsPositive,
} from 'class-validator';

export class UpdateApplicationRequestDTO {
@IsOptional()
@IsEnum(Semester)
semester?: Semester;

@IsOptional()
@IsEnum(Position)
position?: Position;

@IsOptional()
@IsEnum(ApplicationStage)
stage: ApplicationStage;

@IsOptional()
@IsEnum(ApplicationStep)
step: ApplicationStep;

@IsOptional()
@IsArray()
@IsObject({ each: true })
reviews: Review[];

@IsOptional()
@IsArray()
@IsObject({ each: true })
recruiters: User[];

@IsOptional()
@IsPositive()
numApps: number;
}
19 changes: 19 additions & 0 deletions apps/backend/src/applications/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Application } from './application.entity';
import { Cycle } from './dto/cycle';
import { GetApplicationResponseDTO } from './dto/get-application.response.dto';
import { Semester } from './types';

export const getCurrentSemester = (): Semester => {
Expand Down Expand Up @@ -34,3 +35,21 @@ export const getAppForCurrentCycle = (

return null;
};

export const toGetApplicationResponseDTO = (
application: Application,
): GetApplicationResponseDTO => {
return {
id: application.id,
createdAt: application.createdAt,
year: application.year,
semester: application.semester,
position: application.position,
stage: application.stage,
step: application.step,
response: application.response,
recruiters: application.recruiters,
reviews: application.reviews,
numApps: application.numApps,
};
};
1 change: 1 addition & 0 deletions apps/backend/src/testing/factories/user.factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const defaultUser: User = {
team: null,
role: null,
applications: [],
recruiters: [],
};

export const userFactory = (user: Partial<User> = {}): User =>
Expand Down
1 change: 1 addition & 0 deletions apps/backend/src/users/dto/get-user.response.dto.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Entity } from 'typeorm';
import { Role, Team, UserStatus } from '../types';

// TODO: Update this DTO and toGetUserResponseDto to include 'recruiters' field
@Entity()
export class GetUserResponseDto {
id: number;
Expand Down
6 changes: 6 additions & 0 deletions apps/backend/src/users/dto/update-user.request.dto.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { User } from '../../users/user.entity';
import { Application } from '../../applications/application.entity';
import { UserStatus, Role, Team } from '../types';
import {
Expand Down Expand Up @@ -55,4 +56,9 @@ export class UpdateUserRequestDTO {
@IsArray()
@IsObject({ each: true })
applications?: Application[];

@IsOptional()
@IsArray()
@IsObject({ each: true })
recruiters?: User[];
}
6 changes: 6 additions & 0 deletions apps/backend/src/users/user.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,10 @@ export class User {
@IsObject({ each: true })
@OneToMany(() => Application, (application) => application.user)
applications: Application[];

@Column('jsonb', { nullable: true, default: [] })
@IsArray()
@IsObject({ each: true })
@OneToMany(() => User, (user) => user.firstName + user.lastName)
recruiters: User[];
}
9 changes: 9 additions & 0 deletions apps/frontend/src/api/apiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type {
Application,
ApplicationRow,
ApplicationStage,
User,
} from '@components/types';

const defaultBaseUrl =
Expand Down Expand Up @@ -47,6 +48,14 @@ export class ApiClient {
})) as Promise<ApplicationRow[]>;
}

public async getAllRecruiters(accessToken: string): Promise<User[]> {
return (await this.get('/api/users/recruiters', {
headers: {
Authorization: `Bearer ${accessToken}`,
},
})) as Promise<User[]>;
}

public async getApplication(
accessToken: string,
userId: number,
Expand Down
5 changes: 5 additions & 0 deletions apps/frontend/src/components/ApplicationTables/columns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ export const applicationColumns = [
headerName: 'Date',
width: 150,
},
{
field: 'assignedRecruiters',
headerName: 'Assigned Recruiters',
width: 150,
},
{
field: 'meanRatingAllStages',
headerName: 'Rating All Stages',
Expand Down
Loading
Loading