web: Melden- und Verwaltungsblock auf der Meldungsseite hervorheben

Die beiden Hinweise unter der Mitteilung („Inhalt melden“, „mit der
hinterlegten E-Mail anmelden“) waren Textzeilen in 11,5 px und wurden
überlesen. Jetzt zwei klar getrennte Felder mit Buttons; der
Verwaltungsblock nennt die maskierte Empfängeradresse und führt auf die
Claim-Seite des Newsrooms. Derselbe Hinweis noch einmal in der
Seitenleiste unter „Weiterführend“, wie in der Veröffentlichungsmail.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-09-15 12:58:39 +00:00
parent 7f714fb664
commit a69b02193c
4 changed files with 89 additions and 24 deletions

View file

@ -355,6 +355,13 @@ return [
'report_content' => 'Inhalt melden',
'remove_data' => 'Eigene Daten entfernen',
'reports_free' => 'Meldungen sind kostenlos.',
'legal_eyebrow' => 'Rechtliches Anliegen',
'manage_eyebrow' => 'Diese Mitteilung verwalten',
'manage_recipient' => 'Der Anmeldelink geht an die hinterlegte Adresse:',
'manage_cta' => 'Anmeldelink anfordern',
'manage_other_address' => 'Andere Adresse? Anmeldung per E-Mail-Link.',
'manage_sidebar_text' => 'Sie sind als Firma oder Pressekontakt dieser Mitteilung hinterlegt? Dann können Sie sie ohne Passwort verwalten – der Anmeldelink geht an die hinterlegte E-Mail-Adresse.',
'manage_link_short' => 'Mit hinterlegter E-Mail anmelden',
'manage_question' => 'Sie sind als Firma oder Pressekontakt dieser Mitteilung hinterlegt?',
'manage_link' => 'Mit der hinterlegten E-Mail-Adresse bei pressekonto anmelden',
'manage_hint' => 'Sie erhalten einen Anmeldelink per E-Mail – ganz ohne Passwort.',

View file

@ -350,6 +350,13 @@ return [
'report_content' => 'Report content',
'remove_data' => 'Remove my data',
'reports_free' => 'Reports are free of charge.',
'legal_eyebrow' => 'Legal concern',
'manage_eyebrow' => 'Manage this release',
'manage_recipient' => 'The sign-in link goes to the listed address:',
'manage_cta' => 'Request sign-in link',
'manage_other_address' => 'Different address? Sign in via e-mail link.',
'manage_sidebar_text' => 'Listed as the company or press contact of this release? Then you can manage it without a password – the sign-in link goes to the listed e-mail address.',
'manage_link_short' => 'Sign in with listed e-mail',
'manage_question' => 'Are you listed as the company or press contact of this release?',
'manage_link' => 'Sign in to pressekonto with the listed email address',
'manage_hint' => 'You will receive a sign-in link by email – no password required.',

View file

@ -509,32 +509,59 @@
</p>
@endif
{{-- Inhalt melden (T13): sichtbar an jeder Mitteilung, nicht nur in der Sidebar --}}
<div class="mt-8 pt-4 border-t border-bg-rule flex flex-wrap items-center gap-x-4 gap-y-1.5 text-[11.5px] text-ink-3">
<span>{{ __('web.detail.legal_question') }}</span>
<a href="{{ route('legal-request.create', ['slug' => $release->slug, 'type' => 'report']) }}" class="inline-flex items-center gap-1.5 text-ink-2 underline underline-offset-2 hover:text-ink transition-colors">
<svg width="12" height="12" viewBox="0 0 14 14" class="flex-shrink-0" aria-hidden="true"><circle cx="7" cy="7" r="5.5" stroke="currentColor" stroke-width="1.3" fill="none" /><path d="M7 4v3.5M7 9.5v.5" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" /></svg>
{{ __('web.detail.report_content') }}
</a>
<a href="{{ route('legal-request.create', ['slug' => $release->slug, 'type' => 'dsgvo']) }}" class="inline-flex items-center gap-1.5 text-ink-2 underline underline-offset-2 hover:text-ink transition-colors">
<svg width="12" height="12" viewBox="0 0 14 14" class="flex-shrink-0" aria-hidden="true"><path d="M7 1l5 2v4c0 3-2.2 5.2-5 6-2.8-.8-5-3-5-6V3z" stroke="currentColor" stroke-width="1.3" fill="none" stroke-linejoin="round" /></svg>
{{ __('web.detail.remove_data') }}
</a>
<span class="text-ink-4">{{ __('web.detail.reports_free') }}</span>
</div>
{{-- Magic-Link-Hinweis: hinterlegte Firmen-/Pressekontakte verwalten
ihre Mitteilung über den passwortlosen Anmeldelink im pressekonto. --}}
{{-- Zwei Handlungsfelder, klar getrennt und mit Button-Charakter:
rechtliches Anliegen (T13) und Verwaltung über die hinterlegte
Adresse (Roadmap 1b.2/1b.4). Vorher eine Textzeile in 11,5 px,
die überlesen wurde. Die Empfängeradresse steht maskiert dabei,
damit klar ist, wohin der Link geht — ohne sie preiszugeben. --}}
@php
$portalUrl = rtrim(config('domains.domain_portal_url', config('app.url')), '/');
$claimRecipients = ($company && ! $company->isGone() && $company->slug)
? app(\App\Services\Company\NewsroomClaimService::class)->maskedRecipients($company)
: collect();
@endphp
<div class="mt-3 flex flex-wrap items-center gap-x-4 gap-y-1.5 text-[11.5px] text-ink-3">
<span>{{ __('web.detail.manage_question') }}</span>
<a href="{{ $portalUrl }}/anmeldelink" class="inline-flex items-center gap-1.5 text-ink-2 underline underline-offset-2 hover:text-ink transition-colors">
<svg width="12" height="12" viewBox="0 0 14 14" class="flex-shrink-0" aria-hidden="true"><path d="M1 3h12v8H1zM1 3l6 4 6-4" stroke="currentColor" stroke-width="1.3" fill="none" /></svg>
{{ __('web.detail.manage_link') }}
</a>
<span class="text-ink-4">{{ __('web.detail.manage_hint') }}</span>
<div class="mt-8 grid gap-4 sm:grid-cols-2">
<div class="border border-bg-rule-strong bg-bg-elev p-5">
<div class="eyebrow muted text-[10px] mb-2">{{ __('web.detail.legal_eyebrow') }}</div>
<p class="text-[13px] leading-[1.5] text-ink-2 m-0 mb-3.5">{{ __('web.detail.legal_question') }} {{ __('web.detail.reports_free') }}</p>
<div class="flex flex-wrap gap-2">
<a href="{{ route('legal-request.create', ['slug' => $release->slug, 'type' => 'report']) }}" class="inline-flex items-center gap-1.5 px-3.5 py-2 text-[12.5px] font-semibold bg-white border border-bg-rule-strong text-ink hover:border-brand hover:text-brand transition-colors">
<svg width="12" height="12" viewBox="0 0 14 14" class="flex-shrink-0" aria-hidden="true"><circle cx="7" cy="7" r="5.5" stroke="currentColor" stroke-width="1.3" fill="none" /><path d="M7 4v3.5M7 9.5v.5" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" /></svg>
{{ __('web.detail.report_content') }}
</a>
<a href="{{ route('legal-request.create', ['slug' => $release->slug, 'type' => 'dsgvo']) }}" class="inline-flex items-center gap-1.5 px-3.5 py-2 text-[12.5px] font-semibold bg-white border border-bg-rule-strong text-ink hover:border-brand hover:text-brand transition-colors">
<svg width="12" height="12" viewBox="0 0 14 14" class="flex-shrink-0" aria-hidden="true"><path d="M7 1l5 2v4c0 3-2.2 5.2-5 6-2.8-.8-5-3-5-6V3z" stroke="currentColor" stroke-width="1.3" fill="none" stroke-linejoin="round" /></svg>
{{ __('web.detail.remove_data') }}
</a>
</div>
</div>
<div class="border border-bg-rule-strong bg-bg-elev p-5">
<div class="eyebrow muted text-[10px] mb-2">{{ __('web.detail.manage_eyebrow') }}</div>
<p class="text-[13px] leading-[1.5] text-ink-2 m-0 mb-2">{{ __('web.detail.manage_question') }}</p>
@if ($claimRecipients->isNotEmpty())
<p class="text-[12px] text-ink-3 m-0 mb-1">{{ __('web.detail.manage_recipient') }}</p>
<ul class="m-0 mb-3.5 p-0 list-none font-mono text-[13px] text-ink">
@foreach ($claimRecipients as $recipient)
<li>{{ $recipient }}</li>
@endforeach
</ul>
<a href="{{ route('newsroom.claim', ['slug' => $company->slug]) }}" rel="nofollow" class="inline-flex items-center gap-1.5 px-3.5 py-2 text-[12.5px] font-semibold bg-brand text-white hover:opacity-90 transition-opacity">
<svg width="12" height="12" viewBox="0 0 14 14" class="flex-shrink-0" aria-hidden="true"><path d="M1 3h12v8H1zM1 3l6 4 6-4" stroke="currentColor" stroke-width="1.3" fill="none" /></svg>
{{ __('web.detail.manage_cta') }}
</a>
<p class="text-[11.5px] text-ink-3 m-0 mt-3">
{{ __('web.detail.manage_hint') }}
<a href="{{ $portalUrl }}/anmeldelink" class="underline underline-offset-2 hover:text-ink transition-colors">{{ __('web.detail.manage_other_address') }}</a>
</p>
@else
<a href="{{ $portalUrl }}/anmeldelink" class="inline-flex items-center gap-1.5 px-3.5 py-2 text-[12.5px] font-semibold bg-brand text-white hover:opacity-90 transition-opacity">
<svg width="12" height="12" viewBox="0 0 14 14" class="flex-shrink-0" aria-hidden="true"><path d="M1 3h12v8H1zM1 3l6 4 6-4" stroke="currentColor" stroke-width="1.3" fill="none" /></svg>
{{ __('web.detail.manage_link') }}
</a>
<p class="text-[11.5px] text-ink-3 m-0 mt-3">{{ __('web.detail.manage_hint') }}</p>
@endif
</div>
</div>
</div>
@ -646,6 +673,23 @@
</a>
</div>
</div>
{{-- Verwaltungszugang in der Seitenleiste (Roadmap 1b.4): derselbe
Hinweis wie in der Veröffentlichungsmail — wer nur die Leiste
liest, soll ihn nicht verpassen. --}}
<div class="border border-bg-rule-strong bg-bg-elev p-4">
<div class="eyebrow muted text-[9.5px] mb-2">{{ __('web.detail.manage_eyebrow') }}</div>
<p class="text-[12px] leading-[1.5] text-ink-2 m-0 mb-3">{{ __('web.detail.manage_sidebar_text') }}</p>
@if ($claimRecipients->isNotEmpty())
<a href="{{ route('newsroom.claim', ['slug' => $company->slug]) }}" rel="nofollow" class="w-full inline-flex items-center justify-center gap-2 px-3 py-2 text-[12px] font-semibold bg-white border border-bg-rule-strong text-ink hover:border-brand hover:text-brand transition-colors">
{{ __('web.detail.manage_cta') }} →
</a>
@else
<a href="{{ $portalUrl }}/anmeldelink" class="w-full inline-flex items-center justify-center gap-2 px-3 py-2 text-[12px] font-semibold bg-white border border-bg-rule-strong text-ink hover:border-brand hover:text-brand transition-colors">
{{ __('web.detail.manage_link_short') }} →
</a>
@endif
</div>
</aside>
</section>

View file

@ -11,6 +11,7 @@ use App\Models\Company;
use App\Models\Contact;
use App\Models\PressRelease;
use App\Models\User;
use App\Services\Company\NewsroomClaimService;
use Tests\TestCase;
/**
@ -77,7 +78,13 @@ test('businessportal24 release detail renders the editorial shell with real data
->assertSeeText('Wasserstoff')
// Magic-Link-Hinweis: hinterlegte Kontakte verwalten die PM im pressekonto.
->assertSeeText('Sie sind als Firma oder Pressekontakt dieser Mitteilung hinterlegt?')
->assertSee(rtrim(config('domains.domain_portal_url'), '/').'/anmeldelink', false);
->assertSee(rtrim(config('domains.domain_portal_url'), '/').'/anmeldelink', false)
// Verwaltungsblock (1b.4): maskierte Empfängeradresse, Button zur
// Claim-Seite — im Text UND in der Seitenleiste, nie die volle Adresse.
->assertSeeText('Anmeldelink anfordern')
->assertSee('/de/newsroom/'.$company->slug.'/verwalten', false)
->assertSeeText(NewsroomClaimService::mask((string) $company->email))
->assertDontSeeText((string) $company->email);
});
test('release detail falls back to the company contact when the release has none', function () {