@@ -12,8 +12,7 @@ import type { NebulaUserMessage } from "../../api/types";
1212import type { ExamplePrompt } from "../../data/examplePrompts" ;
1313import { NebulaIcon } from "../../icons/NebulaIcon" ;
1414import { ChatBar } from "../ChatBar" ;
15- import { Chats } from "../Chats" ;
16- import type { ChatMessage } from "../Chats" ;
15+ import { type CustomChatMessage , CustomChats } from "./CustomChats" ;
1716
1817export default function CustomChatContent ( props : {
1918 authToken : string | undefined ;
@@ -49,7 +48,7 @@ function CustomChatContentLoggedIn(props: {
4948 networks : NebulaContext [ "networks" ] ;
5049} ) {
5150 const [ userHasSubmittedMessage , setUserHasSubmittedMessage ] = useState ( false ) ;
52- const [ messages , setMessages ] = useState < Array < ChatMessage > > ( [ ] ) ;
51+ const [ messages , setMessages ] = useState < Array < CustomChatMessage > > ( [ ] ) ;
5352 // sessionId is initially undefined, will be set to conversationId from API after first response
5453 const [ sessionId , setSessionId ] = useState < string | undefined > ( undefined ) ;
5554 const [ chatAbortController , setChatAbortController ] = useState <
@@ -149,6 +148,70 @@ function CustomChatContentLoggedIn(props: {
149148 [ props . authToken , props . clientId , props . teamId , sessionId , trackEvent ] ,
150149 ) ;
151150
151+ const handleFeedback = useCallback (
152+ async ( messageIndex : number , feedback : 1 | - 1 ) => {
153+ if ( ! sessionId ) {
154+ console . error ( "Cannot submit feedback: missing session ID" ) ;
155+ return ;
156+ }
157+
158+ // Validate message exists and is of correct type
159+ const message = messages [ messageIndex ] ;
160+ if ( ! message || message . type !== "assistant" ) {
161+ console . error ( "Invalid message for feedback:" , messageIndex ) ;
162+ return ;
163+ }
164+
165+ // Prevent duplicate feedback
166+ if ( message . feedback ) {
167+ console . warn ( "Feedback already submitted for this message" ) ;
168+ return ;
169+ }
170+
171+ try {
172+ trackEvent ( {
173+ category : "siwa" ,
174+ action : "submit-feedback" ,
175+ rating : feedback === 1 ? "good" : "bad" ,
176+ sessionId,
177+ teamId : props . teamId ,
178+ } ) ;
179+
180+ const apiUrl = process . env . NEXT_PUBLIC_SIWA_URL ;
181+ const response = await fetch ( `${ apiUrl } /v1/chat/feedback` , {
182+ method : "POST" ,
183+ headers : {
184+ "Content-Type" : "application/json" ,
185+ Authorization : `Bearer ${ props . authToken } ` ,
186+ ...( props . teamId ? { "x-team-id" : props . teamId } : { } ) ,
187+ } ,
188+ body : JSON . stringify ( {
189+ conversationId : sessionId ,
190+ feedbackRating : feedback ,
191+ } ) ,
192+ } ) ;
193+
194+ if ( ! response . ok ) {
195+ throw new Error ( `HTTP ${ response . status } : ${ response . statusText } ` ) ;
196+ }
197+
198+ // Update the message with feedback
199+ setMessages ( ( prev ) =>
200+ prev . map ( ( msg , index ) =>
201+ index === messageIndex && msg . type === "assistant"
202+ ? { ...msg , feedback }
203+ : msg ,
204+ ) ,
205+ ) ;
206+ } catch ( error ) {
207+ console . error ( "Failed to send feedback:" , error ) ;
208+ // Optionally show user-facing error notification
209+ // Consider implementing retry logic here
210+ }
211+ } ,
212+ [ sessionId , props . authToken , props . teamId , trackEvent , messages ] ,
213+ ) ;
214+
152215 const showEmptyState = ! userHasSubmittedMessage && messages . length === 0 ;
153216 return (
154217 < div className = "flex grow flex-col overflow-hidden" >
@@ -158,7 +221,7 @@ function CustomChatContentLoggedIn(props: {
158221 examplePrompts = { props . examplePrompts }
159222 />
160223 ) : (
161- < Chats
224+ < CustomChats
162225 messages = { messages }
163226 isChatStreaming = { isChatStreaming }
164227 authToken = { props . authToken }
@@ -169,6 +232,7 @@ function CustomChatContentLoggedIn(props: {
169232 setEnableAutoScroll = { setEnableAutoScroll }
170233 useSmallText
171234 sendMessage = { handleSendMessage }
235+ onFeedback = { handleFeedback }
172236 />
173237 ) }
174238 < ChatBar
0 commit comments