Backend-WIP: Kontakt-Queue, Page-Cache, Validierungs-Feedback, i18n

Gebuendelt, da ueber gemeinsame lang-/Service-Dateien verschraenkt:
- Kontakt-Nachrichten: contact_messages-Tabelle + Model/Enums/Factory,
  Admin-Queue (livewire/admin/contact-messages), ContactController
  persistiert das Anliegen zusaetzlich zur Brand-Mail
- Page-Data-Cache (Services/Web/*PageData) + web-archive-Indizes auf
  press_releases
- Validierungs-Feedback + Company-Portal-Restriction (Feature-Tests)
- i18n-Auslagerung lang/de|en (auth, passwords, validation, static)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-07-09 15:38:11 +00:00
parent c6b5ec8479
commit fd8e050cb7
60 changed files with 3963 additions and 327 deletions

View file

@ -1,13 +1,15 @@
<?php
use App\Enums\ContactMessageTopic;
use App\Mail\ContactRequestSubmitted;
use App\Models\ContactMessage;
use Illuminate\Support\Facades\Mail;
use Tests\TestCase;
test('the hub landing renders the contact modal form', function () {
/** @var TestCase $this */
// Die frühere Kontakt-Seite wurde zurückgebaut; das Formular lebt im
// Kontakt-Modal des Hubs und postet weiter auf kontakt.store.
// schwebenden Kontakt-Widget und postet weiter auf kontakt.store.
$this->get('https://pressekonto.test/')
->assertSuccessful()
->assertSee('name="name"', false)
@ -16,6 +18,45 @@ test('the hub landing renders the contact modal form', function () {
->assertSee('Nachricht senden');
});
test('the spoke pages render the floating contact launcher', function () {
/** @var TestCase $this */
$this->get('https://businessportal24.test/de/faq')
->assertSuccessful()
->assertSee('open-contact-modal', false)
->assertSee(route('kontakt.store', ['edition' => 'de']), false)
->assertSee('Fehler melden')
->assertSee('Pressemitteilung melden');
});
test('the english edition renders the launcher with english strings', function () {
/** @var TestCase $this */
$this->get('https://businessportal24.test/en/suche')
->assertSuccessful()
->assertSee('Report a bug')
->assertSee('Report a press release')
->assertSee('Send message')
->assertDontSee('Fehler melden');
});
test('a submission on the english edition flashes an english confirmation', function () {
/** @var TestCase $this */
Mail::fake();
$this->post('https://businessportal24.test/en/kontakt', [
'name' => 'Paula Press',
'email' => 'paula@example.test',
'message' => 'I have a question about your platform, please get back to me.',
])->assertSessionHas('contact-status', __('web.launcher.success', [], 'en'));
});
test('the login page renders the floating contact launcher', function () {
/** @var TestCase $this */
$this->get('https://pressekonto.test/login')
->assertSuccessful()
->assertSee('open-contact-modal', false)
->assertSee(route('kontakt.store', ['edition' => 'de']), false);
});
test('a valid contact request sends a mail to the configured recipient', function () {
/** @var TestCase $this */
Mail::fake();
@ -66,6 +107,64 @@ test('a contact request from the hub domain goes to the pressekonto brand addres
Mail::assertSent(ContactRequestSubmitted::class, fn (ContactRequestSubmitted $mail) => $mail->hasTo('kontakt@pressekonto.de'));
});
test('a contact request is stored in the contact queue', function () {
/** @var TestCase $this */
Mail::fake();
$this->post('https://businessportal24.test/de/kontakt', [
'name' => 'Paula Presse',
'email' => 'paula@example.test',
'message' => 'Ich habe eine Frage zu Ihrer Plattform und bitte um Rückruf.',
])->assertSessionHas('contact-status');
$message = ContactMessage::query()->sole();
expect($message->topic)->toBe(ContactMessageTopic::Contact)
->and($message->portal)->toBe('businessportal24')
->and($message->name)->toBe('Paula Presse')
->and($message->email)->toBe('paula@example.test')
->and($message->status->isOpen())->toBeTrue();
});
test('a widget submission stores topic and page url', function () {
/** @var TestCase $this */
Mail::fake();
$this->post('https://presseecho.test/de/kontakt', [
'name' => 'Bernd Bug',
'email' => 'bernd@example.test',
'message' => 'Die Suche liefert auf der Startseite einen Fehler 500.',
'topic' => 'bug_report',
'page_url' => 'https://presseecho.test/de/suche',
])->assertSessionHas('contact-status');
$message = ContactMessage::query()->sole();
expect($message->topic)->toBe(ContactMessageTopic::BugReport)
->and($message->portal)->toBe('presseecho')
->and($message->page_url)->toBe('https://presseecho.test/de/suche');
Mail::assertSent(ContactRequestSubmitted::class, function (ContactRequestSubmitted $mail) {
return $mail->topicLabel === 'Fehlermeldung'
&& $mail->pageUrl === 'https://presseecho.test/de/suche';
});
});
test('an unknown topic is rejected', function () {
/** @var TestCase $this */
Mail::fake();
$this->post('https://businessportal24.test/de/kontakt', [
'name' => 'Paula Presse',
'email' => 'paula@example.test',
'message' => 'Ich habe eine Frage zu Ihrer Plattform und bitte um Rückruf.',
'topic' => 'not-a-topic',
])->assertSessionHasErrors(['topic']);
expect(ContactMessage::query()->count())->toBe(0);
Mail::assertNothingSent();
});
test('honeypot submissions are silently accepted without sending mail', function () {
/** @var TestCase $this */
Mail::fake();
@ -78,6 +177,7 @@ test('honeypot submissions are silently accepted without sending mail', function
])->assertSessionHas('contact-status');
Mail::assertNothingSent();
expect(ContactMessage::query()->count())->toBe(0);
});
test('the contact form validates its inputs', function () {