'Projektausstattung', 'immobilien' => 'Immobilien', 'sonstiges' => 'Sonstiges', ]; /** Farbwelt der Seite – in der Immobilien-Welt ist das Anliegen vorbelegt. */ public string $world = 'interior'; public string $name = ''; public string $email = ''; public string $phone = ''; public string $topic = ''; public string $preferredDate = ''; public string $message = ''; public bool $privacy = false; public bool $success = false; /** @var string Honeypot – muss leer bleiben. */ public string $website = ''; public ?int $formLoadedAt = null; /** @var array */ public array $content = []; public function mount(): void { $section = cms_theme_section('appointment_widget'); $this->content = is_array($section) ? $section : []; $this->formLoadedAt = time(); $this->world = b2in_world(); if ($this->world === 'immobilien') { $this->topic = 'immobilien'; } $this->applyBrandContext(); } /** * Auf einer Markenseite trägt der Button die Beschriftung dieser Marke. * * Die Zulieferung sieht ausdrücklich keinen zweiten CTA-Button vor, sondern eine * kontextbezogene Beschriftung des vorhandenen Widgets. Da das Widget im Layout steckt und * nicht je Seite eingebunden wird, liest es den Kontext aus der Route. */ private function applyBrandContext(): void { $route = request()->route(); if ($route?->getName() !== 'marken.show') { return; } $slug = (string) $route->parameter('slug'); $brand = CmsBrand::forFrontend()[$slug] ?? null; $label = is_array($brand) ? trim((string) ($brand['widget_label'] ?? '')) : ''; if ($label !== '') { $this->content['button_label'] = $label; } } /** * Auswahl „Anliegen": gepflegte Kernmarken zuerst, danach die festen Einträge. * * @return array */ public function getTopicsProperty(): array { $brands = []; foreach (CmsBrand::forFrontend() as $slug => $brand) { $brands[$slug] = (string) ($brand['name'] ?? $slug); } return $brands + self::EXTRA_TOPICS; } public function getWhatsappUrlProperty(): string { return whatsapp_link(); } public function submit(ContactFormService $service): void { $this->validate([ 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'email:rfc', 'max:255'], 'phone' => ['nullable', 'string', 'max:80'], 'topic' => ['nullable', 'string', 'max:255'], 'preferredDate' => ['nullable', 'date', 'after_or_equal:today'], 'message' => ['nullable', 'string', 'max:2000'], 'privacy' => ['accepted'], 'website' => ['nullable', 'string', 'max:0'], ], [ 'preferredDate.after_or_equal' => __('ui.appointment.date_in_past'), ]); $topicLabel = $this->topics[$this->topic] ?? ''; $payload = [ 'name' => $this->name, 'email' => $this->email, 'phone' => $this->phone, 'topic' => $topicLabel, 'preferred_date' => $this->preferredDate, 'message' => $this->message, 'website' => $this->website, 'source' => request()->path(), 'ip' => request()->ip(), 'user_agent' => request()->userAgent(), 'is_spam' => false, ]; $payload['is_spam'] = SpamDetector::fromConfig()->detect($payload, $this->formLoadedAt); $subject = ($this->world === 'immobilien' ? 'Terminanfrage B2in Immobilien' : 'Terminanfrage B2in Interior') .($topicLabel !== '' ? ': '.$topicLabel : ''); $service->handle($payload, $subject, 'appointment-widget'); $this->success = true; $this->reset(['name', 'email', 'phone', 'topic', 'preferredDate', 'message', 'privacy', 'website']); $this->topic = $this->world === 'immobilien' ? 'immobilien' : ''; $this->formLoadedAt = time(); } public function startOver(): void { $this->success = false; $this->formLoadedAt = time(); } public function render(): \Illuminate\View\View { return view('livewire.web.components.ui.appointment-widget'); } }