b2in/resources/views/livewire/reg/landing.blade.php
2026-01-23 17:33:10 +01:00

291 lines
15 KiB
PHP

<?php
use App\Models\RegistrationCode;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Volt\Component;
use Spatie\Permission\Models\Role;
new #[Layout('web.layouts.web-master-slot'), Title('Registrierung')] class extends Component {
public string $roleSlug;
public string $roleKey;
public string $codePrefix = '';
public string $codePart1 = '';
public string $codePart2 = '';
public string $codePart3 = '';
public string $codePart4 = '';
public string $roleLabel = '';
public string $roleDescription = '';
protected array $roleMap = [];
public function mount(string $role): void
{
$this->loadRoleMap();
$slug = strtolower($role);
if (!isset($this->roleMap[$slug])) {
abort(404);
}
$this->roleSlug = $slug;
$this->roleKey = $this->roleMap[$slug]['key'];
$this->roleLabel = $this->roleMap[$slug]['label'];
$this->roleDescription = $this->roleMap[$slug]['description'];
$this->codePrefix = $this->roleMap[$slug]['prefix'] ?? '';
}
protected function loadRoleMap(): void
{
if (!empty($this->roleMap)) {
return;
}
$roles = Role::whereNotNull('reg_prefix')->where('can_be_invited', true)->get();
foreach ($roles as $role) {
$slug = strtolower(str_replace('-', '', $role->reg_prefix));
$key = strtolower(str_replace('-', '', $role->name));
$this->roleMap[$slug] = [
'key' => $key,
'label' => __('registration.roles.' . $key . '.label'),
'description' => __('registration.roles.' . $key . '.description'),
'prefix' => $role->reg_prefix,
];
}
}
public function submitCode(): void
{
$this->validate([
'codePrefix' => ['required', 'string', 'size:1'],
'codePart1' => ['required', 'digits:2'],
'codePart2' => ['required', 'digits:2'],
'codePart3' => ['required', 'digits:2'],
'codePart4' => ['required', 'digits:2'],
]);
$normalized = $this->normalizeCode(
$this->codePrefix,
$this->codePart1,
$this->codePart2,
$this->codePart3,
$this->codePart4
);
/** @var RegistrationCode|null $registrationCode */
$registrationCode = RegistrationCode::where('code', $normalized)
->where('role', $this->roleKey)
->first();
if (!$registrationCode || !$registrationCode->isAvailable()) {
$this->addError('code', __('registration.messages.code_invalid'));
return;
}
// Merke Code in Session; Verbrauch/Markierung erfolgt nach erfolgreicher Registrierung.
session([
'registration_code_id' => $registrationCode->id,
'registration_role' => $this->roleKey,
'registration_slug' => $this->roleSlug,
]);
session()->flash('message', __('registration.messages.code_accepted'));
$this->redirect(route('partner.create.account'), navigate: true);
}
protected function normalizeCode(string $prefix, string ...$parts): string
{
$prefix = strtoupper(trim($prefix));
$segments = array_map(fn ($p) => str_pad(preg_replace('/\D+/', '', $p), 2, '0', STR_PAD_LEFT), $parts);
return $prefix . implode('', $segments);
}
}; ?>
<div class="bg-background">
<livewire:web.components.ui.header />
<main class="variante-glass-flow">
<div class="max-w-xl mx-auto px-6 space-y-8 pt-20 pb-40" id="code-form">
{{-- Hero --}}
<div class="text-center space-y-3">
<p class="text-xs uppercase tracking-[0.2em] text-accent-500">{{ __('registration.titles.registration') }}</p>
<h1 class="text-3xl md:text-4xl font-bold text-foreground">
{{ __('registration.titles.access_for_role', ['role' => $roleLabel]) }}
</h1>
<p class="text-base text-muted-foreground">
{{ $roleDescription }}
</p>
</div>
<div class="bg-card/80 backdrop-blur shadow-2xl border border-border rounded-3xl p-6 md:p-8 space-y-6">
<form wire:submit="submitCode" class="space-y-4 mt-10">
<flux:field>
<div class="text-center text-lg font-medium text-foreground mb-4">{{ __('registration.titles.enter_code') }}</div>
<div class="flex justify-center items-center gap-3 flex-nowrap mx-auto w-fit">
<input
wire:model.defer="codePrefix"
placeholder="M"
maxlength="1"
autofocus
class="w-12 text-center uppercase text-xl px-3 py-3 bg-gray-50 border border-border rounded-lg outline-1 -outline-offset-1 outline-gray-300 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-sky-600 text-foreground"
/>
<span class="text-muted-foreground">|</span>
<input
wire:model.defer="codePart1"
placeholder="00"
maxlength="2"
class="w-16 text-center text-xl px-3 py-3 bg-gray-50 border border-border rounded-lg outline-1 -outline-offset-1 outline-gray-300 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-sky-600"
/>
<input
wire:model.defer="codePart2"
placeholder="00"
maxlength="2"
class="w-16 text-center text-xl px-3 py-3 bg-gray-50 border border-border rounded-lg outline-1 -outline-offset-1 outline-gray-300 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-sky-600"
/>
<input
wire:model.defer="codePart3"
placeholder="00"
maxlength="2"
class="w-16 text-center text-xl px-3 py-3 bg-gray-50 border border-border rounded-lg outline-1 -outline-offset-1 outline-gray-300 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-sky-600"
/>
<input
wire:model.defer="codePart4"
placeholder="00"
maxlength="2"
class="w-16 text-center text-xl px-3 py-3 bg-gray-50 border border-border rounded-lg outline-1 -outline-offset-1 outline-gray-300 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-sky-600"
/>
</div>
<br />
<div class="flex flex-col gap-3 p-4 mt-10 rounded-2xl bg-accent-50 dark:bg-accent-900/15 border border-accent-100 dark:border-accent-900/30">
<div class="flex items-center gap-3">
<div class="h-10 w-10 rounded-xl bg-accent-100 dark:bg-accent-900/40 flex items-center justify-center">
<flux:icon.key class="h-5 w-5 text-accent-600 dark:text-accent-300" />
</div>
<div>
<div class="font-base text-foreground">{{ __('registration.messages.code_unique') }}</div>
<div class="text-sm text-muted-foreground">{{ __('registration.messages.code_format') }}</div>
</div>
</div>
</div>
<div class="text-xs text-center text-muted-foreground mt-2 mb-2">
{{ __('') }}
</div>
<br />
</flux:field>
<x-error-alert-static light />
<div class="flex justify-between">
<flux:button type="button" variant="primary" icon="arrow-down" href="#how-it-works" class="cursor-pointer">
{{ __('registration.actions.how_it_works') }}
</flux:button>
<button type="submit" class="btn-secondary-accent small cursor-pointer" wire:target="submitCode">
<span class="flex items-center gap-2"><flux:icon.arrow-right class="h-5 w-5" /> {{ __('registration.actions.check_code') }}</span>
</button>
</div>
</form>
@if (session()->has('message'))
<div class="p-3 rounded-lg border border-red-200 bg-red-50 text-red-800">
{{ session('message') }}
</div>
@endif
</div>
<div class="text-center text-sm text-muted-foreground">
{{ __('registration.messages.code_problems') }}
</div>
</div>
<section class="section-padding bg-accent" id="how-it-works">
<div class="container-padding">
<div class="text-center mb-16 slide-up delay-300">
<h2 class="text-section-title text-foreground mb-6">
{{ __('registration.titles.how_it_works') }}
</h2>
<p class="text-large text-muted-foreground max-w-3xl mx-auto">
{{ __('registration.titles.how_it_works_description', ['role' => $roleLabel]) }}
</p>
</div>
<div class="grid md:grid-cols-3 gap-8 mb-16">
<div class="card-elevated p-0 overflow-hidden group hover:shadow-elevated transition-all duration-300 slide-up delay-200">
<div class="relative pt-12 pb-8">
<div class="mx-auto w-20 h-20 icon-secondary-linear glow-soft group-hover:glow-medium rounded-2xl flex items-center justify-center transition-colors duration-300">
@svg('heroicon-o-key', 'w-10 h-10 text-secondary-foreground')
</div>
</div>
<div class="p-8">
<div class="flex items-center gap-3 mb-4">
<div class="w-10 h-10 rounded-lg bg-secondary/10 flex items-center justify-center">
<span class="text-lg font-bold text-secondary">1</span>
</div>
<h3 class="text-2xl font-medium text-foreground">
{{ __('registration.steps.code_entry.title') }}
</h3>
</div>
<p class="text-muted-foreground leading-relaxed mb-0">
{{ __('registration.steps.code_entry.description') }}
</p>
</div>
</div>
<div class="card-elevated p-0 overflow-hidden group hover:shadow-elevated transition-all duration-300 slide-up delay-400">
<div class="relative pt-12 pb-8">
<div class="mx-auto w-20 h-20 icon-secondary-linear glow-soft group-hover:glow-medium rounded-2xl flex items-center justify-center transition-colors duration-300">
@svg('heroicon-o-user-plus', 'w-10 h-10 text-secondary-foreground')
</div>
</div>
<div class="p-8">
<div class="flex items-center gap-3 mb-4">
<div class="w-10 h-10 rounded-lg bg-secondary/10 flex items-center justify-center">
<span class="text-lg font-bold text-secondary">2</span>
</div>
<h3 class="text-2xl font-medium text-foreground">
{{ __('registration.steps.create_account.title') }}
</h3>
</div>
<p class="text-muted-foreground leading-relaxed mb-0">
{{ __('registration.steps.create_account.description', ['role' => $roleLabel]) }}
</p>
</div>
</div>
<div class="card-elevated p-0 overflow-hidden group hover:shadow-elevated transition-all duration-300 slide-up delay-600">
<div class="relative pt-12 pb-8">
<div class="mx-auto w-20 h-20 icon-secondary-linear glow-soft group-hover:glow-medium rounded-2xl flex items-center justify-center transition-colors duration-300">
@svg('heroicon-o-sparkles', 'w-10 h-10 text-secondary-foreground')
</div>
</div>
<div class="p-8">
<div class="flex items-center gap-3 mb-4">
<div class="w-10 h-10 rounded-lg bg-secondary/10 flex items-center justify-center">
<span class="text-lg font-bold text-secondary">3</span>
</div>
<h3 class="text-2xl font-medium text-foreground">
{{ __('registration.steps.complete_onboarding.title') }}
</h3>
</div>
<p class="text-muted-foreground leading-relaxed mb-6">
{{ __('registration.steps.complete_onboarding.description') }}
</p>
<a href="#code-form" class="block">
<button class="btn-secondary-accent small w-full cursor-pointer">
{{ __('registration.actions.start_now') }}
</button>
</a>
</div>
</div>
</div>
</div>
</section>
</main>
<livewire:web.components.ui.footer />
</div>