forked from innovacion/Mayacontigo
ic
This commit is contained in:
184
apps/voz-del-cliente/gui/components/Chat.tsx
Normal file
184
apps/voz-del-cliente/gui/components/Chat.tsx
Normal file
@@ -0,0 +1,184 @@
|
||||
import { FormEvent, useState, useEffect, useRef } from "react";
|
||||
import { ChatMessage } from "./ChatMessage";
|
||||
import "material-symbols";
|
||||
|
||||
export { Chat };
|
||||
|
||||
interface Message {
|
||||
user: boolean;
|
||||
content: string;
|
||||
withDeepResearch: boolean;
|
||||
}
|
||||
|
||||
interface ChatProps {
|
||||
assistant: string;
|
||||
messages: Message[];
|
||||
pushMessage: (message: Message) => void;
|
||||
conversationId: string;
|
||||
setConversationId: (id: string) => void;
|
||||
setAssistantName: (name: string) => void;
|
||||
receivingMsg: boolean;
|
||||
setReceivingMsg: (receiving: boolean) => void;
|
||||
onStartConversation: (
|
||||
user: string,
|
||||
assistant: string,
|
||||
withDeepResearch: boolean
|
||||
) => Promise<string>;
|
||||
sendIcon: string;
|
||||
userAvatar: string;
|
||||
botAvatar: string;
|
||||
onFeedback?: (key: string, rating: string) => Promise<void>;
|
||||
}
|
||||
|
||||
function Chat({
|
||||
assistant,
|
||||
messages,
|
||||
pushMessage,
|
||||
conversationId,
|
||||
setConversationId,
|
||||
setAssistantName,
|
||||
receivingMsg,
|
||||
setReceivingMsg,
|
||||
onStartConversation,
|
||||
sendIcon,
|
||||
userAvatar,
|
||||
botAvatar,
|
||||
onFeedback,
|
||||
}: ChatProps) {
|
||||
const [input, setInput] = useState("");
|
||||
const [isDeepResearch, setIsDeepResearch] = useState(false);
|
||||
const bottomRef = useRef(null);
|
||||
|
||||
async function startConversation() {
|
||||
const newId = await onStartConversation("user", assistant, isDeepResearch);
|
||||
setConversationId(newId);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setAssistantName(assistant);
|
||||
startConversation();
|
||||
}, []);
|
||||
|
||||
function changeInput(e: FormEvent<HTMLInputElement>) {
|
||||
e.preventDefault();
|
||||
setInput(e.currentTarget.value);
|
||||
}
|
||||
|
||||
async function handleSubmit(e: FormEvent) {
|
||||
e.preventDefault();
|
||||
const trimmedInput = input.trim();
|
||||
if (!trimmedInput) return;
|
||||
|
||||
pushMessage({
|
||||
user: true,
|
||||
content: trimmedInput,
|
||||
withDeepResearch: isDeepResearch,
|
||||
});
|
||||
setInput("");
|
||||
pushMessage({
|
||||
user: false,
|
||||
content: trimmedInput,
|
||||
withDeepResearch: isDeepResearch,
|
||||
});
|
||||
}
|
||||
|
||||
function toggleDeepResearch() {
|
||||
setIsDeepResearch(!isDeepResearch);
|
||||
}
|
||||
|
||||
function scrollToBottom() {
|
||||
// @ts-expect-error idk
|
||||
bottomRef.current.scrollIntoView({ behavior: "smooth" });
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-1 flex-col items-center bg-slate-100 h-screen">
|
||||
<div className="mt-5 w-3/5 flex-1 overflow-y-auto scrollbar min-h-0">
|
||||
{messages.map((message, index) => (
|
||||
<ChatMessage
|
||||
key={index}
|
||||
isUser={message.user}
|
||||
content={message.content}
|
||||
event={scrollToBottom}
|
||||
conversationId={conversationId}
|
||||
withDeepResearch={message.withDeepResearch}
|
||||
setReceivingMsg={setReceivingMsg}
|
||||
userAvatar={userAvatar}
|
||||
botAvatar={botAvatar}
|
||||
onFeedback={onFeedback}
|
||||
/>
|
||||
))}
|
||||
<div ref={bottomRef}></div>
|
||||
</div>
|
||||
<form
|
||||
className="flex-shrink-0 ml-5 my-5 flex w-3/4 items-center justify-center mr-5"
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<div
|
||||
className="
|
||||
flex h-auto w-[90%] flex-col
|
||||
rounded-3xl border border-gray-300 bg-white
|
||||
transition-all duration-300
|
||||
focus-within:border-blue-500 focus-within:ring-2 focus-within:ring-blue-300
|
||||
"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
autoFocus
|
||||
value={input}
|
||||
onChange={changeInput}
|
||||
disabled={receivingMsg}
|
||||
placeholder="¡Pregúntame algo!"
|
||||
className="
|
||||
w-full resize-none border-none bg-transparent
|
||||
p-4 pb-2 text-base
|
||||
focus:outline-none focus:ring-0
|
||||
"
|
||||
/>
|
||||
<div className="self-start px-3 pb-2">
|
||||
<button
|
||||
title="Obtén respuestas detalladas"
|
||||
type="button"
|
||||
onClick={toggleDeepResearch}
|
||||
disabled={receivingMsg}
|
||||
className={`flex cursor-pointer items-center rounded-lg p-1 transition-colors duration-200
|
||||
${isDeepResearch ? "bg-red-100" : "hover:bg-slate-100"}
|
||||
`}
|
||||
>
|
||||
<span
|
||||
className="material-symbols-rounded align-middle"
|
||||
style={
|
||||
isDeepResearch
|
||||
? { color: "rgb(235, 0, 41)" }
|
||||
: { color: "rgb(107, 114, 128)" }
|
||||
}
|
||||
>
|
||||
travel_explore
|
||||
</span>
|
||||
<span
|
||||
className="ml-2 text-sm font-medium"
|
||||
style={
|
||||
isDeepResearch
|
||||
? { color: "rgb(235, 0, 41)" }
|
||||
: { color: "rgb(107, 114, 128)" }
|
||||
}
|
||||
>
|
||||
Deep Research
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className={`btn-error ml-4 hover:border-red-200 hover:opacity-80 ${
|
||||
!input.trim() ? "opacity-50" : ""
|
||||
}`}
|
||||
disabled={receivingMsg || !input.trim()}
|
||||
>
|
||||
<img src={sendIcon} alt="Send" className="h-14 w-14" />
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user