Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions packages/backend/src/events/events.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,14 +226,14 @@ export class EventsController {
};

// Check for file removal flag
const removeEventImage = updateEventDto.removeEventImage === 'true';
// const removeEventImage = updateEventDto.removeEventImage === 'true';

return this.eventsService.update(
+id,
user,
updatedEvent,
file,
removeEventImage,
// removeEventImage,
);
}

Expand Down Expand Up @@ -277,7 +277,7 @@ export class EventsController {
'Delete event with ID. \n\n REQUIRED ROLES: **ADMIN | MENTOR**',
})
@ApiBearerAuth()
remove(@Param('id') id: string) {
return this.eventsService.remove(+id);
remove(@GetUser() user: JwtPayload, @Param('id') id: string) {
return this.eventsService.remove(user, +id);
}
}
34 changes: 30 additions & 4 deletions packages/backend/src/events/events.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ export class EventsService {
user: JwtPayload,
updateEventDto: UpdateEventDto,
file: Express.Multer.File | null,
removeEventImage: boolean = false,
// removeEventImage: boolean = false,
) {
try {
// VALIDATE EVENT EXIST OR THROW EXCEPTION
Expand Down Expand Up @@ -322,7 +322,7 @@ export class EventsService {
// Handle image updates
let imagePath: string | undefined;

if (removeEventImage && eventToUpdate.image) {
if (file && eventToUpdate.image) {
// Delete existing image file
try {
await this.filesService.deleteFile(eventToUpdate.image);
Expand Down Expand Up @@ -390,7 +390,33 @@ export class EventsService {
}
}

remove(id: number) {
return `This action removes a #${id} event`;
async remove(user: JwtPayload, event_id: number) {
try {
// VALIDATE EVENT EXIST OR THROW EXCEPTION
const eventToUpdate = await this.findOne(event_id);
if (!eventToUpdate) {
throw new NotFoundException(`Event with ID: ${event_id} not found.`);
}

// VALIDATE IF THE USER IS ADMIN OR MANAGER
const allowedToUpdate =
user.roles === 'ADMIN'
? true
: await this.eventsManagersService.isEventManager(user.sub, event_id);
if (!allowedToUpdate) {
throw new UnauthorizedException(
'You are not authorized to edit this event.',
);
}

// DELETE EVENT
const deletedEvent = await this.prisma.events.delete({
where: { id: event_id },
});

return deletedEvent;
} catch (error) {
throw new BadRequestException('Error deleting event: ' + error.message);
}
}
}
144 changes: 142 additions & 2 deletions packages/backend/src/mentor/entities/mentor.entity.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { IsInt, IsString, IsOptional, IsEnum, Min, Max } from 'class-validator';
import { mentors_status } from '@prisma/client';
import {
IsInt,
IsString,
IsOptional,
IsEnum,
Min,
Max,
IsNumber,
IsArray,
IsObject,
} from 'class-validator';
import { mentors_status, matching_status } from '@prisma/client';
import { ApiProperty } from '@nestjs/swagger';

export class Mentors {
Expand Down Expand Up @@ -43,3 +53,133 @@ export class Mentors {
@IsInt()
user_id: number;
}

export class MentorStatistics {
@ApiProperty({ description: 'Total minutes mentored', example: 1893 })
@IsNumber()
minutesMentored: number = 0;

@ApiProperty({
description: 'Percentage difference with previous month',
example: 25.5,
})
@IsNumber()
minutesMentoredDiff: number = 0;

@ApiProperty({ description: 'Total number of mentees', example: 15 })
@IsNumber()
mentees: number = 0;

@ApiProperty({
description: 'Percentage difference with previous month',
example: -2.5,
})
@IsNumber()
menteesDiff: number = 0;

@ApiProperty({ description: 'Total number of live sessions', example: 64 })
@IsNumber()
liveSessions: number = 0;

@ApiProperty({
description: 'Percentage difference with previous month',
example: -10.0,
})
@IsNumber()
liveSessionsDiff: number = 0;
}

export class MentorUpcomingSessions {
@ApiProperty({ description: 'ID of the upcoming session', example: '10' })
@IsNumber()
id?: number;

@ApiProperty({
description: 'Profile picture of the mentee',
example: 'https://example.com/avatar.jpg',
})
@IsOptional()
@IsString()
avatar?: string;

@ApiProperty({ description: 'Full name of the mentee', example: 'John Doe' })
@IsString()
mentee: string;

@ApiProperty({
description: 'Last session with the mentee',
example: '2024-01-15T14:00:00Z',
})
@IsOptional()
@IsString()
lastMet?: string;

@ApiProperty({
description: 'Profession of the mentee',
example: 'Software Engineer',
})
@IsOptional()
@IsString()
profession?: string;

@ApiProperty({
description: 'Link for the upcoming session',
example: 'https://meet.google.com/abc-defg-hij',
})
@IsOptional()
@IsString()
link?: string;
}

export class MyMentees {
@ApiProperty({ description: 'ID of the mentee', example: '10' })
@IsNumber()
id?: number;

@ApiProperty({
description: 'Full name of the mentee',
example: 'Jane Smith',
})
@IsString()
mentee: string;

@ApiProperty({
description: 'Email of the mentee',
example: 'jane.smith@example.com',
})
@IsString()
email: string;

@ApiProperty({
description: 'Profession of the mentee',
example: 'Data Scientist',
})
@IsOptional()
@IsString()
profession?: string;

@ApiProperty({
description: 'Status of the matching',
example: 'APPROVED',
enum: matching_status,
})
@IsEnum(matching_status)
status: matching_status;
}

export class MyMentorDashboard {
@ApiProperty({ description: 'Mentor statistics', type: MentorStatistics })
@IsObject()
statistics: MentorStatistics;

@ApiProperty({
description: 'List of upcoming sessions',
type: [MentorUpcomingSessions],
})
@IsArray()
upcomingSessions: MentorUpcomingSessions[];

@ApiProperty({ description: 'List of mentees', type: [MyMentees] })
@IsArray()
mentees: MyMentees[];
}
86 changes: 77 additions & 9 deletions packages/backend/src/mentor/mentor.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,22 @@ import {
ApiBadRequestResponse,
ApiConsumes,
} from '@nestjs/swagger';
import { Mentors } from './entities/mentor.entity';
import {
Mentors,
MentorStatistics,
MentorUpcomingSessions,
MyMentees,
MyMentorDashboard,
} from './entities/mentor.entity';

@UseGuards(JwtAuthGuard, RolesGuard)
@ApiTags('Mentors')
@Controller('mentors')
export class MentorController {
constructor(private readonly mentorService: MentorService) {}
// ------------------------------
// MENTOR APPLICATIONS
// ------------------------------

@Get()
@Roles('ADMIN')
Expand All @@ -52,7 +61,7 @@ export class MentorController {
description:
'Fetch any mentor applications. \n\n REQUIRED ROLES: **ADMIN**',
})
@ApiBearerAuth()
@ApiBearerAuth('JWT')
findAll(@Body() filters: FilterMentorDto) {
return this.mentorService.findAll(filters);
}
Expand All @@ -70,12 +79,12 @@ export class MentorController {
description:
'There is no mentor application for this user (USER ID: #`:id` )',
})
@ApiBearerAuth()
@ApiBearerAuth('JWT')
findMyApplication(@GetUser() user: JwtPayload) {
return this.mentorService.findOneByUserId(user.sub);
}

@Get(':mentorApplicationId')
@Get('application-id/:mentorApplicationId')
@Roles('ADMIN')
@ApiParam({ name: 'mentorApplicationId' })
@ApiOkResponse({ type: Mentors })
Expand All @@ -89,7 +98,7 @@ export class MentorController {
description:
'There is no mentor application with ID: `:mentorApplicationId`. Make sure you are not using a user ID',
})
@ApiBearerAuth()
@ApiBearerAuth('JWT')
findOne(@Param('mentorApplicationId') mentorApplicationId: string) {
return this.mentorService.findOneById(+mentorApplicationId);
}
Expand Down Expand Up @@ -156,7 +165,7 @@ export class MentorController {
@ApiNotFoundResponse({
description: 'Mentor application for user with ID: `user_id` not found',
})
@ApiBearerAuth()
@ApiBearerAuth('JWT')
update(
@GetUser() user: JwtPayload,
@Body() updateMentorDto: UpdateMentorDto,
Expand All @@ -172,7 +181,7 @@ export class MentorController {
return this.mentorService.update(+user.sub, updatedMentor);
}

@Put(':mentorApplicationId')
@Put('application-id/:mentorApplicationId')
@Roles('ADMIN')
@ApiParam({ name: 'mentorApplicationId' })
@ApiBody({ type: UpdateMentorStatusDto })
Expand All @@ -193,7 +202,7 @@ export class MentorController {
@ApiBadRequestResponse({
description: 'Error updating mentor application status: `[ERROR MESSAGE]`',
})
@ApiBearerAuth()
@ApiBearerAuth('JWT')
updateStatus(
@Param('mentorApplicationId') mentorApplicationId: string,
@Body() updateMentorStatusDto: UpdateMentorStatusDto,
Expand All @@ -205,7 +214,6 @@ export class MentorController {
}

// TO-DO: REMOVE MENTOR INFORMATION? OR CHANGE STATUS TO "REJECTED" OR "DELETED" TO KEEP THE INFORMATION?

@Delete(':id')
@Roles('ADMIN')
@ApiOkResponse({ type: String })
Expand All @@ -219,4 +227,64 @@ export class MentorController {
remove(@Param('id') id: string) {
return this.mentorService.remove(+id);
}

// ------------------------------
// MENTOR DASHBOARD
// ------------------------------

@Get('my-dashboard')
@Roles('MENTOR')
@ApiOkResponse({ type: MyMentorDashboard })
@ApiInternalServerErrorResponse({ description: 'Internal server error' })
@ApiOperation({
summary: 'Get mentor dashboard',
description:
'Get complete mentor dashboard with statistics, upcoming sessions, and mentees. \n\n REQUIRED ROLES: **MENTOR**',
})
@ApiBearerAuth('JWT')
async getMyDashboard(@GetUser() user: JwtPayload) {
return await this.mentorService.getMyDashboard(user.sub);
}

@Get('my-statistics')
@Roles('MENTOR')
@ApiOkResponse({ type: MentorStatistics })
@ApiInternalServerErrorResponse({ description: 'Internal server error' })
@ApiOperation({
summary: 'Get mentor statistics',
description:
'Get mentor statistics including minutes mentored, mentees count, and live sessions with percentage differences. \n\n REQUIRED ROLES: **MENTOR**',
})
@ApiBearerAuth('JWT')
async getMyStatistics(@GetUser() user: JwtPayload) {
return await this.mentorService.getMyStatistics(user.sub);
}

@Get('my-upcoming-sessions')
@Roles('MENTOR')
@ApiOkResponse({ type: [MentorUpcomingSessions] })
@ApiInternalServerErrorResponse({ description: 'Internal server error' })
@ApiOperation({
summary: 'Get mentor upcoming sessions',
description:
'Get list of upcoming sessions with mentee details. \n\n REQUIRED ROLES: **MENTOR**',
})
@ApiBearerAuth('JWT')
async getMyUpcomingSessions(@GetUser() user: JwtPayload) {
return await this.mentorService.getMyUpcomingSessions(user.sub);
}

@Get('my-mentees')
@Roles('MENTOR')
@ApiOkResponse({ type: [MyMentees] })
@ApiInternalServerErrorResponse({ description: 'Internal server error' })
@ApiOperation({
summary: 'Get mentor mentees',
description:
'Get list of mentees assigned to the mentor. \n\n REQUIRED ROLES: **MENTOR**',
})
@ApiBearerAuth('JWT')
async getMyMentees(@GetUser() user: JwtPayload) {
return await this.mentorService.getMyMentees(user.sub);
}
}
Loading