From 8ce02ac5ad30266cbb63b4adf00897db5d4bb7fd Mon Sep 17 00:00:00 2001 From: Jonas Francisco Date: Thu, 13 Nov 2025 13:39:51 -0300 Subject: [PATCH] fix(paciente) adicionei o chat interface --- .../ZoeIA/ai-assistant-interface.tsx | 761 +++++++----------- .../features/pacientes/chat-widget.tsx | 221 ++--- 2 files changed, 356 insertions(+), 626 deletions(-) diff --git a/susconecta/components/ZoeIA/ai-assistant-interface.tsx b/susconecta/components/ZoeIA/ai-assistant-interface.tsx index b99d520..75481bc 100644 --- a/susconecta/components/ZoeIA/ai-assistant-interface.tsx +++ b/susconecta/components/ZoeIA/ai-assistant-interface.tsx @@ -1,512 +1,317 @@ "use client"; -import type React from "react"; - -import { useState, useRef } from "react"; +import { useMemo, useState } from "react"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { SimpleThemeToggle } from "@/components/ui/simple-theme-toggle"; import { - Search, - Mic, - ArrowUp, + Clock, + Info, + Lock, + MessageCircle, Plus, - FileText, - Code, - BookOpen, - PenTool, - BrainCircuit, - Sparkles, + Upload, } from "lucide-react"; -import { motion, AnimatePresence } from "framer-motion"; -export function AIAssistantInterface() { - const [inputValue, setInputValue] = useState(""); - const [searchEnabled, setSearchEnabled] = useState(false); - const [deepResearchEnabled, setDeepResearchEnabled] = useState(false); - const [reasonEnabled, setReasonEnabled] = useState(false); - const [uploadedFiles, setUploadedFiles] = useState([]); - const [showUploadAnimation, setShowUploadAnimation] = useState(false); - const [activeCommandCategory, setActiveCommandCategory] = useState< - string | null - >(null); - const inputRef = useRef(null); +interface HistoryEntry { + id: string; + text: string; + createdAt: string; +} - const commandSuggestions = { - learn: [ - "Explain the Big Bang theory", - "How does photosynthesis work?", - "What are black holes?", - "Explain quantum computing", - "How does the human brain work?", - ], - code: [ - "Create a React component for a todo list", - "Write a Python function to sort a list", - "How to implement authentication in Next.js", - "Explain async/await in JavaScript", - "Create a CSS animation for a button", - ], - write: [ - "Write a professional email to a client", - "Create a product description for a smartphone", - "Draft a blog post about AI", - "Write a creative story about space exploration", - "Create a social media post about sustainability", - ], - }; +interface AIAssistantInterfaceProps { + onOpenDocuments?: () => void; + onOpenChat?: () => void; + history?: HistoryEntry[]; + onAddHistory?: (entry: HistoryEntry) => void; + onClearHistory?: () => void; +} - const handleUploadFile = () => { - setShowUploadAnimation(true); +export function AIAssistantInterface({ + onOpenDocuments, + onOpenChat, + history: externalHistory, + onAddHistory, + onClearHistory, +}: AIAssistantInterfaceProps) { + const [question, setQuestion] = useState(""); + const [drawerOpen, setDrawerOpen] = useState(false); + const [internalHistory, setInternalHistory] = useState([]); + const history = externalHistory ?? internalHistory; - // Simulate file upload with timeout - setTimeout(() => { - const newFile = `Document.pdf`; - setUploadedFiles((prev) => [...prev, newFile]); - setShowUploadAnimation(false); - }, 1500); - }; + const showHistoryBadge = useMemo(() => history.length > 0, [history.length]); - const handleCommandSelect = (command: string) => { - setInputValue(command); - setActiveCommandCategory(null); - - if (inputRef.current) { - inputRef.current.focus(); + const handleDocuments = () => { + if (onOpenDocuments) { + onOpenDocuments(); + return; } + console.log("[ZoeIA] Abrir fluxo de documentos"); + }; + + const handleOpenRealtimeChat = () => { + if (onOpenChat) { + onOpenChat(); + return; + } + console.log("[ZoeIA] Abrir chat em tempo real"); }; const handleSendMessage = () => { - if (inputValue.trim()) { - console.log("Sending message:", inputValue); - setInputValue(""); + const trimmed = question.trim(); + if (!trimmed) return; + + handlePersistHistory(trimmed); + console.log("[ZoeIA] Mensagem enviada para Zoe", trimmed); + setQuestion(""); + }; + + const RealtimeTriggerButton = () => ( + + ); + + const handlePersistHistory = (text: string) => { + const entry: HistoryEntry = { + id: `hist-${Date.now()}`, + text, + createdAt: new Date().toISOString(), + }; + + if (onAddHistory) { + onAddHistory(entry); + } else { + setInternalHistory((prev) => [...prev, entry]); + } + setDrawerOpen(true); + }; + + const handleClearHistory = () => { + if (onClearHistory) { + onClearHistory(); + } else { + setInternalHistory([]); } }; + const HistoryGlyph = () => ( + + + + + + + ); + return ( -
-
- {/* Logo with animated gradient */} -
- - - - - - - - - - - - - - - - - - - - - - - - - - - +
+
+
+ + + Zoe + +
+

Assistente Clínica Zoe

+

Olá, eu sou Zoe. Como posso ajudar hoje?

+
+
+ +
+ +
+ + Suas informações permanecem criptografadas e seguras com a equipe Zoe.
- {/* Welcome message */} -
- -

- Ready to assist you -

-

- Ask me anything or try one of the suggestions below +

+
+
+ + Informativo importante +
+

+ A Zoe é a assistente virtual da Clínica Zoe. Ela reúne informações sobre seus cuidados e orienta os próximos passos. + O atendimento é informativo e não substitui a avaliação de um profissional de saúde qualificado.

- +

+ Em situações de urgência, procure imediatamente o suporte médico presencial ou ligue para os serviços de emergência. +

+
+ +
+ + +
+ +
+

+ Estamos reunindo o histórico da sua jornada. Enquanto isso, você pode anexar exames, enviar dúvidas ou solicitar contato com a equipe Zoe. +

+
+
+ +
+ + setQuestion(event.target.value)} + onKeyDown={(event) => { + if (event.key === "Enter") { + event.preventDefault(); + handleSendMessage(); + } + }} + placeholder="Pergunte qualquer coisa para a Zoe" + className="border-none bg-transparent text-sm shadow-none focus-visible:ring-0" + /> +
+ + +
- {/* Input area with integrated functions and file upload */} -
-
- setInputValue(e.target.value)} - className="w-full text-gray-700 text-base outline-none placeholder:text-gray-400" - /> -
- - {/* Uploaded files */} - {uploadedFiles.length > 0 && ( -
-
- {uploadedFiles.map((file, index) => ( -
- - {file} - + {drawerOpen && ( +
- - {/* Command categories */} -
- } - label="Learn" - isActive={activeCommandCategory === "learn"} - onClick={() => - setActiveCommandCategory( - activeCommandCategory === "learn" ? null : "learn" - ) - } - /> - } - label="Code" - isActive={activeCommandCategory === "code"} - onClick={() => - setActiveCommandCategory( - activeCommandCategory === "code" ? null : "code" - ) - } - /> - } - label="Write" - isActive={activeCommandCategory === "write"} - onClick={() => - setActiveCommandCategory( - activeCommandCategory === "write" ? null : "write" - ) - } - /> -
- - {/* Command suggestions */} - - {activeCommandCategory && ( - -
-
-

- {activeCommandCategory === "learn" - ? "Learning suggestions" - : activeCommandCategory === "code" - ? "Coding suggestions" - : "Writing suggestions"} -

-
-
    - {commandSuggestions[ - activeCommandCategory as keyof typeof commandSuggestions - ].map((suggestion, index) => ( - handleCommandSelect(suggestion)} - className="p-3 hover:bg-gray-50 cursor-pointer transition-colors duration-75" - > -
    - {activeCommandCategory === "learn" ? ( - - ) : activeCommandCategory === "code" ? ( - - ) : ( - - )} - - {suggestion} - -
    -
    - ))} -
+ + Novo atendimento +
-
- )} -
+ +
+ {history.length === 0 ? ( +

+ Nenhuma conversa registrada ainda. Envie uma mensagem para começar um novo acompanhamento com a Zoe. +

+ ) : ( +
    + {[...history] + .reverse() + .map((entry) => ( +
  • + + + +
    +

    {entry.text}

    +

    + {new Date(entry.createdAt).toLocaleString("pt-BR", { + day: "2-digit", + month: "2-digit", + hour: "2-digit", + minute: "2-digit", + })} +

    +
    +
  • + ))} +
+ )} +
+
+ + )}
); } - -interface CommandButtonProps { - icon: React.ReactNode; - label: string; - isActive: boolean; - onClick: () => void; -} - -function CommandButton({ icon, label, isActive, onClick }: CommandButtonProps) { - return ( - -
- {icon} -
- - {label} - -
- ); -} diff --git a/susconecta/components/features/pacientes/chat-widget.tsx b/susconecta/components/features/pacientes/chat-widget.tsx index e80dd4b..99e8ee9 100644 --- a/susconecta/components/features/pacientes/chat-widget.tsx +++ b/susconecta/components/features/pacientes/chat-widget.tsx @@ -1,58 +1,30 @@ "use client"; -import { useMemo, useState } from "react"; -import { Sparkles, MessageCircle, X, Send } from "lucide-react"; -import { Input } from "@/components/ui/input"; +import { useEffect, useMemo, useState } from "react"; +import { ArrowLeft, Sparkles } from "lucide-react"; import { Button } from "@/components/ui/button"; +import { AIAssistantInterface } from "@/components/ZoeIA/ai-assistant-interface"; -const cannedSuggestions = [ - "Como remarcar minha consulta?", - "Quais documentos preciso levar?", - "Quero falar com suporte humano", -]; - -const supportAvailability = { - title: "Equipe disponível", - description: "Seg–Sex das 08h às 18h", -}; - -interface ChatMessage { +interface HistoryEntry { id: string; - author: "assistant" | "user"; text: string; - timestamp: string; + createdAt: string; } export function ChatWidget() { - const [open, setOpen] = useState(false); - const [input, setInput] = useState(""); - const [messages, setMessages] = useState(() => [ - { - id: "welcome", - author: "assistant", - text: "Olá! Sou sua assistente virtual. Posso ajudar a acompanhar consultas, exames e suporte geral.", - timestamp: new Date().toISOString(), - }, - ]); + const [assistantOpen, setAssistantOpen] = useState(false); + const [history, setHistory] = useState([]); - const toggle = () => setOpen((prev) => !prev); + useEffect(() => { + if (!assistantOpen) return; - const handleSend = () => { - const trimmed = input.trim(); - if (!trimmed) return; - const now = new Date().toISOString(); - setMessages((prev) => [ - ...prev, - { id: `user-${now}`, author: "user", text: trimmed, timestamp: now }, - { - id: `assistant-${now}`, - author: "assistant", - text: "Recebi sua mensagem! Nossa equipe retornará em breve.", - timestamp: now, - }, - ]); - setInput(""); - }; + const original = document.body.style.overflow; + document.body.style.overflow = "hidden"; + + return () => { + document.body.style.overflow = original; + }; + }, [assistantOpen]); const gradientRing = useMemo( () => ( @@ -64,119 +36,72 @@ export function ChatWidget() { [] ); + const openAssistant = () => setAssistantOpen(true); + const closeAssistant = () => setAssistantOpen(false); + + const handleOpenDocuments = () => { + console.log("[ChatWidget] Abrindo fluxo de documentos"); + closeAssistant(); + }; + + const handleOpenChat = () => { + console.log("[ChatWidget] Encaminhando para chat humano"); + closeAssistant(); + }; + + const handleAddHistory = (entry: HistoryEntry) => { + setHistory((prev) => [...prev, entry]); + }; + + const handleClearHistory = () => { + setHistory([]); + }; + return ( -
- {open && ( + <> + {assistantOpen && (
-
-
- -
-
-

Assistente RiseUp

-

Pronta para ajudar no que você precisar

-
- -
- -
- {messages.map((message) => ( -
- - {message.author === "assistant" ? : } - -
- {message.text} -
-
- ))} + + Voltar +
- -
-
-

{supportAvailability.title}

-

{supportAvailability.description}

-
-
- {cannedSuggestions.map((suggestion) => ( - - ))} -
-
- setInput(event.target.value)} - placeholder="Escreva sua mensagem" - className="border-none px-0 text-sm focus-visible:ring-0" - onKeyDown={(event) => { - if (event.key === "Enter") { - event.preventDefault(); - handleSend(); - } - }} - /> - -
+
+
)} - -
+
+ +
+ ); }