@@ -3,7 +3,7 @@ import ReactMarkdown from 'react-markdown'
33import { Components } from 'react-markdown' ;
44import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
55import { nord } from 'react-syntax-highlighter/dist/esm/styles/prism'
6- import { Checkbox , DefaultButton , Dialog , FontIcon , Stack , Text } from '@fluentui/react'
6+ import { Checkbox , DefaultButton , Dialog , FontIcon , Stack , Text , Spinner , MessageBar , MessageBarType , PrimaryButton } from '@fluentui/react'
77import { useBoolean } from '@fluentui/react-hooks'
88import { ThumbDislike20Filled , ThumbLike20Filled } from '@fluentui/react-icons'
99import DOMPurify from 'dompurify'
@@ -22,6 +22,13 @@ interface Props {
2222 onCitationClicked : ( citedDocument : Citation ) => void
2323}
2424
25+ // Add interface for citation content response
26+ interface CitationContentResponse {
27+ content : string
28+ title : string
29+ error ?: string
30+ }
31+
2532export const Answer = ( { answer, onCitationClicked } : Props ) => {
2633 const initializeAnswerFeedback = ( answer : AskResponse ) => {
2734 if ( answer . message_id == undefined ) return undefined
@@ -40,11 +47,72 @@ export const Answer = ({ answer, onCitationClicked }: Props) => {
4047 const [ isFeedbackDialogOpen , setIsFeedbackDialogOpen ] = useState ( false )
4148 const [ showReportInappropriateFeedback , setShowReportInappropriateFeedback ] = useState ( false )
4249 const [ negativeFeedbackList , setNegativeFeedbackList ] = useState < Feedback [ ] > ( [ ] )
50+
51+ // Add new state for citation content dialog
52+ const [ isCitationContentDialogOpen , setIsCitationContentDialogOpen ] = useState ( false )
53+ const [ citationContent , setCitationContent ] = useState < CitationContentResponse | null > ( null )
54+ const [ isLoadingCitationContent , setIsLoadingCitationContent ] = useState ( false )
55+ const [ citationContentError , setCitationContentError ] = useState < string | null > ( null )
56+
4357 const appStateContext = useContext ( AppStateContext )
4458 const FEEDBACK_ENABLED =
4559 appStateContext ?. state . frontendSettings ?. feedback_enabled && appStateContext ?. state . isCosmosDBAvailable ?. cosmosDB
4660 const SANITIZE_ANSWER = appStateContext ?. state . frontendSettings ?. sanitize_answer
4761
62+ // Add function to fetch citation content
63+ const fetchCitationContent = async ( citation : Citation ) => {
64+ setIsLoadingCitationContent ( true )
65+ setCitationContentError ( null )
66+
67+ try {
68+ const payload = {
69+ url : citation . url || '' ,
70+ title : citation . title || 'Citation Content' ,
71+ }
72+
73+ const response = await fetch ( '/fetch-azure-search-content' , {
74+ method : 'POST' ,
75+ headers : {
76+ 'Content-Type' : 'application/json' ,
77+ } ,
78+ body : JSON . stringify ( payload )
79+ } )
80+
81+ if ( ! response . ok ) {
82+ throw new Error ( `HTTP error! status: ${ response . status } ` )
83+ }
84+
85+ const data : CitationContentResponse = await response . json ( )
86+ // {
87+ // content: "abcd",
88+ // title: 'abcdtile'
89+ // }
90+ setCitationContent ( data )
91+ setIsCitationContentDialogOpen ( true )
92+ } catch ( error ) {
93+ console . error ( 'Error fetching citation content:' , error )
94+ setCitationContentError ( error instanceof Error ? error . message : 'Failed to fetch citation content' )
95+ } finally {
96+ setIsLoadingCitationContent ( false )
97+ }
98+ }
99+
100+ // Update the onCitationClicked handler
101+ const handleCitationClick = ( citation : Citation ) => {
102+ // Call the original onCitationClicked prop
103+ onCitationClicked ( citation )
104+
105+ // Fetch citation content and show dialog
106+ fetchCitationContent ( citation )
107+ }
108+
109+ // Add function to close citation content dialog
110+ const closeCitationContentDialog = ( ) => {
111+ setIsCitationContentDialogOpen ( false )
112+ setCitationContent ( null )
113+ setCitationContentError ( null )
114+ }
115+
48116 const handleChevronClick = ( ) => {
49117 setChevronIsExpanded ( ! chevronIsExpanded )
50118 toggleIsRefAccordionOpen ( )
@@ -67,22 +135,22 @@ export const Answer = ({ answer, onCitationClicked }: Props) => {
67135 } , [ appStateContext ?. state . feedbackState , feedbackState , answer . message_id ] )
68136
69137 const createCitationFilepath = ( citation : Citation , index : number , truncate : boolean = false ) => {
70- let citationFilename = ''
71-
72- if ( citation . filepath ) {
73- const part_i = citation . part_index ?? ( citation . chunk_id ? parseInt ( citation . chunk_id ) + 1 : '' )
74- if ( truncate && citation . filepath . length > filePathTruncationLimit ) {
75- const citationLength = citation . filepath . length
76- citationFilename = `${ citation . filepath . substring ( 0 , 20 ) } ...${ citation . filepath . substring ( citationLength - 20 ) } - Part ${ part_i } `
77- } else {
78- citationFilename = `${ citation . filepath } - Part ${ part_i } `
79- }
80- } else if ( citation . filepath && citation . reindex_id ) {
81- citationFilename = `${ citation . filepath } - Part ${ citation . reindex_id } `
82- } else {
83- citationFilename = `Citation ${ index } `
84- }
85- return citationFilename
138+ // let citationFilename = ''
139+ // console.log('createCitationFilepath', citation, index, truncate)
140+ // if (citation.filepath) {
141+ // const part_i = citation.part_index ?? (citation.chunk_id ? parseInt(citation.chunk_id) + 1 : '')
142+ // if (truncate && citation.filepath.length > filePathTruncationLimit) {
143+ // const citationLength = citation.filepath.length
144+ // citationFilename = `${citation.filepath.substring(0, 20)}...${citation.filepath.substring(citationLength - 20)} - Part ${part_i}`
145+ // } else {
146+ // citationFilename = `${citation.filepath} - Part ${part_i}`
147+ // }
148+ // } else if (citation.filepath && citation.reindex_id) {
149+ // citationFilename = `${citation.filepath} - Part ${citation.reindex_id}`
150+ // } else {
151+ // citationFilename = `Citation ${index}`
152+ // }
153+ return citation . title ? citation . title : `Citation ${ index + 1 } `
86154 }
87155
88156 const onLikeResponseClicked = async ( ) => {
@@ -345,22 +413,24 @@ export const Answer = ({ answer, onCitationClicked }: Props) => {
345413 { parsedAnswer . citations . map ( ( citation , idx ) => {
346414 return (
347415 < span
348- title = { createCitationFilepath ( citation , ++ idx ) }
416+ title = { citation . title ?? undefined }
349417 tabIndex = { 0 }
350418 role = "link"
351419 key = { idx }
352- onClick = { ( ) => onCitationClicked ( citation ) }
353- onKeyDown = { e => ( e . key === 'Enter' || e . key === ' ' ? onCitationClicked ( citation ) : null ) }
420+ onClick = { ( ) => handleCitationClick ( citation ) }
421+ onKeyDown = { e => ( e . key === 'Enter' || e . key === ' ' ? handleCitationClick ( citation ) : null ) }
354422 className = { styles . citationContainer }
355423 aria-label = { createCitationFilepath ( citation , idx ) } >
356- < div className = { styles . citation } > { idx } </ div >
424+ < div className = { styles . citation } > { idx + 1 } </ div >
357425 { createCitationFilepath ( citation , idx , true ) }
358426 </ span >
359427 )
360428 } ) }
361429 </ div >
362430 ) }
363431 </ Stack >
432+
433+ { /* Existing feedback dialog */ }
364434 < Dialog
365435 onDismiss = { ( ) => {
366436 resetFeedbackDialog ( )
@@ -389,16 +459,78 @@ export const Answer = ({ answer, onCitationClicked }: Props) => {
389459 } } >
390460 < Stack tokens = { { childrenGap : 4 } } >
391461 < div > Your feedback will improve this experience.</ div >
392-
393462 { ! showReportInappropriateFeedback ? < UnhelpfulFeedbackContent /> : < ReportInappropriateFeedbackContent /> }
394-
395463 < div > By pressing submit, your feedback will be visible to the application owner.</ div >
396-
397464 < DefaultButton disabled = { negativeFeedbackList . length < 1 } onClick = { onSubmitNegativeFeedback } >
398465 Submit
399466 </ DefaultButton >
400467 </ Stack >
401468 </ Dialog >
469+
470+ { /* New citation content dialog */ }
471+ < Dialog
472+ onDismiss = { closeCitationContentDialog }
473+ hidden = { ! isCitationContentDialogOpen }
474+ styles = { {
475+ main : [
476+ {
477+ selectors : {
478+ [ '@media (min-width: 480px)' ] : {
479+ width : '800px' ,
480+ height : '600px' ,
481+ maxWidth : '800px' ,
482+ maxHeight : '600px' ,
483+ minWidth : '800px' ,
484+ minHeight : '600px' ,
485+ background : '#FFFFFF' ,
486+ boxShadow : '0px 14px 28.8px rgba(0, 0, 0, 0.24), 0px 0px 8px rgba(0, 0, 0, 0.2)' ,
487+ borderRadius : '8px'
488+ }
489+ }
490+ }
491+ ]
492+ } }
493+ dialogContentProps = { {
494+ title : citationContent ?. title || 'Citation Content' ,
495+ showCloseButton : true
496+ } } >
497+ < Stack tokens = { { childrenGap : 16 } } styles = { { root : { height : '500px' } } } >
498+ { isLoadingCitationContent && (
499+ < Stack horizontal horizontalAlign = "center" tokens = { { childrenGap : 8 } } >
500+ < Spinner label = "Loading citation content..." />
501+ </ Stack >
502+ ) }
503+
504+ { citationContentError && (
505+ < MessageBar messageBarType = { MessageBarType . error } >
506+ Error loading citation content: { citationContentError }
507+ </ MessageBar >
508+ ) }
509+
510+ { citationContent && ! isLoadingCitationContent && (
511+ < div style = { {
512+ height : '400px' ,
513+ overflowY : 'auto' ,
514+ padding : '16px' ,
515+ border : '1px solid #e1e1e1' ,
516+ borderRadius : '4px' ,
517+ backgroundColor : '#fafafa'
518+ } } >
519+ < ReactMarkdown
520+ remarkPlugins = { [ remarkGfm , supersub ] }
521+ children = { citationContent . content }
522+ components = { components }
523+ />
524+ </ div >
525+ ) }
526+
527+ < Stack horizontal horizontalAlign = "end" >
528+ < PrimaryButton onClick = { closeCitationContentDialog } >
529+ Close
530+ </ PrimaryButton >
531+ </ Stack >
532+ </ Stack >
533+ </ Dialog >
402534 </ >
403535 )
404536}
0 commit comments