23-01-2026
This commit is contained in:
parent
07959c0ba2
commit
854ce02bf6
166 changed files with 32909 additions and 1262 deletions
299
resources/views/livewire/partner/create-account.blade.php
Normal file
299
resources/views/livewire/partner/create-account.blade.php
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
<?php
|
||||
|
||||
use App\Models\RegistrationCode;
|
||||
use App\Models\Partner;
|
||||
use App\Models\User;
|
||||
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('web.layouts.web-master-slot'), Title('Willkommen bei B2In')] class extends Component {
|
||||
public string $firstName = '';
|
||||
public string $lastName = '';
|
||||
public string $email = '';
|
||||
public string $password = '';
|
||||
public string $password_confirmation = '';
|
||||
public bool $acceptTerms = false;
|
||||
|
||||
protected ?RegistrationCode $registrationCode = null;
|
||||
protected ?string $roleKey = null;
|
||||
protected ?string $roleSlug = null;
|
||||
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->loadRegistrationCode();
|
||||
$this->roleSlug = session('registration_slug') ?? null;
|
||||
if (!$this->registrationCode || !$this->roleKey) {
|
||||
session()->flash('message', __('Registrierungscode fehlt oder ist ungültig. Bitte starten Sie erneut über den QR-Link.'));
|
||||
$this->redirect(route('registration.landing', ['role' => $this->roleSlug]), navigate: true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Optional: E-Mail vorbelegen, wenn im Code gespeichert (meta)
|
||||
if ($this->registrationCode->metadata['email'] ?? false) {
|
||||
$this->email = $this->registrationCode->metadata['email'];
|
||||
}
|
||||
}
|
||||
|
||||
public function createAccount(): void
|
||||
{
|
||||
$this->loadRegistrationCode();
|
||||
|
||||
if (!$this->registrationCode || !$this->roleKey || !$this->registrationCode->isAvailable()) {
|
||||
$this->addError('registration_code', __('Registrierungscode fehlt oder ist ungültig. Bitte starten Sie erneut über den QR-Link.'));
|
||||
return;
|
||||
}
|
||||
|
||||
$this->validate([
|
||||
'firstName' => 'required|string|max:255',
|
||||
'lastName' => 'required|string|max:255',
|
||||
'email' => 'required|email|max:255|unique:users,email',
|
||||
'password' => 'required|string|min:8|confirmed',
|
||||
'acceptTerms' => 'accepted',
|
||||
], [
|
||||
'firstName.required' => __('Bitte geben Sie Ihren Vornamen ein.'),
|
||||
'lastName.required' => __('Bitte geben Sie Ihren Nachnamen ein.'),
|
||||
'email.required' => __('Bitte geben Sie Ihre E-Mail-Adresse ein.'),
|
||||
'email.email' => __('Bitte geben Sie eine gültige E-Mail-Adresse ein.'),
|
||||
'email.unique' => __('Diese E-Mail-Adresse ist bereits registriert.'),
|
||||
'password.required' => __('Bitte vergeben Sie ein Passwort.'),
|
||||
'password.min' => __('Das Passwort muss mindestens 8 Zeichen haben.'),
|
||||
'password.confirmed' => __('Die Passwörter stimmen nicht überein.'),
|
||||
'acceptTerms.accepted' => __('Bitte akzeptieren Sie die AGB und Datenschutzbestimmungen.'),
|
||||
]);
|
||||
|
||||
try {
|
||||
\DB::beginTransaction();
|
||||
|
||||
// Parent-Partner-ID ermitteln (Makler oder Händler, für Kunden)
|
||||
$parentPartnerId = null;
|
||||
if ($this->registrationCode->assigned_to_code_id) {
|
||||
$assignedCode = RegistrationCode::with('partner')->find($this->registrationCode->assigned_to_code_id);
|
||||
if ($assignedCode && $assignedCode->partner_id) {
|
||||
$parentPartnerId = $assignedCode->partner_id;
|
||||
}
|
||||
}
|
||||
|
||||
// Aktuelle Marke/Theme ermitteln (aus Domain)
|
||||
$brand = config('app.theme', 'b2in');
|
||||
|
||||
// Partner anlegen (minimal, wird im Wizard vervollständigt)
|
||||
$partnerName = __('roles.' . $this->registrationCode->role . ' :code', ['code' => $this->registrationCode->code]);
|
||||
$partner = Partner::create([
|
||||
'company_name' => $partnerName,
|
||||
'slug' => Str::slug($partnerName . '-' . $this->registrationCode->id),
|
||||
'type' => $this->roleKey,
|
||||
'brand' => $brand,
|
||||
'parent_partner_id' => $parentPartnerId,
|
||||
'is_active' => false,
|
||||
]);
|
||||
|
||||
// User anlegen
|
||||
$user = User::create([
|
||||
'partner_id' => $partner->id,
|
||||
'name' => trim($this->firstName . ' ' . $this->lastName),
|
||||
'display_name' => $this->registrationCode->name ?? null, // Name aus Registrierungscode übernehmen
|
||||
'email' => $this->email,
|
||||
'password' => Hash::make($this->password),
|
||||
'email_verified_at' => null, // E-Mail muss verifiziert werden
|
||||
]);
|
||||
|
||||
// Rolle zuweisen
|
||||
if ($role = Role::whereRaw('LOWER(REPLACE(name, "-", "")) = ?', [strtolower($this->roleKey)])->first()) {
|
||||
$user->assignRole($role);
|
||||
}
|
||||
|
||||
// Registrierungscode verbrauchen
|
||||
$this->registrationCode->markUsed($user);
|
||||
|
||||
// E-Mail-Verifizierung senden
|
||||
$user->sendEmailVerificationNotification();
|
||||
|
||||
session()->forget(['registration_code_id', 'registration_role', 'registration_slug']);
|
||||
|
||||
\DB::commit();
|
||||
|
||||
// Weiterleitung zur Danke-Seite (User ist NICHT eingeloggt)
|
||||
$this->redirect(route('registration.thank-you', ['email' => $user->email]), navigate: true);
|
||||
} catch (\Exception $e) {
|
||||
\DB::rollBack();
|
||||
$this->addError('email', __('Fehler beim Erstellen des Kontos: ') . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected function loadRegistrationCode(): void
|
||||
{
|
||||
if ($this->registrationCode) {
|
||||
return;
|
||||
}
|
||||
|
||||
$codeId = session('registration_code_id');
|
||||
$role = session('registration_role');
|
||||
|
||||
if (!$codeId || !$role) {
|
||||
return;
|
||||
}
|
||||
$this->registrationCode = RegistrationCode::where('id', $codeId)
|
||||
->where('role', $role)
|
||||
->first();
|
||||
|
||||
$this->roleKey = $role;
|
||||
|
||||
// Falls Rolle (z.B. customer) nicht unterstützt, Session leeren
|
||||
if (!$this->roleKey || !$this->registrationCode?->isAvailable()) {
|
||||
session()->forget(['registration_code_id', 'registration_role', 'registration_slug']);
|
||||
$this->registrationCode = null;
|
||||
$this->roleKey = null;
|
||||
}
|
||||
}
|
||||
}; ?>
|
||||
|
||||
<div class="bg-background">
|
||||
<livewire:web.components.ui.header />
|
||||
|
||||
<main class="variante-glass-flow">
|
||||
<div class="max-w-3xl mx-auto px-6 space-y-8 pt-20 pb-40">
|
||||
{{-- Header --}}
|
||||
<div class="text-center space-y-2">
|
||||
<p class="text-xs uppercase tracking-[0.2em] text-accent-500">{{ __('Konto anlegen') }}</p>
|
||||
<h1 class="text-3xl md:text-4xl font-bold text-foreground">
|
||||
{{ __('Zugang erstellen & Setup starten') }}
|
||||
</h1>
|
||||
<p class="text-base text-muted-foreground">
|
||||
{{ __('Mit Ihrem Registrierungscode haben Sie den Zugang freigeschaltet. Legen Sie jetzt Ihr persönliches Konto an.') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-card/80 backdrop-blur shadow-2xl border border-border rounded-3xl p-6 md:p-8 space-y-6">
|
||||
<div class="flex flex-col gap-3 p-4 rounded-2xl bg-blue-50 border border-blue-100">
|
||||
<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">
|
||||
@svg('heroicon-o-key', 'h-5 w-5 text-accent-600')
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-medium text-foreground">{{ __('Code bestätigt') }}</div>
|
||||
<div class="text-sm text-muted-foreground">
|
||||
{{ __('Fortschritt: Konto anlegen, danach Setup-Wizard abschließen.') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<form wire:submit="createAccount" class="space-y-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-foreground mb-2">{{ __('Vorname') }}</label>
|
||||
<input
|
||||
wire:model="firstName"
|
||||
type="text"
|
||||
placeholder="{{ __('z.B. Max') }}"
|
||||
class="w-full px-4 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"
|
||||
/>
|
||||
@error('firstName') <div class="text-sm text-red-600 mt-1">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-foreground mb-2">{{ __('Nachname') }}</label>
|
||||
<input
|
||||
wire:model="lastName"
|
||||
type="text"
|
||||
placeholder="{{ __('z.B. Mustermann') }}"
|
||||
class="w-full px-4 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"
|
||||
/>
|
||||
@error('lastName') <div class="text-sm text-red-600 mt-1">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-foreground mb-2">{{ __('E-Mail-Adresse') }}</label>
|
||||
<input
|
||||
wire:model="email"
|
||||
type="email"
|
||||
placeholder="email@example.com"
|
||||
class="w-full px-4 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"
|
||||
/>
|
||||
@error('email') <div class="text-sm text-red-600 mt-1">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-foreground mb-2">{{ __('Passwort') }}</label>
|
||||
<input
|
||||
wire:model="password"
|
||||
type="password"
|
||||
placeholder="{{ __('Mindestens 8 Zeichen') }}"
|
||||
class="w-full px-4 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"
|
||||
/>
|
||||
@error('password') <div class="text-sm text-red-600 mt-1">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-foreground mb-2">{{ __('Passwort bestätigen') }}</label>
|
||||
<input
|
||||
wire:model="password_confirmation"
|
||||
type="password"
|
||||
placeholder="{{ __('Passwort wiederholen') }}"
|
||||
class="w-full px-4 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"
|
||||
/>
|
||||
@error('password_confirmation') <div class="text-sm text-red-600 mt-1">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="group relative inline-flex w-8 shrink-0 rounded-full bg-gray-200 p-px inset-ring inset-ring-gray-900/5 outline-offset-2 outline-lime-600 transition-colors duration-200 ease-in-out has-checked:bg-lime-600 has-focus-visible:outline-2">
|
||||
<span class="size-4 rounded-full bg-white shadow-xs ring-1 ring-gray-900/5 transition-transform duration-200 ease-in-out group-has-checked:translate-x-3.5"></span>
|
||||
<input id="acceptTerms" type="checkbox" name="acceptTerms" aria-label="Agree to policies" class="absolute inset-0 appearance-none focus:outline-hidden" wire:model="acceptTerms" />
|
||||
</div>
|
||||
|
||||
<div class="text-sm text-muted-foreground">
|
||||
{{ __('Ich akzeptiere die') }}
|
||||
<a href="#" class="text-accent-600 hover:text-accent-900">{{ __('AGB') }}</a>
|
||||
{{ __('und') }}
|
||||
<a href="#" class="text-accent-600 hover:text-accent-900">{{ __('Datenschutzbestimmungen') }}</a>.
|
||||
</div>
|
||||
</div>
|
||||
@error('acceptTerms') <div class="text-sm text-red-600 mt-1">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
|
||||
<x-error-alert-static light />
|
||||
|
||||
<div class="flex justify-end">
|
||||
<button type="submit" class="btn-secondary-accent small cursor-pointer" wire:target="createAccount">
|
||||
<span class="flex items-center gap-2">
|
||||
@svg('heroicon-o-arrow-right', 'h-5 w-5')
|
||||
{{ __('Konto erstellen & Setup starten') }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3 p-4 rounded-2xl bg-lime-50 border border-lime-200">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="h-10 w-10 rounded-xl bg-lime-100 flex items-center justify-center flex-shrink-0">
|
||||
@svg('heroicon-o-arrow-right-circle', 'h-5 w-5 text-lime-600')
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-medium text-foreground">{{ __('Verifizieren Sie Ihre E-Mail-Adresse') }}</div>
|
||||
<div class="text-sm text-muted-foreground">
|
||||
{{ __('Nach erfolgreicher Registrierung erhalten Sie einen Verifizierungslink per E-Mail. Bitte klicken Sie auf den Link, um Ihre E-Mail-Adresse zu verifizieren.') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="text-center text-sm text-muted-foreground">
|
||||
{{ __('Probleme? Bitte wenden Sie sich an Ihren Ansprechpartner oder den Support.') }}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<livewire:web.components.ui.footer />
|
||||
</div>
|
||||
441
resources/views/livewire/partner/my-data.blade.php
Normal file
441
resources/views/livewire/partner/my-data.blade.php
Normal file
|
|
@ -0,0 +1,441 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Partner;
|
||||
use App\Models\Brand;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Str;
|
||||
use Livewire\Attributes\Layout;
|
||||
use Livewire\Attributes\Title;
|
||||
use Livewire\Volt\Component;
|
||||
|
||||
new #[Layout('components.layouts.app'), Title('Meine Daten')] class extends Component {
|
||||
public Partner $partner;
|
||||
public string $partnerType;
|
||||
|
||||
// Stammdaten
|
||||
public string $companyName = '';
|
||||
public string $displayName = '';
|
||||
public string $salutation = '';
|
||||
public string $firstName = '';
|
||||
public string $lastName = '';
|
||||
public string $description = '';
|
||||
public string $street = '';
|
||||
public string $houseNumber = '';
|
||||
public string $zip = '';
|
||||
public string $city = '';
|
||||
public string $country = 'Deutschland';
|
||||
public string $phone = '';
|
||||
public string $website = '';
|
||||
|
||||
// Retailer - Liefergebiete
|
||||
public ?int $deliveryRadius = null;
|
||||
public ?int $assemblyRadius = null;
|
||||
|
||||
// Manufacturer - Marke
|
||||
public string $brandName = '';
|
||||
public string $brandDescription = '';
|
||||
|
||||
public string $roleIcon = 'shield-check';
|
||||
public string $roleName = '-';
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
if (!$user->partner_id) {
|
||||
$this->redirect(route('dashboard'), navigate: true);
|
||||
return;
|
||||
}
|
||||
|
||||
$role = $user->roles->first();
|
||||
if ($role) {
|
||||
$this->roleIcon = $role->icon ?? 'shield-check';
|
||||
$this->roleName = $role->display_name ?? $role->name;
|
||||
}
|
||||
|
||||
$this->partner = Partner::with('users')->findOrFail($user->partner_id);
|
||||
$this->partnerType = $this->partner->type;
|
||||
|
||||
// Vorausfüllen: Partner-Daten
|
||||
$this->companyName = $this->partner->company_name ?? '';
|
||||
$this->displayName = $this->partner->display_name ?? '';
|
||||
$this->salutation = $this->partner->salutation ?? '';
|
||||
$this->firstName = $this->partner->first_name ?? '';
|
||||
$this->lastName = $this->partner->last_name ?? '';
|
||||
$this->description = $this->partner->description ?? '';
|
||||
$this->street = $this->partner->street ?? '';
|
||||
$this->houseNumber = $this->partner->house_number ?? '';
|
||||
$this->zip = $this->partner->zip ?? '';
|
||||
$this->city = $this->partner->city ?? '';
|
||||
$this->country = $this->partner->country ?? 'Deutschland';
|
||||
$this->phone = $this->partner->phone ?? '';
|
||||
$this->website = $this->partner->website ?? '';
|
||||
$this->deliveryRadius = $this->partner->delivery_radius_km;
|
||||
$this->assemblyRadius = $this->partner->assembly_radius_km;
|
||||
|
||||
// Ersetze Platzhalter wie "roles.broker M10000004" durch Übersetzung + ID
|
||||
if(strpos($this->companyName, 'roles.') !== false) {
|
||||
$parts = explode(' ', $this->companyName, 2);
|
||||
$translatedRole = isset($parts[0]) ? __($parts[0]) : $this->companyName;
|
||||
$partnerId = isset($parts[1]) ? ' ' . $parts[1] : '';
|
||||
$this->companyName = $translatedRole . $partnerId;
|
||||
}
|
||||
|
||||
// Namen aus User übernehmen, falls Partner-Felder leer sind
|
||||
if (empty($this->firstName) && empty($this->lastName)) {
|
||||
$nameParts = explode(' ', $user->name, 2);
|
||||
$this->firstName = $nameParts[0] ?? '';
|
||||
$this->lastName = $nameParts[1] ?? '';
|
||||
}
|
||||
|
||||
// Marke laden für Manufacturer
|
||||
if (strtolower(str_replace('-', '', $this->partnerType)) === 'manufacturer') {
|
||||
$brand = Brand::where('partner_id', $this->partner->id)->first();
|
||||
if ($brand) {
|
||||
$this->brandName = $brand->name;
|
||||
$this->brandDescription = $brand->description ?? '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function saveData(): void
|
||||
{
|
||||
$normalizedType = strtolower(str_replace('-', '', $this->partnerType));
|
||||
$isCustomer = $normalizedType === 'customer';
|
||||
$isBroker = $normalizedType === 'broker' || $normalizedType === 'estateagent';
|
||||
$isRetailer = $normalizedType === 'retailer';
|
||||
$isManufacturer = $normalizedType === 'manufacturer';
|
||||
|
||||
$rules = [
|
||||
'salutation' => 'required|in:Herr,Frau,Divers',
|
||||
'firstName' => 'required|string|max:255',
|
||||
'lastName' => 'required|string|max:255',
|
||||
'street' => 'required|string|max:255',
|
||||
'houseNumber' => 'required|string|max:20',
|
||||
'zip' => 'required|string|max:10',
|
||||
'city' => 'required|string|max:255',
|
||||
'country' => 'required|string|max:255',
|
||||
'phone' => 'nullable|string|max:50',
|
||||
];
|
||||
|
||||
if (!$isCustomer) {
|
||||
$rules['companyName'] = 'required|string|max:255';
|
||||
$rules['description'] = 'nullable|string|max:1000';
|
||||
$rules['website'] = 'nullable|url|max:255';
|
||||
}
|
||||
|
||||
if ($isBroker) {
|
||||
$rules['displayName'] = 'required|string|max:255';
|
||||
}
|
||||
|
||||
if ($isRetailer) {
|
||||
$rules['deliveryRadius'] = 'required|integer|min:1|max:500';
|
||||
$rules['assemblyRadius'] = 'required|integer|min:1|max:500';
|
||||
}
|
||||
|
||||
if ($isManufacturer) {
|
||||
$rules['brandName'] = 'required|string|max:255';
|
||||
$rules['brandDescription'] = 'nullable|string|max:1000';
|
||||
}
|
||||
|
||||
$this->validate($rules, [
|
||||
'salutation.required' => __('Bitte wählen Sie eine Anrede.'),
|
||||
'firstName.required' => __('Bitte geben Sie einen Vornamen ein.'),
|
||||
'lastName.required' => __('Bitte geben Sie einen Nachnamen ein.'),
|
||||
'companyName.required' => __('Bitte geben Sie einen Firmennamen ein.'),
|
||||
'displayName.required' => __('Bitte geben Sie einen Anzeigenamen ein.'),
|
||||
'street.required' => __('Bitte geben Sie eine Straße ein.'),
|
||||
'houseNumber.required' => __('Bitte geben Sie eine Hausnummer ein.'),
|
||||
'zip.required' => __('Bitte geben Sie eine Postleitzahl ein.'),
|
||||
'city.required' => __('Bitte geben Sie eine Stadt ein.'),
|
||||
'country.required' => __('Bitte wählen Sie ein Land.'),
|
||||
'website.url' => __('Bitte geben Sie eine gültige URL ein.'),
|
||||
'deliveryRadius.required' => __('Bitte geben Sie einen Lieferradius ein.'),
|
||||
'deliveryRadius.min' => __('Der Lieferradius muss mindestens 1 km betragen.'),
|
||||
'assemblyRadius.required' => __('Bitte geben Sie einen Montageradius ein.'),
|
||||
'assemblyRadius.min' => __('Der Montageradius muss mindestens 1 km betragen.'),
|
||||
'brandName.required' => __('Bitte geben Sie einen Markennamen ein.'),
|
||||
]);
|
||||
|
||||
// Update Partner
|
||||
$updateData = [
|
||||
'salutation' => $this->salutation,
|
||||
'first_name' => $this->firstName,
|
||||
'last_name' => $this->lastName,
|
||||
'street' => $this->street,
|
||||
'house_number' => $this->houseNumber,
|
||||
'zip' => $this->zip,
|
||||
'city' => $this->city,
|
||||
'country' => $this->country,
|
||||
'phone' => $this->phone,
|
||||
];
|
||||
|
||||
if (!$isCustomer) {
|
||||
$updateData['company_name'] = $this->companyName;
|
||||
$updateData['description'] = $this->description;
|
||||
$updateData['website'] = $this->website;
|
||||
}
|
||||
|
||||
if ($isBroker) {
|
||||
$updateData['display_name'] = $this->displayName;
|
||||
}
|
||||
|
||||
if ($isRetailer) {
|
||||
$updateData['delivery_radius_km'] = $this->deliveryRadius;
|
||||
$updateData['assembly_radius_km'] = $this->assemblyRadius;
|
||||
}
|
||||
|
||||
$this->partner->update($updateData);
|
||||
|
||||
// Für Manufacturer: Marke aktualisieren oder erstellen
|
||||
if ($isManufacturer) {
|
||||
Brand::updateOrCreate(
|
||||
['partner_id' => $this->partner->id],
|
||||
[
|
||||
'name' => $this->brandName,
|
||||
'slug' => Str::slug($this->brandName),
|
||||
'description' => $this->brandDescription,
|
||||
'is_active' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
session()->flash('message', __('Ihre Daten wurden erfolgreich aktualisiert.'));
|
||||
}
|
||||
}; ?>
|
||||
|
||||
<div class="space-y-6">
|
||||
{{-- Header --}}
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<flux:heading size="xl">{{ __('Meine Daten') }}</flux:heading>
|
||||
<flux:subheading>{{ __('Verwalten Sie Ihre Firmendaten und Kontaktinformationen') }}</flux:subheading>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
@svg('heroicon-o-'.$roleIcon, 'w-6 h-6 text-accent-600 dark:text-accent-400')
|
||||
<span class="text-sm font-medium text-zinc-700 dark:text-zinc-300">{{ $roleName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Success Message --}}
|
||||
<x-success-alert />
|
||||
|
||||
{{-- Formular --}}
|
||||
<flux:card>
|
||||
<form wire:submit="saveData" class="space-y-6">
|
||||
@php
|
||||
$isCustomer = strtolower(str_replace('-', '', $partnerType)) === 'customer';
|
||||
$isRetailer = strtolower(str_replace('-', '', $partnerType)) === 'retailer';
|
||||
$isManufacturer = strtolower(str_replace('-', '', $partnerType)) === 'manufacturer';
|
||||
$isBroker = strtolower(str_replace('-', '', $partnerType)) === 'broker' || strtolower(str_replace('-', '', $partnerType)) === 'estateagent';
|
||||
@endphp
|
||||
|
||||
{{-- Firmenname (nur für Nicht-Kunden) --}}
|
||||
@if (!$isCustomer)
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Firmenname') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="companyName" name="companyName" icon="building-office" placeholder="{{ __('z.B. Müller GmbH') }}" />
|
||||
@error('companyName')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
@if ($isBroker)
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Anzeigename') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:description>{{ __('Der Name, der Ihren Kunden angezeigt wird') }}</flux:description>
|
||||
<flux:input wire:model="displayName" name="displayName" icon="user" placeholder="{{ __('z.B. Max Schmidt') }}" />
|
||||
@error('displayName')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
@endif
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Kurzbeschreibung') }} ({{ __('optional') }})</flux:label>
|
||||
<flux:textarea wire:model="description" name="description" rows="3" placeholder="{{ __('Ein kurzer Text über Ihr Unternehmen...') }}" />
|
||||
@error('description')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:separator />
|
||||
@endif
|
||||
|
||||
{{-- Persönliche Daten --}}
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Anrede') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:select wire:model="salutation" name="salutation">
|
||||
<flux:select.option value="">{{ __('Bitte wählen') }}</flux:select.option>
|
||||
<flux:select.option value="Herr">{{ __('Herr') }}</flux:select.option>
|
||||
<flux:select.option value="Frau">{{ __('Frau') }}</flux:select.option>
|
||||
<flux:select.option value="Divers">{{ __('Divers') }}</flux:select.option>
|
||||
</flux:select>
|
||||
@error('salutation')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Vorname') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="firstName" name="firstName" icon="user" />
|
||||
@error('firstName')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Nachname') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="lastName" name="lastName" icon="user" />
|
||||
@error('lastName')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
</div>
|
||||
|
||||
<flux:separator />
|
||||
|
||||
{{-- Adresse --}}
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<flux:field class="md:col-span-3">
|
||||
<flux:label>{{ __('Straße') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="street" name="street" icon="map-pin" placeholder="{{ __('Musterstraße') }}" />
|
||||
@error('street')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Hausnummer') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="houseNumber" name="houseNumber" placeholder="{{ __('123a') }}" />
|
||||
@error('houseNumber')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Postleitzahl') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="zip" name="zip" icon="map" placeholder="{{ __('12345') }}" />
|
||||
@error('zip')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:field class="md:col-span-2">
|
||||
<flux:label>{{ __('Ort') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="city" name="city" icon="building-office" placeholder="{{ __('Musterstadt') }}" />
|
||||
@error('city')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
</div>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Land') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:select wire:model="country" name="country">
|
||||
<flux:select.option value="Deutschland">{{ __('Deutschland') }}</flux:select.option>
|
||||
<flux:select.option value="Österreich">{{ __('Österreich') }}</flux:select.option>
|
||||
<flux:select.option value="Schweiz">{{ __('Schweiz') }}</flux:select.option>
|
||||
</flux:select>
|
||||
@error('country')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Telefon') }}</flux:label>
|
||||
<flux:input wire:model="phone" name="phone" type="tel" icon="phone" placeholder="{{ __('optional') }}" />
|
||||
@error('phone')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
@if (!$isCustomer)
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Website') }}</flux:label>
|
||||
<flux:input wire:model="website" name="website" type="url" icon="globe-alt" placeholder="https://..." />
|
||||
@error('website')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
@endif
|
||||
|
||||
{{-- Liefergebiete für Händler (Retailer) --}}
|
||||
@if ($isRetailer)
|
||||
<flux:separator />
|
||||
|
||||
<div class="bg-blue-50 dark:bg-blue-900/20 p-4 rounded-lg">
|
||||
<p class="text-sm text-blue-800 dark:text-blue-200 font-medium mb-1">
|
||||
{{ __('Liefergebiete') }}
|
||||
</p>
|
||||
<p class="text-xs text-blue-600 dark:text-blue-300">
|
||||
{{ __('Definieren Sie, in welchem Umkreis Sie liefern und montieren können.') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Lieferradius (km)') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:description>{{ __('Ich liefere im Umkreis von ... km') }}</flux:description>
|
||||
<flux:input wire:model="deliveryRadius" name="deliveryRadius" type="number" min="1" max="500" placeholder="z.B. 50" />
|
||||
@error('deliveryRadius')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Montageradius (km)') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:description>{{ __('Ich montiere im Umkreis von ... km') }}</flux:description>
|
||||
<flux:input wire:model="assemblyRadius" name="assemblyRadius" type="number" min="1" max="500" placeholder="z.B. 30" />
|
||||
@error('assemblyRadius')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Marke für Hersteller (Manufacturer) --}}
|
||||
@if ($isManufacturer)
|
||||
<flux:separator />
|
||||
|
||||
<div class="bg-purple-50 dark:bg-purple-900/20 p-4 rounded-lg">
|
||||
<p class="text-sm text-purple-800 dark:text-purple-200 font-medium mb-1">
|
||||
{{ __('Ihre Marke') }}
|
||||
</p>
|
||||
<p class="text-xs text-purple-600 dark:text-purple-300">
|
||||
{{ __('Unter dieser Marke werden Ihre Produkte auf B2In gelistet.') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Markenname') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="brandName" name="brandName" icon="tag" placeholder="{{ __('z.B. Möbelwerke Premium') }}" />
|
||||
@error('brandName')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Marken-Beschreibung') }} ({{ __('optional') }})</flux:label>
|
||||
<flux:textarea wire:model="brandDescription" name="brandDescription" rows="4" placeholder="{{ __('Ein kurzer Text über Ihre Marke...') }}" />
|
||||
@error('brandDescription')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
@endif
|
||||
|
||||
<flux:separator />
|
||||
|
||||
<x-error-alert />
|
||||
|
||||
<div class="flex justify-end">
|
||||
<flux:button type="submit" variant="primary" icon="check-circle">
|
||||
{{ __('Änderungen speichern') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
</flux:card>
|
||||
</div>
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
use App\Models\Partner;
|
||||
use App\Models\Brand;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use App\Models\RegistrationCode;
|
||||
use Illuminate\Support\Str;
|
||||
use Livewire\Attributes\Layout;
|
||||
use Livewire\Attributes\Title;
|
||||
|
|
@ -20,12 +21,19 @@ new #[Layout('components.layouts.guest'), Title('Setup-Wizard')] class extends C
|
|||
|
||||
// Schritt 1: Stammdaten (alle Rollen)
|
||||
public string $companyName = '';
|
||||
public $logo = null;
|
||||
public string $displayName = '';
|
||||
public string $salutation = '';
|
||||
public string $firstName = '';
|
||||
public string $lastName = '';
|
||||
public string $description = '';
|
||||
public string $street = '';
|
||||
public string $houseNumber = '';
|
||||
public string $zip = '';
|
||||
public string $city = '';
|
||||
public string $country = 'Deutschland';
|
||||
public string $phone = '';
|
||||
public string $website = '';
|
||||
public $logo = null;
|
||||
|
||||
// Schritt 2: Retailer - Liefergebiete
|
||||
public ?int $deliveryRadius = null;
|
||||
|
|
@ -38,6 +46,7 @@ new #[Layout('components.layouts.guest'), Title('Setup-Wizard')] class extends C
|
|||
|
||||
public string $roleIcon = 'shield-check';
|
||||
public string $roleName = '-';
|
||||
protected ?RegistrationCode $registrationCode = null;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
|
|
@ -55,79 +64,162 @@ new #[Layout('components.layouts.guest'), Title('Setup-Wizard')] class extends C
|
|||
}
|
||||
|
||||
$this->partner = Partner::with('users')->findOrFail($user->partner_id);
|
||||
|
||||
// Wenn Setup bereits abgeschlossen, direkt zum Dashboard
|
||||
if ($this->partner->setup_completed) {
|
||||
$this->redirect(route('dashboard'), navigate: true);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->partnerType = $this->partner->type;
|
||||
|
||||
// Vorausfüllen
|
||||
$this->companyName = $this->partner->company_name;
|
||||
// Vorausfüllen: Partner-Daten
|
||||
$this->companyName = $this->partner->company_name ?? '';
|
||||
$this->displayName = $this->partner->display_name ?? '';
|
||||
$this->salutation = $this->partner->salutation ?? '';
|
||||
$this->firstName = $this->partner->first_name ?? '';
|
||||
$this->lastName = $this->partner->last_name ?? '';
|
||||
$this->description = $this->partner->description ?? '';
|
||||
$this->website = '';
|
||||
$this->street = $this->partner->street ?? '';
|
||||
$this->houseNumber = $this->partner->house_number ?? '';
|
||||
$this->zip = $this->partner->zip ?? '';
|
||||
$this->city = $this->partner->city ?? '';
|
||||
$this->country = $this->partner->country ?? 'Deutschland';
|
||||
$this->phone = $this->partner->phone ?? '';
|
||||
$this->website = $this->partner->website ?? '';
|
||||
$this->deliveryRadius = $this->partner->delivery_radius_km;
|
||||
$this->assemblyRadius = $this->partner->assembly_radius_km;
|
||||
|
||||
// Ersetze Platzhalter wie "roles.broker M10000004" durch Übersetzung + ID
|
||||
if(strpos($this->companyName, 'roles.') !== false) {
|
||||
$parts = explode(' ', $this->companyName, 2);
|
||||
$translatedRole = isset($parts[0]) ? __($parts[0]) : $this->companyName;
|
||||
$partnerId = isset($parts[1]) ? ' ' . $parts[1] : '';
|
||||
$this->companyName = $translatedRole . $partnerId;
|
||||
}
|
||||
|
||||
// Namen aus User übernehmen, falls Partner-Felder leer sind
|
||||
if (empty($this->firstName) && empty($this->lastName)) {
|
||||
$nameParts = explode(' ', $user->name, 2);
|
||||
$this->firstName = $nameParts[0] ?? '';
|
||||
$this->lastName = $nameParts[1] ?? '';
|
||||
}
|
||||
// Definiere Schritte basierend auf Rolle
|
||||
$this->defineSteps();
|
||||
}
|
||||
|
||||
protected function defineSteps(): void
|
||||
{
|
||||
switch ($this->partnerType) {
|
||||
case 'Retailer':
|
||||
$this->steps = ['Stammdaten', 'Liefergebiete', 'Fertig'];
|
||||
$this->totalSteps = 3;
|
||||
break;
|
||||
case 'Manufacturer':
|
||||
$this->steps = ['Stammdaten', 'Marke anlegen', 'Fertig'];
|
||||
$this->totalSteps = 3;
|
||||
break;
|
||||
case 'Estate-Agent':
|
||||
$this->steps = ['Profil', 'Fertig'];
|
||||
$this->totalSteps = 2;
|
||||
break;
|
||||
default:
|
||||
$this->steps = ['Stammdaten', 'Fertig'];
|
||||
$this->totalSteps = 2;
|
||||
break;
|
||||
}
|
||||
// Alle Rollen haben nur noch 2 Schritte: Stammdaten + Fertig
|
||||
$this->steps = ['Stammdaten', 'Fertig'];
|
||||
$this->totalSteps = 2;
|
||||
}
|
||||
|
||||
public function saveStep1(): void
|
||||
{
|
||||
$this->validate(
|
||||
[
|
||||
'companyName' => 'required|string|max:255',
|
||||
'description' => 'nullable|string|max:1000',
|
||||
'street' => 'required|string|max:255',
|
||||
'zip' => 'required|string|max:10',
|
||||
'city' => 'required|string|max:255',
|
||||
'website' => 'nullable|url|max:255',
|
||||
'logo' => 'nullable|image|mimes:jpeg,png,jpg,webp|max:2048',
|
||||
],
|
||||
[
|
||||
'companyName.required' => __('Bitte geben Sie einen Firmennamen ein.'),
|
||||
'street.required' => __('Bitte geben Sie eine Straße ein.'),
|
||||
'zip.required' => __('Bitte geben Sie eine Postleitzahl ein.'),
|
||||
'city.required' => __('Bitte geben Sie eine Stadt ein.'),
|
||||
'website.url' => __('Bitte geben Sie eine gültige URL ein.'),
|
||||
'logo.image' => __('Das Logo muss eine Bilddatei sein.'),
|
||||
'logo.mimes' => __('Das Logo muss im Format JPG, PNG oder WebP sein.'),
|
||||
'logo.max' => __('Das Logo darf maximal 2 MB groß sein.'),
|
||||
],
|
||||
);
|
||||
$normalizedType = strtolower(str_replace('-', '', $this->partnerType));
|
||||
$isCustomer = $normalizedType === 'customer';
|
||||
$isBroker = $normalizedType === 'broker' || $normalizedType === 'estateagent';
|
||||
$isRetailer = $normalizedType === 'retailer';
|
||||
$isManufacturer = $normalizedType === 'manufacturer';
|
||||
|
||||
// Speichere Logo falls hochgeladen
|
||||
$logoPath = null;
|
||||
if ($this->logo) {
|
||||
$logoPath = $this->logo->store('partner-logos', 'public');
|
||||
$rules = [
|
||||
'salutation' => 'required|in:Herr,Frau,Divers',
|
||||
'firstName' => 'required|string|max:255',
|
||||
'lastName' => 'required|string|max:255',
|
||||
'street' => 'required|string|max:255',
|
||||
'houseNumber' => 'required|string|max:20',
|
||||
'zip' => 'required|string|max:10',
|
||||
'city' => 'required|string|max:255',
|
||||
'country' => 'required|string|max:255',
|
||||
'phone' => 'nullable|string|max:50',
|
||||
];
|
||||
|
||||
if (!$isCustomer) {
|
||||
$rules['companyName'] = 'required|string|max:255';
|
||||
$rules['description'] = 'nullable|string|max:1000';
|
||||
$rules['website'] = 'nullable|url|max:255';
|
||||
}
|
||||
|
||||
// Update Partner
|
||||
$this->partner->update([
|
||||
'company_name' => $this->companyName,
|
||||
'description' => $this->description,
|
||||
'logo_url' => $logoPath ?? $this->partner->logo_url,
|
||||
// Für Broker ist displayName Pflicht
|
||||
if ($isBroker) {
|
||||
$rules['displayName'] = 'required|string|max:255';
|
||||
}
|
||||
|
||||
// Für Retailer sind Liefergebiete Pflicht
|
||||
if ($isRetailer) {
|
||||
$rules['deliveryRadius'] = 'required|integer|min:1|max:500';
|
||||
$rules['assemblyRadius'] = 'required|integer|min:1|max:500';
|
||||
}
|
||||
|
||||
// Für Manufacturer ist Markenname Pflicht
|
||||
if ($isManufacturer) {
|
||||
$rules['brandName'] = 'required|string|max:255';
|
||||
$rules['brandDescription'] = 'nullable|string|max:1000';
|
||||
}
|
||||
|
||||
$this->validate($rules, [
|
||||
'salutation.required' => __('Bitte wählen Sie eine Anrede.'),
|
||||
'firstName.required' => __('Bitte geben Sie einen Vornamen ein.'),
|
||||
'lastName.required' => __('Bitte geben Sie einen Nachnamen ein.'),
|
||||
'companyName.required' => __('Bitte geben Sie einen Firmennamen ein.'),
|
||||
'displayName.required' => __('Bitte geben Sie einen Anzeigenamen ein.'),
|
||||
'street.required' => __('Bitte geben Sie eine Straße ein.'),
|
||||
'houseNumber.required' => __('Bitte geben Sie eine Hausnummer ein.'),
|
||||
'zip.required' => __('Bitte geben Sie eine Postleitzahl ein.'),
|
||||
'city.required' => __('Bitte geben Sie eine Stadt ein.'),
|
||||
'country.required' => __('Bitte wählen Sie ein Land.'),
|
||||
'website.url' => __('Bitte geben Sie eine gültige URL ein.'),
|
||||
'deliveryRadius.required' => __('Bitte geben Sie einen Lieferradius ein.'),
|
||||
'deliveryRadius.min' => __('Der Lieferradius muss mindestens 1 km betragen.'),
|
||||
'assemblyRadius.required' => __('Bitte geben Sie einen Montageradius ein.'),
|
||||
'assemblyRadius.min' => __('Der Montageradius muss mindestens 1 km betragen.'),
|
||||
'brandName.required' => __('Bitte geben Sie einen Markennamen ein.'),
|
||||
]);
|
||||
|
||||
// TODO: Adresse speichern (separates Address-Model oder JSON-Feld)
|
||||
// Update Partner
|
||||
$updateData = [
|
||||
'salutation' => $this->salutation,
|
||||
'first_name' => $this->firstName,
|
||||
'last_name' => $this->lastName,
|
||||
'street' => $this->street,
|
||||
'house_number' => $this->houseNumber,
|
||||
'zip' => $this->zip,
|
||||
'city' => $this->city,
|
||||
'country' => $this->country,
|
||||
'phone' => $this->phone,
|
||||
];
|
||||
|
||||
$this->currentStep = 2;
|
||||
if (!$isCustomer) {
|
||||
$updateData['company_name'] = $this->companyName;
|
||||
$updateData['description'] = $this->description;
|
||||
$updateData['website'] = $this->website;
|
||||
}
|
||||
|
||||
if ($isBroker) {
|
||||
$updateData['display_name'] = $this->displayName;
|
||||
}
|
||||
|
||||
if ($isRetailer) {
|
||||
$updateData['delivery_radius_km'] = $this->deliveryRadius;
|
||||
$updateData['assembly_radius_km'] = $this->assemblyRadius;
|
||||
}
|
||||
|
||||
$this->partner->update($updateData);
|
||||
|
||||
// Für Manufacturer: Marke erstellen
|
||||
if ($isManufacturer) {
|
||||
Brand::create([
|
||||
'partner_id' => $this->partner->id,
|
||||
'name' => $this->brandName,
|
||||
'slug' => Str::slug($this->brandName),
|
||||
'description' => $this->brandDescription,
|
||||
'is_active' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
// Alle Rollen: Setup abschließen
|
||||
$this->completeSetup();
|
||||
}
|
||||
|
||||
public function saveStep2Retailer(): void
|
||||
|
|
@ -188,6 +280,7 @@ new #[Layout('components.layouts.guest'), Title('Setup-Wizard')] class extends C
|
|||
$this->completeSetup();
|
||||
}
|
||||
|
||||
|
||||
protected function completeSetup(): void
|
||||
{
|
||||
$this->partner->update([
|
||||
|
|
@ -196,6 +289,12 @@ new #[Layout('components.layouts.guest'), Title('Setup-Wizard')] class extends C
|
|||
'setup_completed_at' => now(),
|
||||
]);
|
||||
|
||||
// Markiere Registrierungscode als verbraucht, falls vorhanden
|
||||
if ($this->registrationCode && $this->registrationCode->isAvailable()) {
|
||||
$this->registrationCode->markUsed(Auth::user());
|
||||
session()->forget(['registration_code_id', 'registration_role']);
|
||||
}
|
||||
|
||||
$this->currentStep = $this->totalSteps;
|
||||
}
|
||||
|
||||
|
|
@ -204,14 +303,35 @@ new #[Layout('components.layouts.guest'), Title('Setup-Wizard')] class extends C
|
|||
$this->redirect(route('dashboard'), navigate: true);
|
||||
}
|
||||
|
||||
public function logout(): void
|
||||
public function handleLogout(): void
|
||||
{
|
||||
Auth::logout();
|
||||
|
||||
request()->session()->invalidate();
|
||||
request()->session()->regenerateToken();
|
||||
Auth::guard('web')->logout();
|
||||
|
||||
$this->redirect(route('home'), navigate: true);
|
||||
session()->invalidate();
|
||||
session()->regenerateToken();
|
||||
|
||||
$this->redirect(route('home'), navigate: false);
|
||||
}
|
||||
|
||||
protected function loadRegistrationCode(): void
|
||||
{
|
||||
$codeId = session('registration_code_id');
|
||||
$role = session('registration_role');
|
||||
|
||||
if (!$codeId || !$role) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->registrationCode = RegistrationCode::where('id', $codeId)
|
||||
->where('role', $role)
|
||||
->first();
|
||||
|
||||
if (!$this->registrationCode || !$this->registrationCode->isAvailable()) {
|
||||
// Ungültig/verbraucht -> Session aufräumen
|
||||
session()->forget(['registration_code_id', 'registration_role']);
|
||||
$this->registrationCode = null;
|
||||
}
|
||||
}
|
||||
}; ?>
|
||||
|
||||
|
|
@ -222,6 +342,7 @@ new #[Layout('components.layouts.guest'), Title('Setup-Wizard')] class extends C
|
|||
<h1 class="text-3xl font-bold text-zinc-900 dark:text-white mb-2">
|
||||
{{ __('Vervollständigen Sie Ihr Profil') }}
|
||||
</h1>
|
||||
|
||||
</div>
|
||||
|
||||
{{-- Progress Indicator --}}
|
||||
|
|
@ -231,20 +352,15 @@ new #[Layout('components.layouts.guest'), Title('Setup-Wizard')] class extends C
|
|||
<flux:card class="shadow-2xl">
|
||||
{{-- Step 1: Stammdaten (für alle Rollen) --}}
|
||||
@if ($currentStep === 1)
|
||||
<form wire:submit="saveStep1" class="space-y-6">
|
||||
@php
|
||||
$isCustomer = strtolower(str_replace('-', '', $partnerType)) === 'customer';
|
||||
@endphp
|
||||
<form wire:submit.prevent="saveStep1" class="space-y-6">
|
||||
<div>
|
||||
<flux:heading size="lg" class="mb-2">
|
||||
<div class="flex items-center gap-2">
|
||||
@svg('heroicon-o-'.$roleIcon, 'w-5 h-5')
|
||||
|
||||
@if ($partnerType === 'Retailer')
|
||||
{{ __('Ihre Stammdaten') }}
|
||||
@elseif($partnerType === 'Manufacturer')
|
||||
{{ __('Ihre Stammdaten') }}
|
||||
@else
|
||||
{{ __('Ihr Profil') }}
|
||||
@endif
|
||||
/ {{ $roleName }}
|
||||
{{ __('Ihre Stammdaten') }} / {{ $roleName }}
|
||||
</div>
|
||||
</flux:heading>
|
||||
<flux:subheading>
|
||||
|
|
@ -254,117 +370,221 @@ new #[Layout('components.layouts.guest'), Title('Setup-Wizard')] class extends C
|
|||
|
||||
<flux:separator />
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Firmenname') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="companyName" icon="building-office" />
|
||||
@error('companyName')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
{{-- Firmenname (nur für Nicht-Kunden) --}}
|
||||
@if (!$isCustomer)
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Logo (optional)') }}</flux:label>
|
||||
<flux:description>{{ __('Laden Sie Ihr Firmenlogo hoch (max. 2 MB, JPG/PNG)') }}</flux:description>
|
||||
|
||||
<div class="space-y-2">
|
||||
<input type="file" wire:model.live="logo" accept="image/jpeg,image/png,image/jpg,image/webp"
|
||||
class="block w-full text-sm text-zinc-500 file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold file:bg-accent-50 file:text-accent-700 hover:file:bg-accent-100 dark:text-zinc-400 dark:file:bg-accent-900/20 dark:file:text-accent-400" />
|
||||
|
||||
@error('logo')
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Firmenname') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="companyName" name="companyName" icon="building-office" placeholder="{{ __('z.B. Müller GmbH') }}" />
|
||||
@error('companyName')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<div wire:loading wire:target="logo" class="text-sm text-accent-600 dark:text-accent-400">
|
||||
<flux:icon.arrow-path class="inline-block h-4 w-4 animate-spin mr-2" />
|
||||
{{ __('Wird hochgeladen...') }}
|
||||
</div>
|
||||
@php
|
||||
$isBroker = strtolower(str_replace('-', '', $partnerType)) === 'broker' || strtolower(str_replace('-', '', $partnerType)) === 'estateagent';
|
||||
@endphp
|
||||
|
||||
@if ($logo)
|
||||
<div wire:loading.remove wire:target="logo">
|
||||
@try
|
||||
<div class="p-2 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg">
|
||||
<div class="flex items-center gap-3">
|
||||
<img src="{{ $logo->temporaryUrl() }}" class="h-16 w-16 object-contain rounded border"
|
||||
alt="Logo Preview">
|
||||
<div class="flex-1">
|
||||
<p class="text-sm font-medium text-green-800 dark:text-green-200">{{ __('Logo erfolgreich hochgeladen') }}</p>
|
||||
<p class="text-xs text-green-600 dark:text-green-400">{{ $logo->getClientOriginalName() }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@catch(\Exception $e)
|
||||
<div class="p-2 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg">
|
||||
<p class="text-sm text-red-800 dark:text-red-200">{{ __('Fehler beim Laden der Vorschau') }}</p>
|
||||
</div>
|
||||
@endtry
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</flux:field>
|
||||
@if ($isBroker)
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Anzeigename') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:description>{{ __('Der Name, der Ihren Kunden angezeigt wird (kann vom Firmennamen abweichen)') }}</flux:description>
|
||||
<flux:input wire:model="displayName" name="displayName" icon="user" placeholder="{{ __('z.B. Max Schmidt oder Immobilien Schmidt') }}" />
|
||||
@error('displayName')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
@endif
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Kurzbeschreibung') }}</flux:label>
|
||||
<flux:textarea wire:model="description" rows="4"
|
||||
placeholder="{{ __('Ein kurzer Text über Ihr Unternehmen...') }}" />
|
||||
@error('description')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Kurzbeschreibung') }} ({{ __('optional') }})</flux:label>
|
||||
<flux:textarea wire:model="description" name="description" rows="3" placeholder="{{ __('Ein kurzer Text über Ihr Unternehmen...') }}" />
|
||||
@error('description')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:separator />
|
||||
@endif
|
||||
|
||||
{{-- Persönliche Daten --}}
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Anrede') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:select wire:model="salutation" name="salutation">
|
||||
<flux:select.option value="">{{ __('Bitte wählen') }}</flux:select.option>
|
||||
<flux:select.option value="Herr">{{ __('Herr') }}</flux:select.option>
|
||||
<flux:select.option value="Frau">{{ __('Frau') }}</flux:select.option>
|
||||
<flux:select.option value="Divers">{{ __('Divers') }}</flux:select.option>
|
||||
</flux:select>
|
||||
@error('salutation')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Vorname') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="firstName" name="firstName" icon="user" />
|
||||
@error('firstName')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Nachname') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="lastName" name="lastName" icon="user" />
|
||||
@error('lastName')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
</div>
|
||||
|
||||
<flux:separator />
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<flux:field class="md:col-span-2">
|
||||
{{-- Adresse --}}
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<flux:field class="md:col-span-3">
|
||||
<flux:label>{{ __('Straße') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="street" wire:/>
|
||||
<flux:input wire:model="street" name="street" icon="map-pin" placeholder="{{ __('Musterstraße') }}" />
|
||||
@error('street')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('PLZ') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="zip" />
|
||||
<flux:label>{{ __('Hausnummer') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="houseNumber" name="houseNumber" placeholder="{{ __('123a') }}" />
|
||||
@error('houseNumber')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Postleitzahl') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="zip" name="zip" icon="map" placeholder="{{ __('12345') }}" />
|
||||
@error('zip')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:field class="md:col-span-2">
|
||||
<flux:label>{{ __('Ort') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="city" name="city" icon="building-office" placeholder="{{ __('Musterstadt') }}" />
|
||||
@error('city')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
</div>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Stadt') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="city" icon="map-pin" />
|
||||
@error('city')
|
||||
<flux:label>{{ __('Land') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:select wire:model="country" name="country">
|
||||
<flux:select.option value="Deutschland">{{ __('Deutschland') }}</flux:select.option>
|
||||
<flux:select.option value="Österreich">{{ __('Österreich') }}</flux:select.option>
|
||||
<flux:select.option value="Schweiz">{{ __('Schweiz') }}</flux:select.option>
|
||||
</flux:select>
|
||||
@error('country')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Website (optional)') }}</flux:label>
|
||||
<flux:input wire:model="website" type="url" icon="globe-alt" placeholder="https://..." />
|
||||
@error('website')
|
||||
<flux:label>{{ __('Telefon') }}</flux:label>
|
||||
<flux:input wire:model="phone" name="phone" type="tel" icon="phone" placeholder="{{ __('optional') }}" />
|
||||
@error('phone')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
@if (!$isCustomer)
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Website') }}</flux:label>
|
||||
<flux:input wire:model="website" name="website" type="url" icon="globe-alt" placeholder="https://..." />
|
||||
@error('website')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
@endif
|
||||
|
||||
{{-- Liefergebiete für Händler (Retailer) --}}
|
||||
@php
|
||||
$isRetailer = strtolower(str_replace('-', '', $partnerType)) === 'retailer';
|
||||
@endphp
|
||||
@if ($isRetailer)
|
||||
<flux:separator />
|
||||
|
||||
<div class="bg-blue-50 dark:bg-blue-900/20 p-4 rounded-lg">
|
||||
<p class="text-sm text-blue-800 dark:text-blue-200 font-medium mb-1">
|
||||
{{ __('Liefergebiete') }}
|
||||
</p>
|
||||
<p class="text-xs text-blue-600 dark:text-blue-300">
|
||||
{{ __('Definieren Sie, in welchem Umkreis Sie liefern und montieren können.') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Lieferradius (km)') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:description>{{ __('Ich liefere im Umkreis von ... km') }}</flux:description>
|
||||
<flux:input wire:model="deliveryRadius" name="deliveryRadius" type="number" min="1" max="500" placeholder="z.B. 50" />
|
||||
@error('deliveryRadius')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Montageradius (km)') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:description>{{ __('Ich montiere im Umkreis von ... km') }}</flux:description>
|
||||
<flux:input wire:model="assemblyRadius" name="assemblyRadius" type="number" min="1" max="500" placeholder="z.B. 30" />
|
||||
@error('assemblyRadius')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Marke für Hersteller (Manufacturer) --}}
|
||||
@php
|
||||
$isManufacturer = strtolower(str_replace('-', '', $partnerType)) === 'manufacturer';
|
||||
@endphp
|
||||
@if ($isManufacturer)
|
||||
<flux:separator />
|
||||
|
||||
<div class="bg-purple-50 dark:bg-purple-900/20 p-4 rounded-lg">
|
||||
<p class="text-sm text-purple-800 dark:text-purple-200 font-medium mb-1">
|
||||
{{ __('Ihre Marke') }}
|
||||
</p>
|
||||
<p class="text-xs text-purple-600 dark:text-purple-300">
|
||||
{{ __('Unter dieser Marke werden Ihre Produkte auf B2In gelistet. Sie können später weitere Marken hinzufügen.') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Markenname') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="brandName" name="brandName" icon="tag" placeholder="{{ __('z.B. Möbelwerke Premium') }}" />
|
||||
@error('brandName')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Marken-Beschreibung') }} ({{ __('optional') }})</flux:label>
|
||||
<flux:textarea wire:model="brandDescription" name="brandDescription" rows="4" placeholder="{{ __('Ein kurzer Text über Ihre Marke...') }}" />
|
||||
@error('brandDescription')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
@endif
|
||||
|
||||
<flux:separator />
|
||||
|
||||
<x-error-alert />
|
||||
|
||||
<div class="flex justify-between">
|
||||
|
||||
<flux:button type="button" variant="outline" icon="arrow-left-start-on-rectangle"
|
||||
wire:click="logout">
|
||||
{{ __('Logout') }}
|
||||
</flux:button>
|
||||
<flux:button type="submit" variant="primary" icon="arrow-right">
|
||||
@if ($partnerType === 'Retailer')
|
||||
{{ __('Weiter zu Liefergebiete') }}
|
||||
@elseif($partnerType === 'Manufacturer')
|
||||
{{ __('Weiter zu Marke') }}
|
||||
@else
|
||||
{{ __('Setup abschließen') }}
|
||||
@endif
|
||||
<div class="flex justify-end">
|
||||
<flux:button type="submit" variant="primary" icon="check-circle" class="cursor-pointer" wire:target="saveStep1">
|
||||
{{ __('Setup abschließen') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -372,7 +592,7 @@ new #[Layout('components.layouts.guest'), Title('Setup-Wizard')] class extends C
|
|||
|
||||
{{-- Step 2: Retailer - Liefergebiete --}}
|
||||
@if ($currentStep === 2 && $partnerType === 'Retailer')
|
||||
<form wire:submit="saveStep2Retailer" class="space-y-6">
|
||||
<form wire:submit.prevent="saveStep2Retailer" class="space-y-6">
|
||||
<div>
|
||||
<flux:heading size="lg" class="mb-2">
|
||||
🚚 {{ __('Ihre Liefergebiete') }}
|
||||
|
|
@ -388,7 +608,7 @@ new #[Layout('components.layouts.guest'), Title('Setup-Wizard')] class extends C
|
|||
<flux:label>{{ __('Lieferradius') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:description>{{ __('Ich liefere im Umkreis von ... km') }}</flux:description>
|
||||
<div class="flex items-center gap-4">
|
||||
<flux:input wire:model="deliveryRadius" type="number" min="1" max="500"
|
||||
<flux:input wire:model="deliveryRadius" name="deliveryRadius" type="number" min="1" max="500"
|
||||
class="flex-1" />
|
||||
<span class="text-sm text-zinc-600 dark:text-zinc-400">km</span>
|
||||
</div>
|
||||
|
|
@ -401,7 +621,7 @@ new #[Layout('components.layouts.guest'), Title('Setup-Wizard')] class extends C
|
|||
<flux:label>{{ __('Montageradius') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:description>{{ __('Ich montiere im Umkreis von ... km') }}</flux:description>
|
||||
<div class="flex items-center gap-4">
|
||||
<flux:input wire:model="assemblyRadius" type="number" min="1" max="500"
|
||||
<flux:input wire:model="assemblyRadius" name="assemblyRadius" type="number" min="1" max="500"
|
||||
class="flex-1" />
|
||||
<span class="text-sm text-zinc-600 dark:text-zinc-400">km</span>
|
||||
</div>
|
||||
|
|
@ -415,7 +635,7 @@ new #[Layout('components.layouts.guest'), Title('Setup-Wizard')] class extends C
|
|||
<x-error-alert />
|
||||
|
||||
<div class="flex justify-end">
|
||||
<flux:button type="submit" variant="primary" icon="check">
|
||||
<flux:button type="submit" variant="primary" icon="check-circle">
|
||||
{{ __('Setup abschließen') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
|
|
@ -424,7 +644,7 @@ new #[Layout('components.layouts.guest'), Title('Setup-Wizard')] class extends C
|
|||
|
||||
{{-- Step 2: Manufacturer - Marke anlegen --}}
|
||||
@if ($currentStep === 2 && $partnerType === 'Manufacturer')
|
||||
<form wire:submit="saveStep2Manufacturer" class="space-y-6">
|
||||
<form wire:submit.prevent="saveStep2Manufacturer" class="space-y-6">
|
||||
<div>
|
||||
<flux:heading size="lg" class="mb-2">
|
||||
™️ {{ __('Ihre Marke') }}
|
||||
|
|
@ -438,7 +658,7 @@ new #[Layout('components.layouts.guest'), Title('Setup-Wizard')] class extends C
|
|||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Markenname') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="brandName" icon="tag"
|
||||
<flux:input wire:model="brandName" name="brandName" icon="tag"
|
||||
placeholder="{{ __('z.B. Möbelwerke Premium') }}" />
|
||||
@error('brandName')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
|
|
@ -450,7 +670,7 @@ new #[Layout('components.layouts.guest'), Title('Setup-Wizard')] class extends C
|
|||
<flux:description>{{ __('Laden Sie Ihr Marken-Logo hoch (max. 2 MB, JPG/PNG)') }}</flux:description>
|
||||
|
||||
<div class="space-y-2">
|
||||
<input type="file" wire:model.live="brandLogo" accept="image/jpeg,image/png,image/jpg,image/webp"
|
||||
<input type="file" wire:model.live="brandLogo" name="brandLogo" accept="image/jpeg,image/png,image/jpg,image/webp"
|
||||
class="block w-full text-sm text-zinc-500 file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold file:bg-accent-50 file:text-accent-700 hover:file:bg-accent-100 dark:text-zinc-400 dark:file:bg-accent-900/20 dark:file:text-accent-400" />
|
||||
|
||||
@error('brandLogo')
|
||||
|
|
@ -487,7 +707,7 @@ new #[Layout('components.layouts.guest'), Title('Setup-Wizard')] class extends C
|
|||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Marken-Beschreibung') }}</flux:label>
|
||||
<flux:textarea wire:model="brandDescription" rows="4"
|
||||
<flux:textarea wire:model="brandDescription" name="brandDescription" rows="4"
|
||||
placeholder="{{ __('Ein kurzer Text über Ihre Marke...') }}" />
|
||||
@error('brandDescription')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
|
|
@ -499,7 +719,7 @@ new #[Layout('components.layouts.guest'), Title('Setup-Wizard')] class extends C
|
|||
<x-error-alert />
|
||||
|
||||
<div class="flex justify-end">
|
||||
<flux:button type="submit" variant="primary" icon="check">
|
||||
<flux:button type="submit" variant="primary" icon="check-circle">
|
||||
{{ __('Setup abschließen') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
|
|
@ -508,17 +728,52 @@ new #[Layout('components.layouts.guest'), Title('Setup-Wizard')] class extends C
|
|||
|
||||
{{-- Final Step: Fertig! --}}
|
||||
@if ($currentStep === $totalSteps)
|
||||
<div class="text-center space-y-6 py-8">
|
||||
<div class="text-center space-y-6 py-8" id="success-step"
|
||||
x-data
|
||||
x-init="
|
||||
setTimeout(function() {
|
||||
let script = document.createElement('script');
|
||||
script.src = 'https://cdn.jsdelivr.net/npm/canvas-confetti@1.6.0/dist/confetti.browser.min.js';
|
||||
script.onload = function() {
|
||||
|
||||
let duration = 3 * 1000;
|
||||
let end = Date.now() + duration;
|
||||
|
||||
(function frame() {
|
||||
confetti({
|
||||
particleCount: 5,
|
||||
angle: 60,
|
||||
spread: 55,
|
||||
origin: { x: 0 },
|
||||
colors: ['#10b981', '#3b82f6', '#f59e0b', '#ef4444', '#8b5cf6']
|
||||
});
|
||||
confetti({
|
||||
particleCount: 5,
|
||||
angle: 120,
|
||||
spread: 55,
|
||||
origin: { x: 1 },
|
||||
colors: ['#10b981', '#3b82f6', '#f59e0b', '#ef4444', '#8b5cf6']
|
||||
});
|
||||
|
||||
if (Date.now() < end) {
|
||||
requestAnimationFrame(frame);
|
||||
}
|
||||
}());
|
||||
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
}, 200);
|
||||
">
|
||||
<div class="flex justify-center">
|
||||
<div
|
||||
class="flex items-center justify-center w-20 h-20 rounded-full bg-green-100 dark:bg-green-900/20">
|
||||
class="flex items-center justify-center w-20 h-20 rounded-full bg-green-100 dark:bg-green-900/20 animate-bounce">
|
||||
<flux:icon.check-circle class="h-12 w-12 text-green-600 dark:text-green-400" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<flux:heading size="xl" class="mb-2">
|
||||
✅ {{ __('Einrichtung abgeschlossen!') }}
|
||||
✅ {{ __('Einrichtung abgeschlossen!') }}1
|
||||
</flux:heading>
|
||||
<flux:subheading>
|
||||
@if ($partnerType === 'Retailer')
|
||||
|
|
@ -534,16 +789,27 @@ new #[Layout('components.layouts.guest'), Title('Setup-Wizard')] class extends C
|
|||
<flux:separator />
|
||||
|
||||
<div class="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
@if ($partnerType !== 'Estate-Agent')
|
||||
<flux:button variant="primary" icon="plus" size="lg">
|
||||
{{ __('Erstes Produkt anlegen') }}
|
||||
</flux:button>
|
||||
@endif
|
||||
<flux:button wire:click="goToDashboard" variant="outline" icon="home" size="lg">
|
||||
|
||||
<button type="button" wire:click="goToDashboard"
|
||||
class="inline-flex items-center justify-center gap-2 px-6 py-3 text-base font-semibold text-white bg-accent-600 hover:bg-accent-700 dark:bg-accent-500 dark:hover:bg-accent-600 rounded-lg transition-colors">
|
||||
@svg('heroicon-o-home', 'w-5 h-5')
|
||||
{{ __('Zum Dashboard') }}
|
||||
</flux:button>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endif
|
||||
</flux:card>
|
||||
|
||||
<div class="mt-4 text-center">
|
||||
<form method="POST" action="{{ route('auth.logout') }}" class="inline">
|
||||
@csrf
|
||||
<button type="submit"
|
||||
class="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-zinc-700 dark:text-zinc-300 hover:text-zinc-900 dark:hover:text-white transition-colors cursor-pointer rounded-lg">
|
||||
@svg('heroicon-o-arrow-left-start-on-rectangle', 'w-5 h-5')
|
||||
{{ __('Logout') }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
719
resources/views/livewire/partner/setup-wizard_bak.blade.php
Normal file
719
resources/views/livewire/partner/setup-wizard_bak.blade.php
Normal file
|
|
@ -0,0 +1,719 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Partner;
|
||||
use App\Models\Brand;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use App\Models\RegistrationCode;
|
||||
use Illuminate\Support\Str;
|
||||
use Livewire\Attributes\Layout;
|
||||
use Livewire\Attributes\Title;
|
||||
use Livewire\WithFileUploads;
|
||||
use Livewire\Volt\Component;
|
||||
|
||||
new #[Layout('components.layouts.guest'), Title('Setup-Wizard')] class extends Component {
|
||||
use WithFileUploads;
|
||||
|
||||
public Partner $partner;
|
||||
public string $partnerType;
|
||||
public int $currentStep = 1;
|
||||
public int $totalSteps;
|
||||
public array $steps = [];
|
||||
|
||||
// Schritt 1: Stammdaten (alle Rollen)
|
||||
public string $companyName = '';
|
||||
public string $displayName = '';
|
||||
public string $salutation = '';
|
||||
public string $firstName = '';
|
||||
public string $lastName = '';
|
||||
public string $description = '';
|
||||
public string $street = '';
|
||||
public string $houseNumber = '';
|
||||
public string $zip = '';
|
||||
public string $city = '';
|
||||
public string $country = 'Deutschland';
|
||||
public string $phone = '';
|
||||
public string $website = '';
|
||||
public $logo = null;
|
||||
|
||||
// Schritt 2: Retailer - Liefergebiete
|
||||
public ?int $deliveryRadius = null;
|
||||
public ?int $assemblyRadius = null;
|
||||
|
||||
// Schritt 2: Manufacturer - Marke
|
||||
public string $brandName = '';
|
||||
public $brandLogo = null;
|
||||
public string $brandDescription = '';
|
||||
|
||||
public string $roleIcon = 'shield-check';
|
||||
public string $roleName = '-';
|
||||
protected ?RegistrationCode $registrationCode = null;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
if (!$user->partner_id) {
|
||||
$this->redirect(route('dashboard'), navigate: true);
|
||||
return;
|
||||
}
|
||||
|
||||
$role = $user->roles->first();
|
||||
if ($role) {
|
||||
$this->roleIcon = $role->icon ?? 'shield-check';
|
||||
$this->roleName = $role->display_name ?? $role->name;
|
||||
}
|
||||
|
||||
$this->partner = Partner::with('users')->findOrFail($user->partner_id);
|
||||
$this->partnerType = $this->partner->type;
|
||||
|
||||
// Vorausfüllen: Partner-Daten
|
||||
$this->companyName = $this->partner->company_name ?? '';
|
||||
$this->displayName = $this->partner->display_name ?? '';
|
||||
$this->salutation = $this->partner->salutation ?? '';
|
||||
$this->firstName = $this->partner->first_name ?? '';
|
||||
$this->lastName = $this->partner->last_name ?? '';
|
||||
$this->description = $this->partner->description ?? '';
|
||||
$this->street = $this->partner->street ?? '';
|
||||
$this->houseNumber = $this->partner->house_number ?? '';
|
||||
$this->zip = $this->partner->zip ?? '';
|
||||
$this->city = $this->partner->city ?? '';
|
||||
$this->country = $this->partner->country ?? 'Deutschland';
|
||||
$this->phone = $this->partner->phone ?? '';
|
||||
$this->website = $this->partner->website ?? '';
|
||||
|
||||
// Namen aus User übernehmen, falls Partner-Felder leer sind
|
||||
if (empty($this->firstName) && empty($this->lastName)) {
|
||||
$nameParts = explode(' ', $user->name, 2);
|
||||
$this->firstName = $nameParts[0] ?? '';
|
||||
$this->lastName = $nameParts[1] ?? '';
|
||||
}
|
||||
// Definiere Schritte basierend auf Rolle
|
||||
$this->defineSteps();
|
||||
}
|
||||
|
||||
protected function defineSteps(): void
|
||||
{
|
||||
$normalizedType = strtolower(str_replace('-', '', $this->partnerType));
|
||||
|
||||
switch ($normalizedType) {
|
||||
case 'retailer':
|
||||
$this->steps = ['Stammdaten', 'Liefergebiete', 'Fertig'];
|
||||
$this->totalSteps = 3;
|
||||
break;
|
||||
case 'manufacturer':
|
||||
$this->steps = ['Stammdaten', 'Marke anlegen', 'Fertig'];
|
||||
$this->totalSteps = 3;
|
||||
break;
|
||||
case 'broker':
|
||||
case 'estateagent':
|
||||
$this->steps = ['Stammdaten', 'Fertig'];
|
||||
$this->totalSteps = 2;
|
||||
break;
|
||||
case 'customer':
|
||||
$this->steps = ['Stammdaten', 'Fertig'];
|
||||
$this->totalSteps = 2;
|
||||
break;
|
||||
default:
|
||||
$this->steps = ['Stammdaten', 'Fertig'];
|
||||
$this->totalSteps = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function saveStep1(): void
|
||||
{
|
||||
$normalizedType = strtolower(str_replace('-', '', $this->partnerType));
|
||||
$isCustomer = $normalizedType === 'customer';
|
||||
$isBroker = $normalizedType === 'broker' || $normalizedType === 'estateagent';
|
||||
$rules = [
|
||||
'salutation' => 'required|in:Herr,Frau,Divers',
|
||||
'firstName' => 'required|string|max:255',
|
||||
'lastName' => 'required|string|max:255',
|
||||
'street' => 'required|string|max:255',
|
||||
'houseNumber' => 'required|string|max:20',
|
||||
'zip' => 'required|string|max:10',
|
||||
'city' => 'required|string|max:255',
|
||||
'country' => 'required|string|max:255',
|
||||
'phone' => 'nullable|string|max:50',
|
||||
];
|
||||
|
||||
if (!$isCustomer) {
|
||||
$rules['companyName'] = 'required|string|max:255';
|
||||
$rules['description'] = 'nullable|string|max:1000';
|
||||
$rules['website'] = 'nullable|url|max:255';
|
||||
}
|
||||
|
||||
// Für Broker ist displayName Pflicht
|
||||
if ($isBroker) {
|
||||
$rules['displayName'] = 'required|string|max:255';
|
||||
}
|
||||
|
||||
$this->validate($rules, [
|
||||
'salutation.required' => __('Bitte wählen Sie eine Anrede.'),
|
||||
'firstName.required' => __('Bitte geben Sie einen Vornamen ein.'),
|
||||
'lastName.required' => __('Bitte geben Sie einen Nachnamen ein.'),
|
||||
'companyName.required' => __('Bitte geben Sie einen Firmennamen ein.'),
|
||||
'displayName.required' => __('Bitte geben Sie einen Anzeigenamen ein.'),
|
||||
'street.required' => __('Bitte geben Sie eine Straße ein.'),
|
||||
'houseNumber.required' => __('Bitte geben Sie eine Hausnummer ein.'),
|
||||
'zip.required' => __('Bitte geben Sie eine Postleitzahl ein.'),
|
||||
'city.required' => __('Bitte geben Sie eine Stadt ein.'),
|
||||
'country.required' => __('Bitte wählen Sie ein Land.'),
|
||||
'website.url' => __('Bitte geben Sie eine gültige URL ein.'),
|
||||
]);
|
||||
|
||||
// Update Partner
|
||||
$updateData = [
|
||||
'salutation' => $this->salutation,
|
||||
'first_name' => $this->firstName,
|
||||
'last_name' => $this->lastName,
|
||||
'street' => $this->street,
|
||||
'house_number' => $this->houseNumber,
|
||||
'zip' => $this->zip,
|
||||
'city' => $this->city,
|
||||
'country' => $this->country,
|
||||
'phone' => $this->phone,
|
||||
];
|
||||
|
||||
if (!$isCustomer) {
|
||||
$updateData['company_name'] = $this->companyName;
|
||||
$updateData['description'] = $this->description;
|
||||
$updateData['website'] = $this->website;
|
||||
}
|
||||
|
||||
if ($isBroker) {
|
||||
$updateData['display_name'] = $this->displayName;
|
||||
}
|
||||
|
||||
$this->partner->update($updateData);
|
||||
|
||||
// Für Broker und Kunden: Direkt zum Erfolg
|
||||
if ($isBroker || $isCustomer) {
|
||||
$this->completeSetup();
|
||||
} else {
|
||||
$this->currentStep = 2;
|
||||
}
|
||||
}
|
||||
|
||||
public function saveStep2Retailer(): void
|
||||
{
|
||||
$this->validate(
|
||||
[
|
||||
'deliveryRadius' => 'required|integer|min:1|max:500',
|
||||
'assemblyRadius' => 'required|integer|min:1|max:500',
|
||||
],
|
||||
[
|
||||
'deliveryRadius.required' => __('Bitte geben Sie einen Lieferradius ein.'),
|
||||
'deliveryRadius.min' => __('Der Lieferradius muss mindestens 1 km betragen.'),
|
||||
'assemblyRadius.required' => __('Bitte geben Sie einen Montageradius ein.'),
|
||||
'assemblyRadius.min' => __('Der Montageradius muss mindestens 1 km betragen.'),
|
||||
],
|
||||
);
|
||||
|
||||
$this->partner->update([
|
||||
'delivery_radius_km' => $this->deliveryRadius,
|
||||
'assembly_radius_km' => $this->assemblyRadius,
|
||||
]);
|
||||
|
||||
$this->completeSetup();
|
||||
}
|
||||
|
||||
public function saveStep2Manufacturer(): void
|
||||
{
|
||||
$this->validate(
|
||||
[
|
||||
'brandName' => 'required|string|max:255',
|
||||
'brandDescription' => 'nullable|string|max:1000',
|
||||
'brandLogo' => 'nullable|image|mimes:jpeg,png,jpg,webp|max:2048',
|
||||
],
|
||||
[
|
||||
'brandName.required' => __('Bitte geben Sie einen Markennamen ein.'),
|
||||
'brandLogo.image' => __('Das Marken-Logo muss eine Bilddatei sein.'),
|
||||
'brandLogo.mimes' => __('Das Marken-Logo muss im Format JPG, PNG oder WebP sein.'),
|
||||
'brandLogo.max' => __('Das Marken-Logo darf maximal 2 MB groß sein.'),
|
||||
],
|
||||
);
|
||||
|
||||
// Speichere Brand-Logo falls hochgeladen
|
||||
$brandLogoPath = null;
|
||||
if ($this->brandLogo) {
|
||||
$brandLogoPath = $this->brandLogo->store('brand-logos', 'public');
|
||||
}
|
||||
|
||||
// Erstelle Brand
|
||||
Brand::create([
|
||||
'partner_id' => $this->partner->id,
|
||||
'name' => $this->brandName,
|
||||
'slug' => Str::slug($this->brandName),
|
||||
'description' => $this->brandDescription,
|
||||
'logo_url' => $brandLogoPath,
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$this->completeSetup();
|
||||
}
|
||||
|
||||
|
||||
protected function completeSetup(): void
|
||||
{
|
||||
$this->partner->update([
|
||||
'is_active' => true,
|
||||
'setup_completed' => true,
|
||||
'setup_completed_at' => now(),
|
||||
]);
|
||||
|
||||
// Markiere Registrierungscode als verbraucht, falls vorhanden
|
||||
if ($this->registrationCode && $this->registrationCode->isAvailable()) {
|
||||
$this->registrationCode->markUsed(Auth::user());
|
||||
session()->forget(['registration_code_id', 'registration_role']);
|
||||
}
|
||||
|
||||
$this->currentStep = $this->totalSteps;
|
||||
}
|
||||
|
||||
public function goToDashboard(): void
|
||||
{
|
||||
$this->redirect(route('dashboard'), navigate: true);
|
||||
}
|
||||
|
||||
public function handleLogout(): void
|
||||
{
|
||||
|
||||
Auth::guard('web')->logout();
|
||||
|
||||
session()->invalidate();
|
||||
session()->regenerateToken();
|
||||
|
||||
$this->redirect(route('home'), navigate: false);
|
||||
}
|
||||
|
||||
protected function loadRegistrationCode(): void
|
||||
{
|
||||
$codeId = session('registration_code_id');
|
||||
$role = session('registration_role');
|
||||
|
||||
if (!$codeId || !$role) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->registrationCode = RegistrationCode::where('id', $codeId)
|
||||
->where('role', $role)
|
||||
->first();
|
||||
|
||||
if (!$this->registrationCode || !$this->registrationCode->isAvailable()) {
|
||||
// Ungültig/verbraucht -> Session aufräumen
|
||||
session()->forget(['registration_code_id', 'registration_role']);
|
||||
$this->registrationCode = null;
|
||||
}
|
||||
}
|
||||
}; ?>
|
||||
|
||||
{{-- Konfetti Script laden --}}
|
||||
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.6.0/dist/confetti.browser.min.js"></script>
|
||||
|
||||
<div class="w-full max-w-3xl">
|
||||
{{-- Header --}}
|
||||
<div class="text-center mb-8">
|
||||
@include('partials.logo-head')
|
||||
<h1 class="text-3xl font-bold text-zinc-900 dark:text-white mb-2">
|
||||
{{ __('Vervollständigen Sie Ihr Profil') }}
|
||||
</h1>
|
||||
|
||||
</div>
|
||||
|
||||
{{-- Progress Indicator --}}
|
||||
<x-wizard-progress :currentStep="$currentStep" :totalSteps="$totalSteps" :steps="$steps" />
|
||||
|
||||
{{-- Wizard Content --}}
|
||||
<flux:card class="shadow-2xl">
|
||||
{{-- Step 1: Stammdaten (für alle Rollen) --}}
|
||||
@if ($currentStep === 1)
|
||||
@php
|
||||
$isCustomer = strtolower(str_replace('-', '', $partnerType)) === 'customer';
|
||||
@endphp
|
||||
<form wire:submit.prevent="saveStep1" class="space-y-6">
|
||||
<div>
|
||||
<flux:heading size="lg" class="mb-2">
|
||||
<div class="flex items-center gap-2">
|
||||
@svg('heroicon-o-'.$roleIcon, 'w-5 h-5')
|
||||
{{ __('Ihre Stammdaten') }} / {{ $roleName }}
|
||||
</div>
|
||||
</flux:heading>
|
||||
<flux:subheading>
|
||||
{{ __('Diese Informationen helfen uns, Ihr Profil zu vervollständigen.') }}
|
||||
</flux:subheading>
|
||||
</div>
|
||||
|
||||
<flux:separator />
|
||||
|
||||
{{-- Firmenname (nur für Nicht-Kunden) --}}
|
||||
@if (!$isCustomer)
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Firmenname') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="companyName" name="companyName" icon="building-office" placeholder="{{ __('z.B. Müller GmbH') }}" />
|
||||
@error('companyName')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
@php
|
||||
$isBroker = strtolower(str_replace('-', '', $partnerType)) === 'broker' || strtolower(str_replace('-', '', $partnerType)) === 'estateagent';
|
||||
@endphp
|
||||
|
||||
@if ($isBroker)
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Anzeigename') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:description>{{ __('Der Name, der Ihren Kunden angezeigt wird (kann vom Firmennamen abweichen)') }}</flux:description>
|
||||
<flux:input wire:model="displayName" name="displayName" icon="user" placeholder="{{ __('z.B. Max Schmidt oder Immobilien Schmidt') }}" />
|
||||
@error('displayName')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
@endif
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Kurzbeschreibung') }}</flux:label>
|
||||
<flux:textarea wire:model="description" name="description" rows="3" placeholder="{{ __('Ein kurzer Text über Ihr Unternehmen...') }}" />
|
||||
@error('description')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:separator />
|
||||
@endif
|
||||
|
||||
{{-- Persönliche Daten --}}
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Anrede') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:select wire:model="salutation" name="salutation">
|
||||
<flux:select.option value="">{{ __('Bitte wählen') }}</flux:select.option>
|
||||
<flux:select.option value="Herr">{{ __('Herr') }}</flux:select.option>
|
||||
<flux:select.option value="Frau">{{ __('Frau') }}</flux:select.option>
|
||||
<flux:select.option value="Divers">{{ __('Divers') }}</flux:select.option>
|
||||
</flux:select>
|
||||
@error('salutation')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Vorname') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="firstName" name="firstName" icon="user" value="{{ $firstName }}" />
|
||||
@error('firstName')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Nachname') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="lastName" name="lastName" icon="user" value="{{ $lastName }}" />
|
||||
@error('lastName')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
</div>
|
||||
|
||||
<flux:separator />
|
||||
|
||||
{{-- Adresse --}}
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<flux:field class="md:col-span-3">
|
||||
<flux:label>{{ __('Straße') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="street" name="street" icon="map-pin" placeholder="{{ __('Musterstraße') }}" />
|
||||
@error('street')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Hausnummer') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="houseNumber" name="houseNumber" placeholder="{{ __('123a') }}" />
|
||||
@error('houseNumber')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Postleitzahl') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="zip" name="zip" icon="map" placeholder="{{ __('12345') }}" />
|
||||
@error('zip')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:field class="md:col-span-2">
|
||||
<flux:label>{{ __('Ort') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="city" name="city" icon="building-office" placeholder="{{ __('Musterstadt') }}" />
|
||||
@error('city')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
</div>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Land') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:select wire:model="country" name="country">
|
||||
<flux:select.option value="Deutschland">{{ __('Deutschland') }}</flux:select.option>
|
||||
<flux:select.option value="Österreich">{{ __('Österreich') }}</flux:select.option>
|
||||
<flux:select.option value="Schweiz">{{ __('Schweiz') }}</flux:select.option>
|
||||
</flux:select>
|
||||
@error('country')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Telefon') }}</flux:label>
|
||||
<flux:input wire:model="phone" name="phone" type="tel" icon="phone" placeholder="{{ __('optional') }}" />
|
||||
@error('phone')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
@if (!$isCustomer)
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Website') }}</flux:label>
|
||||
<flux:input wire:model="website" name="website" type="url" icon="globe-alt" placeholder="https://..." />
|
||||
@error('website')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
@endif
|
||||
|
||||
<flux:separator />
|
||||
|
||||
<x-error-alert />
|
||||
|
||||
<div class="flex justify-end">
|
||||
@php
|
||||
$normalized = strtolower(str_replace('-', '', $partnerType));
|
||||
$isBrokerOrCustomer = $normalized === 'broker' || $normalized === 'estateagent' || $isCustomer;
|
||||
@endphp
|
||||
<flux:button type="submit" variant="primary" icon="{{ $isBrokerOrCustomer ? 'check-circle' : 'arrow-right' }}" class="cursor-pointer" wire:target="saveStep1">
|
||||
@if ($normalized === 'retailer')
|
||||
{{ __('Weiter zu Liefergebiete') }}
|
||||
@elseif($normalized === 'manufacturer')
|
||||
{{ __('Weiter zu Marke') }}
|
||||
@else
|
||||
{{ __('Setup abschließen') }}
|
||||
@endif
|
||||
</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
{{-- Step 2: Retailer - Liefergebiete --}}
|
||||
@if ($currentStep === 2 && $partnerType === 'Retailer')
|
||||
<form wire:submit.prevent="saveStep2Retailer" class="space-y-6">
|
||||
<div>
|
||||
<flux:heading size="lg" class="mb-2">
|
||||
🚚 {{ __('Ihre Liefergebiete') }}
|
||||
</flux:heading>
|
||||
<flux:subheading>
|
||||
{{ __('Wie weit liefern und montieren Sie von Ihrer Adresse (:zip :city) aus?', ['zip' => $zip, 'city' => $city]) }}
|
||||
</flux:subheading>
|
||||
</div>
|
||||
|
||||
<flux:separator />
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Lieferradius') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:description>{{ __('Ich liefere im Umkreis von ... km') }}</flux:description>
|
||||
<div class="flex items-center gap-4">
|
||||
<flux:input wire:model="deliveryRadius" name="deliveryRadius" type="number" min="1" max="500"
|
||||
class="flex-1" />
|
||||
<span class="text-sm text-zinc-600 dark:text-zinc-400">km</span>
|
||||
</div>
|
||||
@error('deliveryRadius')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Montageradius') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:description>{{ __('Ich montiere im Umkreis von ... km') }}</flux:description>
|
||||
<div class="flex items-center gap-4">
|
||||
<flux:input wire:model="assemblyRadius" name="assemblyRadius" type="number" min="1" max="500"
|
||||
class="flex-1" />
|
||||
<span class="text-sm text-zinc-600 dark:text-zinc-400">km</span>
|
||||
</div>
|
||||
@error('assemblyRadius')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:separator />
|
||||
|
||||
<x-error-alert />
|
||||
|
||||
<div class="flex justify-end">
|
||||
<flux:button type="submit" variant="primary" icon="check-circle">
|
||||
{{ __('Setup abschließen') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
{{-- Step 2: Manufacturer - Marke anlegen --}}
|
||||
@if ($currentStep === 2 && $partnerType === 'Manufacturer')
|
||||
<form wire:submit.prevent="saveStep2Manufacturer" class="space-y-6">
|
||||
<div>
|
||||
<flux:heading size="lg" class="mb-2">
|
||||
™️ {{ __('Ihre Marke') }}
|
||||
</flux:heading>
|
||||
<flux:subheading>
|
||||
{{ __('Unter dieser Marke werden Ihre Produkte auf B2In gelistet. (Sie können später weitere Marken hinzufügen)') }}
|
||||
</flux:subheading>
|
||||
</div>
|
||||
|
||||
<flux:separator />
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Markenname') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="brandName" name="brandName" icon="tag"
|
||||
placeholder="{{ __('z.B. Möbelwerke Premium') }}" />
|
||||
@error('brandName')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Marken-Logo (optional)') }}</flux:label>
|
||||
<flux:description>{{ __('Laden Sie Ihr Marken-Logo hoch (max. 2 MB, JPG/PNG)') }}</flux:description>
|
||||
|
||||
<div class="space-y-2">
|
||||
<input type="file" wire:model.live="brandLogo" name="brandLogo" accept="image/jpeg,image/png,image/jpg,image/webp"
|
||||
class="block w-full text-sm text-zinc-500 file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold file:bg-accent-50 file:text-accent-700 hover:file:bg-accent-100 dark:text-zinc-400 dark:file:bg-accent-900/20 dark:file:text-accent-400" />
|
||||
|
||||
@error('brandLogo')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
|
||||
<div wire:loading wire:target="brandLogo" class="text-sm text-accent-600 dark:text-accent-400">
|
||||
<flux:icon.arrow-path class="inline-block h-4 w-4 animate-spin mr-2" />
|
||||
{{ __('Wird hochgeladen...') }}
|
||||
</div>
|
||||
|
||||
@if ($brandLogo)
|
||||
<div wire:loading.remove wire:target="brandLogo">
|
||||
@try
|
||||
<div class="p-2 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg">
|
||||
<div class="flex items-center gap-3">
|
||||
<img src="{{ $brandLogo->temporaryUrl() }}"
|
||||
class="h-16 w-16 object-contain rounded border" alt="Brand Logo Preview">
|
||||
<div class="flex-1">
|
||||
<p class="text-sm font-medium text-green-800 dark:text-green-200">{{ __('Logo erfolgreich hochgeladen') }}</p>
|
||||
<p class="text-xs text-green-600 dark:text-green-400">{{ $brandLogo->getClientOriginalName() }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@catch(\Exception $e)
|
||||
<div class="p-2 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg">
|
||||
<p class="text-sm text-red-800 dark:text-red-200">{{ __('Fehler beim Laden der Vorschau') }}</p>
|
||||
</div>
|
||||
@endtry
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Marken-Beschreibung') }}</flux:label>
|
||||
<flux:textarea wire:model="brandDescription" name="brandDescription" rows="4"
|
||||
placeholder="{{ __('Ein kurzer Text über Ihre Marke...') }}" />
|
||||
@error('brandDescription')
|
||||
<flux:error>{{ $message }}</flux:error>
|
||||
@enderror
|
||||
</flux:field>
|
||||
|
||||
<flux:separator />
|
||||
|
||||
<x-error-alert />
|
||||
|
||||
<div class="flex justify-end">
|
||||
<flux:button type="submit" variant="primary" icon="check-circle">
|
||||
{{ __('Setup abschließen') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
{{-- Final Step: Fertig! --}}
|
||||
@if ($currentStep === $totalSteps)
|
||||
<div class="text-center space-y-6 py-8" x-data x-init="
|
||||
// Konfetti-Effekt
|
||||
const duration = 3 * 1000;
|
||||
const end = Date.now() + duration;
|
||||
|
||||
(function frame() {
|
||||
confetti({
|
||||
particleCount: 5,
|
||||
angle: 60,
|
||||
spread: 55,
|
||||
origin: { x: 0 },
|
||||
colors: ['#10b981', '#3b82f6', '#f59e0b', '#ef4444', '#8b5cf6']
|
||||
});
|
||||
confetti({
|
||||
particleCount: 5,
|
||||
angle: 120,
|
||||
spread: 55,
|
||||
origin: { x: 1 },
|
||||
colors: ['#10b981', '#3b82f6', '#f59e0b', '#ef4444', '#8b5cf6']
|
||||
});
|
||||
|
||||
if (Date.now() < end) {
|
||||
requestAnimationFrame(frame);
|
||||
}
|
||||
}());
|
||||
">
|
||||
<div class="flex justify-center">
|
||||
<div
|
||||
class="flex items-center justify-center w-20 h-20 rounded-full bg-green-100 dark:bg-green-900/20 animate-bounce">
|
||||
<flux:icon.check-circle class="h-12 w-12 text-green-600 dark:text-green-400" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<flux:heading size="xl" class="mb-2">
|
||||
✅ {{ __('Einrichtung abgeschlossen!') }}
|
||||
</flux:heading>
|
||||
<flux:subheading>
|
||||
@if ($partnerType === 'Retailer')
|
||||
{{ __('Sie sind nun ein aktiver Händler. Sie können jetzt Ihr Sortiment pflegen.') }}
|
||||
@elseif($partnerType === 'Manufacturer')
|
||||
{{ __('Sie sind nun ein aktiver Hersteller. Sie können jetzt Ihre Produkte anlegen.') }}
|
||||
@else
|
||||
{{ __('Ihr Profil ist aktiv. In Ihrem Dashboard finden Sie Ihre persönlichen Einladungslinks.') }}
|
||||
@endif
|
||||
</flux:subheading>
|
||||
</div>
|
||||
|
||||
<flux:separator />
|
||||
|
||||
<div class="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
|
||||
<button type="button" wire:click="goToDashboard"
|
||||
class="inline-flex items-center justify-center gap-2 px-6 py-3 text-base font-semibold text-white bg-accent-600 hover:bg-accent-700 dark:bg-accent-500 dark:hover:bg-accent-600 rounded-lg transition-colors">
|
||||
@svg('heroicon-o-home', 'w-5 h-5')
|
||||
{{ __('Zum Dashboard') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</flux:card>
|
||||
|
||||
<div class="mt-4 text-center">
|
||||
<form method="POST" action="{{ route('auth.logout') }}" class="inline">
|
||||
@csrf
|
||||
<button type="submit"
|
||||
class="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-zinc-700 dark:text-zinc-300 hover:text-zinc-900 dark:hover:text-white transition-colors cursor-pointer rounded-lg">
|
||||
@svg('heroicon-o-arrow-left-start-on-rectangle', 'w-5 h-5')
|
||||
{{ __('Logout') }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
Loading…
Add table
Add a link
Reference in a new issue