@aslaluroba/help-center-react 2.0.4 → 2.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/api.d.ts +4 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.esm.js +994 -25294
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +995 -25295
- package/dist/index.js.map +1 -1
- package/dist/lib/config.d.ts +1 -1
- package/dist/lib/types.d.ts +4 -0
- package/dist/ui/chatbot-popup/chat-window-screen/footer.d.ts +1 -0
- package/dist/ui/chatbot-popup/chat-window-screen/index.d.ts +1 -1
- package/dist/ui/help-center.d.ts +1 -1
- package/dist/ui/help-popup.d.ts +9 -3
- package/dist/ui/review-dialog/index.d.ts +8 -0
- package/dist/ui/review-dialog/rating.d.ts +12 -0
- package/package.json +26 -5
- package/src/assets/icons/arrowRight.svg +1 -1
- package/src/assets/icons/closeCircle.svg +1 -1
- package/src/components/ui/agent-response/agent-response.tsx +36 -34
- package/src/components/ui/header.tsx +2 -3
- package/src/core/SignalRService.ts +25 -25
- package/src/core/api.ts +180 -44
- package/src/globals.css +0 -9
- package/src/index.ts +3 -2
- package/src/lib/config.ts +25 -25
- package/src/lib/types.ts +5 -0
- package/src/locales/ar.json +18 -1
- package/src/locales/en.json +26 -8
- package/src/ui/chatbot-popup/chat-window-screen/footer.tsx +31 -33
- package/src/ui/chatbot-popup/chat-window-screen/header.tsx +47 -53
- package/src/ui/chatbot-popup/chat-window-screen/index.tsx +182 -88
- package/src/ui/chatbot-popup/options-list-screen/header.tsx +24 -20
- package/src/ui/chatbot-popup/options-list-screen/index.tsx +24 -24
- package/src/ui/chatbot-popup/options-list-screen/option-card.tsx +9 -4
- package/src/ui/help-center.tsx +189 -159
- package/src/ui/help-popup.tsx +241 -165
- package/src/ui/review-dialog/index.tsx +106 -0
- package/src/ui/review-dialog/rating.tsx +78 -0
- package/tsconfig.json +48 -0
package/src/ui/help-popup.tsx
CHANGED
|
@@ -1,71 +1,79 @@
|
|
|
1
|
-
import { cn } from '@/lib'
|
|
2
|
-
import { ChatWindow } from '@/ui/chatbot-popup/chat-window-screen'
|
|
3
|
-
import ChatWindowHeader from '@/ui/chatbot-popup/chat-window-screen/header'
|
|
4
|
-
import ChatBotErrorScreen from '@/ui/chatbot-popup/error-screen'
|
|
5
|
-
import HomeScreen from '@/ui/chatbot-popup/home-screen'
|
|
6
|
-
import ChatBotLoadingScreen from '@/ui/chatbot-popup/loading-screen'
|
|
7
|
-
import OptionsListScreen from '@/ui/chatbot-popup/options-list-screen'
|
|
8
|
-
import React, { useEffect, useRef, useState } from 'react'
|
|
9
|
-
import ChatIcon from '../assets/icons/chat.svg'
|
|
10
|
-
import { Button } from '
|
|
11
|
-
import { HelpScreenData, Message, Option } from '
|
|
1
|
+
import { cn } from '@/lib';
|
|
2
|
+
import { ChatWindow } from '@/ui/chatbot-popup/chat-window-screen';
|
|
3
|
+
import ChatWindowHeader from '@/ui/chatbot-popup/chat-window-screen/header';
|
|
4
|
+
import ChatBotErrorScreen from '@/ui/chatbot-popup/error-screen';
|
|
5
|
+
import HomeScreen from '@/ui/chatbot-popup/home-screen';
|
|
6
|
+
import ChatBotLoadingScreen from '@/ui/chatbot-popup/loading-screen';
|
|
7
|
+
import OptionsListScreen from '@/ui/chatbot-popup/options-list-screen';
|
|
8
|
+
import React, { useEffect, useRef, useState, useCallback, useMemo } from 'react';
|
|
9
|
+
import ChatIcon from '../assets/icons/chat.svg';
|
|
10
|
+
import { Button } from '@/components';
|
|
11
|
+
import { HelpScreenData, Message, Option } from '@/lib/types';
|
|
12
|
+
import { useLocalTranslation } from '../useLocalTranslation';
|
|
12
13
|
|
|
13
14
|
type HelpPopupProps = {
|
|
14
|
-
isOpen: boolean
|
|
15
|
-
onClose: () => void
|
|
16
|
-
helpScreen: HelpScreenData | null
|
|
17
|
-
status: string
|
|
18
|
-
error: string | null
|
|
19
|
-
user: any
|
|
20
|
-
onStartChat: (option: Option) => void
|
|
21
|
-
onSendMessage: (message: string) => void
|
|
22
|
-
onEndChat: () => void
|
|
23
|
-
messages: Message[]
|
|
24
|
-
needsAgent: boolean
|
|
25
|
-
assistantStatus: string
|
|
26
|
-
sessionId: string | null
|
|
27
|
-
isChatClosed: boolean
|
|
28
|
-
isSignalRConnected: boolean
|
|
29
|
-
selectedOption: Option | null
|
|
30
|
-
setSelectedOption: (option: Option | null) => void
|
|
31
|
-
showHelpScreen: boolean
|
|
32
|
-
}
|
|
15
|
+
isOpen: boolean;
|
|
16
|
+
onClose: () => void;
|
|
17
|
+
helpScreen: HelpScreenData | null;
|
|
18
|
+
status: string;
|
|
19
|
+
error: string | null;
|
|
20
|
+
user: any;
|
|
21
|
+
onStartChat: (option: Option) => void;
|
|
22
|
+
onSendMessage: (message: string) => void;
|
|
23
|
+
onEndChat: (option?: Option) => void;
|
|
24
|
+
messages: Message[];
|
|
25
|
+
needsAgent: boolean;
|
|
26
|
+
assistantStatus: string;
|
|
27
|
+
sessionId: string | null;
|
|
28
|
+
isChatClosed: boolean;
|
|
29
|
+
isSignalRConnected: boolean;
|
|
30
|
+
selectedOption: Option | null;
|
|
31
|
+
setSelectedOption: (option: Option | null) => void;
|
|
32
|
+
showHelpScreen: boolean;
|
|
33
|
+
};
|
|
33
34
|
|
|
34
35
|
// Confirmation Modal Component
|
|
35
|
-
const ConfirmationModal = ({
|
|
36
|
+
export const ConfirmationModal = ({
|
|
36
37
|
title,
|
|
37
38
|
message,
|
|
38
39
|
onCancel,
|
|
39
|
-
onConfirm
|
|
40
|
+
onConfirm,
|
|
40
41
|
}: {
|
|
41
|
-
title: string
|
|
42
|
-
message: string
|
|
43
|
-
onCancel: () => void
|
|
44
|
-
onConfirm: () => void
|
|
42
|
+
title: string;
|
|
43
|
+
message: string;
|
|
44
|
+
onCancel: () => void;
|
|
45
|
+
onConfirm: () => void;
|
|
45
46
|
}) => {
|
|
47
|
+
const { t } = useLocalTranslation();
|
|
48
|
+
|
|
46
49
|
return (
|
|
47
|
-
<div className=
|
|
48
|
-
<div className=
|
|
49
|
-
<div className=
|
|
50
|
-
<h3 className=
|
|
51
|
-
<p className=
|
|
52
|
-
<div className=
|
|
53
|
-
<Button
|
|
54
|
-
|
|
50
|
+
<div className='babylai-absolute babylai-inset-0 babylai-z-50 babylai-flex babylai-items-center babylai-justify-center babylai-rounded-3xl babylai-overflow-hidden'>
|
|
51
|
+
<div className='babylai-absolute babylai-inset-0 babylai-bg-black/60' onClick={onCancel}></div>
|
|
52
|
+
<div className='babylai-bg-black-white-100 babylai-rounded-3xl babylai-p-4 babylai-w-[220px] babylai-z-50 babylai-shadow-lg'>
|
|
53
|
+
<h3 className='babylai-text-black-white-900 babylai-font-bold babylai-mb-2 babylai-text-center'>{title}</h3>
|
|
54
|
+
<p className='babylai-text-black-white-700 babylai-text-xs babylai-mb-4'>{message}</p>
|
|
55
|
+
<div className='babylai-flex babylai-justify-end babylai-gap-2 babylai-w-full'>
|
|
56
|
+
<Button
|
|
57
|
+
variant='default'
|
|
58
|
+
size='sm'
|
|
59
|
+
onClick={onConfirm}
|
|
60
|
+
className='babylai-text-sm babylai-w-full !babylai-font-bold'
|
|
61
|
+
>
|
|
62
|
+
{t('homeSdk.ConfirmationModal.confirmation_button')}
|
|
55
63
|
</Button>
|
|
56
64
|
<Button
|
|
57
|
-
variant=
|
|
58
|
-
size=
|
|
65
|
+
variant='outline'
|
|
66
|
+
size='sm'
|
|
59
67
|
onClick={onCancel}
|
|
60
|
-
className=
|
|
68
|
+
className='babylai-text-sm babylai-w-full babylai-text-primary-500 !babylai-font-bold'
|
|
61
69
|
>
|
|
62
|
-
|
|
70
|
+
{t('homeSdk.ConfirmationModal.cancel_button')}
|
|
63
71
|
</Button>
|
|
64
72
|
</div>
|
|
65
73
|
</div>
|
|
66
74
|
</div>
|
|
67
|
-
)
|
|
68
|
-
}
|
|
75
|
+
);
|
|
76
|
+
};
|
|
69
77
|
|
|
70
78
|
export function HelpPopup({
|
|
71
79
|
onClose,
|
|
@@ -79,121 +87,110 @@ export function HelpPopup({
|
|
|
79
87
|
assistantStatus,
|
|
80
88
|
needsAgent,
|
|
81
89
|
sessionId,
|
|
82
|
-
isChatClosed,
|
|
83
|
-
isSignalRConnected,
|
|
84
90
|
selectedOption,
|
|
85
91
|
setSelectedOption,
|
|
86
|
-
showHelpScreen
|
|
92
|
+
showHelpScreen,
|
|
93
|
+
isSignalRConnected,
|
|
87
94
|
}: HelpPopupProps) {
|
|
88
|
-
|
|
89
|
-
const [
|
|
90
|
-
const [isShowList, setIsShowList] = useState<boolean>(!showHelpScreen || false)
|
|
91
|
-
const [expandedOption, setExpandedOption] = useState<Option | null>(null)
|
|
92
|
-
const [endChatConfirmation, setEndChatConfirmation] = useState<boolean>(false)
|
|
95
|
+
// ALL HOOKS MUST BE CALLED FIRST - BEFORE ANY EARLY RETURNS
|
|
96
|
+
const [showChat, setShowChat] = useState(false);
|
|
97
|
+
const [isShowList, setIsShowList] = useState<boolean>(!showHelpScreen || false);
|
|
98
|
+
const [expandedOption, setExpandedOption] = useState<Option | null>(null);
|
|
99
|
+
const [endChatConfirmation, setEndChatConfirmation] = useState<boolean>(false);
|
|
100
|
+
const [startNewChatConfirmation, setStartNewChatConfirmation] = useState<boolean>(false);
|
|
101
|
+
const [tempSelectedOption, setTempSelectedOption] = useState<Option | null>(null);
|
|
93
102
|
|
|
94
|
-
const chatBoxRef = useRef<HTMLDivElement>(null)
|
|
103
|
+
const chatBoxRef = useRef<HTMLDivElement>(null);
|
|
104
|
+
const { t } = useLocalTranslation();
|
|
105
|
+
const messagesRef = useRef<Message[]>([]);
|
|
95
106
|
|
|
96
|
-
//
|
|
97
|
-
|
|
107
|
+
// Memoize the current messages to prevent unnecessary re-renders
|
|
108
|
+
const memoizedMessages = useMemo(() => {
|
|
109
|
+
return messages;
|
|
110
|
+
}, [messages]);
|
|
111
|
+
|
|
112
|
+
// Add this function to show the end chat confirmation modal
|
|
113
|
+
const showEndChatConfirmation = useCallback(() => setEndChatConfirmation(true), []);
|
|
114
|
+
|
|
115
|
+
// Optimize scroll to bottom with debouncing
|
|
116
|
+
const scrollToBottom = useCallback(() => {
|
|
98
117
|
if (chatBoxRef.current) {
|
|
99
|
-
|
|
118
|
+
requestAnimationFrame(() => {
|
|
119
|
+
if (chatBoxRef.current) {
|
|
120
|
+
chatBoxRef.current.scrollTop = chatBoxRef.current.scrollHeight;
|
|
121
|
+
}
|
|
122
|
+
});
|
|
100
123
|
}
|
|
101
|
-
|
|
102
|
-
}, [messages])
|
|
124
|
+
}, []);
|
|
103
125
|
|
|
104
|
-
const handleBack = () => {
|
|
126
|
+
const handleBack = useCallback(() => {
|
|
105
127
|
if (showChat) {
|
|
106
|
-
setShowChat(false)
|
|
128
|
+
setShowChat(false);
|
|
107
129
|
// onEndChat()
|
|
108
130
|
} else if (selectedOption) {
|
|
109
|
-
setSelectedOption(null)
|
|
131
|
+
setSelectedOption(null);
|
|
110
132
|
} else {
|
|
111
|
-
setIsShowList(false)
|
|
133
|
+
setIsShowList(false);
|
|
112
134
|
}
|
|
113
|
-
}
|
|
135
|
+
}, [showChat, selectedOption, setSelectedOption]);
|
|
114
136
|
|
|
115
|
-
const handleStartChat = (
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
{
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
137
|
+
const handleStartChat = useCallback(
|
|
138
|
+
(option: Option) => {
|
|
139
|
+
if (option) {
|
|
140
|
+
if (sessionId) {
|
|
141
|
+
setStartNewChatConfirmation(true);
|
|
142
|
+
setTempSelectedOption(option);
|
|
143
|
+
} else {
|
|
144
|
+
setShowChat(true);
|
|
145
|
+
onStartChat(option);
|
|
146
|
+
setSelectedOption(option);
|
|
124
147
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const handleSendMessage = (message: string) => {
|
|
133
|
-
if (message.trim() && isSignalRConnected && !isChatClosed) {
|
|
134
|
-
onSendMessage(message.trim())
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const hideEndChatConfirmation = () => {
|
|
139
|
-
setEndChatConfirmation(false)
|
|
140
|
-
}
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
[onStartChat, setSelectedOption, sessionId, setStartNewChatConfirmation, setTempSelectedOption]
|
|
151
|
+
);
|
|
141
152
|
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
{
|
|
147
|
-
id: prev.length + 1,
|
|
148
|
-
senderType: 3,
|
|
149
|
-
messageContent: selectedOption?.assistant?.closing || '',
|
|
150
|
-
sentAt: new Date(),
|
|
151
|
-
isSeen: true
|
|
153
|
+
const handleSendMessage = useCallback(
|
|
154
|
+
(message: string) => {
|
|
155
|
+
if (message.trim() && isSignalRConnected) {
|
|
156
|
+
onSendMessage(message.trim());
|
|
152
157
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
onEndChat()
|
|
157
|
-
setShowChat(false)
|
|
158
|
-
setSelectedOption(null)
|
|
159
|
-
}, 3000)
|
|
160
|
-
}
|
|
158
|
+
},
|
|
159
|
+
[onSendMessage]
|
|
160
|
+
);
|
|
161
161
|
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
}
|
|
162
|
+
const hideEndChatConfirmation = useCallback(() => {
|
|
163
|
+
setEndChatConfirmation(false);
|
|
164
|
+
}, []);
|
|
165
165
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
</div>
|
|
175
|
-
)
|
|
176
|
-
}
|
|
166
|
+
const handleEndAndStartNewChat = useCallback(async () => {
|
|
167
|
+
if (tempSelectedOption) {
|
|
168
|
+
setStartNewChatConfirmation(false);
|
|
169
|
+
setShowChat(true);
|
|
170
|
+
onEndChat(tempSelectedOption);
|
|
171
|
+
setTempSelectedOption(null);
|
|
172
|
+
}
|
|
173
|
+
}, [onEndChat, setTempSelectedOption, tempSelectedOption, setStartNewChatConfirmation]);
|
|
177
174
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
175
|
+
const handleEndChat = useCallback(() => {
|
|
176
|
+
setEndChatConfirmation(false);
|
|
177
|
+
onEndChat();
|
|
178
|
+
setShowChat(false);
|
|
179
|
+
setSelectedOption(null);
|
|
180
|
+
}, [selectedOption, onEndChat, setSelectedOption]);
|
|
181
|
+
|
|
182
|
+
const handleShowActiveChat = useCallback(() => {
|
|
183
|
+
setShowChat(true);
|
|
184
|
+
}, []);
|
|
189
185
|
|
|
190
|
-
|
|
186
|
+
// Memoize render content function
|
|
187
|
+
const renderContent = useCallback(() => {
|
|
191
188
|
if (showChat && selectedOption) {
|
|
192
189
|
return (
|
|
193
190
|
<>
|
|
194
191
|
<ChatWindowHeader
|
|
195
192
|
handleBack={handleBack}
|
|
196
|
-
handleEndChat={
|
|
193
|
+
handleEndChat={showEndChatConfirmation}
|
|
197
194
|
onClose={onClose}
|
|
198
195
|
isShowList={isShowList}
|
|
199
196
|
showChat={showChat}
|
|
@@ -201,12 +198,13 @@ md:babylai-bottom-[6rem] md:babylai-right-4 babylai-rounded-none md:babylai-roun
|
|
|
201
198
|
/>
|
|
202
199
|
<ChatWindow
|
|
203
200
|
onSendMessage={handleSendMessage}
|
|
204
|
-
messages={
|
|
201
|
+
messages={memoizedMessages}
|
|
205
202
|
assistantStatus={assistantStatus}
|
|
206
203
|
needsAgent={needsAgent}
|
|
204
|
+
isSignalRConnected={isSignalRConnected}
|
|
207
205
|
/>
|
|
208
206
|
</>
|
|
209
|
-
)
|
|
207
|
+
);
|
|
210
208
|
}
|
|
211
209
|
|
|
212
210
|
if (isShowList) {
|
|
@@ -219,46 +217,124 @@ md:babylai-bottom-[6rem] md:babylai-right-4 babylai-rounded-none md:babylai-roun
|
|
|
219
217
|
handleBack={showHelpScreen ? handleBack : onClose}
|
|
220
218
|
showHelpScreen={showHelpScreen}
|
|
221
219
|
/>
|
|
222
|
-
)
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
return <HomeScreen setIsShowList={setIsShowList} onClose={onClose} />;
|
|
223
|
+
}, [
|
|
224
|
+
showChat,
|
|
225
|
+
selectedOption,
|
|
226
|
+
handleBack,
|
|
227
|
+
showEndChatConfirmation,
|
|
228
|
+
onClose,
|
|
229
|
+
isShowList,
|
|
230
|
+
setIsShowList,
|
|
231
|
+
handleSendMessage,
|
|
232
|
+
memoizedMessages,
|
|
233
|
+
assistantStatus,
|
|
234
|
+
needsAgent,
|
|
235
|
+
helpScreen,
|
|
236
|
+
expandedOption,
|
|
237
|
+
setExpandedOption,
|
|
238
|
+
handleStartChat,
|
|
239
|
+
showHelpScreen,
|
|
240
|
+
]);
|
|
241
|
+
|
|
242
|
+
// Memoize confirmation modal
|
|
243
|
+
const confirmationModal = useMemo(() => {
|
|
244
|
+
if (!endChatConfirmation) return null;
|
|
245
|
+
|
|
246
|
+
return (
|
|
247
|
+
<ConfirmationModal
|
|
248
|
+
title={t('homeSdk.ConfirmationModal.title')}
|
|
249
|
+
message={t('homeSdk.ConfirmationModal.message')}
|
|
250
|
+
onCancel={hideEndChatConfirmation}
|
|
251
|
+
onConfirm={handleEndChat}
|
|
252
|
+
/>
|
|
253
|
+
);
|
|
254
|
+
}, [endChatConfirmation, t, hideEndChatConfirmation, handleEndChat]);
|
|
255
|
+
|
|
256
|
+
// Memoize active chat button
|
|
257
|
+
const activeChatButton = useMemo(() => {
|
|
258
|
+
if (!sessionId || showChat) return null;
|
|
259
|
+
|
|
260
|
+
return (
|
|
261
|
+
<Button
|
|
262
|
+
variant='rounded-icon'
|
|
263
|
+
size='icon'
|
|
264
|
+
className='babylai-bg-primary-500 babylai-absolute babylai-bottom-4 babylai-right-3 babylai-z-20 !babylai-w-10 !babylai-h-10 babylai-p-2'
|
|
265
|
+
onClick={handleShowActiveChat}
|
|
266
|
+
>
|
|
267
|
+
<ChatIcon className='babylai-w-5 babylai-h-5 babylai-text-black-white-50' />
|
|
268
|
+
</Button>
|
|
269
|
+
);
|
|
270
|
+
}, [sessionId, showChat, handleShowActiveChat]);
|
|
271
|
+
|
|
272
|
+
// Scroll to bottom when new messages arrive - optimize with ref comparison
|
|
273
|
+
useEffect(() => {
|
|
274
|
+
if (messagesRef.current !== messages) {
|
|
275
|
+
messagesRef.current = messages;
|
|
276
|
+
scrollToBottom();
|
|
223
277
|
}
|
|
224
|
-
|
|
278
|
+
}, [messages, scrollToBottom]);
|
|
279
|
+
|
|
280
|
+
// EARLY RETURNS MUST COME AFTER ALL HOOKS
|
|
281
|
+
// Early returns for performance - moved after all hooks
|
|
282
|
+
if (status === 'loading' && !helpScreen) {
|
|
283
|
+
return (
|
|
284
|
+
<div
|
|
285
|
+
className='babylai-fixed babylai-inset-0 md:babylai-inset-auto md:babylai-max-w-sm md:babylai-min-w-sm md:babylai-h-[calc(100vh-240px)]
|
|
286
|
+
babylai-overflow-auto babylai-w-full babylai-bg-black-white-50 md:babylai-mb-4
|
|
287
|
+
md:babylai-bottom-[6rem] md:babylai-right-4 babylai-rounded-none md:babylai-rounded-3xl babylai-shadow-lg babylai-z-50 babylai-flex babylai-flex-col'
|
|
288
|
+
>
|
|
289
|
+
<ChatBotLoadingScreen isShowList={isShowList} onClose={onClose} />
|
|
290
|
+
</div>
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (error) {
|
|
295
|
+
return (
|
|
296
|
+
<div
|
|
297
|
+
className='babylai-fixed babylai-inset-0 md:babylai-inset-auto md:babylai-max-w-sm md:babylai-min-w-sm md:babylai-h-[calc(100vh-240px)]
|
|
298
|
+
babylai-overflow-auto babylai-w-full babylai-bg-black-white-50 md:babylai-mb-4
|
|
299
|
+
md:babylai-bottom-[6rem] md:babylai-right-4 babylai-rounded-none md:babylai-rounded-3xl babylai-shadow-lg babylai-z-50 babylai-flex babylai-flex-col'
|
|
300
|
+
>
|
|
301
|
+
<ChatBotErrorScreen onClose={onClose} error={error || ''} />
|
|
302
|
+
</div>
|
|
303
|
+
);
|
|
225
304
|
}
|
|
226
305
|
|
|
227
306
|
return (
|
|
228
307
|
<div
|
|
229
|
-
className=
|
|
308
|
+
className='babylai-fixed babylai-inset-0 md:babylai-inset-auto md:babylai-max-w-sm md:babylai-min-w-sm md:babylai-h-[calc(100vh-240px)]
|
|
230
309
|
babylai-overflow-auto babylai-w-full babylai-bg-black-white-50 md:babylai-mb-4
|
|
231
|
-
md:babylai-bottom-[6rem] md:babylai-right-4 babylai-rounded-none md:babylai-rounded-3xl babylai-shadow-lg babylai-z-50 babylai-flex babylai-flex-col
|
|
310
|
+
md:babylai-bottom-[6rem] md:babylai-right-4 babylai-rounded-none md:babylai-rounded-3xl babylai-shadow-lg babylai-z-50 babylai-flex babylai-flex-col'
|
|
232
311
|
>
|
|
233
312
|
<div
|
|
234
|
-
className={cn(
|
|
235
|
-
'babylai-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
<Button
|
|
241
|
-
variant="rounded-icon"
|
|
242
|
-
size="icon"
|
|
243
|
-
className="babylai-bg-primary-500 babylai-absolute babylai-bottom-4 babylai-right-3 babylai-z-20 !babylai-w-10 !babylai-h-10 babylai-p-2"
|
|
244
|
-
onClick={handleShowActiveChat}
|
|
245
|
-
>
|
|
246
|
-
<ChatIcon className="babylai-w-5 babylai-h-5 babylai-text-black-white-50" />
|
|
247
|
-
</Button>
|
|
313
|
+
className={cn(
|
|
314
|
+
'babylai-h-full babylai-rounded-none md:babylai-rounded-3xl babylai-flex babylai-flex-col babylai-relative',
|
|
315
|
+
{
|
|
316
|
+
'babylai-bg-gradient-to-b babylai-from-primary-500 babylai-to-primary-500/60': !isShowList,
|
|
317
|
+
'babylai-bg-black-white-100': isShowList,
|
|
318
|
+
}
|
|
248
319
|
)}
|
|
320
|
+
>
|
|
321
|
+
{activeChatButton}
|
|
322
|
+
|
|
249
323
|
{/* Content */}
|
|
250
324
|
{renderContent()}
|
|
251
325
|
|
|
252
326
|
{/* End Chat Confirmation Modal */}
|
|
253
|
-
{
|
|
327
|
+
{confirmationModal}
|
|
328
|
+
|
|
329
|
+
{startNewChatConfirmation && (
|
|
254
330
|
<ConfirmationModal
|
|
255
|
-
title=
|
|
256
|
-
message=
|
|
257
|
-
onCancel={
|
|
258
|
-
onConfirm={
|
|
331
|
+
title={t('homeSdk.ConfirmationModal.endAndStartNewChatTitle')}
|
|
332
|
+
message={t('homeSdk.ConfirmationModal.endAndStartNewChatMessage')}
|
|
333
|
+
onCancel={() => setStartNewChatConfirmation(false)}
|
|
334
|
+
onConfirm={handleEndAndStartNewChat}
|
|
259
335
|
/>
|
|
260
336
|
)}
|
|
261
337
|
</div>
|
|
262
338
|
</div>
|
|
263
|
-
)
|
|
339
|
+
);
|
|
264
340
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { ReviewProps } from '@/lib/types'
|
|
2
|
+
import { Rating } from '@/ui/review-dialog/rating'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { useLocalTranslation } from '../../useLocalTranslation'
|
|
5
|
+
import CloseCircle from '../../assets/icons/closeCircle.svg'
|
|
6
|
+
|
|
7
|
+
interface ReviewDialogProps {
|
|
8
|
+
handleSubmit: ({ comment, rating }: ReviewProps) => void;
|
|
9
|
+
onClose: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const ReviewDialog: React.FC<ReviewDialogProps> = (props) => {
|
|
13
|
+
const { t } = useLocalTranslation()
|
|
14
|
+
const [comment, setComment] = React.useState<string>('')
|
|
15
|
+
const [rating, setRating] = React.useState<number>(0)
|
|
16
|
+
const [error, setError] = React.useState<{ comment?: string; rating?: string }>({})
|
|
17
|
+
|
|
18
|
+
const isCommentValid = comment.length >= 10 && comment.length <= 500
|
|
19
|
+
const isRatingValid = rating >= 1 && rating <= 5
|
|
20
|
+
|
|
21
|
+
const validateAndSubmit = () => {
|
|
22
|
+
const newError: typeof error = {}
|
|
23
|
+
if (!isCommentValid) {
|
|
24
|
+
newError.comment = t('homeSdk.ReviewDialog.comment_error') || 'Comment must be between 10 and 500 characters.'
|
|
25
|
+
}
|
|
26
|
+
if (!isRatingValid) {
|
|
27
|
+
newError.rating = t('homeSdk.ReviewDialog.rating_error') || 'Rating must be between 1 and 5.'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (Object.keys(newError).length > 0) {
|
|
31
|
+
setError(newError)
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
setError({})
|
|
36
|
+
props.handleSubmit({ comment, rating })
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const handleCommentChange = (val: string) => {
|
|
40
|
+
setComment(val)
|
|
41
|
+
if (error.comment && val.length >= 10 && val.length <= 500) {
|
|
42
|
+
setError((prev) => ({ ...prev, comment: undefined }))
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const handleRatingChange = (val: number) => {
|
|
47
|
+
setRating(val)
|
|
48
|
+
if (error.rating && val >= 1 && val <= 5) {
|
|
49
|
+
setError((prev) => ({ ...prev, rating: undefined }))
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<section className="babylai-p-6 babylai-gap-6 babylai-max-w-sm babylai-max-h-[calc(100vh-90px)] babylai-overflow-auto babylai-w-full babylai-bg-black-white-50 babylai-fixed babylai-bottom-20 babylai-right-0 md:babylai-right-4 babylai-rounded-3xl babylai-shadow-lg babylai-z-50 babylai-flex babylai-flex-col">
|
|
55
|
+
<header className='border-b pb-4 babylai-flex babylai-items-center babylai-justify-between babylai-gap-4'>
|
|
56
|
+
<h2 className="babylai-text-lg babylai-font-semibold">{t('homeSdk.ReviewDialog.title')}</h2>
|
|
57
|
+
<CloseCircle className="babylai-w-6 babylai-h-6 babylai-cursor-pointer" onClick={props.onClose} />
|
|
58
|
+
</header>
|
|
59
|
+
<div className="babylai-flex babylai-flex-col babylai-gap-2">
|
|
60
|
+
<p className="babylai-text-sm babylai-text-gray-600 mb-3">
|
|
61
|
+
{t('homeSdk.ReviewDialog.description')}
|
|
62
|
+
</p>
|
|
63
|
+
|
|
64
|
+
<div className="babylai-flex babylai-items-center babylai-gap-2">
|
|
65
|
+
<span className="babylai-text-base babylai-font-medium">{t('homeSdk.ReviewDialog.rating_label')}</span>
|
|
66
|
+
<Rating value={rating} onChange={handleRatingChange} />
|
|
67
|
+
<span className={`babylai-text-sm babylai-text-red-500 transition-opacity duration-300 ${error.rating ? 'opacity-100' : 'opacity-0'}`}>
|
|
68
|
+
{error.rating}
|
|
69
|
+
</span>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<div className="babylai-flex babylai-flex-col babylai-gap-2">
|
|
73
|
+
<label htmlFor='comment' className="babylai-text-base babylai-font-medium">{t('homeSdk.ReviewDialog.comment_label')}</label>
|
|
74
|
+
<textarea
|
|
75
|
+
id='comment'
|
|
76
|
+
className="babylai-bg-black-white-100 babylai-p-6 babylai-rounded-xl babylai-resize-none"
|
|
77
|
+
rows={4}
|
|
78
|
+
placeholder="Write your comment here..."
|
|
79
|
+
value={comment}
|
|
80
|
+
onChange={(e) => handleCommentChange(e.target.value)}
|
|
81
|
+
/>
|
|
82
|
+
<span className={`babylai-text-sm babylai-text-red-500 transition-opacity duration-300 ${error.comment ? 'opacity-100' : 'opacity-0'}`}>
|
|
83
|
+
{error.comment}
|
|
84
|
+
</span>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<footer className="babylai-flex babylai-justify-between babylai-gap-4 babylai-border-t babylai-pt-4">
|
|
89
|
+
<button
|
|
90
|
+
className="babylai-px-4 babylai-py-2 babylai-rounded-lg babylai-bg-gray-200 babylai-text-gray-700 hover:babylai-bg-gray-300 transition-all"
|
|
91
|
+
onClick={props.onClose}
|
|
92
|
+
>
|
|
93
|
+
{t('homeSdk.ReviewDialog.skip_button')}
|
|
94
|
+
</button>
|
|
95
|
+
<button
|
|
96
|
+
className='babylai-px-4 babylai-py-2 babylai-rounded-lg babylai-shadow transition-all babylai-bg-primary-500 babylai-text-white hover:babylai-bg-primary-600'
|
|
97
|
+
onClick={validateAndSubmit}
|
|
98
|
+
>
|
|
99
|
+
{t('homeSdk.ReviewDialog.submit_button')}
|
|
100
|
+
</button>
|
|
101
|
+
</footer>
|
|
102
|
+
</section>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export default ReviewDialog
|