forked from innovacion/Mayacontigo
185 lines
5.0 KiB
TypeScript
185 lines
5.0 KiB
TypeScript
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>
|
|
);
|
|
}
|