Lou·Ka — tous les logements à louer du Québec, un seul endroit.
HTML 98.9%
Python 0.6%
1// -----------------------------------------------------------------------------2// Lou-Ka — Agrégateur de logements à louer (province de Québec)3// Auteur : Simon-Pierre Boucher — contact@spboucher.ai4// fiche/KaAssistant.tsx : « ✦ Demander à Ka » — bouton discret (bas droite,5// au-dessus du CTA) qui remplace la grosse bulle KA Agent sur la fiche, et6// bottom sheet de démarrage : contexte du logement, suggestions prédéfinies,7// champ libre. L'envoi passe par le widget KA Agent existant (ka-agent.js,8// backend api-ka inchangé) : on ouvre son panneau et on lui transmet la9// question, enrichie du contexte de la fiche (titre, ville, prix, lien).10// -----------------------------------------------------------------------------11import { useEffect, useState } from "react";12import { Listing, fmtPrice } from "../api";13import { IcoChevronRight, IcoSparkles } from "../components/Icons";14import BottomSheet from "./BottomSheet";1516const SUGGESTIONS = [17 "Est-ce une bonne affaire ?",18 "Compare ce loyer au quartier",19 "Le secteur est-il bon sans voiture ?",20 "Quels sont les points négatifs ?",21 "Explique le registre des loyers",22];2324/** Ouvre le panneau KA Agent (widget partagé) et envoie la question. */25function envoyerAKa(question: string): boolean {26 const btn = document.querySelector<HTMLButtonElement>(".kaa-btn");27 const ta = document.querySelector<HTMLTextAreaElement>(".kaa-panel textarea");28 const send = document.querySelector<HTMLButtonElement>(".kaa-in button");29 if (!btn || !ta || !send) return false;30 btn.click(); // ouvre le panneau (plein écran)31 ta.value = question;32 setTimeout(() => send.click(), 60);33 return true;34}3536export default function KaAssistant({ l, hidden }: { l: Listing; hidden?: boolean }) {37 const [open, setOpen] = useState(false);38 const [q, setQ] = useState("");39 const [err, setErr] = useState(false);40 // ouverture depuis un autre composant (bouton de l'aside desktop)41 useEffect(() => {42 const on = () => setOpen(true);43 window.addEventListener("lk:askka", on);44 return () => window.removeEventListener("lk:askka", on);45 }, []);4647 const contexte = () =>48 `(Logement consulté sur Lou-Ka : ${l.title || l.address}${l.city ? `, ${l.city}` : ""}` +49 `${l.price != null ? ` — ${fmtPrice(l.price)}/mois` : ""}${l.unit_type ? ` — ${l.unit_type}` : ""}` +50 ` — https://www.lou-ka.com/logement/${encodeURIComponent(l.uid)})`;5152 const poser = (question: string) => {53 const ok = envoyerAKa(`${question}\n\n${contexte()}`);54 if (ok) { setOpen(false); setQ(""); setErr(false); } else setErr(true);55 };5657 return (58 <>59 <button type="button" className={`lk-ka-btn ${hidden ? "hide" : ""}`} onClick={() => setOpen(true)}60 aria-label="Demander à Ka, l'assistant Groupe KA">61 <IcoSparkles size={16} /> Demander à Ka62 </button>63 <BottomSheet open={open} onClose={() => setOpen(false)} title="Demander à Ka" sub="Assistant Groupe KA · connaît cette fiche"64 footer={65 <form className="lk-ka-in" onSubmit={(e) => { e.preventDefault(); if (q.trim()) poser(q.trim()); }}>66 <input value={q} onChange={(e) => setQ(e.target.value)} placeholder="Posez votre question sur ce logement…" aria-label="Votre question" />67 <button type="submit" aria-label="Envoyer" disabled={!q.trim()}><IcoChevronRight size={20} /></button>68 </form>69 }>70 <div className="lk-ka-intro">71 <span className="lk-ka-avatar" aria-hidden="true"><IcoSparkles size={18} /></span>72 <p>Je peux comparer ce loyer au quartier, expliquer les données de la fiche et chercher dans les autres plateformes Groupe KA.</p>73 </div>74 <div className="lk-ka-ctx">75 {l.images?.[0] && <img src={`/api/img?u=${encodeURIComponent(l.images[0])}&w=160`} alt="" />}76 <span style={{ minWidth: 0 }}>77 <b>{l.title || l.address}</b>78 {[l.city, l.price != null ? `${fmtPrice(l.price)}/mois` : null, l.unit_type].filter(Boolean).join(" · ")}79 </span>80 </div>81 <div className="lk-ka-sugs">82 {SUGGESTIONS.map((s) => (83 <button type="button" className="lk-ka-sug" key={s} onClick={() => poser(s)}>84 {s} <IcoChevronRight size={16} />85 </button>86 ))}87 </div>88 {err && <p className="lk-note warn">L'assistant n'est pas encore chargé — réessayez dans un instant.</p>}89 </BottomSheet>90 </>91 );92}93