import React, { useEffect, useState, useRef } from "react"; import conniImage from "./images/CONNI.png"; /** * Chatbot.tsx * React + TypeScript component designed for MediConnect. * - Floating action button (bottom-right) * - Modal / popup chat window * - Sends user messages to a backend endpoint (/api/chat) which proxies to an LLM * - DOES NOT send/collect any sensitive data (PHI). The frontend strips/flags sensitive fields. * - Configurable persona: "Assistente Virtual do MediConnect" * * Integration notes (short): * - Backend should be a Supabase Edge Function (or Cloudflare Worker) at /api/chat * - The Edge Function will contain the OpenAI (or other LLM) key and apply the system prompt. * - Frontend only uses a short-term session id; it never stores patient-identifying data. */ type Message = { id: string; role: "user" | "assistant" | "system"; text: string; time?: string; }; interface ChatbotProps { className?: string; } const Chatbot: React.FC = ({ className = "" }) => { const [isOpen, setIsOpen] = useState(false); const [messages, setMessages] = useState([ { id: "welcome", role: "assistant", text: "Olá! 👋 Sou a Conni, sua Assistente Virtual do MediConnect. Estou aqui para ajudá-lo com dúvidas sobre agendamento de consultas, navegação no sistema, funcionalidades e suporte. Como posso ajudar você hoje?", time: new Date().toLocaleTimeString("pt-BR", { hour: "2-digit", minute: "2-digit", }), }, ]); const [inputValue, setInputValue] = useState(""); const [isLoading, setIsLoading] = useState(false); const messagesEndRef = useRef(null); const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); }; useEffect(() => { scrollToBottom(); }, [messages]); /** * Sanitize user input before sending. * This is a basic approach. For production, you might do more thorough checks. */ function sanitizeUserMessage(text: string): string { // Remove potential HTML/script tags (very naive approach) const cleaned = text.replace(/<[^>]*>/g, ""); // Truncate if too long return cleaned.slice(0, 1000); } /** * Send message to backend /api/chat. * The backend returns { reply: string } in JSON. */ async function callChatApi(userText: string): Promise { try { // Get auth token from localStorage const authData = localStorage.getItem( "sb-yuanqfswhberkoevtmfr-auth-token" ); let token = ""; if (authData) { try { const parsedAuth = JSON.parse(authData); token = parsedAuth?.access_token || ""; } catch (e) { console.warn("Failed to parse auth token:", e); } } console.log("[Chatbot] Sending message to /api/chat"); const response = await fetch("/api/chat", { method: "POST", headers: { "Content-Type": "application/json", ...(token && { Authorization: `Bearer ${token}` }), }, body: JSON.stringify({ messages: [ { role: "user", content: userText, }, ], token: token, }), }); console.log("[Chatbot] Response status:", response.status); if (!response.ok) { const errorText = await response.text(); console.error( "Chat API error:", response.status, response.statusText, errorText ); return "Desculpe, ocorreu um erro ao processar sua mensagem. Por favor, tente novamente em alguns instantes."; } const data = await response.json(); console.log("[Chatbot] Response data:", data); return data.reply || "Sem resposta do servidor."; } catch (error) { console.error("Erro ao chamar a API de chat:", error); return "Não foi possível conectar ao servidor. Verifique sua conexão e tente novamente."; } } const handleSend = async () => { if (!inputValue.trim()) return; const sanitized = sanitizeUserMessage(inputValue); const userMessage: Message = { id: Date.now().toString(), role: "user", text: sanitized, time: new Date().toLocaleTimeString("pt-BR", { hour: "2-digit", minute: "2-digit", }), }; setMessages((prev) => [...prev, userMessage]); setInputValue(""); setIsLoading(true); // Call AI backend const reply = await callChatApi(sanitized); const assistantMessage: Message = { id: (Date.now() + 1).toString(), role: "assistant", text: reply, time: new Date().toLocaleTimeString("pt-BR", { hour: "2-digit", minute: "2-digit", }), }; setMessages((prev) => [...prev, assistantMessage]); setIsLoading(false); }; const handleKeyPress = (e: React.KeyboardEvent) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); handleSend(); } }; const quickReplies = [ "Como agendar uma consulta?", "Como cancelar um agendamento?", "Esqueci minha senha", "Onde vejo minhas consultas?", ]; const handleQuickReply = (text: string) => { setInputValue(text); }; return (
{/* Floating Button */} {!isOpen && ( )} {/* Chat Window */} {isOpen && (
{/* Header */}
Conni

Conni - Assistente MediConnect

Online • AI-Powered

{/* Messages */}
{messages.map((message) => (

{message.text}

{message.time && (

{message.time}

)}
))} {isLoading && (
)}
{/* Quick Replies */} {messages.length <= 1 && (

Perguntas frequentes:

{quickReplies.map((reply, index) => ( ))}
)} {/* Input */}
setInputValue(e.target.value)} onKeyPress={handleKeyPress} placeholder="Digite sua mensagem..." className="flex-1 border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" />
)}
); }; export default Chatbot;