Feat: Implement email notification about new disposition
This commit is contained in:
@@ -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
@@ -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&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">
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
@@ -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(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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&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 %}
|
||||||
@@ -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&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>
|
||||||
|
|||||||
@@ -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 %}
|
|
||||||
Reference in New Issue
Block a user