validate([ 'telefono' => 'required|min:7|max:20', 'nombre' => 'nullable|max:80', ]); $contact = ChatContact::firstOrCreate( ['canal' => 'web', 'canal_id' => $this->telefono], ['nombre' => $this->nombre ?: $this->telefono, 'telefono' => $this->telefono] ); if ($this->nombre && $contact->nombre !== $this->nombre) { $contact->update(['nombre' => $this->nombre]); } $conv = $contact->activeConversation(); if (! $conv) { // Primera vez — el engine inicia el flujo con "hola" $engine = new ChatBotEngine(); $engine->handle('web', $this->telefono, 'hola', $contact->nombre); $contact->refresh(); $conv = $contact->activeConversation(); } if ($conv) { $this->convId = $conv->id; $this->estadoConv = $conv->estado; $this->cargarMensajes(); } $this->paso = 'chat'; } public function enviar() { if (! $this->convId || trim($this->input) === '') { return; } $texto = trim($this->input); $this->input = ''; $engine = new ChatBotEngine(); $engine->handle('web', $this->telefono, $texto, $this->nombre ?: $this->telefono); $conv = ChatConversation::find($this->convId); if ($conv) { $this->estadoConv = $conv->fresh()->estado; } $this->cargarMensajes(); // scroll ya se dispara desde cargarMensajes() } public function cargarMensajes() { if (! $this->convId) { return; } $this->mensajes = ChatMessage::where('conversation_id', $this->convId) ->orderBy('id') ->get() ->map(fn($m) => [ 'tipo' => $m->tipo, 'contenido' => $m->contenido, 'created_at' => $m->created_at->format('H:i'), ]) ->toArray(); $this->dispatchBrowserEvent('scroll-chat'); } public function render() { return view('livewire.chat.public-chat'); } }