diff --git a/config/services.yaml b/config/services.yaml
index 590cccba..83ea8197 100644
--- a/config/services.yaml
+++ b/config/services.yaml
@@ -435,6 +435,9 @@ services:
App\BpnConnect\ContingentsClient:
parent: App\BpnConnect\AbstractApiClient
+ App\BpnConnect\TravelsClient:
+ parent: App\BpnConnect\AbstractApiClient
+
App\ClickUp\ApiClient:
arguments:
$apiToken: '%env(default::CLICKUP_API_TOKEN)%'
diff --git a/src/BpnConnect/Model/TravelMailingRecipient.php b/src/BpnConnect/Model/TravelMailingRecipient.php
new file mode 100644
index 00000000..3e3fd4f0
--- /dev/null
+++ b/src/BpnConnect/Model/TravelMailingRecipient.php
@@ -0,0 +1,15 @@
+request(self::MAILING_PATH, [
+ 'travelCode' => $travelCode,
+ ]);
+
+ $recipients = array_map(
+ static fn (array $item) => new TravelMailingRecipient(
+ email: $item['email'],
+ firstName: $item['first_name'] ?? '',
+ lastName: $item['last_name'] ?? '',
+ ),
+ $payload['data'] ?? [],
+ );
+
+ return new TravelMailingResponse($payload['meta']['travel_code'] ?? $travelCode, $recipients);
+ }
+}
diff --git a/src/Controller/Admin/TravelMailing/IndexController.php b/src/Controller/Admin/TravelMailing/IndexController.php
new file mode 100644
index 00000000..bbc67a2e
--- /dev/null
+++ b/src/Controller/Admin/TravelMailing/IndexController.php
@@ -0,0 +1,51 @@
+createForm(TravelMailingLookupType::class, $lookup, [
+ 'action' => $this->generateUrl('app_admin_travelmailing'),
+ ]);
+ $form->handleRequest($request);
+
+ $result = null;
+ $failed = false;
+ if ($form->isSubmitted() && $form->isValid()) {
+ try {
+ $result = $this->travelsClient->getMailingRecipients((string) $lookup->travelCode);
+ } catch (BpnConnectException) {
+ // The client has already logged the cause; the page only needs to say it failed.
+ $failed = true;
+ }
+ }
+
+ return $this->render('admin/travel_mailing/index.html.twig', [
+ 'form' => $form,
+ 'result' => $result,
+ 'failed' => $failed,
+ ]);
+ }
+}
diff --git a/src/Form/Admin/TravelMailingLookupType.php b/src/Form/Admin/TravelMailingLookupType.php
new file mode 100644
index 00000000..d2dd243e
--- /dev/null
+++ b/src/Form/Admin/TravelMailingLookupType.php
@@ -0,0 +1,44 @@
+
+ */
+class TravelMailingLookupType extends AbstractType
+{
+ public function buildForm(FormBuilderInterface $builder, array $options): void
+ {
+ $builder->add('travelCode', TextType::class, [
+ 'label' => 'Reisecode',
+ 'attr' => [
+ 'placeholder' => 'z. B. LPJMP281126',
+ ],
+ ]);
+ }
+
+ public function configureOptions(OptionsResolver $resolver): void
+ {
+ $resolver->setDefaults([
+ 'data_class' => TravelMailingLookupDto::class,
+ 'method' => 'GET',
+ 'csrf_protection' => false,
+ ]);
+ }
+
+ public function getBlockPrefix(): string
+ {
+ return '';
+ }
+}
diff --git a/src/Form/Model/TravelMailingLookupDto.php b/src/Form/Model/TravelMailingLookupDto.php
new file mode 100644
index 00000000..324d4fff
--- /dev/null
+++ b/src/Form/Model/TravelMailingLookupDto.php
@@ -0,0 +1,13 @@
+ [['pattern' => '/^app_admin_log/']],
],
]);
+ $menu->addChild('Reise-Mailingliste', [
+ 'route' => 'app_admin_travelmailing',
+ 'linkAttributes' => [
+ 'title' => 'Reise-Mailingliste',
+ ],
+ 'extras' => [
+ 'icon' => 'mail',
+ ],
+ ]);
}
$this->addLogoutItem($menu);
diff --git a/templates/admin/travel_mailing/index.html.twig b/templates/admin/travel_mailing/index.html.twig
new file mode 100644
index 00000000..702d8814
--- /dev/null
+++ b/templates/admin/travel_mailing/index.html.twig
@@ -0,0 +1,71 @@
+{% extends 'layout_admin.html.twig' %}
+
+{% form_theme form 'forms_admin.html.twig' %}
+
+{% block content %}
+
%s gefunden: Reisecode unbekannt oder keine bestätigten Teilnehmer.'|format(result.travelCode|e)],
+ } %}
+ {% else %}
+
+ {{ result.data|length }} Empfänger für {{ result.travelCode }}
+
| + Vorname + | ++ Nachname + | ++ E-Mail + | +
|---|---|---|
| + {{ recipient.firstName }} + | ++ {{ recipient.lastName }} + | ++ {{ recipient.email }} + | +