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
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public enum TemplateType {
REMINDER_MENTEE_LONG("reminder_long_term_mentorship_mentee.yml"),
MENTEES_MENTOR_LONG("list_potential_mentees_mentor_long.yml"),
FOLLOWUP_MENTEES_LONG("list_potential_mentees_follow_up_mentor_long.yml"),
NEW_MENTEES_LONG("alert_new_mentees_applications_mentor_long.yml"),
NEW_MENTEES_REVIEW("alert_new_mentees_applications_mentor.yml"),
STUDY_GROUP_AVAIL("confirm_availability_study_group_mentor.yml"),
STUDY_GROUPS_MENTEE("mentor_led_study_groups_mentee.yml"),
STUDY_GROUP_INTRO("study_group_introduction_email_mentee.yml"),
Expand All @@ -44,7 +44,9 @@ public enum TemplateType {
MENTEE_LIST_LINK("potential_list_mentees_for_mentor_using_link.yml"),
MENTEE_LIST_EMAIL("potential_list_mentees_for_mentor_using_email.yml"),
MENTEE_FEEDBACK_ADHOC("reminder_adhoc_mentorship_feedback_mentee.yml"),
RESET_PASSWORD("reset_password.yml");
RESET_PASSWORD("reset_password.yml"),
CONFIRM_LONG_TERM_PAIRING("confirm_long_term_pairing.yml"),
CONFIRM_ADHOC_PAIRING("confirm_adhoc_pairing.yml");

private final String templateFile;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
import com.wcc.platform.domain.platform.mentorship.MenteeApplication;
import com.wcc.platform.domain.platform.mentorship.MenteeApplicationAdminResponse;
import com.wcc.platform.domain.platform.mentorship.MenteeApplicationResponse;
import com.wcc.platform.domain.platform.mentorship.Mentor;
import com.wcc.platform.domain.platform.mentorship.MentorshipCycleEntity;
import com.wcc.platform.repository.MenteeApplicationRepository;
import com.wcc.platform.repository.MenteeRepository;
import com.wcc.platform.repository.MentorRepository;
import com.wcc.platform.repository.MentorshipCycleRepository;
import com.wcc.platform.repository.MentorshipMatchRepository;
import java.util.List;
Expand All @@ -37,11 +39,13 @@
@RequiredArgsConstructor
public class MenteeWorkflowService {

public static final String CYCLE_NOT_FOUND = "Cycle not found: ";
private final MenteeApplicationRepository applicationRepository;
private final MentorshipMatchRepository matchRepository;
private final MentorshipCycleRepository cycleRepository;
private final MenteeRepository menteeRepository;
private final MentorshipService mentorshipService;
private final MentorRepository mentorRepository;

/**
* Find applications for admin view by cycle, statuses, and optionally mentor.
Expand Down Expand Up @@ -97,19 +101,33 @@ public MenteeApplication approveApplication(final Long applicationId) {
throw new ContentNotFoundException("No pending application with id " + applicationId);
}

final Long mentorId = application.getMentorId();
final Mentor mentor =
mentorRepository
.findById(mentorId)
.orElseThrow(() -> new MentorNotFoundException("Mentor not found for id: " + mentorId));

final MenteeApplication updated =
applicationRepository.updateStatus(applicationId, ApplicationStatus.MENTOR_REVIEWING, null);

log.info(
"Application {} from mentee {} approved and to be reviewed by mentor {}",
applicationId,
application.getMenteeId(),
application.getMentorId());
mentorId);

final Long cycleId = application.getCycleId();
final MentorshipCycleEntity cycle =
cycleRepository
.findById(cycleId)
.orElseThrow(() -> new IllegalArgumentException(CYCLE_NOT_FOUND + cycleId));

mentorshipService
.getNotificationService()
.sendApplicationUpdate(Optional.of(application), updated);

mentorshipService.getNotificationService().sendNewMenteesNotification(mentor, cycle);

return updated;
}

Expand Down Expand Up @@ -332,9 +350,11 @@ public List<MenteeApplication> getApplicationsByStatus(final ApplicationStatus s
allEntries = true)
public MenteeApplication assignMentor(
final Long menteeId, final Long cycleId, final Long mentorId, final String notes) {
if (mentorshipService.getMentorRepository().findById(mentorId).isEmpty()) {
throw new MentorNotFoundException(mentorId);
}
final Mentor mentor =
mentorshipService
.getMentorRepository()
.findById(mentorId)
.orElseThrow(() -> new MentorNotFoundException(mentorId));

final var existingApp =
applicationRepository.findByMenteeMentorCycle(menteeId, mentorId, cycleId);
Expand Down Expand Up @@ -364,6 +384,13 @@ public MenteeApplication assignMentor(
final MenteeApplication created = applicationRepository.create(newApplication);
log.info("Manually assigned mentor {} to mentee {} in cycle {}", mentorId, menteeId, cycleId);

final MentorshipCycleEntity cycle =
cycleRepository
.findById(cycleId)
.orElseThrow(() -> new IllegalArgumentException(CYCLE_NOT_FOUND + cycleId));

mentorshipService.getNotificationService().sendNewMenteesNotification(mentor, cycle);

mentorshipService.getNotificationService().sendApplicationUpdate(Optional.empty(), created);

return created;
Expand Down Expand Up @@ -435,7 +462,7 @@ private void checkMentorCapacity(final Long mentorId, final Long cycleId) {
final MentorshipCycleEntity cycle =
cycleRepository
.findById(cycleId)
.orElseThrow(() -> new IllegalArgumentException("Cycle not found: " + cycleId));
.orElseThrow(() -> new IllegalArgumentException(CYCLE_NOT_FOUND + cycleId));

final int currentMentees =
matchRepository.countActiveMenteesByMentorAndCycle(mentorId, cycleId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

import com.wcc.platform.domain.exceptions.ApplicationNotFoundException;
import com.wcc.platform.domain.exceptions.MentorCapacityExceededException;
import com.wcc.platform.domain.exceptions.MentorNotFoundException;
import com.wcc.platform.domain.exceptions.MentorshipCycleClosedException;
import com.wcc.platform.domain.platform.mentorship.ApplicationStatus;
import com.wcc.platform.domain.platform.mentorship.MatchStatus;
import com.wcc.platform.domain.platform.mentorship.MenteeApplication;
import com.wcc.platform.domain.platform.mentorship.MentorshipCycleEntity;
import com.wcc.platform.domain.platform.mentorship.MentorshipMatch;
import com.wcc.platform.repository.MenteeApplicationRepository;
import com.wcc.platform.repository.MenteeRepository;
import com.wcc.platform.repository.MentorshipCycleRepository;
import com.wcc.platform.repository.MentorshipMatchRepository;
import java.time.LocalDate;
Expand All @@ -34,6 +36,7 @@ public class MentorshipMatchingService {
private final MenteeApplicationRepository applicationRepository;
private final MentorshipCycleRepository cycleRepository;
private final MentorshipService mentorshipService;
private final MenteeRepository menteeRepository;

/**
* Confirm a match from an accepted application. This is typically done by mentorship team after
Expand Down Expand Up @@ -93,6 +96,21 @@ public MentorshipMatch confirmMatch(final Long applicationId) {
// Reject all other pending applications for this mentee in this cycle
rejectOtherApplications(application.getMenteeId(), application.getCycleId(), applicationId);

final var mentor =
mentorshipService
.getMentorRepository()
.findById(application.getMentorId())
.orElseThrow(() -> new MentorNotFoundException(application.getMentorId()));

final var mentee =
menteeRepository
.findById(application.getMenteeId())
.orElseThrow(
() ->
new IllegalArgumentException("Mentee not found: " + application.getMenteeId()));

mentorshipService.getNotificationService().sendPairingConfirmation(mentor, mentee, cycle);

log.info(
"Match confirmed: mentor {} with mentee {} for cycle {}",
application.getMentorId(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,31 @@
import com.wcc.platform.configuration.NotificationConfig;
import com.wcc.platform.domain.email.EmailRequest;
import com.wcc.platform.domain.exceptions.EmailSendException;
import com.wcc.platform.domain.platform.mentorship.Mentee;
import com.wcc.platform.domain.platform.mentorship.MenteeApplication;
import com.wcc.platform.domain.platform.mentorship.Mentor;
import com.wcc.platform.domain.platform.mentorship.MentorshipCycleEntity;
import com.wcc.platform.domain.platform.mentorship.MentorshipMatch;
import com.wcc.platform.domain.platform.mentorship.MentorshipType;
import com.wcc.platform.domain.template.TemplateType;
import com.wcc.platform.repository.MemberRepository;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.Month;
import java.time.ZoneId;
import java.time.format.TextStyle;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.mail.MailAuthenticationException;
import org.springframework.stereotype.Service;

/**
Expand All @@ -30,6 +39,8 @@
@Service
@RequiredArgsConstructor
public class MentorshipNotificationService {

public static final ZoneId ZONE_ID = ZoneId.of("Europe/London");
private final EmailTemplateService emailTemplateService;
private final EmailService emailService;
private final NotificationConfig notificationConfig;
Expand Down Expand Up @@ -99,6 +110,59 @@ public void sendApplicationUpdate(
TemplateType.MENTEE_APPLICATIONS, params, List.of(notificationConfig.getMentorshipEmail()));
}

/**
* Sends a CONFIRM_LONG_TERM_PAIRING notification email to both mentor and mentee when a match is
* confirmed.
*
* @param mentor the matched mentor
* @param mentee the matched mentee
* @param cycle the mentorship cycle
*/
public void sendPairingConfirmation(
final Mentor mentor, final Mentee mentee, final MentorshipCycleEntity cycle) {
final int year =
cycle.getCycleYear() != null
? cycle.getCycleYear().getValue()
: LocalDate.now(ZONE_ID).getYear();
final Month month =
cycle.getCycleMonth() != null ? cycle.getCycleMonth() : LocalDate.now(ZONE_ID).getMonth();
sendNotification(
cycle.getMentorshipType() == MentorshipType.AD_HOC
? TemplateType.CONFIRM_ADHOC_PAIRING
: TemplateType.CONFIRM_LONG_TERM_PAIRING,
Map.of(
"mentor_name", mentor.getFullName(),
"mentee_name", mentee.getFullName(),
"mentor_email", mentor.getEmail(),
"mentee_email", mentee.getEmail(),
"mentor_calendly_link", Optional.ofNullable(mentor.getCalendlyLink()).orElse(""),
"meeting_link", "",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This will be populated in next PR

"month", month.getDisplayName(TextStyle.FULL, Locale.ENGLISH),
"year", year),
List.of(mentor.getEmail(), mentee.getEmail(), notificationConfig.getMentorshipEmail()));
}

/**
* Sends a NEW_MENTEES_REVIEW notification email to a mentor when manually assigned a mentee.
*
* @param mentor the mentor to notify
* @param cycle the mentorship cycle
*/
public void sendNewMenteesNotification(final Mentor mentor, final MentorshipCycleEntity cycle) {
final int year =
cycle.getCycleYear() != null
? cycle.getCycleYear().getValue()
: LocalDate.now(ZONE_ID).getYear();
sendNotification(
TemplateType.NEW_MENTEES_REVIEW,
Map.of(
"mentorName", mentor.getFullName(),
"year", year,
"cycleType", cycle.getMentorshipType().getDescription(),
"mentorshipEmail", notificationConfig.getMentorshipEmail()),
List.of(mentor.getEmail(), notificationConfig.getMentorshipEmail()));
}

/**
* Renders an email template and sends a notification email to the specified recipient.
*
Expand All @@ -123,7 +187,7 @@ public void sendApplicationUpdate(

emailService.sendEmail(emailRequest);
log.info("{} notification successfully sent to {}", templateType, recipientEmails);
} catch (EmailSendException e) {
} catch (EmailSendException | MailAuthenticationException e) {
log.error("Failed to send {} notification to {}", templateType, recipientEmails, e);
}
}
Expand Down
15 changes: 14 additions & 1 deletion src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,20 @@ app:
cors:
allowed-origins: http://localhost:3000,https://localhost:3000
email:
team-signature: "WCC Mentorship Team"
team-signature: |
Best Regards<br>
<strong>Mentorship Programme Team</strong><br>
<a href="https://www.womencodingcommunity.com/">Women Coding Community</a><br>
<br>
<img src="https://www.womencodingcommunity.com/assets/images/logo.svg" alt="Women Coding Community Logo" width="50"><br>
<br>
Follow us:<br>
<a href="https://www.linkedin.com/company/womencodingcommunity/">LinkedIn</a> |
<a href="https://www.youtube.com/@womencodingcommunity">YouTube</a> |
<a href="https://www.meetup.com/women-coding-community">Meetup</a> |
<a href="https://github.com/Women-Coding-Community">GitHub</a> |
<a href="https://join.slack.com/t/womencodingcommunity/shared_invite/zt-2hpjwpx7l-rgceYBIWp6pCiwc0hVsX8A">Slack</a> |
<a href="mailto:mentorship@womencodingcommunity.com">email</a>
reset-password:
base-url: ${APP_RESET_PASSWORD_BASE_URL:http://localhost:3000}
ttl-minutes: 7200
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-- V38: Add meeting_link to mentor

ALTER TABLE mentors
ADD COLUMN meeting_link VARCHAR(500);

COMMENT ON COLUMN mentors.meeting_link IS
'Unique Meeting link for the mentor, set by admin for approved mentors.';
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
NEW_MENTEES_LONG:
subject: "[Action Required] Pending Mentee Application(s) for {{year}} WCC Mentorship Programme"
NEW_MENTEES_REVIEW:
subject: "[Action Required] Pending Mentee Application(s) for {{year}} {{cycleType}} WCC Mentorship Programme"
body: |
<p>Dear {{mentorName}},</p>
<p>
Thank you for being part of the <strong>Women Coding Community {{year}} Long-Term Mentorship Programme</strong>!
Thank you for being part of the <strong>Women Coding Community {{year}} {{cycleType}} Mentorship Programme</strong>!
</p>

<p>
Expand All @@ -16,8 +16,7 @@ NEW_MENTEES_LONG:
</p>

<p>
<strong>Please note:</strong> If you have not logged into the dashboard before, you should have received a password reset email from <strong>admin@womencodingcommunity.com</strong> prior to this email.
If you cannot find it, please check your spam/junk folder first. If you still haven’t received it, please reply to this email and we’ll resend it for you.
<strong>Please note:</strong> If you have not logged into the dashboard before, please reply to this email, so we can send a password reset email from <strong>{{mentorshipEmail}}</strong>.
</p>

<p>
Expand All @@ -31,3 +30,5 @@ NEW_MENTEES_LONG:
<p>
{{teamEmailSignature}}
</p>

<p>[Automated Message]</p>
59 changes: 59 additions & 0 deletions src/main/resources/email-templates/confirm_adhoc_pairing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
CONFIRM_ADHOC_PAIRING:
subject: "You are paired up in WCC Mentorship Adhoc session for {{month}}-{{year}}"
body: |
<p>Dear {{mentor_name}} and {{mentee_name}},</p>

<p>
Welcome to Women Coding Community <strong>Adhoc Mentorship programme for {{month}}, {{year}}</strong>.
</p>

<p>
Mentorship is a powerful tool for personal and professional growth, and we hope this relationship is a meaningful opportunity for you to learn, grow, and achieve your goals.
</p>

<p>
<strong>Your Pairing:</strong>
</p>

<ul>
<li>Mentor: {{mentor_name}} (<a href="mailto:{{mentor_email}}">{{mentor_email}}</a>)</li>
<li>Mentee: {{mentee_name}} (<a href="mailto:{{mentee_email}}">{{mentee_email}}</a>)</li>
<li>Mentor's Scheduling Link: (<a href="{{mentor_calendly_link}}">{{mentor_calendly_link}}</a>)</li>
<li>Google Meet Link: (<a href="{{meeting_link}}">{{meeting_link}}</a>)</li>
</ul>

<p>
We've provided the Google Meet link above for your sessions so you (please ignore the corresponding calendar invite). If you use the provided meeting link, you can choose whether to record, transcribe, or take notes during your call. Please be aware that if you choose to record or keep notes, WCC will automatically receive a copy. This information is fully confidential and will not be shared with anyone. <strong>Note:</strong> You're welcome to use a personal meeting link or alternative communication method instead if you prefer.
</p>

<p>
<strong>Getting Started</strong>
</p>

<ol>
<li>As a mentee, please take the lead on scheduling your one-time session with your mentor - {{mentor_name}} using the mentor’s scheduling link. These sessions are typically one hour long.</li>
<li>We recommend writing down what you hope to accomplish beforehand. This will help make the session more impactful.</li>
<li>Once you’ve booked your session, kindly reply to this email with the scheduled date.</li>
</ol>

<p>
<strong>Helpful Resources</strong>
</p>

<p>
To support you through this session:
</p>

<ul>
<li><a href="https://mentorship.womencodingcommunity.com/mentorship/resources">Mentorship Resources</a></li>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The website -frontend is not fixed yet, I think @Deejarh was the one to fix this changes.

<li><a href="https://mentorship.womencodingcommunity.com/mentorship/code-of-conduct">Code of Conduct for Mentors and Mentees</a></li>
</ul>

<p>We hope these resources help you make the most of this mentorship opportunity. If you have any questions or concerns, please don’t hesitate to reach out to us via email or the Slack channel #mentorship.</p>
<p>Wishing you a rewarding mentorship session!</p>

<p>
{{teamEmailSignature}}
</p>

<p>[Automated Message]</p>
Loading
Loading