feat: instant preview of edited email template

This commit is contained in:
Björn Fromme
2026-08-17 12:17:05 +02:00
parent 489aafed3e
commit 0e1df6da7c
12 changed files with 250 additions and 58 deletions
@@ -4,9 +4,9 @@ namespace App\Controller\Admin\System\EmailText;
use App\Config\EmailTextCatalog;
use App\Config\EmailTextKey;
use App\Email\EmailTextRenderer;
use App\Entity\EmailText;
use App\Form\EmailTextType;
use App\Htmx\HxRedirectResponse;
use App\Repository\EmailTextRepository;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
@@ -21,6 +21,7 @@ class EditController extends AbstractController
public function __construct(
private readonly EmailTextCatalog $catalog,
private readonly EmailTextRepository $emailTextRepository,
private readonly EmailTextRenderer $emailTextRenderer,
private readonly EntityManagerInterface $entityManager,
private readonly LoggerInterface $logger,
) {
@@ -46,7 +47,6 @@ class EditController extends AbstractController
$form = $this->createForm(EmailTextType::class, $emailText, [
'definition' => $definition,
'hx_post' => $request->getUri(),
]);
$form->handleRequest($request);
@@ -63,12 +63,22 @@ class EditController extends AbstractController
'email_text_key' => $key->value,
]);
return new HxRedirectResponse($this->generateUrl('app_admin_system_email_text_index'));
return $this->redirectToRoute('app_admin_system_email_text_index');
}
return $this->render('admin/system/email_text/modal_edit.html.twig', [
return $this->render('admin/system/email_text/edit.html.twig', [
'form' => $form->createView(),
'definition' => $definition,
'emailText' => $emailText,
// Filling the preview pane here saves the page a round trip on first paint;
// from then on the pane refreshes itself while the admin types.
'text' => $this->emailTextRenderer->renderValues(
$key,
$emailText->getSubject(),
$emailText->getHeadline(),
$emailText->getBody(),
$definition->getSamplePlaceholders()
),
]);
}
}
@@ -30,18 +30,9 @@ class PreviewController extends AbstractController
{
$definition = $this->catalog->get($key);
// The placeholders stand in for themselves: no disposition is at hand here, and a
// visible [destination] tells an admin where a real value lands better than an
// invented one would.
$placeholders = [];
foreach ($definition->getPlaceholderNames() as $name) {
$placeholders[$name] = '['.$name.']';
}
return $this->render('admin/system/email_text/modal_preview.html.twig', [
'definition' => $definition,
'text' => $this->emailTextRenderer->render($key, $placeholders),
'text' => $this->emailTextRenderer->render($key, $definition->getSamplePlaceholders()),
]);
}
}
@@ -0,0 +1,57 @@
<?php
namespace App\Controller\Admin\System\EmailText;
use App\Config\EmailTextCatalog;
use App\Config\EmailTextKey;
use App\Email\EmailTextRenderer;
use App\Entity\EmailText;
use App\Form\EmailTextType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
class PreviewDraftController extends AbstractController
{
public function __construct(
private readonly EmailTextCatalog $catalog,
private readonly EmailTextRenderer $emailTextRenderer,
) {
}
/**
* Renders the fragment the edit page swaps in while an admin types, so the wording is
* checked before it is saved rather than after. Unlike PreviewController this reads
* the submitted values instead of the stored ones.
*/
#[Route('/admin/system/email-text/preview-draft/{key}', name: 'app_admin_system_email_text_preview_draft', methods: ['POST'])]
#[IsGranted('ROLE_ADMIN')]
public function index(EmailTextKey $key, Request $request): Response
{
$definition = $this->catalog->get($key);
$form = $this->createForm(EmailTextType::class, new EmailText($key), [
'definition' => $definition,
]);
$form->handleRequest($request);
/** @var EmailText $draft */
$draft = $form->getData();
// Deliberately rendered whether or not the form is valid: a text carrying a typo
// in a placeholder is exactly what an admin needs to see in the preview, and the
// field error is shown by the form itself when the text is saved.
return $this->render('admin/system/email_text/_preview.html.twig', [
'definition' => $definition,
'text' => $this->emailTextRenderer->renderValues(
$key,
$draft->getSubject(),
$draft->getHeadline(),
$draft->getBody(),
$definition->getSamplePlaceholders()
),
]);
}
}