Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/app/actions/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,26 @@ import { formatHistory } from "@/utils/shared";
import { GoogleGenerativeAIFetchError } from "@google/generative-ai";
import { revalidatePath } from "next/cache";
import { ChatMessage } from "../types";
import { saveMessage } from "./supabase";
import { saveMessage, updateChatTitle } from "./supabase";

export async function sendQuery(
data: FormData,
chatId: string,
history: ChatMessage[],
) {
try {

const prompt = data.get("prompt") as string;
const chatSession = startChatSession({ history: formatHistory(history) });

const [result] = await Promise.all([
chatSession.sendMessage(prompt),
saveMessage(prompt, "user", chatId),
]);
if (history.length===0){
await updateChatTitle(chatId,prompt)
}

const response = result.response.text();
await saveMessage(response, "model", chatId);

Expand All @@ -38,3 +44,4 @@ export async function sendQuery(
return { response: null, error: error as string };
}
}

19 changes: 17 additions & 2 deletions src/app/actions/supabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
import { createClient } from "@/supabase/server";
import { ChatMessage, Role } from "../types";


export async function getChatTitles(): Promise<
{ id: string; chat_id: string; title: string }[] | null
> {
{ id: string; chat_id: string; title: string }[] | null>
{
const supabase = await createClient();
const { data, error } = await supabase.rpc("get_chat_titles");

Expand All @@ -17,6 +18,20 @@ export async function getChatTitles(): Promise<
return data;
}

export async function updateChatTitle(chatId: string, title: string) {
const supabase = await createClient()
const { data, error } = await supabase
.from('chats')
.update({ title })
.eq('chat_id', chatId)
.single();

if (error) {
console.error(error)
}
return data
}

export async function fetchChatHistory(chatId: string) {
try {
const supabase = await createClient();
Expand Down
71 changes: 56 additions & 15 deletions src/app/components/PromptForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { useParams } from "next/navigation";
import { useActionState, useRef } from "react";
import { sendQuery } from "../actions/chat";
import { ChatMessage, PromptState, Role } from "../types";
import { Box, TextField, Button, Alert } from "@mui/material";
import SendIcon from '@mui/icons-material/Send';

export default function PromptForm({
onPrompt,
Expand Down Expand Up @@ -43,28 +45,67 @@ export default function PromptForm({
}

return (
<form
<Box
component="form"
action={queryAction}
ref={formRef}
className="sticky bottom-0 bg-slate-400 py-4 px-2"
sx={{
position: 'sticky',
bottom: 0,
bgcolor: 'background.paper',
p: 2,
borderTop: 1,
borderColor: 'divider',
display: 'flex',
flexDirection: 'column',
borderRadius:'20px',
border:'2px solid black',
gap: 1
}}
>
<textarea
name="prompt"
id="prompt"
disabled={queryIsPending}
placeholder="Ask a question"
className="w-full p-2 rounded-md border border-gray-300 focus:outline-hidden focus:border-blue-500 max-h-32 min-h-16 overflow-y-auto resize-none"
></textarea>
<button type="submit" disabled={queryIsPending}>
{queryIsPending ? "Loading..." : "Send"}
</button>
<Box sx={{ display: 'flex', gap: 1 }}>
<TextField
name="prompt"
id="prompt"
disabled={queryIsPending}
placeholder="Ask a question..."
multiline
maxRows={4}
fullWidth
variant="outlined"
size="medium"
sx={{
'& .MuiOutlinedInput-root': {
backgroundColor: 'background.paper',
}
}}
/>
<Button
type="submit"
disabled={queryIsPending}
variant="contained"
color="primary"
sx={{
minWidth: '56px',
height: '56px',
borderRadius: '8px'
}}
>
<SendIcon />
</Button>
</Box>

{formState?.error && !queryIsPending && (
<FormError error={formState.error} />
)}
</form>
</Box>
);
}

function FormError({ error }: { error: string }) {
return <div className="text-red-500">{error}</div>;
}
return (
<Alert severity="error" sx={{ mt: 1 }}>
{error}
</Alert>
);
}
76 changes: 62 additions & 14 deletions src/app/components/chat-interface/ChatInterface.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import { useRouter } from "next/navigation";
import { useLayoutEffect, useState } from "react";
import Markdown from "react-markdown";
import PromptForm from "../PromptForm";
import { Box, Paper, Container } from "@mui/material";
import PersonIcon from '@mui/icons-material/Person';
import SmartToyIcon from '@mui/icons-material/SmartToy';

export default function ChatInterface({
history,
Expand Down Expand Up @@ -35,31 +38,76 @@ export default function ChatInterface({
}

return (
<div className="flex flex-col justify-end gap-y-16 px-2 py-4">
<Chats chats={chats} />
<PromptForm onPrompt={handlePrompt} history={chats} />
</div>
<Container>
<Box
sx={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
height: '100%',
py: 4,
gap: 2
}}
>
<Chats chats={chats} />
<PromptForm onPrompt={handlePrompt} history={chats} />
</Box>
</Container>
);
}

function Chats({ chats }: { chats: ChatMessage[] }) {
return (
<ol className="flex flex-col gap-y-4">
<Box sx={{
display: 'flex',
flexDirection: 'column',
gap: 2,
overflowY: 'auto',
flexGrow: 1
}}>
{chats.map((chat, index) => {
const isUser = chat.role === "user";
return (
<li
<Box
key={chat.id ?? index}
className={`max-w-[75%] overflow-x-auto px-3 py-2 rounded-md ${isUser ? "self-end" : ""} ${
isUser ? "bg-zinc-300" : "bg-lime-100"
}`}
sx={{
display: 'flex',
justifyContent: isUser ? 'flex-end' : 'flex-start',
width: '100%'
}}
>
<div>
<Markdown>{chat.message}</Markdown>
</div>
</li>
<Paper
elevation={1}
sx={{
maxWidth: '75%',
p: 2,
borderRadius: 2,
backgroundColor: isUser ? 'primary.light' : 'success.light',
display: 'flex',
gap: 1.5,
alignItems: 'flex-start'
}}
>
{/* Icon for the message sender */}
{isUser ? (
<PersonIcon color="primary" />
) : (
<SmartToyIcon color="success" />
)}

{/* Message content */}
<Box sx={{
'& > div': {
'& > p': { m: 0 },
color: isUser ? 'primary.dark' : 'success.dark'
}
}}>
<Markdown>{chat.message}</Markdown>
</Box>
</Paper>
</Box>
);
})}
</ol>
</Box>
);
}
2 changes: 1 addition & 1 deletion src/app/components/chat-links/ChatLinks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default async function ChatLinks() {
<ul className="flex flex-col gap-y-2">
{chats?.map(function renderLink(chat) {
return (
<li key={chat.id}>
<li key={chat.chat_id}>
<ChatLink id={chat.chat_id}>{chat.title}</ChatLink>
</li>
);
Expand Down