254 lines
11 KiB
PHP
254 lines
11 KiB
PHP
<?php
|
|
|
|
use App\Models\User;
|
|
use App\Models\Partner;
|
|
use App\Models\PartnerInvitation;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use Illuminate\Support\Str;
|
|
use Livewire\Attributes\Layout;
|
|
use Livewire\Attributes\Title;
|
|
use Livewire\Volt\Component;
|
|
use Spatie\Permission\Models\Role;
|
|
|
|
new #[Layout('components.layouts.guest'), Title('Willkommen bei B2In')] class extends Component {
|
|
public PartnerInvitation $invitation;
|
|
|
|
public string $firstName = '';
|
|
public string $lastName = '';
|
|
public string $email = '';
|
|
public string $password = '';
|
|
public string $password_confirmation = '';
|
|
public bool $acceptTerms = false;
|
|
|
|
public function mount(string $token): void
|
|
{
|
|
$this->invitation = PartnerInvitation::with('role')->where('token', $token)->firstOrFail();
|
|
|
|
// Prüfe ob Einladung abgelaufen ist
|
|
if ($this->invitation->isExpired()) {
|
|
$this->invitation->markAsExpired();
|
|
$this->redirect('/partner/invitation/expired/' . $token);
|
|
}
|
|
|
|
// Prüfe ob Einladung bereits verwendet wurde
|
|
if ($this->invitation->status !== 'pending') {
|
|
$this->redirect('/partner/invitation/used/' . $token);
|
|
}
|
|
|
|
|
|
|
|
$this->email = $this->invitation->email;
|
|
$this->firstName = $this->invitation->contact_first_name ?? '';
|
|
$this->lastName = $this->invitation->contact_last_name ?? '';
|
|
}
|
|
|
|
public function createAccount(): void
|
|
{
|
|
$this->validate([
|
|
'firstName' => 'required|string|max:255',
|
|
'lastName' => 'required|string|max:255',
|
|
'email' => 'required|email',
|
|
'password' => 'required|string|min:8|confirmed',
|
|
'acceptTerms' => 'accepted',
|
|
], [
|
|
'firstName.required' => __('Bitte geben Sie Ihren Vornamen ein.'),
|
|
'firstName.max' => __('Der Vorname darf maximal 255 Zeichen lang sein.'),
|
|
'lastName.required' => __('Bitte geben Sie Ihren Nachnamen ein.'),
|
|
'lastName.max' => __('Der Nachname darf maximal 255 Zeichen lang sein.'),
|
|
'email.required' => __('Bitte geben Sie Ihre E-Mail-Adresse ein.'),
|
|
'email.email' => __('Bitte geben Sie eine gültige E-Mail-Adresse ein.'),
|
|
'password.required' => __('Bitte geben Sie ein Passwort ein.'),
|
|
'password.min' => __('Das Passwort muss mindestens 8 Zeichen lang sein.'),
|
|
'password.confirmed' => __('Die Passwörter stimmen nicht überein.'),
|
|
'acceptTerms.accepted' => __('Sie müssen die AGB und Datenschutzbestimmungen akzeptieren.'),
|
|
]);
|
|
|
|
try {
|
|
\DB::beginTransaction();
|
|
|
|
// 1. Erstelle Partner-Firma
|
|
$partner = Partner::create([
|
|
'company_name' => $this->invitation->company_name,
|
|
'slug' => Str::slug($this->invitation->company_name),
|
|
'type' => $this->invitation->role->name,
|
|
'is_active' => false, // Wird später im Setup-Wizard aktiviert
|
|
]);
|
|
|
|
// 2. Erstelle User
|
|
$user = User::create([
|
|
'partner_id' => $partner->id,
|
|
'name' => $this->firstName . ' ' . $this->lastName,
|
|
'email' => $this->email,
|
|
'password' => Hash::make($this->password),
|
|
'email_verified_at' => now(), // Auto-verifiziert durch Einladung
|
|
]);
|
|
|
|
// 3. Weise Rolle zu
|
|
$user->assignRole($this->invitation->role);
|
|
|
|
// 4. Markiere Einladung als akzeptiert
|
|
$this->invitation->markAsAccepted($partner);
|
|
|
|
// 5. Logge User ein
|
|
Auth::login($user);
|
|
|
|
\DB::commit();
|
|
|
|
// 6. Weiterleitung zum Setup-Wizard
|
|
session()->flash('message', __('Willkommen bei B2In! Vervollständigen Sie nun Ihr Profil.'));
|
|
$this->redirect(route('partner.setup.wizard'), navigate: true);
|
|
|
|
} catch (\Exception $e) {
|
|
\DB::rollBack();
|
|
$this->addError('email', __('Fehler beim Erstellen des Kontos: ') . $e->getMessage());
|
|
}
|
|
}
|
|
}; ?>
|
|
|
|
<div class="w-full max-w-2xl">
|
|
{{-- Header --}}
|
|
<div class="text-center mb-8">
|
|
@include('partials.logo-head')
|
|
<h1 class="text-4xl font-bold text-zinc-900 dark:text-white mb-2">
|
|
{{ __('Willkommen, :company!', ['company' => $invitation->company_name]) }}
|
|
</h1>
|
|
<p class="text-lg text-zinc-600 dark:text-zinc-400">
|
|
{{ __('Erstellen Sie Ihr persönliches Konto, um Ihr Partner-Profil einzurichten.') }}
|
|
</p>
|
|
</div>
|
|
|
|
{{-- Card mit Formular --}}
|
|
<flux:card class="shadow-2xl">
|
|
<form wire:submit="createAccount" class="space-y-6">
|
|
{{-- Partner Info Badge --}}
|
|
<div class="flex items-center justify-center gap-3 p-4 bg-accent-50 dark:bg-accent-900/20 rounded-lg">
|
|
<flux:icon.briefcase class="h-6 w-6 text-accent-600 dark:text-accent-400" />
|
|
<div>
|
|
<div class="text-sm text-zinc-600 dark:text-zinc-400">{{ __('Partner-Typ') }}</div>
|
|
<div class="font-semibold text-zinc-900 dark:text-white">
|
|
{{ $invitation->role?->display_name ?? $invitation->role?->name }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<flux:separator />
|
|
|
|
{{-- Name Felder --}}
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<flux:field>
|
|
<flux:label>{{ __('Ihr Vorname') }} <span class="text-red-500">*</span></flux:label>
|
|
<flux:input
|
|
wire:model="firstName"
|
|
placeholder="{{ __('z.B. Max') }}"
|
|
icon="user"
|
|
autofocus
|
|
/>
|
|
@error('firstName') <flux:error>{{ $message }}</flux:error> @enderror
|
|
</flux:field>
|
|
|
|
<flux:field>
|
|
<flux:label>{{ __('Ihr Nachname') }} <span class="text-red-500">*</span></flux:label>
|
|
<flux:input
|
|
wire:model="lastName"
|
|
placeholder="{{ __('z.B. Mustermann') }}"
|
|
icon="user"
|
|
/>
|
|
@error('lastName') <flux:error>{{ $message }}</flux:error> @enderror
|
|
</flux:field>
|
|
</div>
|
|
|
|
{{-- E-Mail (gesperrt) --}}
|
|
<flux:field>
|
|
<flux:label>{{ __('E-Mail-Adresse') }}</flux:label>
|
|
<flux:input
|
|
wire:model="email"
|
|
type="email"
|
|
icon="envelope"
|
|
disabled
|
|
/>
|
|
<flux:description>{{ __('Diese E-Mail-Adresse wurde in der Einladung festgelegt und kann nicht geändert werden.') }}</flux:description>
|
|
@error('email') <flux:error>{{ $message }}</flux:error> @enderror
|
|
</flux:field>
|
|
|
|
<flux:separator />
|
|
|
|
{{-- Passwort --}}
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<flux:field>
|
|
<flux:label>{{ __('Passwort festlegen') }} <span class="text-red-500">*</span></flux:label>
|
|
<flux:input
|
|
wire:model="password"
|
|
type="password"
|
|
placeholder="{{ __('Mindestens 8 Zeichen') }}"
|
|
icon="lock-closed"
|
|
/>
|
|
@error('password') <flux:error>{{ $message }}</flux:error> @enderror
|
|
</flux:field>
|
|
|
|
<flux:field>
|
|
<flux:label>{{ __('Passwort bestätigen') }} <span class="text-red-500">*</span></flux:label>
|
|
<flux:input
|
|
wire:model="password_confirmation"
|
|
type="password"
|
|
placeholder="{{ __('Passwort wiederholen') }}"
|
|
icon="lock-closed"
|
|
/>
|
|
@error('password_confirmation') <flux:error>{{ $message }}</flux:error> @enderror
|
|
</flux:field>
|
|
</div>
|
|
|
|
{{-- AGB Checkbox --}}
|
|
<flux:field>
|
|
<div class="flex items-start gap-3">
|
|
<flux:checkbox wire:model="acceptTerms" />
|
|
<div class="text-sm text-zinc-600 dark:text-zinc-400">
|
|
{{ __('Ich akzeptiere die') }}
|
|
<a href="#" class="text-accent-600 hover:text-accent-700 dark:text-accent-400">{{ __('AGB') }}</a>
|
|
{{ __('und') }}
|
|
<a href="#" class="text-accent-600 hover:text-accent-700 dark:text-accent-400">{{ __('Datenschutzbestimmungen') }}</a>.
|
|
</div>
|
|
</div>
|
|
@error('acceptTerms') <flux:error>{{ $message }}</flux:error> @enderror
|
|
</flux:field>
|
|
|
|
<flux:separator />
|
|
|
|
{{-- Error Alert --}}
|
|
<x-error-alert />
|
|
|
|
{{-- Submit Button --}}
|
|
<div class="flex justify-end">
|
|
<flux:button
|
|
type="submit"
|
|
variant="primary"
|
|
icon="arrow-right"
|
|
class="w-full md:w-auto"
|
|
wire:loading.attr="disabled"
|
|
wire:target="createAccount"
|
|
>
|
|
<span wire:loading.remove wire:target="createAccount">
|
|
{{ __('Konto erstellen & Setup starten') }}
|
|
</span>
|
|
<span wire:loading wire:target="createAccount">
|
|
<flux:icon.arrow-path class="animate-spin inline-block mr-2 h-4 w-4" />
|
|
{{ __('Wird erstellt...') }}
|
|
</span>
|
|
</flux:button>
|
|
</div>
|
|
</form>
|
|
</flux:card>
|
|
|
|
{{-- Footer Hinweis --}}
|
|
<div class="mt-6 text-center">
|
|
<p class="text-sm text-zinc-500 dark:text-zinc-400">
|
|
{{ __('Diese Einladung ist gültig bis zum') }}
|
|
@if($invitation->expires_at)
|
|
<strong>{{ $invitation->expires_at->format('d.m.Y H:i') }}</strong> {{ __('Uhr') }}.
|
|
@else
|
|
<strong>{{ __('unbegrenzt') }}</strong> {{ __('Uhr') }}.
|
|
@endif
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|