Files
Rogelio 325f1ef439 ic
2025-10-13 18:16:25 +00:00

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>
);
}