Feat: Implement email notification about new disposition

This commit is contained in:
Björn Fromme
2023-11-08 11:08:27 +01:00
parent c4e0ef7d5a
commit 342119a456
14 changed files with 129 additions and 69 deletions
+2
View File
@@ -40,6 +40,8 @@ MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0
MAILER_DSN=null://null MAILER_DSN=null://null
###< symfony/mailer ### ###< symfony/mailer ###
APP_BASE_URI=https://myep-team.ddev.site
APP_BPN_USER= APP_BPN_USER=
APP_BPN_PASSWORD= APP_BPN_PASSWORD=
APP_BPN_ENDPOINT= APP_BPN_ENDPOINT=
+15 -10
View File
@@ -9,18 +9,20 @@
<mj-style> <mj-style>
h1 { h1 {
font-weight: bold; font-weight: bold;
font-size: 32px;
line-height: 36px;
margin-bottom: 0;
padding-bottom: 16px;
}
h2 {
font-weight: normal;
font-size: 24px; font-size: 24px;
line-height: 28px; line-height: 28px;
margin-bottom: 0; margin-bottom: 0;
padding-bottom: 8px;
}
h2 {
font-weight: normal;
font-size: 20px;
line-height: 24px;
margin-bottom: 0;
padding-bottom: 0;
} }
p { p {
font-size: 16px;
padding-bottom: 16px; padding-bottom: 16px;
} }
p.small { p.small {
@@ -43,11 +45,14 @@
padding: 8px 16px; padding: 8px 16px;
border-radius: 4px; border-radius: 4px;
} }
strong {
font-weight: bold;
}
</mj-style> </mj-style>
</mj-head> </mj-head>
<mj-body background-color="#666666"> <mj-body background-color="#666666">
<mj-hero mode="fluid-height" height="285px" background-color="#FFFFFF" padding-top="20px" padding-bottom="20px"> <mj-hero mode="fluid-height" height="285px" background-color="#FFFFFF" padding-top="10px" padding-bottom="10px">
<mj-image src="{{ email.image('@images/logo.png') }}" width="200px" align="left" alt="Logo E&P" /> <mj-image src="{{ email.image('@images/logo.png') }}" width="160px" align="left" alt="Logo E&amp;P" />
</mj-hero> </mj-hero>
<mj-section background-color="#FFFFFF" border-top="1px solid #3d8ccb"> <mj-section background-color="#FFFFFF" border-top="1px solid #3d8ccb">
<mj-column> <mj-column>
@@ -65,7 +70,7 @@
</h2> </h2>
<p> <p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Architecto culpa delectus dolores Lorem ipsum dolor sit amet, consectetur adipisicing elit. Architecto culpa delectus dolores
earum eius fugiat in nesciunt quas quidem vitae? <strong>earum eius fugiat in nesciunt quas quidem vitae</strong>?
</p> </p>
<p> <p>
<a href="#" class="button"> <a href="#" class="button">
+3
View File
@@ -18,6 +18,9 @@ framework:
php_errors: php_errors:
log: true log: true
router:
default_uri: '%env(APP_BASE_URI)%'
when@test: when@test:
framework: framework:
test: true test: true
+2 -2
View File
@@ -13,10 +13,10 @@ framework:
max_retries: 3 max_retries: 3
multiplier: 2 multiplier: 2
failed: 'doctrine://default?queue_name=failed' failed: 'doctrine://default?queue_name=failed'
# sync: 'sync://' sync: 'sync://'
routing: routing:
Symfony\Component\Mailer\Messenger\SendEmailMessage: async Symfony\Component\Mailer\Messenger\SendEmailMessage: sync
Symfony\Component\Notifier\Message\ChatMessage: async Symfony\Component\Notifier\Message\ChatMessage: async
Symfony\Component\Notifier\Message\SmsMessage: async Symfony\Component\Notifier\Message\SmsMessage: async
@@ -4,17 +4,20 @@ namespace App\Controller\Admin\Application;
use App\Entity\Application; use App\Entity\Application;
use App\Entity\Disposition; use App\Entity\Disposition;
use App\Event\DispositionCreatedEvent;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted; use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
class DisposeController extends AbstractController class DisposeController extends AbstractController
{ {
public function __construct( public function __construct(
private readonly EntityManagerInterface $entityManager, private readonly EntityManagerInterface $entityManager,
private readonly EventDispatcherInterface $eventDispatcher,
private readonly LoggerInterface $logger private readonly LoggerInterface $logger
) { ) {
} }
@@ -30,6 +33,8 @@ class DisposeController extends AbstractController
$this->entityManager->remove($application); $this->entityManager->remove($application);
$this->entityManager->flush(); $this->entityManager->flush();
$this->eventDispatcher->dispatch(new DispositionCreatedEvent($disposition), DispositionCreatedEvent::NAME);
$this->addFlash('success', 'Der Teamer wurde eingeteilt'); $this->addFlash('success', 'Der Teamer wurde eingeteilt');
$this->logger->info('Create disposition', [ $this->logger->info('Create disposition', [
'disposition_id' => $disposition->getId(), 'disposition_id' => $disposition->getId(),
-28
View File
@@ -1,28 +0,0 @@
<?php
namespace App\Email;
use App\Entity\User;
use Psr\Log\LoggerInterface;
class WelcomeEmailMailer
{
public function __construct(private readonly Mailer $mailer, private readonly LoggerInterface $logger)
{}
public function send(User $user): void
{
$this->mailer->createAndSendEmail([
'user' => $user,
], [
'to' => $user->getEmail(),
'subject' => 'Willkommen bei MyE&P Team',
'template' => 'email/welcome.html.twig',
]);
$this->logger->info('Send welcome email', [
'user_id' => $user->getId(),
'user_email' => $user->getEmail(),
]);
}
}
+2 -3
View File
@@ -51,11 +51,10 @@ class Destination implements TimestampableEntityInterface, SoftDeletableEntityIn
public function __toString(): string public function __toString(): string
{ {
return sprintf('%s - %s: %s, %s', return sprintf('%s - %s %s',
$this->getDateFrom()->format('d.m.y'), $this->getDateFrom()->format('d.m.y'),
$this->getDateTo()->format('d.m.y'), $this->getDateTo()->format('d.m.y'),
$this->getProduct(), $this->getProduct()
$this->getHotel()
); );
} }
+19
View File
@@ -0,0 +1,19 @@
<?php
namespace App\Event;
use App\Entity\Disposition;
use Symfony\Contracts\EventDispatcher\Event;
class DispositionCreatedEvent extends Event
{
public const NAME = 'disposition.created';
public function __construct(private readonly Disposition $disposition)
{}
public function getDisposition(): Disposition
{
return $this->disposition;
}
}
@@ -0,0 +1,35 @@
<?php
namespace App\EventListener;
use App\Email\Mailer;
use App\Event\DispositionCreatedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class EmailNotificationSubscriber implements EventSubscriberInterface
{
public function __construct(private readonly Mailer $mailer)
{
}
public static function getSubscribedEvents(): array
{
return [
DispositionCreatedEvent::NAME => 'onDispositionCreated',
];
}
public function onDispositionCreated(DispositionCreatedEvent $event): void
{
$disposition = $event->getDisposition();
$teamer = $disposition->getTeamer();
$this->mailer->createAndSendEmail([
'disposition' => $disposition,
], [
'to' => $teamer->getCommunication()->getEmail(),
'subject' => 'Dein Einsatz wurde angenommen',
'template' => 'email/application_accepted.html.twig',
]);
}
}
@@ -12,7 +12,7 @@
Destination Destination
</dt> </dt>
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0"> <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
{{ assignment.destination.product }}, {{ assignment.destination.hotel }} {{ assignment.destination.product }}
</dd> </dd>
</div> </div>
<div class="py-6 first:pt-0 last:pb-0 sm:grid sm:grid-cols-3 sm:gap-4"> <div class="py-6 first:pt-0 last:pb-0 sm:grid sm:grid-cols-3 sm:gap-4">
@@ -13,7 +13,7 @@
Destination Destination
</dt> </dt>
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0"> <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
{{ assignment.destination.product }}, {{ assignment.destination.hotel }} {{ assignment.destination.product }}
</dd> </dd>
</div> </div>
<div class="py-6 first:pt-0 last:pb-0 sm:grid sm:grid-cols-3 sm:gap-4"> <div class="py-6 first:pt-0 last:pb-0 sm:grid sm:grid-cols-3 sm:gap-4">
@@ -0,0 +1,29 @@
{% extends 'email/layout.html.twig' %}
{% block body %}
<h1>
Herzlichen Glückwunsch!
</h1>
<p>
<strong>
Dein Einsatz <a href="{{ url('app_teamer_disposition_detail', { 'uuid': disposition.uuid }) }}">{{ disposition.assignment.destination}}</a>
wurde angenommen!
</strong>
</p>
<p>
Schau einmal in das <a href="{{ url('app_teamer_disposition_detail', { 'uuid': disposition.uuid }) }}">My E&amp;P-Team Portal</a>,
um die Einsatzdetails einzusehen und deinen Honorarvertrag zu unterschreiben.
</p>
<p>
Mit dem Erhalt dieser E-Mail hast du 7 Tage Zeit deinen Einsatz zu bestätigen, indem du den unterschriebenen
Vertrag hochlädst. Ist diese Frist vergangen, wird der Einsatz für deine Teamkolleg:innen freigeschaltet.
</p>
<p>
Schön, dass du dabei bist und ganz viel Spaß in den Bergen! 😊
</p>
<p>
<a href="{{ url('app_teamer_index') }}" class="button">
Zum Portal
</a>
</p>
{% endblock %}
+15 -9
View File
@@ -105,20 +105,22 @@
<style type="text/css"> <style type="text/css">
h1 { h1 {
font-weight: bold; font-weight: bold;
font-size: 32px; font-size: 24px;
line-height: 36px; line-height: 28px;
margin-bottom: 0; margin-bottom: 0;
padding-bottom: 16px; padding-bottom: 8px;
} }
h2 { h2 {
font-weight: normal; font-weight: normal;
font-size: 24px; font-size: 20px;
line-height: 28px; line-height: 24px;
margin-bottom: 0; margin-bottom: 0;
padding-bottom: 0;
} }
p { p {
font-size: 16px;
padding-bottom: 16px; padding-bottom: 16px;
} }
@@ -146,6 +148,10 @@
border-radius: 4px; border-radius: 4px;
} }
strong {
font-weight: bold;
}
</style> </style>
</head> </head>
@@ -158,7 +164,7 @@
<tbody> <tbody>
<tr style="vertical-align:top;"> <tr style="vertical-align:top;">
<td style="width:0.01%;padding-bottom:NaN%;mso-padding-bottom-alt:0;" /> <td style="width:0.01%;padding-bottom:NaN%;mso-padding-bottom-alt:0;" />
<td style="background:#FFFFFF;background-position:center center;background-repeat:no-repeat;padding:0px;padding-top:20px;padding-bottom:20px;vertical-align:top;"> <td style="background:#FFFFFF;background-position:center center;background-repeat:no-repeat;padding:0px;padding-top:10px;padding-bottom:10px;vertical-align:top;">
<!--[if mso | IE]><table border="0" cellpadding="0" cellspacing="0" style="width:600px;" width="600" ><tr><td style=""><![endif]--> <!--[if mso | IE]><table border="0" cellpadding="0" cellspacing="0" style="width:600px;" width="600" ><tr><td style=""><![endif]-->
<div class="mj-hero-content" style="margin:0px auto;"> <div class="mj-hero-content" style="margin:0px auto;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;margin:0px;"> <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;margin:0px;">
@@ -172,8 +178,8 @@
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;"> <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
<tbody> <tbody>
<tr> <tr>
<td style="width:200px;"> <td style="width:160px;">
<img alt="Logo E&P" src="{{ email.image('@images/logo.png') }}" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;" width="200" height="auto" /> <img alt="Logo E&amp;P" src="{{ email.image('@images/logo.png') }}" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;" width="160" height="auto" />
</td> </td>
</tr> </tr>
</tbody> </tbody>
@@ -209,7 +215,7 @@
<div style="font-family:Lato, Verdana, Arial, 'Helvetica neue', sans-serif;font-size:16px;line-height:24px;text-align:left;color:#666666;">{% block body %} <h1> Lorem ipsum dolor sit amet </h1> <div style="font-family:Lato, Verdana, Arial, 'Helvetica neue', sans-serif;font-size:16px;line-height:24px;text-align:left;color:#666666;">{% block body %} <h1> Lorem ipsum dolor sit amet </h1>
<p> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Architecto culpa delectus dolores earum eius fugiat in nesciunt quas quidem vitae? </p> <p> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Architecto culpa delectus dolores earum eius fugiat in nesciunt quas quidem vitae? </p>
<h2> Lorem ipsum dolor </h2> <h2> Lorem ipsum dolor </h2>
<p> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Architecto culpa delectus dolores earum eius fugiat in nesciunt quas quidem vitae? </p> <p> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Architecto culpa delectus dolores <strong>earum eius fugiat in nesciunt quas quidem vitae</strong>? </p>
<p> <p>
<a href="#" class="button"> Button </a> <a href="#" class="button"> Button </a>
</p> </p>
-15
View File
@@ -1,15 +0,0 @@
{% extends 'email/layout.html.twig' %}
{% block body %}
<h1>
Willkommen bei MyE&P Team
</h1>
<p>
Hallo {{ user.firstName }},
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aliquid asperiores atque, commodi deleniti
dignissimos dolorum earum ex facere facilis necessitatibus non omnis pariatur possimus quas repudiandae sit
soluta tenetur, voluptatem.
</p>
{% endblock %}