Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ibm
GitHub Repository: ibm/watson-machine-learning-samples
Path: blob/master/cloud/ai-service-apps/nextjs-carbon-react-ui/src/components/QAPanel/QAPanel.js
6408 views
1
"use client";
2
3
import { useState, useRef, useCallback, useMemo, useEffect } from "react";
4
import { Button } from "@carbon/react";
5
import { StopOutline, Send } from "@carbon/react/icons";
6
import { TextNode } from "lexical";
7
import { LexicalComposer } from "@lexical/react/LexicalComposer";
8
import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
9
import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";
10
import { ClearEditorPlugin } from "@lexical/react/LexicalClearEditorPlugin";
11
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
12
import { PlainTextPlugin } from "@lexical/react/LexicalPlainTextPlugin";
13
import { AutoFocusPlugin } from "@lexical/react/LexicalAutoFocusPlugin";
14
import InitialMessage from "../InitialMessage/InitialMessage";
15
import InputPlugin from "../InputPlugin/InputPlugin";
16
import ChatItem from "../ChatItem/ChatItem";
17
import { MESSAGE_ROLE, MESSAGE_STATUS } from "@/utils/constants";
18
19
const QAPanel = ({ messages, onInput, onAbort, intersectorRef, isRunning }) => {
20
const [questionText, setQuestionText] = useState("");
21
const hasMessages = messages.length > 0;
22
const chatMessagesRef = useRef(null);
23
24
const handleOnSubmit = useCallback(() => {
25
onInput(questionText);
26
setQuestionText("");
27
}, [onInput, questionText]);
28
29
const handleOnAbort = useCallback(() => {
30
onAbort();
31
}, [onAbort]);
32
33
const textareaElement = (
34
<LexicalComposer
35
initialConfig={{
36
namespace: "chat-input",
37
editable: true,
38
onError: (err) => logger.error(err),
39
editorState: () => {},
40
nodes: [TextNode],
41
}}
42
>
43
<div className="qa-panel__editorContainer">
44
<div className="qa-panel__inputWrapper">
45
<PlainTextPlugin
46
contentEditable={<ContentEditable className="qa-panel__input" />}
47
placeholder={<div className="qa-panel__inputPlaceholder">Type something...</div>}
48
ErrorBoundary={LexicalErrorBoundary}
49
/>
50
</div>
51
<InputPlugin
52
onChange={(text) => setQuestionText(text)}
53
input={questionText}
54
onSubmit={handleOnSubmit}
55
/>
56
<AutoFocusPlugin />
57
<HistoryPlugin />
58
<ClearEditorPlugin />
59
</div>
60
</LexicalComposer>
61
);
62
63
const _renderInitialMessage = useMemo(
64
() => (
65
<div className="qa-panel__chatHistory qa-panel__initialMessageDisplayed">
66
<ChatItem
67
message={{
68
status: MESSAGE_STATUS.READY,
69
content: <InitialMessage onQuestion={onInput} />,
70
role: MESSAGE_ROLE.ASSISTANT,
71
initial: true,
72
}}
73
/>
74
</div>
75
),
76
[onInput]
77
);
78
79
const submitButton = useMemo(
80
() => (
81
<Button
82
id="question-enter"
83
className="qa-panel__submitBtn"
84
onClick={isRunning ? handleOnAbort : handleOnSubmit}
85
kind={isRunning ? "danger--ghost" : "ghost"}
86
hasIconOnly
87
renderIcon={isRunning ? StopOutline : Send}
88
iconDescription={isRunning ? "Abort" : "Submit"}
89
size="sm"
90
disabled={!questionText && !isRunning}
91
/>
92
),
93
[isRunning, handleOnAbort, handleOnSubmit, questionText]
94
);
95
96
useEffect(() => {
97
if (!messages.length) {
98
setQuestionText("");
99
}
100
}, [messages]);
101
102
return (
103
<div className="qa-panel__container">
104
<div className="qa-panel__innerPanel">
105
{!hasMessages ? (
106
_renderInitialMessage
107
) : (
108
<div id="chat-messages" ref={chatMessagesRef} className="qa-panel__chatHistory">
109
<div id="auto-scroll" ref={intersectorRef} />
110
{messages.map((message, index) => (
111
<ChatItem
112
key={`${message.role}:${message.timestamp}`}
113
message={message}
114
last={!index}
115
/>
116
))}
117
</div>
118
)}
119
<div className="qa-panel__questionSection">
120
<div className="qa-panel__questionInputContainer">
121
{textareaElement}
122
{submitButton}
123
</div>
124
</div>
125
</div>
126
</div>
127
);
128
};
129
130
export default QAPanel;
131
132