diff --git a/notification/pom.xml b/notification/pom.xml index 8ac8840fa4e8472d504fad0f4e20a5147daa435d..4d4ad7051513585c4a1a475fa39e1aa0f5976d64 100644 --- a/notification/pom.xml +++ b/notification/pom.xml @@ -116,6 +116,10 @@ <artifactId>micrometer-registry-prometheus</artifactId> </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-thymeleaf</artifactId> + </dependency> </dependencies> </project> diff --git a/notification/src/main/java/cz/muni/pa165/config/MailConfig.java b/notification/src/main/java/cz/muni/pa165/config/MailConfig.java index 46df3353e6a02993e8e78a4bc6bb941c9551c2bc..7c0086038db3c19d54be5fc61a483d0bb3c35ec8 100644 --- a/notification/src/main/java/cz/muni/pa165/config/MailConfig.java +++ b/notification/src/main/java/cz/muni/pa165/config/MailConfig.java @@ -2,9 +2,16 @@ package cz.muni.pa165.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.JavaMailSenderImpl; +import org.thymeleaf.TemplateEngine; +import org.thymeleaf.spring6.SpringTemplateEngine; +import org.thymeleaf.templatemode.TemplateMode; +import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver; +import org.thymeleaf.templateresolver.ITemplateResolver; +import java.util.Collections; import java.util.Properties; @Configuration @@ -26,4 +33,20 @@ public class MailConfig { return mailSender; } + + @Bean + public TemplateEngine emailTemplateEngine() { + final SpringTemplateEngine templateEngine = new SpringTemplateEngine(); + templateEngine.addTemplateResolver(htmlTemplateResolver()); + return templateEngine; + } + + private ITemplateResolver htmlTemplateResolver() { + final ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver(); + templateResolver.setPrefix("templates/mails/"); + templateResolver.setSuffix(".html"); + templateResolver.setTemplateMode(TemplateMode.HTML); + templateResolver.setCacheable(false); + return templateResolver; + } } \ No newline at end of file diff --git a/notification/src/main/java/cz/muni/pa165/service/NotificationService.java b/notification/src/main/java/cz/muni/pa165/service/NotificationService.java index 444378a4e7bdba907aa31b0becb481b9395d3279..c96fac552777a2ffa0034aec46fdccb3232e3234 100644 --- a/notification/src/main/java/cz/muni/pa165/service/NotificationService.java +++ b/notification/src/main/java/cz/muni/pa165/service/NotificationService.java @@ -1,25 +1,38 @@ package cz.muni.pa165.service; import cz.muni.pa165.generated.model.*; +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeMessage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Service; +import org.thymeleaf.TemplateEngine; +import org.thymeleaf.context.Context; +import java.text.SimpleDateFormat; +import java.time.LocalDate; import java.util.List; @Service public class NotificationService { private static final String EMAIL = "formula.team.management@gmail.com"; private final JavaMailSender emailSender; + private final TemplateEngine templateEngine; + + private static final Logger log = LoggerFactory.getLogger(NotificationService.class); @Autowired - public NotificationService(JavaMailSender emailSender) { + public NotificationService(JavaMailSender emailSender, TemplateEngine templateEngine) { this.emailSender = emailSender; + this.templateEngine = templateEngine; } public ConfirmationDto notify(NotificationDto notificationDto) { - return sendEmail(notificationDto.getSubject(), notificationDto.getMessage(), notificationDto.getReceivers()); + return sendEmail(notificationDto.getSubject(), notificationDto.getMessage(), notificationDto.getMessage(), notificationDto.getReceivers()); } public ConfirmationDto notifyNewComponent(CarComponentNotificationDto carComponentNotificationDto) { @@ -30,7 +43,7 @@ public class NotificationService { + "\nInformation: " + c.getInformation() + "\nWeight: " + c.getWeight(); - return sendEmail(subject, text, carComponentNotificationDto.getReceivers()); + return sendEmail(subject, text, text, carComponentNotificationDto.getReceivers()); } public ConfirmationDto notifyNewApplication(ApplicationNotificationDto applicationNotificationDto) { @@ -45,29 +58,24 @@ public class NotificationService { + "\nFilling out date: " + a.getFillingOutDate(); - return sendEmail(subject, text, applicationNotificationDto.getReceivers()); + return sendEmail(subject, text, text, applicationNotificationDto.getReceivers()); } public ConfirmationDto notifyApplicationStatusChange(ApplicationNotificationDto applicationNotificationDto) { var a = applicationNotificationDto.getApplication(); var subject = "Application for Formula Driver Position"; - var text = ""; + var dtoText = ""; + var htmlContent = ""; if (a.getStatus() == ApplicationStatus.ACCEPTED) { - text = "Dear " + getFullName(a) + ",\n" + - "\n" + - "I am pleased to inform you that your application for the position of Formula Driver has been accepted. We are excited to have you join our team and look forward to working with you.\n" + - "\n" + - "Your experience, skills, and passion for racing make you an excellent fit for our team. We believe that you will be a valuable asset to our organization and we are eager to see what you can accomplish on the track.\n" + - "\n" + - "As a next step, we will contact you shortly to discuss the details of your employment contract and to schedule your training and orientation. In the meantime, please do not hesitate to contact us if you have any questions or concerns.\n" + - "\n" + - "Thank you for your interest in our team and for taking the time to apply. We look forward to a long and successful partnership with you.\n" + - "\n" + - "Best regards,\n" + - "\n" + - "Formula Team Management"; + + Context ctx = new Context(); + ctx.setVariable("fullName", getFullName(a)); + ctx.setVariable("today", new SimpleDateFormat("dd/MM/yyyy").format(LocalDate.now())); + + htmlContent = templateEngine.process("accepted-email.html", ctx); + dtoText = "Application has been accepted"; } else { - text = "Dear " + getFullName(a) + ",\n" + + dtoText = "Dear " + getFullName(a) + ",\n" + "\n" + "I regret to inform you that your application for the Formula Driver position has been declined. While we appreciate your interest in the role, we have decided to pursue other candidates whose skills and experience more closely match our requirements.\n" + "\n" + @@ -82,23 +90,29 @@ public class NotificationService { "Formula Team Management"; } - return sendEmail(subject, text, applicationNotificationDto.getReceivers()); + return sendEmail(subject, htmlContent, dtoText, applicationNotificationDto.getReceivers()); } - private ConfirmationDto sendEmail(String subject, String content, List<String> receivers) { - var message = new SimpleMailMessage(); - - message.setFrom(EMAIL); - message.setTo(receivers.toArray(String[]::new)); - message.setSubject(subject); - message.setText(content); - emailSender.send(message); - - return new ConfirmationDto() - .sender(EMAIL) - .receivers(receivers) - .subject(subject) - .message(content); + private ConfirmationDto sendEmail(String subject, String htmlContent, String dtoContent, List<String> receivers) { + MimeMessage message = emailSender.createMimeMessage(); + + try { + MimeMessageHelper helper = new MimeMessageHelper(message, true); + helper.setFrom(EMAIL); + helper.setTo(receivers.toArray(String[]::new)); + helper.setSubject(subject); + helper.setText(htmlContent, true); + + emailSender.send(message); + + return new ConfirmationDto() + .sender(EMAIL) + .receivers(receivers) + .subject(subject) + .message(dtoContent); + } catch (MessagingException e) { + throw new RuntimeException("Failed to send email", e); + } } private String getFullName(ApplicationDto applicationDto) { diff --git a/notification/src/main/resources/templates/mails/accepted-email.html b/notification/src/main/resources/templates/mails/accepted-email.html new file mode 100644 index 0000000000000000000000000000000000000000..b22344880eab5473d89061782bfde7ef340e3f30 --- /dev/null +++ b/notification/src/main/resources/templates/mails/accepted-email.html @@ -0,0 +1,78 @@ +<!DOCTYPE html> +<html xmlns:th="http://www.thymeleaf.org"> + <head> + <meta charset="utf-8"> + <title>Application for Formula Driver Position</title> + <style> + body { + font-family: Arial, sans-serif; + margin: 0; + padding: 0; + background-color: #f6f6f6; + } + + .container { + max-width: 600px; + margin: 0 auto; + background-color: #fff; + padding: 20px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + position: relative; + } + + h1 { + font-size: 24px; + font-weight: bold; + margin: 0; + text-align: left; + } + + .date { + font-size: 14px; + font-weight: normal; + margin: 0; + position: absolute; + top: 10px; + right: 20px; + } + + .location { + font-size: 14px; + font-weight: normal; + margin: 0; + position: absolute; + top: 30px; + right: 20px; + } + + p { + font-size: 16px; + line-height: 1.5; + margin: 20px 0; + text-align: justify; + } + + .signature { + font-size: 14px; + font-weight: normal; + margin: 50px 0 0 0; + text-align: right; + } + </style> + </head> + <body> + <div class="container"> + <h1>Application for Formula Driver Position</h1> + <p><b>Dear <span th:text="${fullName}"></span>,</b></p> + <p>I am pleased to inform you that your application for the position of Formula Driver has been <b>Accepted</b>. We are excited to have you join our team and look forward to working with you.</p> + <p>Your experience, skills, and passion for racing make you an excellent fit for our team. We believe that you will be a valuable asset to our organization and we are eager to see what you can accomplish on the track.</p> + <p>As a next step, we will contact you shortly to discuss the details of your employment contract and to schedule your training and orientation. In the meantime, please do not hesitate to contact us if you have any questions or concerns.</p> + <p>Thank you for your interest in our team and for taking the time to apply. We look forward to a long and successful partnership with you.</p> + <p>Best regards,</p> + <p>Formula Team Management</p> + <div class="date" th:text="${today}"></div> + <div class="location">Brno, Czech Republic</div> + <div class="signature">-- Formula Team Management</div> + </div> + </body> +</html> \ No newline at end of file