Skip to content

Commit a68d23b

Browse files
committed
updated chat section
1 parent 9f7fa14 commit a68d23b

File tree

8 files changed

+269
-84
lines changed

8 files changed

+269
-84
lines changed

client/package-lock.json

Lines changed: 57 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@
1212
"dependencies": {
1313
"@hookform/resolvers": "^3.9.0",
1414
"@radix-ui/react-accordion": "^1.2.0",
15+
"@radix-ui/react-avatar": "^1.1.1",
1516
"@radix-ui/react-dialog": "^1.1.1",
1617
"@radix-ui/react-dropdown-menu": "^2.1.2",
1718
"@radix-ui/react-icons": "^1.3.0",
1819
"@radix-ui/react-label": "^2.1.0",
1920
"@radix-ui/react-navigation-menu": "^1.2.0",
21+
"@radix-ui/react-scroll-area": "^1.2.0",
2022
"@radix-ui/react-select": "^2.1.1",
2123
"@radix-ui/react-slot": "^1.1.0",
2224
"@radix-ui/react-toast": "^1.2.1",
Lines changed: 85 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
1-
import Sidebar from "../Sidebar/Sidebar";
2-
import MobileSidebar from "../MobileSidebar/MobileSidebar";
3-
import { PlaceholdersAndVanishInput } from '../ui/placeholders-and-vanish-input';
4-
import { useState, useEffect, useRef } from 'react';
5-
import axios from 'axios';
1+
import { useState, useEffect, useRef } from 'react'
2+
import axios from 'axios'
3+
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
4+
import { PlaceholdersAndVanishInput } from '@/components/ui/placeholders-and-vanish-input'
5+
import Sidebar from "@/components/Sidebar/Sidebar"
6+
import MobileSidebar from "@/components/MobileSidebar/MobileSidebar"
67

78
const backendUrl = import.meta.env.VITE_BACKEND_URL || 'http://localhost:5000';
89

910
interface Message {
10-
query: string;
11-
response: string;
12-
isLoading?: boolean;
11+
query: string
12+
response: string
13+
isLoading?: boolean
1314
}
1415

1516
export default function Chat() {
16-
const [messages, setMessages] = useState<Array<Message>>([]);
17-
const messagesEndRef = useRef<HTMLDivElement>(null);
18-
19-
17+
const [messages, setMessages] = useState<Array<Message>>([])
18+
const scrollAreaRef = useRef<HTMLDivElement>(null)
2019

2120
const placeholders = [
2221
'How to set up a React project with Vite?',
@@ -25,114 +24,129 @@ export default function Chat() {
2524
'What is a closure in JavaScript?',
2625
'How to optimize React app performance?',
2726
'Explain event delegation in JavaScript',
28-
];
27+
]
2928

3029
const scrollToBottom = () => {
31-
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
32-
};
30+
if (scrollAreaRef.current) {
31+
scrollAreaRef.current.scrollTop = scrollAreaRef.current.scrollHeight
32+
}
33+
}
34+
3335
useEffect(() => {
34-
scrollToBottom();
35-
}, [messages]);
36+
scrollToBottom()
37+
}, [messages])
3638

37-
const username = localStorage.getItem('devhub_username');
39+
const username = typeof window !== 'undefined' ? localStorage.getItem('devhub_username') : null
3840

3941
useEffect(() => {
4042
const fetchChatHistory = async () => {
4143
if (!username) {
42-
console.error('Username not found in localStorage');
43-
return;
44+
console.error('Username not found in localStorage')
45+
return
4446
}
4547

4648
try {
4749
const result = await axios.get(`${backendUrl}/chat_history`, {
4850
params: { username },
4951
withCredentials: true
50-
});
52+
})
5153

5254
const chatHistory =
5355
result.data.chat_history.length > 0
5456
? result.data.chat_history
55-
: [{ query: 'Hi', response: 'Welcome to DevHub!' }];
56-
setMessages(chatHistory);
57+
: [{ query: 'Hi', response: 'Welcome to DevHub!' }]
58+
setMessages(chatHistory)
5759
} catch (error) {
58-
console.error('Error fetching chat history:', error);
60+
console.error('Error fetching chat history:', error)
5961
}
60-
};
62+
}
6163

62-
fetchChatHistory();
63-
}, [username]);
64+
fetchChatHistory()
65+
}, [username])
6466

6567
const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
66-
e.preventDefault();
67-
const query = e.currentTarget.querySelector('input')?.value;
68-
if (!query) return;
68+
e.preventDefault()
69+
const query = e.currentTarget.querySelector('input')?.value
70+
if (!query) return
6971

7072
if (!username) {
71-
console.error('Username not found in localStorage');
72-
return;
73+
console.error('Username not found in localStorage')
74+
return
7375
}
7476

7577
setMessages((prevMessages) => [
7678
...prevMessages,
7779
{ query, response: '', isLoading: true },
78-
]);
80+
])
7981

