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
66 changes: 63 additions & 3 deletions src/main/java/com/wcc/platform/controller/MenteeController.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package com.wcc.platform.controller;

import com.wcc.platform.configuration.security.RequiresPermission;
import com.wcc.platform.domain.auth.Permission;
import com.wcc.platform.domain.platform.mentorship.ApplicationRejectRequest;
import com.wcc.platform.domain.platform.mentorship.Mentee;
import com.wcc.platform.domain.platform.mentorship.MenteeRegistration;
import com.wcc.platform.service.MenteeAdminService;
import com.wcc.platform.service.MenteeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
Expand All @@ -13,6 +18,8 @@
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
Expand All @@ -31,6 +38,7 @@
public class MenteeController {

private final MenteeService menteeService;
private final MenteeAdminService menteeAdminService;

/**
* API to create mentee.
Expand All @@ -48,14 +56,66 @@ public ResponseEntity<Mentee> createMentee(
}

/**
* Retrieves a list of all registered mentees.
* Retrieves a list of all active mentees (status_id = 1).
*
* @return a list of Mentee existent mentees
* @return a list of active mentees
*/
@GetMapping("/mentees")
@Operation(summary = "API to list all mentees")
@Operation(summary = "API to list all active mentees")
@ResponseStatus(HttpStatus.OK)
public ResponseEntity<List<Mentee>> listMentees() {
return new ResponseEntity<>(menteeService.getAllMentees(), HttpStatus.OK);
}

/**
* Retrieves all mentees with PENDING status awaiting admin review.
*
* @return a list of pending mentees
*/
@GetMapping("/mentees/pending")
@RequiresPermission(Permission.MENTEE_APPROVE)
@Operation(
summary = "Get all pending mentees awaiting admin activation",
security = {@SecurityRequirement(name = "apiKey"), @SecurityRequirement(name = "bearerAuth")})
@ResponseStatus(HttpStatus.OK)
public ResponseEntity<List<Mentee>> getPendingMentees() {
return ResponseEntity.ok(menteeAdminService.getPendingMentees());
}

/**
* Admin activates a mentee by setting their profile status to ACTIVE.
*
* @param menteeId The mentee ID
* @return the activated mentee
*/
@PatchMapping("/mentees/{menteeId}/activate")
@RequiresPermission(Permission.MENTEE_APPROVE)
@Operation(
summary = "Admin activates a mentee (sets status to ACTIVE)",
security = {@SecurityRequirement(name = "apiKey"), @SecurityRequirement(name = "bearerAuth")})
@ResponseStatus(HttpStatus.OK)
public ResponseEntity<Mentee> activateMentee(
@Parameter(description = "Mentee ID") @PathVariable final Long menteeId) {
return ResponseEntity.ok(menteeAdminService.activateMentee(menteeId));
}

/**
* Admin rejects a mentee by setting their profile status to REJECTED and rejecting all pending
* applications.
*
* @param menteeId The mentee ID
* @param request Rejection request containing the reason
* @return the rejected mentee
*/
@PatchMapping("/mentees/{menteeId}/reject")
@RequiresPermission(Permission.MENTEE_APPROVE)
@Operation(
summary = "Admin rejects a mentee (sets status to REJECTED and rejects all applications)",
security = {@SecurityRequirement(name = "apiKey"), @SecurityRequirement(name = "bearerAuth")})
@ResponseStatus(HttpStatus.OK)
public ResponseEntity<Mentee> rejectMentee(
@Parameter(description = "Mentee ID") @PathVariable final Long menteeId,
@Valid @RequestBody final ApplicationRejectRequest request) {
return ResponseEntity.ok(menteeAdminService.rejectMentee(menteeId, request.reason()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,70 +4,71 @@
import lombok.RequiredArgsConstructor;

/**
* Enum representing the status of a mentee application to a mentor.
* Corresponds to the application_status enum in the database.
* Tracks the complete workflow from application submission to matching.
* Enum representing the status of a mentee application to a mentor. Corresponds to the
* application_status enum in the database. Tracks the complete workflow from application submission
* to matching.
*/
@Getter
@RequiredArgsConstructor
public enum ApplicationStatus {
PENDING("pending", "Mentee submitted application, awaiting mentor response"),
MENTOR_REVIEWING("mentor_reviewing", "Mentor is actively reviewing the application"),
MENTOR_ACCEPTED("mentor_accepted", "Mentor accepted, awaiting team confirmation"),
MENTOR_DECLINED("mentor_declined", "Mentor declined this application"),
MATCHED("matched", "Successfully matched and confirmed"),
DROPPED("dropped", "Mentee withdrew application"),
REJECTED("rejected", "Rejected by Mentorship Team"),
EXPIRED("expired", "Application expired (no response within timeframe)");
PENDING("pending", "Mentee submitted application, awaiting mentor response"),
MENTOR_REVIEWING("mentor_reviewing", "Mentor is actively reviewing the application"),
MENTOR_ACCEPTED("mentor_accepted", "Mentor accepted, awaiting team confirmation"),
MENTOR_DECLINED("mentor_declined", "Mentor declined this application"),
MATCHED("matched", "Successfully matched and confirmed"),
DROPPED("dropped", "Mentee withdrew application"),
REJECTED("rejected", "Rejected by Mentorship Team"),
EXPIRED("expired", "Application expired (no response within timeframe)");

private final String value;
private final String description;
private final String value;

/**
* Get ApplicationStatus from database string value.
*
* @param value the database string value
* @return the corresponding ApplicationStatus
* @throws IllegalArgumentException if the value doesn't match any enum
*/
public static ApplicationStatus fromValue(final String value) {
for (final ApplicationStatus status : values()) {
if (status.value.equalsIgnoreCase(value)) {
return status;
}
}
throw new IllegalArgumentException("Unknown application status: " + value);
}
private final String description;

/**
* Check if the application is in a terminal state (no further changes expected).
*
* @return true if status is terminal
*/
public boolean isTerminal() {
return this == MATCHED || this == REJECTED || this == DROPPED || this == EXPIRED;
/**
* Get ApplicationStatus from database string value.
*
* @param value the database string value
* @return the corresponding ApplicationStatus
* @throws IllegalArgumentException if the value doesn't match any enum
*/
public static ApplicationStatus fromValue(final String value) {
for (final ApplicationStatus status : values()) {
if (status.value.equalsIgnoreCase(value)) {
return status;
}
}
throw new IllegalArgumentException("Unknown application status: " + value);
}

/**
* Check if the application is pending mentor action.
*
* @return true if awaiting mentor response
*/
public boolean isPendingMentorAction() {
return this == PENDING || this == MENTOR_REVIEWING;
}
/**
* Check if the application is in a terminal state (no further changes expected).
*
* @return true if status is terminal
*/
public boolean isTerminal() {
return this == MATCHED || this == REJECTED || this == DROPPED || this == EXPIRED;
}

/**
* Check if the application has been accepted by mentor.
*
* @return true if mentor accepted
*/
public boolean isMentorAccepted() {
return this == MENTOR_ACCEPTED || this == MATCHED;
}
/**
* Check if the application is pending mentor action.
*
* @return true if awaiting mentor response
*/
public boolean isPendingMentorAction() {
return this == PENDING || this == MENTOR_REVIEWING;
}

@Override
public String toString() {
return value;
}
/**
* Check if the application has been accepted by mentor.
*
* @return true if mentor accepted
*/
public boolean isMentorAccepted() {
return this == MENTOR_ACCEPTED || this == MATCHED;
}

@Override
public String toString() {
return value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,22 @@ public interface MenteeApplicationRepository extends CrudRepository<MenteeApplic
* @return the total number of applications submitted by the mentee in the specified cycle
*/
Long countMenteeApplications(Long menteeId, Long cycleId);

/**
* Find all applications with a specific status and priority order.
*
* @param status the application status to filter by
* @param priorityOrder the priority order to filter by (1 = highest priority)
* @return list of applications matching both filters, ordered by applied date descending
*/
List<MenteeApplication> findByStatusAndPriorityOrder(
ApplicationStatus status, Integer priorityOrder);

/**
* Find all PENDING applications for a specific mentee across all cycles, ordered by priority.
*
* @param menteeId the mentee ID
* @return list of PENDING applications for the mentee ordered by priority
*/
List<MenteeApplication> findPendingByMenteeId(Long menteeId);
}
18 changes: 18 additions & 0 deletions src/main/java/com/wcc/platform/repository/MenteeRepository.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.wcc.platform.repository;

import com.wcc.platform.domain.platform.member.ProfileStatus;
import com.wcc.platform.domain.platform.mentorship.Mentee;
import java.util.List;

Expand All @@ -15,4 +16,21 @@ public interface MenteeRepository extends CrudRepository<Mentee, Long> {
* @return list of mentees
*/
List<Mentee> getAll();

/**
* Return all mentees with the given profile status.
*
* @param status the profile status to filter by
* @return list of mentees with the given status
*/
List<Mentee> findByStatus(ProfileStatus status);

/**
* Update the profile status of a mentee.
*
* @param menteeId the mentee ID
* @param status the new profile status
* @return the updated mentee
*/
Mentee updateProfileStatus(Long menteeId, ProfileStatus status);
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ public class PostgresMenteeApplicationRepository implements MenteeApplicationRep
"SELECT * FROM mentee_applications WHERE application_status = ?::application_status "
+ "ORDER BY applied_at DESC";

private static final String SEL_BY_STATUS_PRIO =
"SELECT * FROM mentee_applications WHERE application_status = ?::application_status "
+ "AND priority_order = ? ORDER BY applied_at DESC";

private static final String SEL_PENDING_MENTEE =
"SELECT * FROM mentee_applications WHERE mentee_id = ? "
+ "AND application_status = 'pending' ORDER BY priority_order";

private static final String SEL_BY_MENTOR =
"SELECT * FROM mentee_applications "
+ "WHERE mentee_id = ? AND mentor_id = ? AND cycle_id = ?";
Expand Down Expand Up @@ -167,6 +175,21 @@ public Long countMenteeApplications(final Long menteeId, final Long cycleId) {
return jdbc.queryForObject(COUNT_MENTEE_APPS, Long.class, menteeId, cycleId);
}

@Override
public List<MenteeApplication> findByStatusAndPriorityOrder(
final ApplicationStatus status, final Integer priorityOrder) {
return jdbc.query(
SEL_BY_STATUS_PRIO,
(rs, rowNum) -> mapRow(rs),
status.getValue(),
priorityOrder);
}

@Override
public List<MenteeApplication> findPendingByMenteeId(final Long menteeId) {
return jdbc.query(SEL_PENDING_MENTEE, (rs, rowNum) -> mapRow(rs), menteeId);
}

private MenteeApplication mapRow(final ResultSet rs) throws SQLException {
return MenteeApplication.builder()
.applicationId(rs.getLong("application_id"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ public class PostgresMenteeRepository implements MenteeRepository {
private static final String SQL_GET_BY_ID = "SELECT * FROM mentees WHERE mentee_id = ?";
private static final String SQL_DELETE_BY_ID = "DELETE FROM mentees WHERE mentee_id = ?";
private static final String SELECT_ALL_MENTEES = "SELECT * FROM mentees";
private static final String SELECT_BY_STATUS =
"SELECT * FROM mentees WHERE mentees_profile_status = ?";
private static final String SQL_SET_STATUS =
"UPDATE mentees SET mentees_profile_status = ? WHERE mentee_id = ?";
private static final String SQL_INSERT_MENTEE =
"INSERT INTO mentees (mentee_id, mentees_profile_status, bio, years_experience, "
+ "spoken_languages, available_hs_month) VALUES (?, ?, ?, ?, ?, ?)";
Expand Down Expand Up @@ -131,6 +135,20 @@ public void deleteById(final Long menteeId) {
jdbc.update(SQL_DELETE_BY_ID, menteeId);
}

@Override
public List<Mentee> findByStatus(final ProfileStatus status) {
return jdbc.query(SELECT_BY_STATUS, (rs, rowNum) -> menteeMapper.mapRowToMentee(rs),
status.getStatusId());
}

@Override
public Mentee updateProfileStatus(final Long menteeId, final ProfileStatus status) {
jdbc.update(SQL_SET_STATUS, status.getStatusId(), menteeId);
return findById(menteeId)
.orElseThrow(
() -> new MenteeNotSavedException("Mentee not found after status update: " + menteeId));
}

private void updateMenteeDetails(final Mentee mentee, final Long memberId) {
final var profileStatus = mentee.getProfileStatus();
final var skills = mentee.getSkills();
Expand Down
Loading
Loading