clupilot/app/resources/views/livewire/pages/auth/register.blade.php

328 lines
16 KiB
PHP

<?php
use App\Models\User;
use Illuminate\Auth\Events\Registered;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules;
use Livewire\Attributes\Layout;
use Livewire\Volt\Component;
new #[Layout('layouts.login')] class extends Component
{
public string $firstname = '';
public string $lastname = '';
public string $email = '';
public string $workspace = '';
public string $password = '';
public bool $terms = false;
public function register(): void
{
$this->validate([
'firstname' => ['required', 'string', 'max:255'],
'lastname' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class],
'workspace' => ['required', 'string', 'max:255'],
'password' => ['required', 'string', Rules\Password::min(10)],
'terms' => ['accepted'],
]);
$user = User::create([
'name' => trim($this->firstname . ' ' . $this->lastname),
'email' => $this->email,
'password' => Hash::make($this->password),
]);
event(new Registered($user));
Auth::login($user);
$this->redirect(route('dashboard', absolute: false), navigate: true);
}
}; ?>
<div class="clu-login-grid">
{{-- ===== LEFT: FORM ===== --}}
<section class="clu-form-side flex flex-col min-h-screen">
{{-- Top row --}}
<div class="flex items-center justify-between gap-4 flex-wrap gap-y-2">
<div class="flex items-center gap-[10px] shrink-0">
<div class="clu-brand-mark w-[30px] h-[30px] rounded-[8px]" aria-hidden="true"></div>
<div class="font-semibold text-[15px] tracking-[-0.01em]">
CluPilot<span class="text-t2 font-medium ml-[5px] text-[11.5px]">v2.4</span>
</div>
</div>
<div class="clu-login-status text-[12.5px] text-t2 shrink-0">
Schon Konto?
<a href="{{ route('login') }}" wire:navigate class="ml-[6px] font-medium text-akzent hover:underline">Anmelden →</a>
</div>
</div>
{{-- Stepper --}}
<nav class="clu-stepper" aria-label="Registrierungs-Schritte">
<div class="clu-step active">
<div class="clu-step-num" aria-current="step">1</div>
<div class="clu-step-label">Konto</div>
</div>
<div class="clu-step-line" role="separator"></div>
<div class="clu-step">
<div class="clu-step-num">2</div>
<div class="clu-step-label">Workspace</div>
</div>
<div class="clu-step-line" role="separator"></div>
<div class="clu-step">
<div class="clu-step-num">3</div>
<div class="clu-step-label">Erster Server</div>
</div>
</nav>
{{-- Form wrap --}}
<div class="my-auto flex flex-col gap-[22px] w-full py-6 sm:py-10 mx-auto" style="max-width:420px;">
<header>
<h1 class="m-0 text-[24px] font-semibold tracking-[-0.02em] leading-[1.2]">14 Tage kostenlos testen</h1>
<p class="mt-[6px] text-[13.5px] text-t2">Keine Kreditkarte. Volle Funktionen. Jederzeit kündbar.</p>
</header>
{{-- SSO --}}
<div class="clu-sso-row">
<button class="clu-sso-btn" type="button">
<svg width="15" height="15" viewBox="0 0 24 24" aria-hidden="true">
<path fill="#4285F4" d="M22.5 12.27c0-.79-.07-1.54-.2-2.27H12v4.51h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.32z"/>
<path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.99.66-2.25 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84A11 11 0 0 0 12 23z"/>
<path fill="#FBBC05" d="M5.84 14.10A6.6 6.6 0 0 1 5.5 12c0-.73.13-1.44.34-2.10V7.06H2.18A11 11 0 0 0 1 12c0 1.77.42 3.45 1.18 4.94l3.66-2.84z"/>
<path fill="#EA4335" d="M12 5.38c1.62 0 3.07.56 4.21 1.64l3.15-3.15C17.45 2.10 14.96 1 12 1A11 11 0 0 0 2.18 7.06l3.66 2.84C6.71 7.30 9.14 5.38 12 5.38z"/>
</svg>
Mit Google fortfahren
</button>
<button class="clu-sso-btn" type="button">
<svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M12 .5C5.65.5.5 5.65.5 12c0 5.08 3.29 9.39 7.86 10.91.58.11.79-.25.79-.55v-2.02c-3.2.7-3.87-1.36-3.87-1.36-.52-1.33-1.27-1.68-1.27-1.68-1.04-.71.08-.7.08-.7 1.15.08 1.76 1.18 1.76 1.18 1.02 1.75 2.69 1.24 3.35.95.1-.74.4-1.24.73-1.53-2.55-.29-5.24-1.28-5.24-5.69 0-1.26.45-2.28 1.18-3.09-.12-.29-.51-1.46.11-3.05 0 0 .97-.31 3.18 1.18.92-.26 1.91-.39 2.89-.39.98 0 1.97.13 2.89.39 2.20-1.49 3.17-1.18 3.17-1.18.63 1.59.23 2.76.12 3.05.74.81 1.18 1.83 1.18 3.09 0 4.42-2.69 5.40-5.25 5.68.41.36.78 1.06.78 2.14v3.18c0 .31.21.67.79.55C20.21 21.39 23.5 17.08 23.5 12c0-6.35-5.15-11.5-11.5-11.5z"/>
</svg>
Mit GitHub fortfahren
</button>
</div>
<div class="clu-divider">oder mit E-Mail</div>
{{-- Form --}}
<form wire:submit="register" class="flex flex-col gap-[14px]">
{{-- First / Last name --}}
<div class="clu-field-row">
<div class="flex flex-col gap-[6px]">
<label for="firstname" class="text-[11.5px] text-t2 font-medium">Vorname</label>
<div class="clu-input">
<input wire:model="firstname" id="firstname" name="firstname" type="text"
placeholder="Marie" autocomplete="given-name" required autofocus />
</div>
<x-input-error :messages="$errors->get('firstname')" class="mt-1" />
</div>
<div class="flex flex-col gap-[6px]">
<label for="lastname" class="text-[11.5px] text-t2 font-medium">Nachname</label>
<div class="clu-input">
<input wire:model="lastname" id="lastname" name="lastname" type="text"
placeholder="Weber" autocomplete="family-name" required />
</div>
<x-input-error :messages="$errors->get('lastname')" class="mt-1" />
</div>
</div>
{{-- Email --}}
<div class="flex flex-col gap-[6px]">
<label for="reg-email" class="text-[11.5px] text-t2 font-medium">Arbeits-E-Mail</label>
<div class="clu-input" id="reg-email-wrap">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="text-dim flex-none" aria-hidden="true">
<rect x="3" y="5" width="18" height="14" rx="2"/><path d="M3 7l9 6 9-6"/>
</svg>
<input wire:model="email" id="reg-email" name="email" type="email"
placeholder="du@firma.de" autocomplete="email" required />
<svg class="clu-input-check" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true">
<path d="M5 12l5 5L20 7"/>
</svg>
</div>
<x-input-error :messages="$errors->get('email')" class="mt-1" />
</div>
{{-- Workspace --}}
<div class="flex flex-col gap-[6px]">
<label for="reg-workspace" class="flex items-center justify-between text-[11.5px] text-t2 font-medium">
Workspace-Name
<span class="font-normal font-mono text-[10.5px] text-dim">URL für dein Team</span>
</label>
<div class="clu-input with-suffix">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="text-dim flex-none" aria-hidden="true">
<rect x="3" y="4" width="18" height="7" rx="1.5"/><rect x="3" y="13" width="18" height="7" rx="1.5"/>
<circle cx="7" cy="7.5" r="0.7" fill="currentColor"/><circle cx="7" cy="16.5" r="0.7" fill="currentColor"/>
</svg>
<input wire:model="workspace" id="reg-workspace" name="workspace" type="text"
placeholder="Acme Cluster" required />
<span class="clu-input-suffix">.clupilot.io</span>
</div>
<div class="clu-slug-preview">Erreichbar unter <b id="reg-slug-preview">dein-team</b>.clupilot.io</div>
<x-input-error :messages="$errors->get('workspace')" class="mt-1" />
</div>
{{-- Password --}}
<div class="flex flex-col gap-[6px]">
<label for="reg-password" class="flex items-center justify-between text-[11.5px] text-t2 font-medium">
Passwort
<span class="font-normal font-mono text-[10.5px] text-dim">min. 10 Zeichen</span>
</label>
<div class="clu-input">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="text-dim flex-none" aria-hidden="true">
<rect x="5" y="11" width="14" height="9" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/>
</svg>
<input wire:model="password" id="reg-password" name="password" type="password"
placeholder="••••••••••••" autocomplete="new-password" required />
<button type="button" id="reg-toggle-eye" aria-label="Passwort anzeigen"
class="text-t2 p-[4px] rounded-[4px] grid place-items-center transition-colors hover:text-t1 hover:bg-surface2">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/>
</svg>
</button>
</div>
<div class="clu-pw-meter" id="reg-pw-meter"><span></span><span></span><span></span><span></span></div>
<div class="clu-pw-rules">
<span class="clu-pw-rule" data-rule="len"><span class="clu-pw-bullet"></span>10+ Zeichen</span>
<span class="clu-pw-rule" data-rule="upper"><span class="clu-pw-bullet"></span>Großbuchstabe</span>
<span class="clu-pw-rule" data-rule="num"><span class="clu-pw-bullet"></span>Zahl</span>
<span class="clu-pw-rule" data-rule="sym"><span class="clu-pw-bullet"></span>Sonderzeichen</span>
</div>
<x-input-error :messages="$errors->get('password')" class="mt-1" />
</div>
{{-- Terms --}}
<label class="clu-checkbox">
<input wire:model="terms" id="reg-terms" type="checkbox" class="sr-only" />
<span class="clu-check-box">
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M5 12l5 5L20 7"/>
</svg>
</span>
<span>Ich akzeptiere die <a href="#" class="text-akzent font-medium hover:underline">AGB</a> und stimme der <a href="#" class="text-akzent font-medium hover:underline">Datenschutzerklärung</a> zu. CluPilot speichert SSH-Keys verschlüsselt in der EU.</span>
</label>
<x-input-error :messages="$errors->get('terms')" class="mt-[-6px]" />
{{-- Submit --}}
<button class="clu-btn-primary" type="submit" wire:loading.attr="disabled">
<svg wire:loading class="clu-spin" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true">
<path d="M21 12a9 9 0 1 1-6.2-8.55"/>
</svg>
<span wire:loading.remove>Konto erstellen</span>
<span wire:loading>Konto wird angelegt…</span>
<svg class="arrow" wire:loading.remove width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true">
<path d="M5 12h14M13 5l7 7-7 7"/>
</svg>
</button>
</form>
</div>
{{-- Footer --}}
<footer class="flex items-center justify-between gap-3 text-t2 text-[11.5px] font-mono pt-3 border-t border-white/6 flex-wrap">
<div class="flex items-center gap-[6px]">
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" aria-hidden="true">
<rect x="5" y="11" width="14" height="9" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/>
</svg>
TLS 1.3 · AES-256 · region eu-central-1
</div>
<div class="flex gap-[14px]">
<a href="#" class="text-t2 hover:text-t1 no-underline transition-colors">Datenschutz</a>
<a href="#" class="text-t2 hover:text-t1 no-underline transition-colors">AGB</a>
<a href="#" class="text-t2 hover:text-t1 no-underline transition-colors">Status</a>
<a href="#" class="text-t2 hover:text-t1 no-underline transition-colors">Hilfe</a>
</div>
</footer>
</section>
{{-- ===== RIGHT: BENEFITS PANEL ===== --}}
<aside class="clu-live-side relative border-l border-white/6 overflow-hidden flex flex-col gap-[22px] min-h-screen"
style="background:linear-gradient(180deg,#0c0c12 0%,#0a0a0f 100%);padding:36px 40px;"
aria-hidden="true">
<div class="clu-trial-card">
<div class="clu-trial-row">
<span class="clu-trial-tag">14 Tage Pro · gratis</span>
<span class="clu-trial-count">
<span class="clu-pulse-dot w-[6px] h-[6px] rounded-full bg-success flex-none"></span>
47 Teams diese Woche
</span>
</div>
<div class="clu-trial-title">In <em>60 Sekunden</em> deinen ersten Server verbunden.</div>
<div class="clu-trial-sub">SSH-Schlüssel hochladen, Site importieren, fertig. Wir kümmern uns ums Monitoring, Backups und Updates — du um dein Business.</div>
</div>
<ul class="clu-benefit-list">
<li class="clu-benefit">
<div class="clu-benefit-ico" aria-hidden="true">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M4 17l5-5-5-5M13 19h7"/>
</svg>
</div>
<div>
<div class="clu-benefit-title">SSH-First Setup</div>
<div class="clu-benefit-desc">Verbinde Hetzner, Hostinger oder deinen eigenen Server in 2 Klicks — keine Agents, kein Custom-DNS.</div>
</div>
</li>
<li class="clu-benefit">
<div class="clu-benefit-ico success" aria-hidden="true">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 12c0 5-9 9-9 9s-9-4-9-9V5l9-3 9 3v7z"/><path d="M9 11l2 2 4-4"/>
</svg>
</div>
<div>
<div class="clu-benefit-title">Stündliche Backups inklusive</div>
<div class="clu-benefit-desc">Verschlüsselt nach Frankfurt-S3, 30 Tage Retention, One-Click-Restore — auch für die WP-Datenbank.</div>
</div>
</li>
<li class="clu-benefit">
<div class="clu-benefit-ico purple" aria-hidden="true">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 12a9 9 0 1 1-3-6.7M21 4v5h-5"/>
</svg>
</div>
<div>
<div class="clu-benefit-title">Bulk-Updates ohne Drama</div>
<div class="clu-benefit-desc">WordPress, Plugins und Themes auf 100 Sites gleichzeitig — mit Staging-Diff und automatischem Rollback bei Fehler.</div>
</div>
</li>
<li class="clu-benefit">
<div class="clu-benefit-ico warn" aria-hidden="true">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 3l8 4v5c0 5-3.5 8-8 9-4.5-1-8-4-8-9V7l8-4z"/><path d="M12 8v5M12 16h.01"/>
</svg>
</div>
<div>
<div class="clu-benefit-title">Brute-Force-Defense aktiv</div>
<div class="clu-benefit-desc">Geo-Fencing, Rate-Limits und WP-User-Lockout — schon im Trial dabei. Letzte 24h: 12.488 Versuche blockiert.</div>
</div>
</li>
</ul>
<div class="clu-quote-card">
<div class="clu-quote-text">Statt 18 SSH-Sessions in Terminal-Tabs habe ich jetzt eine Konsole. Mein Agency-Team hostet 87 Sites — und schläft endlich wieder durch.</div>
<div class="clu-quote-author">
<div class="clu-avatar">JK</div>
<div>
<div class="clu-author-name">Julian Koch</div>
<div class="clu-author-role">CTO · Helio Digital · 87 Sites</div>
</div>
</div>
</div>
<div class="clu-logos">
<span>vertrauen uns</span>
<span class="clu-logo-pill">helio.</span>
<span class="clu-logo-pill">acme▲</span>
<span class="clu-logo-pill">opencrate</span>
<span class="clu-logo-pill">bergmann</span>
<span class="clu-logo-pill">+ 2.1k</span>
</div>
</aside>
</div>