diff --git a/website/src/components/Messages/MessageTableEntry.tsx b/website/src/components/Messages/MessageTableEntry.tsx index 2bc17201..d272b960 100644 --- a/website/src/components/Messages/MessageTableEntry.tsx +++ b/website/src/components/Messages/MessageTableEntry.tsx @@ -3,7 +3,17 @@ import { boolean } from "boolean"; import NextLink from "next/link"; import { FlaggableElement } from "../FlaggableElement"; -export function MessageTableEntry({ item, idx }) { +interface Message { + text: string; + id: string; + is_assistant: boolean; +} +interface MessageTableEntryProps { + item: Message; + idx: number; +} +export function MessageTableEntry(props: MessageTableEntryProps) { + const { item, idx } = props; const bgColor = useColorModeValue(idx % 2 === 0 ? "bg-slate-800" : "bg-black", "bg-sky-900"); return ( diff --git a/website/src/components/Messages/MessageWithChildren.tsx b/website/src/components/Messages/MessageWithChildren.tsx new file mode 100644 index 00000000..8fcd8658 --- /dev/null +++ b/website/src/components/Messages/MessageWithChildren.tsx @@ -0,0 +1,105 @@ +import { Box, CircularProgress, Flex, HStack, StackDivider, Text, TextProps, StackProps } from "@chakra-ui/react"; +import { useState } from "react"; +import useSWR from "swr"; + +import fetcher from "src/lib/fetcher"; +import { MessageTableEntry } from "./MessageTableEntry"; +import { boolean } from "boolean"; + +const MessageHeaderProps: TextProps = { + align: "center", + fontSize: "xl", + py: "2", +}; + +const MessageStackProps: StackProps = { + spacing: "2", + alignItems: "start", + justifyContent: "center", + divider: , +}; + +interface MessageWithChildrenProps { + id: string; + depth?: number; + maxDepth?: number; + isOnlyChild?: boolean; +} + +export function MessageWithChildren(props: MessageWithChildrenProps) { + const { id, depth, maxDepth, isOnlyChild = true } = props; + + const [message, setMessage] = useState(null); + const [children, setChildren] = useState(null); + + const { isLoading } = useSWR(id ? `/api/messages/${id}` : null, fetcher, { + onSuccess: (data) => { + setMessage(data); + }, + onError: (err, key, config) => { + setMessage(null); + }, + }); + const { isLoading: isLoadingChildren } = useSWR(id ? `/api/messages/${id}/children` : null, fetcher, { + onSuccess: (data) => { + setChildren(data); + }, + onError: (err, key, config) => { + setChildren(null); + }, + }); + + const renderRecursive = maxDepth && ((depth && depth < maxDepth) || !depth); + const isFirst = depth === 0 || !depth; + const isFirstOrOnly = isFirst || boolean(isOnlyChild); + + if (isLoading || isLoadingChildren) { + return ; + } + + return ( + <> + {message && ( + <> + {isFirst ? "Message" : depth === 1 ? "Children" : "Ancestor"} + + + + + + + + + )} + {children && Array.isArray(children) && children.length > 0 ? ( + renderRecursive ? ( + + {children.map((item, idx) => ( + + + + ))} + + ) : ( + <> + {isFirstOrOnly ? "Children" : "Ancestor"} + + {children.map((item, idx) => ( + + + + ))} + + + ) + ) : ( + <> + )} + + ); +} diff --git a/website/src/pages/api/messages/[id]/children.ts b/website/src/pages/api/messages/[id]/children.ts new file mode 100644 index 00000000..9c8fb84a --- /dev/null +++ b/website/src/pages/api/messages/[id]/children.ts @@ -0,0 +1,27 @@ +import { getToken } from "next-auth/jwt"; + +const handler = async (req, res) => { + const token = await getToken({ req }); + + // Return nothing if the user isn't registered. + if (!token) { + res.status(401).end(); + return; + } + + const { id } = req.query; + + const messagesRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/messages/${id}/children`, { + method: "GET", + headers: { + "X-API-Key": process.env.FASTAPI_KEY, + "Content-Type": "application/json", + }, + }); + const messages = await messagesRes.json(); + + // Send recieved messages to the client. + res.status(200).json(messages); +}; + +export default handler; diff --git a/website/src/pages/api/messages/[id]/conversation.ts b/website/src/pages/api/messages/[id]/conversation.ts new file mode 100644 index 00000000..6fa8feb9 --- /dev/null +++ b/website/src/pages/api/messages/[id]/conversation.ts @@ -0,0 +1,27 @@ +import { getToken } from "next-auth/jwt"; + +const handler = async (req, res) => { + const token = await getToken({ req }); + + // Return nothing if the user isn't registered. + if (!token) { + res.status(401).end(); + return; + } + + const { id } = req.query; + + const messagesRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/messages/${id}/conversation`, { + method: "GET", + headers: { + "X-API-Key": process.env.FASTAPI_KEY, + "Content-Type": "application/json", + }, + }); + const messages = await messagesRes.json(); + + // Send recieved messages to the client. + res.status(200).json(messages); +}; + +export default handler; diff --git a/website/src/pages/api/messages/[id]/index.ts b/website/src/pages/api/messages/[id]/index.ts new file mode 100644 index 00000000..8e056532 --- /dev/null +++ b/website/src/pages/api/messages/[id]/index.ts @@ -0,0 +1,27 @@ +import { getToken } from "next-auth/jwt"; + +const handler = async (req, res) => { + const token = await getToken({ req }); + + // Return nothing if the user isn't registered. + if (!token) { + res.status(401).end(); + return; + } + + const { id } = req.query; + + const messageRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/messages/${id}`, { + method: "GET", + headers: { + "X-API-Key": process.env.FASTAPI_KEY, + "Content-Type": "application/json", + }, + }); + const message = await messageRes.json(); + + // Send recieved messages to the client. + res.status(200).json(message); +}; + +export default handler; diff --git a/website/src/pages/api/messages/[id]/parent.ts b/website/src/pages/api/messages/[id]/parent.ts new file mode 100644 index 00000000..de6fbac6 --- /dev/null +++ b/website/src/pages/api/messages/[id]/parent.ts @@ -0,0 +1,48 @@ +import { getToken } from "next-auth/jwt"; + +const handler = async (req, res) => { + const token = await getToken({ req }); + + // Return nothing if the user isn't registered. + if (!token) { + res.status(401).end(); + return; + } + + const { id } = req.query; + + if (!id) { + res.status(400).end(); + return; + } + + const messageRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/messages/${id}`, { + method: "GET", + headers: { + "X-API-Key": process.env.FASTAPI_KEY, + "Content-Type": "application/json", + }, + }); + + const message = await messageRes.json(); + + if (!message.parent_id) { + res.status(404).end(); + return; + } + + const parentRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/messages/${message.parent_id}`, { + method: "GET", + headers: { + "X-API-Key": process.env.FASTAPI_KEY, + "Content-Type": "application/json", + }, + }); + + const parent = await parentRes.json(); + + // Send recieved messages to the client. + res.status(200).json(parent); +}; + +export default handler; diff --git a/website/src/pages/api/messages/index.tsx b/website/src/pages/api/messages/index.ts similarity index 100% rename from website/src/pages/api/messages/index.tsx rename to website/src/pages/api/messages/index.ts diff --git a/website/src/pages/api/messages/user.tsx b/website/src/pages/api/messages/user.ts similarity index 100% rename from website/src/pages/api/messages/user.tsx rename to website/src/pages/api/messages/user.ts diff --git a/website/src/pages/messages/[id]/index.tsx b/website/src/pages/messages/[id]/index.tsx new file mode 100644 index 00000000..e778e74a --- /dev/null +++ b/website/src/pages/messages/[id]/index.tsx @@ -0,0 +1,64 @@ +import { Box, Container, Text, useColorModeValue } from "@chakra-ui/react"; +import Head from "next/head"; +import { useRouter } from "next/router"; +import { useState } from "react"; +import useSWR from "swr"; + +import fetcher from "src/lib/fetcher"; +import { MessageTableEntry } from "src/components/Messages/MessageTableEntry"; +import { LoadingScreen } from "src/components/Loading/LoadingScreen"; +import { MessageWithChildren } from "src/components/Messages/MessageWithChildren"; + +const MessageDetail = ({ id }) => { + const mainBg = useColorModeValue("bg-slate-300", "bg-slate-900"); + + const [parent, setParent] = useState(null); + + const { isLoading: isLoadingParent } = useSWR(id ? `/api/messages/${id}/parent` : null, fetcher, { + onSuccess: (data) => { + setParent(data); + }, + onError: (err, key, config) => { + setParent(null); + }, + }); + + if (isLoadingParent) { + return ; + } + return ( + <> + + Open Assistant + + +
+ + {parent && ( + <> + + Parent + + + + + + )} + + + + +
+ + ); +}; + +MessageDetail.getInitialProps = async ({ query }) => { + const { id } = query; + return { id }; +}; + +export default MessageDetail;