diff --git a/src/client/ChatPage.jsx b/src/client/ChatPage.jsx
deleted file mode 100644
index d556921..0000000
--- a/src/client/ChatPage.jsx
+++ /dev/null
@@ -1,213 +0,0 @@
-// import { User } from '@wasp/entities';
-import { useQuery } from '@wasp/queries'
-import getChats from '@wasp/queries/getChats'
-import getConversations from '@wasp/queries/getConversations'
-import logout from '@wasp/auth/logout';
-import { useState, useEffect, useRef } from 'react';
-// import { Chat, Conversation } from '@wasp/entities'
-import { Link } from '@wasp/router'
-import Markdown from "markdown-to-jsx";
-
-import logo from './static/captn-logo.png'
-import createChat from '@wasp/actions/createChat'
-import updateConversation from '@wasp/actions/updateConversation'
-import getAgentResponse from '@wasp/actions/getAgentResponse'
-import { useHistory } from 'react-router-dom';
-
-const ChatsList = ({ chats }) => {
- if (!chats?.length) return
- return (
-
- {chats.map((chat, idx) => (
-
-
-
-
-
- ))}
-
- )
- }
-
-const Loader = () => {
- return (
-
- )
-}
-
-const ConversationsList = ({ conversations }) => {
- if (!conversations?.length) return No conversations
-
- return (
-
- {conversations.map((conversation, idx) => {
- const conversationBgColor = conversation.role === "user" ? "captn-light-blue" : "captn-dark-blue";
- const conversationTextColor = conversation.role === "user" ? "captn-dark-blue" : "captn-light-cream";
- const conversationLogo = conversation.role === "user" ?
:
- return (
-
-
-
-
-
- {conversationLogo}
-
-
- {conversation.content}
-
-
-
-
- );
- })}
-
- )
-
-}
-
-// export default function ChatPage({ user }: { user: User }, props: RouteComponentProps<{ id: string }>) {
-export default function ChatPage(props) {
- const [isLoading, setIsLoading] = useState(false);
- const [chatConversations, setChatConversations] = useState([{}]);
- const [conversationId, setConversationId] = useState(null);
- const chatContainerRef = useRef(null);
- // const [chatId, setChatId] = useState(null);
-
- const { data: chats, isLoading: isLoadingChats } = useQuery(getChats)
- const { data: conversations, isLoading: isLoadingConversations } = useQuery(getConversations,
- {
- chatId: Number(props.match.params.id),
- }
- )
- // if(conversations) {
- // setChatConversations(conversations.conversation)
- // }
- // setChatId(props.match.params.id)
- // console.log(`chatId: ${chatId}`)
-
- useEffect(() => {
- if (chatContainerRef.current) {
- chatContainerRef.current.scrollTop = chatContainerRef.current.scrollHeight;
- }
- }, [conversations]);
-
-
- const history = useHistory();
-
- const chatContainerClass = `flex h-full flex-col items-center justify-between pb-24 overflow-y-auto bg-captn-light-blue ${isLoading ? 'opacity-40' : 'opacity-100'}`
-
- const handleClick = async (event) => {
- event.preventDefault()
- try {
- const newChatConversations = await createChat();
- setChatConversations(newChatConversations.conversation);
- setConversationId(newChatConversations.id);
- history.push(`/chat/${newChatConversations.chatId}`);
- } catch (err) {
- window.alert('Error: ' + err.message)
- }
- // console.log(event.target);
- // console.log(event.currentTarget);
- };
-
- const handleFormSubmit = async (event) => {
- event.preventDefault()
- try {
- const target = event.target
- const userQuery = target.userQuery.value
- target.reset()
-
- // 1. add new conversation to table
- const payload = {
- conversation_id: conversations.id,
- conversations: [...conversations.conversation, ...[{ "role": "user", "content": userQuery }]]
- }
- await updateConversation(payload)
- // 2. call backend python server to get agent response
- setIsLoading(true)
- const response = await getAgentResponse({
- message: userQuery,
- conv_id: payload.conversation_id,
- })
- setIsLoading(false)
- // 3. add agent response as new conversation in the table
- const openAIPayload = {
- conversation_id: conversations.id,
- conversations: [...payload.conversations, ...[{ "role": "assistant", "content": response.content }]]
- }
- await updateConversation(openAIPayload)
- } catch (err) {
- window.alert('Error: ' + err.message)
- }
-
- }
-
- return (
-
-
-
-
-
-
-
- {conversations && }
-
- {isLoading &&
}
- {props.match.params.id ? (
) :
Please initiate a new chat or select existing chats to resume your conversation.
}
-
-
-
-
- {/*
-
-
*/}
-
- )
-}
\ No newline at end of file
diff --git a/src/client/ChatPage.tsx b/src/client/ChatPage.tsx
new file mode 100644
index 0000000..ce6ed2e
--- /dev/null
+++ b/src/client/ChatPage.tsx
@@ -0,0 +1,39 @@
+import { useQuery } from "@wasp/queries";
+import getChats from "@wasp/queries/getChats";
+
+import CreateNewChatBtn from "./components/CreateNewChat";
+import ChatsList from "./components/ChatList";
+import ConversationWrapper from "./components/ConversationWrapper";
+
+export default function ChatPage() {
+ let { data: chats, isLoading: isLoadingChats } = useQuery(getChats);
+
+ return (
+
+ );
+}
diff --git a/src/client/components/ChatList.tsx b/src/client/components/ChatList.tsx
new file mode 100644
index 0000000..e1fc974
--- /dev/null
+++ b/src/client/components/ChatList.tsx
@@ -0,0 +1,36 @@
+import { Link } from "@wasp/router";
+import type { Chat } from "@wasp/entities";
+
+export default function ChatsList(chats: Chat[]) {
+ return (
+
+ {
+ // Todo: remove the below ignore comment
+ // @ts-ignore
+ chats.chats.map((chat, idx) => (
+
+
+
+
+
+ ))
+ }
+
+ );
+}
diff --git a/src/client/components/ConversationList.tsx b/src/client/components/ConversationList.tsx
new file mode 100644
index 0000000..508b930
--- /dev/null
+++ b/src/client/components/ConversationList.tsx
@@ -0,0 +1,85 @@
+import Markdown from "markdown-to-jsx";
+
+import type { Conversation } from "@wasp/entities";
+
+import logo from "../static/captn-logo.png";
+
+export default function ConversationsList(conversations: Conversation[]) {
+ return (
+
+ {
+ // Todo: remove the below ignore comment
+ // @ts-ignore
+ conversations.conversations.map((conversation, idx) => {
+ const conversationBgColor =
+ conversation.role === "user"
+ ? "captn-light-blue"
+ : "captn-dark-blue";
+ const conversationTextColor =
+ conversation.role === "user"
+ ? "captn-dark-blue"
+ : "captn-light-cream";
+ const conversationLogo =
+ conversation.role === "user" ? (
+
+ ) : (
+
+ );
+ return (
+
+
+
+
+ {conversationLogo}
+
+
+ {conversation.content}
+
+
+
+
+ );
+ })
+ }
+
+ );
+}
diff --git a/src/client/components/ConversationWrapper.tsx b/src/client/components/ConversationWrapper.tsx
new file mode 100644
index 0000000..65226d5
--- /dev/null
+++ b/src/client/components/ConversationWrapper.tsx
@@ -0,0 +1,206 @@
+import React from "react";
+import { useState, useRef, useEffect } from "react";
+import { useParams } from "react-router";
+import { Redirect, useLocation } from "react-router-dom";
+
+import { useQuery } from "@wasp/queries";
+import updateConversation from "@wasp/actions/updateConversation";
+import getAgentResponse from "@wasp/actions/getAgentResponse";
+import getConversations from "@wasp/queries/getConversations";
+
+// import type { Conversation } from "@wasp/entities";
+
+import ConversationsList from "./ConversationList";
+import Loader from "./Loader";
+
+// A custom hook that builds on useLocation to parse
+// the query string for you.
+function getQueryParam(paramName: string) {
+ const { search } = useLocation();
+ return React.useMemo(() => new URLSearchParams(search), [search]).get(
+ paramName
+ );
+}
+
+export async function triggerSubmit(
+ loginMsgQuery: string,
+ // Todo: remove the below ignore comment
+ // @ts-ignore
+ submitBtnRef,
+ // Todo: remove the below ignore comment
+ // @ts-ignore
+ formInputRef
+) {
+ if (loginMsgQuery) {
+ setTimeout(() => {
+ formInputRef.current.value = decodeURIComponent(loginMsgQuery);
+ submitBtnRef.current.click();
+ }, 500);
+
+ // set it in
+ }
+}
+
+export default function ConversationWrapper() {
+ // Todo: remove the below ignore comment
+ // @ts-ignore
+ const { id } = useParams();
+ const [isLoading, setIsLoading] = useState(false);
+ const loginMsgQuery = getQueryParam("msg");
+ const chatContainerRef = useRef(null);
+ const submitBtnRef = useRef(null);
+ const formInputRef = useRef(null);
+ const {
+ data: conversations,
+ isLoading: isConversationLoading,
+ error: isConversationError,
+ } = useQuery(
+ getConversations,
+ {
+ chatId: Number(id),
+ },
+ { enabled: !!id }
+ );
+
+ // componentDidMount
+ useEffect(() => {
+ // Todo: remove the below ignore comment
+ // @ts-ignore
+ triggerSubmit(loginMsgQuery, submitBtnRef, formInputRef);
+ }, [loginMsgQuery]);
+
+ useEffect(() => {
+ if (chatContainerRef.current) {
+ // Todo: remove the below ignore comment
+ // @ts-ignore
+ chatContainerRef.current.scrollTop =
+ // Todo: remove the below ignore comment
+ // @ts-ignore
+ chatContainerRef.current.scrollHeight;
+ }
+ }, [conversations]);
+
+ async function callAgent(userQuery: string) {
+ try {
+ // 1. add new conversation to table
+ const payload = {
+ conversation_id: conversations.id,
+ conversations: [
+ // Todo: remove the below ignore comment
+ // @ts-ignore
+ ...conversations.conversation,
+ ...[{ role: "user", content: userQuery }],
+ ],
+ };
+
+ await updateConversation(payload);
+ // 2. call backend python server to get agent response
+ setIsLoading(true);
+ const response = await getAgentResponse({
+ message: userQuery,
+ conv_id: payload.conversation_id,
+ });
+ // 3. add agent response as new conversation in the table
+ const openAIPayload = {
+ conversation_id: conversations.id,
+ conversations: [
+ ...payload.conversations,
+ // Todo: remove the below ignore comment
+ // @ts-ignore
+ ...[{ role: "assistant", content: response.content }],
+ ],
+ };
+ await updateConversation(openAIPayload);
+ setIsLoading(false);
+ } catch (err: any) {
+ setIsLoading(false);
+ window.alert("Error: " + err.message);
+ }
+ }
+
+ const handleFormSubmit = async (event: React.FormEvent) => {
+ event.preventDefault();
+ const target = event.target;
+ // Todo: remove the below ignore comment
+ // @ts-ignore
+ const userQuery = target.userQuery.value;
+ // Todo: remove the below ignore comment
+ // @ts-ignore
+ target.reset();
+ callAgent(userQuery);
+ };
+
+ if (isConversationLoading && !!id) return ;
+ if (isConversationError) {
+ return (
+ <>
+
+ >
+ );
+ }
+
+ const chatContainerClass = `flex h-full flex-col items-center justify-between pb-24 overflow-y-auto bg-captn-light-blue ${
+ isLoading ? "opacity-40" : "opacity-100"
+ }`;
+
+ return (
+
+
+
+
+
+ {conversations && (
+ // Todo: remove the below ignore comment
+ // @ts-ignore
+
+ )}
+
+ {isLoading &&
}
+ {id ? (
+
+ ) : (
+
+ Please initiate a new chat or select existing chats to resume
+ your conversation.
+
+ )}
+
+
+
+
+ );
+}
diff --git a/src/client/components/CreateNewChat.tsx b/src/client/components/CreateNewChat.tsx
new file mode 100644
index 0000000..eb4eb06
--- /dev/null
+++ b/src/client/components/CreateNewChat.tsx
@@ -0,0 +1,45 @@
+import { useHistory } from "react-router-dom";
+
+import createChat from "@wasp/actions/createChat";
+
+export default function CreateNewChatBtn() {
+ const history = useHistory();
+
+ const handleClick = async (
+ event: React.MouseEvent
+ ): Promise => {
+ event.preventDefault();
+ try {
+ const newChatConversations = await createChat();
+ history.push(`/chat/${newChatConversations.chatId}`);
+ } catch (err: any) {
+ window.alert("Error: " + err.message);
+ }
+ };
+ return (
+
+
+
+ );
+}
diff --git a/src/client/components/Loader.tsx b/src/client/components/Loader.tsx
new file mode 100644
index 0000000..f10ad50
--- /dev/null
+++ b/src/client/components/Loader.tsx
@@ -0,0 +1,7 @@
+export default function Loader() {
+ return (
+
+ );
+}