// assistant.jsx — workspace-scoped AI assistant chat UI. const { useState: useStateAs, useMemo: useMemoAs, useRef: useRefAs, useEffect: useEffectAs } = React; function Assistant() { const t = window.MSStore.useT(); const store = window.MSStore.useStore(); const [messages, setMessages] = useStateAs([]); const [draft, setDraft] = useStateAs(""); const [pending, setPending] = useStateAs(false); const [consentReady, setConsentReady] = useStateAs(false); const [grantingConsent, setGrantingConsent] = useStateAs(false); const [error, setError] = useStateAs(""); const listRef = useRefAs(null); const suggestions = useMemoAs(() => [ t("asst.example1"), t("asst.example2"), t("asst.example3"), ], [t]); useEffectAs(() => { const node = listRef.current; if (node) node.scrollTop = node.scrollHeight; }, [messages, pending]); useEffectAs(() => { let alive = true; (async () => { try { const rows = await window.MSApi.apiFetch("/api/compliance/consent"); const hasAiConsent = (rows || []).some(row => row.consent_type === "ai_processing" && !!row.granted); if (alive) setConsentReady(hasAiConsent); } catch (_) { if (alive) setConsentReady(false); } })(); return () => { alive = false; }; }, []); async function grantConsent() { if (grantingConsent) return; setError(""); setGrantingConsent(true); try { await window.MSApi.apiFetch("/api/compliance/consent", { method: "POST", body: { consent_type: "ai_processing", granted: true }, }); setConsentReady(true); } catch (e) { setError(e && e.message ? e.message : t("asst.error")); } finally { setGrantingConsent(false); } } async function send(text) { const content = String(text || draft || "").trim(); if (!content || pending) return; if (!consentReady) { setError(t("asst.consentRequired")); return; } setDraft(""); setError(""); const next = [...messages, { role: "user", content }]; setMessages(next); setPending(true); try { const payloadMessages = next .filter(m => m.role === "user" || m.role === "assistant") .map(m => ({ role: m.role, content: m.content })); const res = await window.MSApi.apiFetch("/api/assistant/chat", { method: "POST", body: { messages: payloadMessages }, }); setMessages([...next, { role: "assistant", content: (res && res.reply) || t("asst.error") }]); } catch (e) { const msg = e && e.message ? e.message : t("asst.error"); if (e && e.status === 403) setConsentReady(false); setError(msg); setMessages(next); } finally { setPending(false); } } function onKeyDown(e) { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send(); } } return (

{t("asst.title")}

{t("asst.sub")}
{t("asst.medSync")}
{t("asst.statusSub")}
{messages.length === 0 && (
{t("asst.emptyTitle")}
{t("asst.emptySub")}
)} {messages.map((m, i) => (
{m.content}
))} {pending && (
{t("asst.thinking")}
)}
{!consentReady && (
{t("asst.consentTitle")}
{t("asst.consentCopy")}
)} {error &&
{error}
}