1+ import React , { useState } from 'react'
2+
3+ const ChatAPI = {
4+ send : async ( message : string ) : Promise < ChatResponse > =>
5+ await ( await fetch ( '/api/chat' , {
6+ method : 'POST' ,
7+ headers : {
8+ 'Content-Type' : 'application/json' ,
9+ } ,
10+ body : JSON . stringify ( { message } ) ,
11+ } ) ) . json ( ) ,
12+ }
13+
14+ interface ChatMessage {
15+ id : string ;
16+ message : string ;
17+ response : string ;
18+ timestamp : Date ;
19+ }
20+
21+ export const Chat = ( ) => {
22+ const [ message , setMessage ] = useState < string > ( '' )
23+ const [ chatHistory , setChatHistory ] = useState < ChatMessage [ ] > ( [ ] )
24+ const [ processing , setProcessing ] = useState < boolean > ( false )
25+
26+ const sendMessage = async ( userMessage : string ) => {
27+ if ( ! userMessage . trim ( ) ) return
28+
29+ setProcessing ( true )
30+
31+ try {
32+ const response = await ChatAPI . send ( userMessage )
33+
34+ const chatMessage : ChatMessage = {
35+ id : Date . now ( ) . toString ( ) ,
36+ message : userMessage ,
37+ response : response . response ,
38+ timestamp : new Date ( )
39+ }
40+
41+ setChatHistory ( prev => [ ...prev , chatMessage ] )
42+ setMessage ( '' )
43+ } catch ( error ) {
44+ console . error ( 'Chat error:' , error )
45+
46+ const errorMessage : ChatMessage = {
47+ id : Date . now ( ) . toString ( ) ,
48+ message : userMessage ,
49+ response : "Sorry, I'm having trouble processing your request right now. Please try again later." ,
50+ timestamp : new Date ( )
51+ }
52+
53+ setChatHistory ( prev => [ ...prev , errorMessage ] )
54+ setMessage ( '' )
55+ }
56+
57+ setProcessing ( false )
58+ }
59+
60+ const clearHistory = ( ) => {
61+ setChatHistory ( [ ] )
62+ }
63+
64+ return (
65+ < div style = { { display : 'flex' , flexFlow : 'column' , textAlign : 'left' , height : '70vh' } } >
66+ < div style = { { display : 'flex' , justifyContent : 'space-between' , alignItems : 'center' } } >
67+ < h1 > Procurement Assistant</ h1 >
68+ { chatHistory . length > 0 && (
69+ < button onClick = { clearHistory } style = { { marginBottom : '20px' } } >
70+ Clear History
71+ </ button >
72+ ) }
73+ </ div >
74+
75+ < div style = { {
76+ flex : 1 ,
77+ overflowY : 'auto' ,
78+ marginBottom : '20px' ,
79+ border : '1px solid #ddd' ,
80+ borderRadius : '4px' ,
81+ padding : '10px'
82+ } } >
83+ { chatHistory . length === 0 && (
84+ < div style = { { color : '#666' , fontStyle : 'italic' } } >
85+ Welcome! I'm your procurement assistant. I can help you find the best suppliers based on your needs.
86+ Ask me about suppliers, locations, logistics, or any procurement-related questions.
87+ </ div >
88+ ) }
89+
90+ { chatHistory . map ( ( chat ) => (
91+ < div key = { chat . id } style = { { marginBottom : '20px' } } >
92+ < div className = "Form" style = { { marginBottom : '5px' } } >
93+ < div style = { { fontWeight : 'bold' , color : '#0066cc' } } >
94+ You ({ chat . timestamp . toLocaleTimeString ( ) } ):
95+ </ div >
96+ < div style = { { marginTop : '5px' } } >
97+ { chat . message }
98+ </ div >
99+ </ div >
100+
101+ < div className = "Form" >
102+ < div style = { { fontWeight : 'bold' , color : '#006600' } } >
103+ Procurement Assistant:
104+ </ div >
105+ < div style = { {
106+ marginTop : '5px' ,
107+ whiteSpace : 'pre-wrap' ,
108+ lineHeight : '1.4'
109+ } } >
110+ { chat . response }
111+ </ div >
112+ </ div >
113+ </ div >
114+ ) ) }
115+
116+ { processing && (
117+ < div className = "Form" style = { { opacity : 0.7 } } >
118+ < div style = { { fontWeight : 'bold' , color : '#006600' } } >
119+ Procurement Assistant:
120+ </ div >
121+ < div style = { { marginTop : '5px' , fontStyle : 'italic' } } >
122+ Thinking... (analyzing supplier data and generating response)
123+ </ div >
124+ </ div >
125+ ) }
126+ </ div >
127+
128+ < div className = "Form" style = { {
129+ padding : '20px' ,
130+ borderTop : '2px solid #ddd' ,
131+ backgroundColor : '#f9f9f9'
132+ } } >
133+ < div style = { {
134+ display : 'flex' ,
135+ gap : '10px' ,
136+ alignItems : 'stretch'
137+ } } >
138+ < textarea
139+ style = { {
140+ flex : 1 ,
141+ minHeight : '60px' ,
142+ padding : '12px' ,
143+ fontSize : '16px' ,
144+ borderRadius : '8px' ,
145+ border : '2px solid #ccc' ,
146+ resize : 'vertical' ,
147+ fontFamily : 'inherit' ,
148+ lineHeight : '1.4'
149+ } }
150+ placeholder = "Ask about suppliers, locations, or procurement advice..."
151+ value = { message }
152+ onChange = { ( e ) => setMessage ( e . target . value ) }
153+ onKeyDown = { ( e ) => {
154+ if ( e . key === 'Enter' && ! e . shiftKey ) {
155+ e . preventDefault ( )
156+ sendMessage ( message )
157+ }
158+ } }
159+ disabled = { processing }
160+ />
161+ < button
162+ disabled = { processing || ! message . trim ( ) }
163+ style = { {
164+ minHeight : '60px' ,
165+ minWidth : '100px' ,
166+ fontSize : '16px' ,
167+ fontWeight : 'bold' ,
168+ borderRadius : '8px' ,
169+ backgroundColor : processing || ! message . trim ( ) ? '#ccc' : '#007bff' ,
170+ color : 'white' ,
171+ border : 'none' ,
172+ cursor : processing || ! message . trim ( ) ? 'not-allowed' : 'pointer'
173+ } }
174+ onClick = { ( ) => sendMessage ( message ) }
175+ >
176+ { processing ? 'Sending...' : 'Send' }
177+ </ button >
178+ </ div >
179+ < div style = { {
180+ fontSize : '14px' ,
181+ color : '#666' ,
182+ marginTop : '10px' ,
183+ textAlign : 'center'
184+ } } >
185+ Press Enter to send, Shift+Enter for new line
186+ </ div >
187+ </ div >
188+ </ div >
189+ )
190+ }
0 commit comments