Recht (Web): Vertrag-widerrufen-Button + zweistufiges Widerrufs-Modal im Hub
Gut sichtbare Schaltflaeche "Vertrag widerrufen" im Footer; das Modal fuehrt zweistufig durch den Widerruf: Schritt 1 nur Zuordnungsdaten, Schritt 2 Kontrollansicht mit Bestaetigungsbutton "Jetzt widerrufen". Erfolgs-/Fehlerzustand oeffnet automatisch (getrennter Error-Bag, kollidiert nicht mit dem Kontakt-Modal). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
8c0bf00604
commit
515e249132
3 changed files with 160 additions and 0 deletions
|
|
@ -66,6 +66,11 @@
|
|||
<li><a href="{{ route('datenschutz', ['edition' => 'de']) }}" class="hover:text-white transition-colors">Datenschutz</a></li>
|
||||
<li><a href="{{ route('cookies', ['edition' => 'de']) }}" class="hover:text-white transition-colors">Cookies</a></li>
|
||||
</ul>
|
||||
{{-- Gesetzlicher Widerrufsbutton: gut sichtbare Schaltfläche mit klarer Aufschrift. --}}
|
||||
<button type="button" x-data @click="$dispatch('open-withdrawal-modal')"
|
||||
class="mt-4 inline-flex items-center justify-center px-4 py-2 text-[12.5px] font-semibold text-white border border-white/40 hover:border-white hover:bg-white/10 rounded-[4px] transition-colors cursor-pointer">
|
||||
Vertrag widerrufen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
154
resources/views/components/web/hub/withdrawal-modal.blade.php
Normal file
154
resources/views/components/web/hub/withdrawal-modal.blade.php
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
{{--
|
||||
Gesetzlicher Widerrufsbutton (Online-Widerruf ohne Login), zweistufig:
|
||||
Schritt 1 erfasst nur die Zuordnungsdaten (Name, E-Mail, optional
|
||||
Bestellnummer — KEIN Widerrufsgrund, darf nicht verlangt werden),
|
||||
Schritt 2 zeigt die Angaben zur Kontrolle und schließt mit dem
|
||||
Bestätigungsbutton „Jetzt widerrufen" ab. Der Eingang wird sofort
|
||||
automatisch per E-Mail bestätigt (WithdrawalController).
|
||||
Öffnet auf das Window-Event `open-withdrawal-modal`.
|
||||
--}}
|
||||
@php($withdrawalErrors = $errors->getBag('withdrawal'))
|
||||
|
||||
<div
|
||||
x-data="{
|
||||
open: @js(session()->has('withdrawal-status') || $withdrawalErrors->any()),
|
||||
step: @js($withdrawalErrors->any() ? 1 : 1),
|
||||
name: @js(old('name', '')),
|
||||
email: @js(old('email', '')),
|
||||
reference: @js(old('order_reference', '')),
|
||||
}"
|
||||
@open-withdrawal-modal.window="open = true"
|
||||
@keydown.escape.window="open = false"
|
||||
>
|
||||
<div x-show="open" x-cloak class="fixed inset-0 z-50 flex items-center justify-center p-4">
|
||||
<div class="absolute inset-0 bg-black/50" @click="open = false" aria-hidden="true"></div>
|
||||
|
||||
<div class="relative bg-white border border-bg-rule rounded-[4px] w-full max-w-[520px] max-h-[90vh] overflow-y-auto p-7"
|
||||
role="dialog" aria-modal="true" aria-label="Vertrag widerrufen">
|
||||
|
||||
<button type="button" @click="open = false" aria-label="Schließen"
|
||||
class="absolute top-4 right-4 w-8 h-8 flex items-center justify-center text-ink-3 hover:text-ink transition-colors cursor-pointer">
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M3 3l10 10M13 3L3 13" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
@if (session('withdrawal-status'))
|
||||
<div class="section-eyebrow mb-4">Widerruf</div>
|
||||
<h3 class="text-[20px] font-bold text-ink tracking-[-0.4px] m-0 mb-3">Widerruf eingegangen</h3>
|
||||
<p class="text-[13.5px] text-ink-2 leading-[1.6] m-0" role="status">
|
||||
{{ session('withdrawal-status') }}
|
||||
</p>
|
||||
<button type="button" @click="open = false"
|
||||
class="mt-6 inline-flex items-center justify-center px-5 py-2.5 text-[13px] font-semibold text-white bg-hub hover:bg-hub-2 rounded-[4px] transition-colors cursor-pointer">
|
||||
Schließen
|
||||
</button>
|
||||
@else
|
||||
<div class="section-eyebrow mb-4">Widerruf</div>
|
||||
<h3 class="text-[20px] font-bold text-ink tracking-[-0.4px] m-0 mb-2">Vertrag widerrufen</h3>
|
||||
|
||||
<form method="POST" action="{{ route('widerruf.store') }}" novalidate
|
||||
@submit="if (step !== 2) { $event.preventDefault(); }">
|
||||
@csrf
|
||||
|
||||
{{-- Schritt 1: Zuordnungsdaten --}}
|
||||
<div x-show="step === 1">
|
||||
<p class="text-[12.5px] text-ink-3 leading-[1.55] m-0 mb-6">
|
||||
Hier können Sie Ihren Vertrag ohne Anmeldung widerrufen. Wir benötigen nur
|
||||
die Daten zur Zuordnung — eine Begründung ist nicht erforderlich.
|
||||
</p>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label for="withdrawal-name" class="block text-[12px] font-semibold text-ink mb-1.5">Ihr Name</label>
|
||||
<input id="withdrawal-name" type="text" name="name" x-model="name" required
|
||||
autocomplete="name"
|
||||
class="w-full border border-bg-rule px-3 py-2.5 text-[13px] bg-white text-ink focus:outline-none focus:border-hub" />
|
||||
@error('name', 'withdrawal')
|
||||
<p class="mt-1 text-[12px] text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="withdrawal-email" class="block text-[12px] font-semibold text-ink mb-1.5">E-Mail-Adresse</label>
|
||||
<input id="withdrawal-email" type="email" name="email" x-model="email" required
|
||||
autocomplete="email"
|
||||
class="w-full border border-bg-rule px-3 py-2.5 text-[13px] bg-white text-ink focus:outline-none focus:border-hub" />
|
||||
@error('email', 'withdrawal')
|
||||
<p class="mt-1 text-[12px] text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="withdrawal-reference" class="block text-[12px] font-semibold text-ink mb-1.5">
|
||||
Bestell-/Rechnungsnummer <span class="text-ink-3 font-normal">(falls zur Hand)</span>
|
||||
</label>
|
||||
<input id="withdrawal-reference" type="text" name="order_reference" x-model="reference"
|
||||
class="w-full border border-bg-rule px-3 py-2.5 text-[13px] bg-white text-ink focus:outline-none focus:border-hub" />
|
||||
@error('order_reference', 'withdrawal')
|
||||
<p class="mt-1 text-[12px] text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Bot-Schutz: versteckte Honeypot-Felder (siehe WithdrawalController). --}}
|
||||
<div class="hidden" aria-hidden="true">
|
||||
<label for="withdrawal-website">Website</label>
|
||||
<input id="withdrawal-website" type="text" name="website" tabindex="-1" autocomplete="off" value="{{ old('website') }}" />
|
||||
<label for="withdrawal-homepage">Homepage</label>
|
||||
<input id="withdrawal-homepage" type="text" name="homepage" tabindex="-1" autocomplete="off" value="{{ old('homepage') }}" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between gap-4 pt-5">
|
||||
<p class="text-[11px] text-ink-3 leading-[1.5] m-0">
|
||||
Ihre Angaben werden nur zur Bearbeitung des Widerrufs verarbeitet
|
||||
(<a href="{{ route('datenschutz', ['edition' => 'de']) }}" class="underline hover:text-ink">Datenschutz</a>).
|
||||
</p>
|
||||
<button type="button" @click="if (name.trim() && email.trim()) { step = 2 }"
|
||||
:disabled="! name.trim() || ! email.trim()"
|
||||
:class="(! name.trim() || ! email.trim()) ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'"
|
||||
class="inline-flex items-center justify-center px-5 py-2.5 text-[13px] font-semibold text-white bg-hub hover:bg-hub-2 rounded-[4px] whitespace-nowrap transition-colors">
|
||||
Weiter zur Bestätigung
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Schritt 2: Kontrolle + Bestätigungsbutton --}}
|
||||
<div x-show="step === 2" x-cloak>
|
||||
<p class="text-[12.5px] text-ink-3 leading-[1.55] m-0 mb-5">
|
||||
Bitte prüfen Sie Ihre Angaben. Mit dem Klick auf „Jetzt widerrufen" geht Ihre
|
||||
Widerrufserklärung bei uns ein — Sie erhalten sofort eine automatische
|
||||
Eingangsbestätigung per E-Mail.
|
||||
</p>
|
||||
|
||||
<dl class="border border-bg-rule divide-y divide-bg-rule text-[13px] m-0 mb-6">
|
||||
<div class="flex items-baseline justify-between gap-4 px-4 py-3">
|
||||
<dt class="text-ink-3">Name</dt>
|
||||
<dd class="font-semibold text-ink m-0 text-right" x-text="name"></dd>
|
||||
</div>
|
||||
<div class="flex items-baseline justify-between gap-4 px-4 py-3">
|
||||
<dt class="text-ink-3">E-Mail</dt>
|
||||
<dd class="font-semibold text-ink m-0 text-right break-all" x-text="email"></dd>
|
||||
</div>
|
||||
<div class="flex items-baseline justify-between gap-4 px-4 py-3">
|
||||
<dt class="text-ink-3">Bestell-/Rechnungsnummer</dt>
|
||||
<dd class="font-semibold text-ink m-0 text-right" x-text="reference.trim() || '—'"></dd>
|
||||
</div>
|
||||
</dl>
|
||||
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<button type="button" @click="step = 1"
|
||||
class="inline-flex items-center justify-center px-4 py-2.5 text-[13px] font-semibold text-ink border border-bg-rule-strong hover:bg-bg rounded-[4px] transition-colors cursor-pointer">
|
||||
Zurück
|
||||
</button>
|
||||
<button type="submit"
|
||||
class="inline-flex items-center justify-center px-5 py-2.5 text-[13px] font-semibold text-white bg-hub hover:bg-hub-2 rounded-[4px] whitespace-nowrap transition-colors cursor-pointer">
|
||||
Jetzt widerrufen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -932,6 +932,7 @@
|
|||
</main>
|
||||
|
||||
<x-web.hub.contact-modal />
|
||||
<x-web.hub.withdrawal-modal />
|
||||
|
||||
<x-web.hub.site-footer />
|
||||
@endsection
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue