From 4e34335e600b5d9d29e8a4af99fda6b2a6d3ba69 Mon Sep 17 00:00:00 2001
From: aidahosa1 <aisosa_idahosa@brown.edu>
Date: Tue, 10 Sep 2024 10:54:29 -0400
Subject: more chat features

---
 src/client/views/pdf/GPTPopup/GPTPopup.tsx | 319 ++++++++++++++++++++++-------
 1 file changed, 242 insertions(+), 77 deletions(-)

(limited to 'src/client/views/pdf/GPTPopup/GPTPopup.tsx')

diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index d216cc421..1b44508d7 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -34,6 +34,13 @@ export enum GPTPopupMode {
     QUIZ
 }
 
+export enum GPTQuizType {
+    CURRENT = 0,
+    CHOOSE = 1,
+    MULTIPLE = 2
+
+}
+
 
 
 
@@ -151,7 +158,8 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
         this.sortDesc = t;
     };
 
-    @observable onSortComplete?: (sortResult: string) => void;
+    @observable onSortComplete?: (sortResult: string, questionType: string, tag?: string) => void;
+    @observable onQuizRandom?: () => void;
     @observable cardsDoneLoading = false;
 
     @action setCardsDoneLoading(done: boolean) {
@@ -165,6 +173,9 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
         this.sortRespText = resp
     }
 
+    //mode where gpt says to select a specific card
+    //
+
 
     @observable chatSortPrompt: string = ""
 
@@ -179,39 +190,65 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
         this.quizAnswer = e.target.value;
     });
 
+   
+    @observable correctAnswer: string = ''
+    @observable setCorrectAnswer = (s: string) => {
+        this.correctAnswer = s
+    }
+
     generateQuiz = async () => {
         this.setLoading(true);
         this.setSortDone(false);
 
+        const quizType = this.quizMode;
 
         const selected = DocumentView.SelectedDocs().lastElement();
 
-        const questionText = 'Question: ' + StrCast(selected['gptInputText']);
 
-        if (StrCast(selected['gptRubric']) === '') {
-            const rubricText = 'Rubric: ' + await this.generateRubric(StrCast(selected['gptInputText']), selected)
-        }
+                const questionText = 'Question: ' + StrCast(selected['gptInputText']);
 
-        const rubricText = 'Rubric: ' + (StrCast(selected['gptRubric']))
-        // const rubricText = 'Rubric: ' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text);
-        const queryText = questionText + ' UserAnswer: ' + this.quizAnswer + '. ' + 'Rubric' + rubricText;
+                if (StrCast(selected['gptRubric']) === '') {
+                    const rubricText = 'Rubric: ' + await this.generateRubric(StrCast(selected['gptInputText']), selected)
+                }
 
-        try {
-            const res = await gptAPICall(queryText, GPTCallType.QUIZ);
-            if (!res) {
-                console.error('GPT call failed');
-                return;
-            }
-            console.log(res)
-            this.setQuizResp(res)
+                const rubricText = 'Rubric: ' + (StrCast(selected['gptRubric']))
+                // const rubricText = 'Rubric: ' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text);
+                const queryText = questionText + ' UserAnswer: ' + this.quizAnswer + '. ' + 'Rubric' + rubricText;
 
-            this.setLoading(false);
-            this.setSortDone(true);
+                try {
+                    const res = await gptAPICall(queryText, GPTCallType.QUIZ);
+                    if (!res) {
+                        console.error('GPT call failed');
+                        return;
+                    }
+                    console.log(res)
+                    this.setQuizResp(res)
+                    this.conversationArray.push(res)
+
+                    this.setLoading(false);
+                    this.setSortDone(true);
+
+                    // this._outputValue = res;
+                } catch (err) {
+                    console.error('GPT call failed');
+                }
+
+
+                if (this.onQuizRandom){
+                    this.onQuizRandom()
+                }
 
-            // this._outputValue = res;
-        } catch (err) {
-            console.error('GPT call failed');
-        }
+
+            
+
+
+        // switch(quizType){
+        //     default:
+                
+        // }
+
+
+        
 
 
     }
@@ -254,11 +291,7 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
 
     }
 
-
-    /**
-     * Sorts cards in the CollectionCardDeckView
-     */
-    generateSort = async () => {
+    generateCard = async () => {
         console.log(this.chatSortPrompt + "USER PROMPT")
         this.setLoading(true);
         this.setSortDone(false);
@@ -268,17 +301,50 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
         }
     
         try {
-            const res = await gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt);
+            // const res = await gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt);
+            const questionType = await gptAPICall(this.chatSortPrompt, GPTCallType.TYPE);
+            const questionNumber = questionType.split(' ')[0]
+            console.log(questionType)
+            let res = ''
+
+            switch (questionNumber) {
+                case '1':
+                case '2':
+                case '4':
+                    res = await gptAPICall(this.sortDesc, GPTCallType.SUBSET, this.chatSortPrompt);
+                    break
+                case '6':
+                    res = await gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt);
+                    break
+                default:
+
+                    const selected = DocumentView.SelectedDocs().lastElement();
+                    const questionText = StrCast(selected!['gptInputText']);
+
+
+                    res = await gptAPICall(questionText, GPTCallType.INFO, this.chatSortPrompt);
+                    break
+            }
+
+
+            // const res = await gptAPICall(this.sortDesc, GPTCallType.SUBSET, this.chatSortPrompt);
             // Trigger the callback with the result
             if (this.onSortComplete) {
-                this.onSortComplete(res || 'Something went wrong :(');
+                this.onSortComplete(res || 'Something went wrong :(', questionNumber, questionType.split(' ').slice(1).join(' '));
+
+                let explanation = res
+
+                if (questionType != '5' && questionType != '3'){
     
                 // Extract explanation surrounded by ------ at the top or both at the top and bottom
                 const explanationMatch = res.match(/------\s*([\s\S]*?)\s*(?:------|$)/) || [];
-                const explanation = explanationMatch[1] ? explanationMatch[1].trim() : 'No explanation found';
+                explanation = explanationMatch[1] ? explanationMatch[1].trim() : 'No explanation found';
+            } 
     
                 // Set the extracted explanation to sortRespText
                 this.setSortRespText(explanation);
+                this.conversationArray.push(this.sortRespText)
+                this.scrollToBottom()
     
                 console.log(res);
             }
@@ -289,6 +355,43 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
         this.setLoading(false);
         this.setSortDone(true);
     };
+
+
+    // /**
+    //  * Sorts cards in the CollectionCardDeckView
+    //  */
+    // generateSort = async () => {
+    //     console.log(this.chatSortPrompt + "USER PROMPT")
+    //     this.setLoading(true);
+    //     this.setSortDone(false);
+
+    //     if (this.regenerateCallback) {
+    //         await this.regenerateCallback();
+    //     }
+    
+    //     try {
+    //         const res = await gptAPICall(this.sortDesc, GPTCallType.SORT, this.chatSortPrompt);
+
+    //         // Trigger the callback with the result
+    //         if (this.onSortComplete) {
+    //             this.onSortComplete(res || 'Something went wrong :(');
+    
+    //             // Extract explanation surrounded by ------ at the top or both at the top and bottom
+    //             const explanationMatch = res.match(/------\s*([\s\S]*?)\s*(?:------|$)/) || [];
+    //             const explanation = explanationMatch[1] ? explanationMatch[1].trim() : 'No explanation found';
+    
+    //             // Set the extracted explanation to sortRespText
+    //             this.setSortRespText(explanation);
+    
+    //             console.log(res);
+    //         }
+    //     } catch (err) {
+    //         console.error(err);
+    //     }
+    
+    //     this.setLoading(false);
+    //     this.setSortDone(true);
+    // };
     
     
     /**
@@ -422,19 +525,40 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
         super(props);
         makeObservable(this);
         GPTPopup.Instance = this;
+        this.messagesEndRef = React.createRef();
+
     }
 
+    private messagesEndRef: React.RefObject<HTMLDivElement>;
+
+
+    scrollToBottom = () => {
+            setTimeout(() => {
+                // Code to execute after 1 second (1000 ms)
+                if (this.messagesEndRef.current) {
+
+                this.messagesEndRef.current.scrollIntoView({ behavior: 'smooth', block: 'end' });
+                }
+
+              }, 50);
+      }
+
     componentDidUpdate = () => {
         if (this.loading) {
             this.setDone(false);
         }
     };
 
+    @observable quizMode : GPTQuizType = GPTQuizType.CURRENT
+    @action setQuizMode (g: GPTQuizType) {
+        this.quizMode = g
+    }
+
     cardMenu = () => (
         <div className="btns-wrapper-gpt">
             <Button
-                tooltip="Have ChatGPT sort your cards for you!"
-                text="Sort Cards!"
+                tooltip="Have ChatGPT sort, tag, define, or filter your cards for you!"
+                text="Modify/Sort Cards!"
                 onClick={() => this.setMode(GPTPopupMode.SORT)}
                 color={StrCast(Doc.UserDoc().userVariantColor)}
                 type={Type.TERT}
@@ -450,7 +574,16 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
             <Button
                 tooltip="Test your knowledge with ChatGPT!"
                 text="Quiz Cards!"
-                onClick={() => this.setMode(GPTPopupMode.QUIZ)}
+                onClick={() => {
+                    this.conversationArray = ['Define the selected card!']
+                    this.setMode(GPTPopupMode.QUIZ)
+                    if (this.onQuizRandom){
+                        this.onQuizRandom()
+                    }
+                    
+
+
+                }}
                 color={StrCast(Doc.UserDoc().userVariantColor)}
                 type={Type.TERT}
                 style={{
@@ -464,62 +597,94 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
             />
         </div>
     )
+
+    @observable conversationArray: string[] = ["Hi! In this pop up, you can ask ChatGPT questions about your documents and filter / sort them. If you're not sure if something is possible-- just ask!"]
+
+
+    handleKeyPress = async (e: React.KeyboardEvent, isSort: boolean) => {
+        if (e.key === 'Enter') {
+            e.stopPropagation();
+
+            if (isSort) {
+                this.conversationArray.push(this.chatSortPrompt);
+                await this.generateCard();
+                this.chatSortPrompt = ''
+
+            } else {
+                this.conversationArray.push(this.quizAnswer);
+                await this.generateQuiz();
+                this.quizAnswer = ''
+
+            }
+
+            this.scrollToBottom();
+        }
+    }
     
     cardActual = (opt: GPTPopupMode) => {
         const isSort = opt === GPTPopupMode.SORT
         // if (opt === GPTPopupMode.SORT) {
             return (
-                !this.sortDone ? (
-                    <>
-                        <div className="btns-wrapper-gpt">
+                // !this.sortDone ? (
+                    // <>
+                    <div className="btns-wrapper-gpt">
+                        {/* Chat bubble container with scroll */}
+                        <div className="chat-wrapper">
+                            <div className="chat-bubbles">
+                                {this.conversationArray.map((message, index) => (
+                                    <div
+                                        key={index}
+                                        className={`chat-bubble ${index % 2 === 1 ? 'user-message' : 'chat-message'}`}
+                                    >
+                                        {message}
+                                    </div>
+                                ))}
+
+                                {/* Conditional Loading message */}
+                                {(!this.cardsDoneLoading || this.loading) && (
+                                    <div className={`chat-bubble chat-message`}>
+                                       ...
+                                    </div>
+                                )}
+                            </div>
+
+                            <div ref={this.messagesEndRef} style= {{height: '100px'}} />
+
+                        </div>
+
+                        <div className="inputWrapper">
                             <input
                                 className="searchBox-input"
                                 defaultValue=""
                                 autoComplete="off"
                                 onChange={isSort ? this.sortPromptChanged : this.quizAnswerChanged}
-                                onKeyDown={e => {
-                                    if (e.key === 'Enter') {
-                                        isSort ? this.generateSort() : this.generateQuiz();
+                                onKeyDown={(e) => {
+                                    this.handleKeyPress(e, isSort)
+
+                                            
                                     }
-                                    e.stopPropagation();
-                                }}
+                                }
                                 type="text"
-                                placeholder={`${isSort ? 'How do you want to sort your cards?' : 'Define the selected card?'}`}
-                                id="search-input"
-                                style={{ width: '100%' }}
-                            />
-                        {/* </div>
-                        <div className="btns-wrapper-gpt"> */}
-                            <Button
-                                tooltip={`${isSort ? 'HaveChatGPT sort ypur cards for you!' : 'See how close you get to the right answer!'}`}
-                                text={`${isSort ? 'Sort!' : 'Check!'}`}
-                                onClick={isSort ? this.generateSort : this.generateQuiz}
-                                color={StrCast(Doc.UserDoc().userVariantColor)}
-                                type={Type.TERT}
-                                style={{
-                                    width: '100%',
-                                    textAlign: 'center',
-                                    color: '#ffffff',
-                                    fontSize: '16px',
-                                    height: '40%',
-
-                                }}
-                            />
-                        </div>
-                    </>
-                ) : (
-                    <div>
-                        <div className="content-wrapper">
-                            <p>{this.text === 'Something went wrong :(' ? 'Something went wrong :(' : `${isSort ? this.sortRespText : this.quizRespText}`}</p>
-                            <IconButton
-                                tooltip="Generate Again"
-                                onClick={() => this.setSortDone(false)}
-                                icon={<FontAwesomeIcon icon="redo-alt" size="lg" />}
-                                color={StrCast(Doc.UserDoc().userVariantColor)}
+                                placeholder={`${isSort ? 'Have ChatGPT sort, tag, define, or filter your cards for you!' : 'Define the selected card!'}`}
                             />
                         </div>
                     </div>
-                )
+
+
+                    // </>
+                // ) : (
+                //     <div>
+                //         <div className="content-wrapper">
+                //             <p>{this.text === 'Something went wrong :(' ? 'Something went wrong :(' : `${isSort ? this.sortRespText : this.quizRespText}`}</p>
+                //             <IconButton
+                //                 tooltip="Generate Again"
+                //                 onClick={() => this.setSortDone(false)}
+                //                 icon={<FontAwesomeIcon icon="redo-alt" size="lg" />}
+                //                 color={StrCast(Doc.UserDoc().userVariantColor)}
+                //             />
+                //         </div>
+                //     </div>
+                // )
             );
         // } else if (opt === GPTPopupMode.QUIZ) {
         //     return (
@@ -563,10 +728,10 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
     };
     
     sortBox = () => (
-        <div>
+        <div style = {{height: '80%'}}>
             {this.heading(this.mode === GPTPopupMode.SORT ? 'SORTING' : 'QUIZ')}
             <>
-                {!this.cardsDoneLoading || this.loading ? (
+                {!this.cardsDoneLoading? (
                     <div className="content-wrapper">
                         <div className="loading-spinner">
                             <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} />
@@ -737,7 +902,7 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
                             tooltip="back"
                             icon={<CgCornerUpLeft size="16px" />}
                             onClick={() => this.mode = GPTPopupMode.CARD}
-                            style = {{right: '-20%'}}
+                            style = {{right: '50px', position: 'absolute'}}
                         />
                     )}
                     <IconButton
-- 
cgit v1.2.3-70-g09d2

