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"
6
7
7
8
const backendUrl = import . meta. env . VITE_BACKEND_URL || 'http://localhost:5000' ;
8
9
9
10
interface Message {
10
- query : string ;
11
- response : string ;
12
- isLoading ?: boolean ;
11
+ query : string
12
+ response : string
13
+ isLoading ?: boolean
13
14
}
14
15
15
16
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 )
20
19
21
20
const placeholders = [
22
21
'How to set up a React project with Vite?' ,
@@ -25,114 +24,129 @@ export default function Chat() {
25
24
'What is a closure in JavaScript?' ,
26
25
'How to optimize React app performance?' ,
27
26
'Explain event delegation in JavaScript' ,
28
- ] ;
27
+ ]
29
28
30
29
const scrollToBottom = ( ) => {
31
- messagesEndRef . current ?. scrollIntoView ( { behavior : 'smooth' } ) ;
32
- } ;
30
+ if ( scrollAreaRef . current ) {
31
+ scrollAreaRef . current . scrollTop = scrollAreaRef . current . scrollHeight
32
+ }
33
+ }
34
+
33
35
useEffect ( ( ) => {
34
- scrollToBottom ( ) ;
35
- } , [ messages ] ) ;
36
+ scrollToBottom ( )
37
+ } , [ messages ] )
36
38
37
- const username = localStorage . getItem ( 'devhub_username' ) ;
39
+ const username = typeof window !== 'undefined' ? localStorage . getItem ( 'devhub_username' ) : null
38
40
39
41
useEffect ( ( ) => {
40
42
const fetchChatHistory = async ( ) => {
41
43
if ( ! username ) {
42
- console . error ( 'Username not found in localStorage' ) ;
43
- return ;
44
+ console . error ( 'Username not found in localStorage' )
45
+ return
44
46
}
45
47
46
48
try {
47
49
const result = await axios . get ( `${ backendUrl } /chat_history` , {
48
50
params : { username } ,
49
51
withCredentials : true
50
- } ) ;
52
+ } )
51
53
52
54
const chatHistory =
53
55
result . data . chat_history . length > 0
54
56
? result . data . chat_history
55
- : [ { query : 'Hi' , response : 'Welcome to DevHub!' } ] ;
56
- setMessages ( chatHistory ) ;
57
+ : [ { query : 'Hi' , response : 'Welcome to DevHub!' } ]
58
+ setMessages ( chatHistory )
57
59
} catch ( error ) {
58
- console . error ( 'Error fetching chat history:' , error ) ;
60
+ console . error ( 'Error fetching chat history:' , error )
59
61
}
60
- } ;
62
+ }
61
63
62
- fetchChatHistory ( ) ;
63
- } , [ username ] ) ;
64
+ fetchChatHistory ( )
65
+ } , [ username ] )
64
66
65
67
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
69
71
70
72
if ( ! username ) {
71
- console . error ( 'Username not found in localStorage' ) ;
72
- return ;
73
+ console . error ( 'Username not found in localStorage' )
74
+ return
73
75
}
74
76
75
77
setMessages ( ( prevMessages ) => [
76
78
...prevMessages ,
77
79
{ query, response : '' , isLoading : true } ,
78
- ] ) ;
80
+ ] )
79
81
80
82
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 } )
82
84
83
85
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
+ } )
90
92
} catch ( error ) {
91
- console . error ( 'Error querying the model:' , error ) ;
93
+ console . error ( 'Error querying the model:' , error )
92
94
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
+ } )
99
101
}
100
- } ;
102
+ }
103
+
101
104
return (
102
105
< div className = "grid min-h-screen w-full md:grid-cols-[220px_1fr] lg:grid-cols-[280px_1fr]" >
103
106
< Sidebar />
104
107
< 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" >
106
109
< MobileSidebar />
107
110
</ 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 >
116
139
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 } />
127
140
</ div >
128
- < div className = 'p-4 mb-auto' >
141
+ </ main >
142
+
143
+ < div className = "p-4" >
129
144
< PlaceholdersAndVanishInput
130
145
placeholders = { placeholders }
131
146
onSubmit = { onSubmit }
132
147
/>
133
148
</ div >
134
149
</ div >
135
150
</ div >
136
- </ div >
137
- ) ;
138
- }
151
+ )
152
+ }
0 commit comments