8082
try {
81-
const result = await axios.post(`${backendUrl}/chat`, { query, username }, { withCredentials: true });
83+
const result = await axios.post(`${backendUrl}/chat`, { query, username }, { withCredentials: true })
8284

8385
setMessages((prevMessages) => {
84-
const newMessages = [...prevMessages];
85-
const lastMessage = newMessages[newMessages.length - 1];
86-
lastMessage.response = result.data.result;
87-
lastMessage.isLoading = false;
88-
return newMessages;
89-
});
86+
const newMessages = [...prevMessages]
87+
const lastMessage = newMessages[newMessages.length - 1]
88+
lastMessage.response = result.data.result
89+
lastMessage.isLoading = false
90+
return newMessages
91+
})
9092
} catch (error) {
91-
console.error('Error querying the model:', error);
93+
console.error('Error querying the model:', error)
9294
setMessages((prevMessages) => {
93-
const newMessages = [...prevMessages];
94-
const lastMessage = newMessages[newMessages.length - 1];
95-
lastMessage.response = 'An error occurred while processing your request.';
96-
lastMessage.isLoading = false;
97-
return newMessages;
98-
});
95+
const newMessages = [...prevMessages]
96+
const lastMessage = newMessages[newMessages.length - 1]
97+
lastMessage.response = 'An error occurred while processing your request.'
98+
lastMessage.isLoading = false
99+
return newMessages
100+
})
99101
}
100-
};
102+
}
103+
101104
return (
102105
<div className="grid min-h-screen w-full md:grid-cols-[220px_1fr] lg:grid-cols-[280px_1fr]">
103106
<Sidebar />
104107
<div className="flex flex-col h-screen">
105-
<header className="sticky top-0 z-10 bg-background border-b">
108+
<header className="sticky top-0 z-10">
106109
<MobileSidebar />
107110
</header>
108-
<div className='h-screen md:w-[60%] w-full mx-auto justify-center flex flex-col'>
109-
<div className='flex-grow overflow-y-auto px-4'>
110-
{messages.map((message, index) => (
111-
<div key={index} className='mb-4'>
112-
113-
<p className='font-bold'>You:</p>
114-
<p>{message.query}</p>
115-
111+
<main className="flex flex-col flex-grow p-4 overflow-hidden">
112+
<div className="flex flex-col h-full md:w-[80%] lg:w-[70%] xl:w-[60%] mx-auto flex-grow overflow-y-auto p-4" ref={scrollAreaRef}>
113+
<div className="space-y-4 p-4">
114+
{messages.map((message, index) => (
115+
<div key={index} className="mb-6 last:mb-0">
116+
<div className="flex items-start justify-end">
117+
<div className="flex-grow text-right p-4">
118+
<p className="text-sm">{message.query}</p>
119+
</div>
120+
121+
</div>
122+
<div className="flex items-start mb-4">
123+
<Avatar className="mr-4">
124+
<AvatarImage src="/ai-avatar.png" alt="dh" />
125+
<AvatarFallback>dh</AvatarFallback>
126+
</Avatar>
127+
<div className="flex-grow mt-2">
128+
{message.isLoading ? (
129+
<p className="text-muted-foreground">Thinking...</p>
130+
) : (
131+
<p className="text-sm">{message.response}</p>
132+
)}
133+
</div>
134+
</div>
135+
136+
</div>
137+
))}
138+
</div>
116139

117-
<p className='font-bold'>AI:</p>
118-
{message.isLoading ? (
119-
<p>Loading...</p>
120-
) : (
121-
<p>{message.response}</p>
122-
)}
123-
124-
</div>
125-
))}
126-
<div ref={messagesEndRef} />
127140
</div>
128-
<div className='p-4 mb-auto'>
141+
</main>
142+
143+
<div className="p-4">
129144
<PlaceholdersAndVanishInput
130145
placeholders={placeholders}
131146
onSubmit={onSubmit}
132147
/>
133148
</div>
134149
</div>
135150
</div>
136-
</div>
137-
);
138-
}
151+
)
152+
}

client/src/components/MobileSidebar/MobileSidebar.tsx

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,11 @@ import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"
1616
import { Button } from "@/components/ui/button"
1717
import { useNavigate } from "react-router-dom";
1818

19-
// const BACKEND_URL = import.meta.env.VITE_BACKEND_URL || "http://localhost:5000";
20-
2119
const MobileSidebar = () => {
2220
const navigate = useNavigate();
2321

24-
25-
26-
27-
2822
return (
29-
<header className="flex h-14 items-center gap-4 border-b bg-muted/40 px-4 lg:h-[60px] lg:px-6">
23+
<header className="flex h-14 items-center gap-4 border-b bg-muted/40 px-4 lg:h-[60px] lg:px-6 lg:border-b-0 lg:bg-transparent">
3024
<Sheet>
3125
<SheetTrigger asChild>
3226
<Button variant="outline" size="icon" className="shrink-0 md:hidden">
@@ -60,8 +54,6 @@ const MobileSidebar = () => {
6054
<DropdownMenu>
6155
<DropdownMenuTrigger asChild>
6256
<Button variant="secondary" size="icon" className="rounded-full">
63-
64-
6557
<CircleUser className="h-5 w-5" />
6658
</Button>
6759
</DropdownMenuTrigger>
@@ -82,7 +74,10 @@ const MobileSidebar = () => {
8274
</DropdownMenuItem>
8375
<DropdownMenuItem onClick={() => navigate('/settings')}>Settings</DropdownMenuItem>
8476
<DropdownMenuSeparator />
85-
<DropdownMenuItem>Logout</DropdownMenuItem>
77+
<DropdownMenuItem onClick={() => {
78+
localStorage.removeItem('devhub_username');
79+
navigate('/');
80+
}}>Logout</DropdownMenuItem>
8681
</DropdownMenuContent>
8782
</DropdownMenu>
8883
</header>

0 commit comments

Comments
 (0)