"use client"; import { useEffect, useRef, useState } from "react"; import Link from "next/link"; import type { ListingCard } from "@/lib/types"; import { ListingCardView } from "@/components/listing-card"; import { IconSpark } from "@/components/icons"; import { Notice } from "@/components/ui"; type Msg = { role: "user" | "assistant"; content: string; citations?: string[]; cards?: ListingCard[] }; const STARTERS = ["Quels sont les meilleurs quartiers familiaux à Québec ?", "Montre-moi des condos à louer à Montréal sous 1 800 $", "Combien vaut le 1234 rue des Érables à Québec ?", "Quel est le prix médian des maisons en Montérégie ?", "Versement mensuel pour 500 000 $ avec 10 % de mise de fonds à 4,9 %"]; export function AssistantView({ uid, compact = false }: { uid?: string; compact?: boolean }) { const [msgs, setMsgs] = useState([]); const [q, setQ] = useState(""); const [busy, setBusy] = useState(false); const end = useRef(null); useEffect(() => { end.current?.scrollIntoView({ behavior: "smooth", block: "nearest" }); }, [msgs, busy]); const send = async (text: string) => { if (!text.trim() || busy) return; const next: Msg[] = [...msgs, { role: "user", content: text.trim() }]; setMsgs(next); setQ(""); setBusy(true); const r = await fetch("/api/assistant", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ messages: next.map(({ role, content }) => ({ role, content })), context: uid ? { uid } : undefined }) }); const d = await r.json(); let cards: ListingCard[] = []; if (r.ok && d.citations?.length) { const c = await fetch(`/api/listings?uids=${encodeURIComponent(d.citations.slice(0, 6).join(","))}`).then((r) => r.json()).catch(() => null); cards = c?.items ?? []; } setMsgs([...next, { role: "assistant", content: r.ok ? d.text : (d.error ?? "Erreur"), citations: d.citations, cards }]); setBusy(false); }; return (
{!compact &&

Assistant Toveli

Il interroge les données Toveli (annonces, marché, rôle, ventes) et cite les fiches qu'il utilise. Il ne devine pas de prix.

}
{msgs.length === 0 &&
{STARTERS.map((s) => )}
} {msgs.map((m, i) => (
{m.content} {m.cards && m.cards.length > 0 &&
{m.cards.map((c) => )}
}
))} {busy &&
Je consulte les données…
}
{ e.preventDefault(); send(q); }} className="mt-3 flex gap-2"> setQ(e.target.value)} className="tv-input !pl-4 flex-1" placeholder="Posez votre question…" aria-label="Votre question" disabled={busy} />
Réponses générées par un modèle de langage à partir des données Toveli ; vérifiez sur la fiche d'origine. Pas de conseil financier, juridique ou d'évaluation agréée. Vos messages ne sont pas conservés.
); }