@aslaluroba/help-center-react 2.0.4 → 2.0.5
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 +31 -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 -34
- package/src/ui/chatbot-popup/chat-window-screen/header.tsx +47 -53
- package/src/ui/chatbot-popup/chat-window-screen/index.tsx +178 -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 +366 -136
- package/src/ui/help-popup.tsx +239 -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,109 @@ 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,
|
|
87
93
|
}: 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)
|
|
94
|
+
// ALL HOOKS MUST BE CALLED FIRST - BEFORE ANY EARLY RETURNS
|
|
95
|
+
const [showChat, setShowChat] = useState(false);
|
|
96
|
+
const [isShowList, setIsShowList] = useState<boolean>(!showHelpScreen || false);
|
|
97
|
+
const [expandedOption, setExpandedOption] = useState<Option | null>(null);
|
|
98
|
+
const [endChatConfirmation, setEndChatConfirmation] = useState<boolean>(false);
|
|
99
|
+
const [startNewChatConfirmation, setStartNewChatConfirmation] = useState<boolean>(false);
|
|
100
|
+
const [tempSelectedOption, setTempSelectedOption] = useState<Option | null>(null);
|
|
93
101
|
|
|
94
|
-
const chatBoxRef = useRef<HTMLDivElement>(null)
|
|
102
|
+
const chatBoxRef = useRef<HTMLDivElement>(null);
|
|
103
|
+
const { t } = useLocalTranslation();
|
|
104
|
+
const messagesRef = useRef<Message[]>([]);
|
|
95
105
|
|
|
96
|
-
//
|
|
97
|
-
|
|
106
|
+
// Memoize the current messages to prevent unnecessary re-renders
|
|
107
|
+
const memoizedMessages = useMemo(() => {
|
|
108
|
+
return messages;
|
|
109
|
+
}, [messages]);
|
|
110
|
+
|
|
111
|
+
// Add this function to show the end chat confirmation modal
|
|
112
|
+
const showEndChatConfirmation = useCallback(() => setEndChatConfirmation(true), []);
|
|
113
|
+
|
|
114
|
+
// Optimize scroll to bottom with debouncing
|
|
115
|
+
const scrollToBottom = useCallback(() => {
|
|
98
116
|
if (chatBoxRef.current) {
|
|
99
|
-
|
|
117
|
+
requestAnimationFrame(() => {
|
|
118
|
+
if (chatBoxRef.current) {
|
|
119
|
+
chatBoxRef.current.scrollTop = chatBoxRef.current.scrollHeight;
|
|
120
|
+
}
|
|
121
|
+
});
|
|
100
122
|
}
|
|
101
|
-
|
|
102
|
-
}, [messages])
|
|
123
|
+
}, []);
|
|
103
124
|
|
|
104
|
-
const handleBack = () => {
|
|
125
|
+
const handleBack = useCallback(() => {
|
|
105
126
|
if (showChat) {
|
|
106
|
-
setShowChat(false)
|
|
127
|
+
setShowChat(false);
|
|
107
128
|
// onEndChat()
|
|
108
129
|
} else if (selectedOption) {
|
|
109
|
-
setSelectedOption(null)
|
|
130
|
+
setSelectedOption(null);
|
|
110
131
|
} else {
|
|
111
|
-
setIsShowList(false)
|
|
132
|
+
setIsShowList(false);
|
|
112
133
|
}
|
|
113
|
-
}
|
|
134
|
+
}, [showChat, selectedOption, setSelectedOption]);
|
|
114
135
|
|
|
115
|
-
const handleStartChat = (
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
{
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
136
|
+
const handleStartChat = useCallback(
|
|
137
|
+
(option: Option) => {
|
|
138
|
+
if (option) {
|
|
139
|
+
if (sessionId) {
|
|
140
|
+
setStartNewChatConfirmation(true);
|
|
141
|
+
setTempSelectedOption(option);
|
|
142
|
+
} else {
|
|
143
|
+
setShowChat(true);
|
|
144
|
+
onStartChat(option);
|
|
145
|
+
setSelectedOption(option);
|
|
124
146
|
}
|
|
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
|
-
}
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
[onStartChat, setSelectedOption, sessionId, setStartNewChatConfirmation, setTempSelectedOption]
|
|
150
|
+
);
|
|
141
151
|
|
|
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
|
|
152
|
+
const handleSendMessage = useCallback(
|
|
153
|
+
(message: string) => {
|
|
154
|
+
if (message.trim()) {
|
|
155
|
+
onSendMessage(message.trim());
|
|
152
156
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
onEndChat()
|
|
157
|
-
setShowChat(false)
|
|
158
|
-
setSelectedOption(null)
|
|
159
|
-
}, 3000)
|
|
160
|
-
}
|
|
157
|
+
},
|
|
158
|
+
[onSendMessage]
|
|
159
|
+
);
|
|
161
160
|
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
}
|
|
161
|
+
const hideEndChatConfirmation = useCallback(() => {
|
|
162
|
+
setEndChatConfirmation(false);
|
|
163
|
+
}, []);
|
|
165
164
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
</div>
|
|
175
|
-
)
|
|
176
|
-
}
|
|
165
|
+
const handleEndAndStartNewChat = useCallback(async () => {
|
|
166
|
+
if (tempSelectedOption) {
|
|
167
|
+
setStartNewChatConfirmation(false);
|
|
168
|
+
setShowChat(true);
|
|
169
|
+
onEndChat(tempSelectedOption);
|
|
170
|
+
setTempSelectedOption(null);
|
|
171
|
+
}
|
|
172
|
+
}, [onEndChat, setTempSelectedOption, tempSelectedOption, setStartNewChatConfirmation]);
|
|
177
173
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
174
|
+
const handleEndChat = useCallback(() => {
|
|
175
|
+
setEndChatConfirmation(false);
|
|
176
|
+
onEndChat();
|
|
177
|
+
setShowChat(false);
|
|
178
|
+
setSelectedOption(null);
|
|
179
|
+
}, [selectedOption, onEndChat, setSelectedOption]);
|
|
180
|
+
|
|
181
|
+
const handleShowActiveChat = useCallback(() => {
|
|
182
|
+
setShowChat(true);
|
|
183
|
+
}, []);
|
|
189
184
|
|
|
190
|
-
|
|
185
|
+
// Memoize render content function
|
|
186
|
+
const renderContent = useCallback(() => {
|
|
191
187
|
if (showChat && selectedOption) {
|
|
192
188
|
return (
|
|
193
189
|
<>
|
|
194
190
|
<ChatWindowHeader
|
|
195
191
|
handleBack={handleBack}
|
|
196
|
-
handleEndChat={
|
|
192
|
+
handleEndChat={showEndChatConfirmation}
|
|
197
193
|
onClose={onClose}
|
|
198
194
|
isShowList={isShowList}
|
|
199
195
|
showChat={showChat}
|
|
@@ -201,12 +197,12 @@ md:babylai-bottom-[6rem] md:babylai-right-4 babylai-rounded-none md:babylai-roun
|
|
|
201
197
|
/>
|
|
202
198
|
<ChatWindow
|
|
203
199
|
onSendMessage={handleSendMessage}
|
|
204
|
-
messages={
|
|
200
|
+
messages={memoizedMessages}
|
|
205
201
|
assistantStatus={assistantStatus}
|
|
206
202
|
needsAgent={needsAgent}
|
|
207
203
|
/>
|
|
208
204
|
</>
|
|
209
|
-
)
|
|
205
|
+
);
|
|
210
206
|
}
|
|
211
207
|
|
|
212
208
|
if (isShowList) {
|
|
@@ -219,46 +215,124 @@ md:babylai-bottom-[6rem] md:babylai-right-4 babylai-rounded-none md:babylai-roun
|
|
|
219
215
|
handleBack={showHelpScreen ? handleBack : onClose}
|
|
220
216
|
showHelpScreen={showHelpScreen}
|
|
221
217
|
/>
|
|
222
|
-
)
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
return <HomeScreen setIsShowList={setIsShowList} onClose={onClose} />;
|
|
221
|
+
}, [
|
|
222
|
+
showChat,
|
|
223
|
+
selectedOption,
|
|
224
|
+
handleBack,
|
|
225
|
+
showEndChatConfirmation,
|
|
226
|
+
onClose,
|
|
227
|
+
isShowList,
|
|
228
|
+
setIsShowList,
|
|
229
|
+
handleSendMessage,
|
|
230
|
+
memoizedMessages,
|
|
231
|
+
assistantStatus,
|
|
232
|
+
needsAgent,
|
|
233
|
+
helpScreen,
|
|
234
|
+
expandedOption,
|
|
235
|
+
setExpandedOption,
|
|
236
|
+
handleStartChat,
|
|
237
|
+
showHelpScreen,
|
|
238
|
+
]);
|
|
239
|
+
|
|
240
|
+
// Memoize confirmation modal
|
|
241
|
+
const confirmationModal = useMemo(() => {
|
|
242
|
+
if (!endChatConfirmation) return null;
|
|
243
|
+
|
|
244
|
+
return (
|
|
245
|
+
<ConfirmationModal
|
|
246
|
+
title={t('homeSdk.ConfirmationModal.title')}
|
|
247
|
+
message={t('homeSdk.ConfirmationModal.message')}
|
|
248
|
+
onCancel={hideEndChatConfirmation}
|
|
249
|
+
onConfirm={handleEndChat}
|
|
250
|
+
/>
|
|
251
|
+
);
|
|
252
|
+
}, [endChatConfirmation, t, hideEndChatConfirmation, handleEndChat]);
|
|
253
|
+
|
|
254
|
+
// Memoize active chat button
|
|
255
|
+
const activeChatButton = useMemo(() => {
|
|
256
|
+
if (!sessionId || showChat) return null;
|
|
257
|
+
|
|
258
|
+
return (
|
|
259
|
+
<Button
|
|
260
|
+
variant='rounded-icon'
|
|
261
|
+
size='icon'
|
|
262
|
+
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'
|
|
263
|
+
onClick={handleShowActiveChat}
|
|
264
|
+
>
|
|
265
|
+
<ChatIcon className='babylai-w-5 babylai-h-5 babylai-text-black-white-50' />
|
|
266
|
+
</Button>
|
|
267
|
+
);
|
|
268
|
+
}, [sessionId, showChat, handleShowActiveChat]);
|
|
269
|
+
|
|
270
|
+
// Scroll to bottom when new messages arrive - optimize with ref comparison
|
|
271
|
+
useEffect(() => {
|
|
272
|
+
if (messagesRef.current !== messages) {
|
|
273
|
+
messagesRef.current = messages;
|
|
274
|
+
scrollToBottom();
|
|
223
275
|
}
|
|
224
|
-
|
|
276
|
+
}, [messages, scrollToBottom]);
|
|
277
|
+
|
|
278
|
+
// EARLY RETURNS MUST COME AFTER ALL HOOKS
|
|
279
|
+
// Early returns for performance - moved after all hooks
|
|
280
|
+
if (status === 'loading' && !helpScreen) {
|
|
281
|
+
return (
|
|
282
|
+
<div
|
|
283
|
+
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)]
|
|
284
|
+
babylai-overflow-auto babylai-w-full babylai-bg-black-white-50 md:babylai-mb-4
|
|
285
|
+
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'
|
|
286
|
+
>
|
|
287
|
+
<ChatBotLoadingScreen isShowList={isShowList} onClose={onClose} />
|
|
288
|
+
</div>
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (error) {
|
|
293
|
+
return (
|
|
294
|
+
<div
|
|
295
|
+
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)]
|
|
296
|
+
babylai-overflow-auto babylai-w-full babylai-bg-black-white-50 md:babylai-mb-4
|
|
297
|
+
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'
|
|
298
|
+
>
|
|
299
|
+
<ChatBotErrorScreen onClose={onClose} error={error || ''} />
|
|
300
|
+
</div>
|
|
301
|
+
);
|
|
225
302
|
}
|
|
226
303
|
|
|
227
304
|
return (
|
|
228
305
|
<div
|
|
229
|
-
className=
|
|
306
|
+
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
307
|
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
|
|
308
|
+
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
309
|
>
|
|
233
310
|
<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>
|
|
311
|
+
className={cn(
|
|
312
|
+
'babylai-h-full babylai-rounded-none md:babylai-rounded-3xl babylai-flex babylai-flex-col babylai-relative',
|
|
313
|
+
{
|
|
314
|
+
'babylai-bg-gradient-to-b babylai-from-primary-500 babylai-to-primary-500/60': !isShowList,
|
|
315
|
+
'babylai-bg-black-white-100': isShowList,
|
|
316
|
+
}
|
|
248
317
|
)}
|
|
318
|
+
>
|
|
319
|
+
{activeChatButton}
|
|
320
|
+
|
|
249
321
|
{/* Content */}
|
|
250
322
|
{renderContent()}
|
|
251
323
|
|
|
252
324
|
{/* End Chat Confirmation Modal */}
|
|
253
|
-
{
|
|
325
|
+
{confirmationModal}
|
|
326
|
+
|
|
327
|
+
{startNewChatConfirmation && (
|
|
254
328
|
<ConfirmationModal
|
|
255
|
-
title=
|
|
256
|
-
message=
|
|
257
|
-
onCancel={
|
|
258
|
-
onConfirm={
|
|
329
|
+
title={t('homeSdk.ConfirmationModal.endAndStartNewChatTitle')}
|
|
330
|
+
message={t('homeSdk.ConfirmationModal.endAndStartNewChatMessage')}
|
|
331
|
+
onCancel={() => setStartNewChatConfirmation(false)}
|
|
332
|
+
onConfirm={handleEndAndStartNewChat}
|
|
259
333
|
/>
|
|
260
334
|
)}
|
|
261
335
|
</div>
|
|
262
336
|
</div>
|
|
263
|
-
)
|
|
337
|
+
);
|
|
264
338
|
}
|
|
@@ -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
|