@aslaluroba/help-center-react 3.2.4 → 3.2.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/README.md +0 -3
- package/dist/components/shared/Button/button.d.ts +3 -3
- package/dist/components/ui/index.d.ts +0 -1
- package/dist/core/AblyService.d.ts +4 -1
- package/dist/core/api.d.ts +4 -4
- package/dist/index.css +1 -1
- package/dist/index.esm.js +16040 -15655
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +16043 -15664
- package/dist/index.js.map +1 -1
- package/dist/lib/custom-hooks/useActionHandler.d.ts +8 -0
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/theme-utils.d.ts +0 -9
- package/dist/lib/types.d.ts +6 -3
- package/dist/services.esm.js +9588 -9443
- package/dist/services.esm.js.map +1 -1
- package/dist/services.js +9582 -9439
- package/dist/services.js.map +1 -1
- package/dist/ui/chatbot-popup/active-chat-actions.d.ts +7 -0
- package/dist/ui/chatbot-popup/chat-window-screen/action-button.d.ts +10 -0
- package/dist/ui/chatbot-popup/chat-window-screen/footer.d.ts +1 -0
- package/dist/ui/chatbot-popup/chat-window-screen/header.d.ts +2 -5
- package/dist/ui/chatbot-popup/chat-window-screen/in-chat-review.d.ts +9 -0
- package/dist/ui/chatbot-popup/chat-window-screen/index.d.ts +7 -3
- package/dist/ui/chatbot-popup/chat-window-screen/typing-indicator.d.ts +6 -0
- package/dist/ui/chatbot-popup/error-screen/index.d.ts +0 -1
- package/dist/ui/chatbot-popup/loading-screen/index.d.ts +0 -2
- package/dist/ui/chatbot-popup/options-list-screen/company-card.d.ts +9 -0
- package/dist/ui/chatbot-popup/options-list-screen/header.d.ts +1 -2
- package/dist/ui/chatbot-popup/options-list-screen/helpscreen-intro.d.ts +6 -0
- package/dist/ui/chatbot-popup/options-list-screen/helpscreen-list.d.ts +10 -0
- package/dist/ui/chatbot-popup/options-list-screen/helpscreen-option.d.ts +9 -0
- package/dist/ui/chatbot-popup/options-list-screen/index.d.ts +1 -4
- package/dist/ui/help-center.d.ts +2 -5
- package/dist/ui/help-popup.d.ts +16 -7
- package/dist/ui/review-dialog/index.d.ts +2 -1
- package/package.json +35 -29
- package/src/components/shared/Button/button.tsx +11 -18
- package/src/components/shared/Card/card.tsx +8 -8
- package/src/components/ui/agent-response/agent-response.tsx +4 -4
- package/src/components/ui/image-attachment.tsx +8 -8
- package/src/components/ui/image-preview-dialog.tsx +41 -41
- package/src/components/ui/index.ts +0 -1
- package/src/core/AblyService.ts +60 -23
- package/src/core/api.ts +9 -7
- package/src/globals.css +216 -50
- package/src/lib/custom-hooks/useActionHandler.ts +102 -0
- package/src/lib/index.ts +1 -0
- package/src/lib/theme-utils.ts +1 -33
- package/src/lib/types.ts +7 -4
- package/src/locales/ar.json +16 -6
- package/src/locales/en.json +16 -6
- package/src/types/icons.d.ts +6 -0
- package/src/ui/chatbot-popup/active-chat-actions.tsx +39 -0
- package/src/ui/chatbot-popup/chat-window-screen/action-button.tsx +37 -0
- package/src/ui/chatbot-popup/chat-window-screen/footer.tsx +42 -43
- package/src/ui/chatbot-popup/chat-window-screen/header.tsx +34 -67
- package/src/ui/chatbot-popup/chat-window-screen/in-chat-review.tsx +83 -0
- package/src/ui/chatbot-popup/chat-window-screen/index.tsx +49 -42
- package/src/ui/chatbot-popup/chat-window-screen/typing-indicator.tsx +27 -0
- package/src/ui/chatbot-popup/error-screen/index.tsx +7 -7
- package/src/ui/chatbot-popup/loading-screen/index.tsx +6 -17
- package/src/ui/chatbot-popup/options-list-screen/company-card.tsx +37 -0
- package/src/ui/chatbot-popup/options-list-screen/header.tsx +12 -31
- package/src/ui/chatbot-popup/options-list-screen/helpscreen-intro.tsx +32 -0
- package/src/ui/chatbot-popup/options-list-screen/helpscreen-list.tsx +48 -0
- package/src/ui/chatbot-popup/options-list-screen/helpscreen-option.tsx +38 -0
- package/src/ui/chatbot-popup/options-list-screen/index.tsx +44 -38
- package/src/ui/confirmation-modal/index.tsx +27 -12
- package/src/ui/floating-message.tsx +8 -7
- package/src/ui/help-button.tsx +5 -5
- package/src/ui/help-center.tsx +95 -58
- package/src/ui/help-popup.tsx +114 -91
- package/src/ui/powered-by.tsx +49 -7
- package/src/ui/review-dialog/index.tsx +48 -65
- package/src/ui/review-dialog/rating.tsx +7 -7
- package/dist/components/ui/header.d.ts +0 -6
- package/dist/ui/chatbot-popup/home-screen/card.d.ts +0 -6
- package/dist/ui/chatbot-popup/home-screen/chat-now-card.d.ts +0 -5
- package/dist/ui/chatbot-popup/home-screen/index.d.ts +0 -7
- package/dist/ui/chatbot-popup/options-list-screen/expanded-option.d.ts +0 -7
- package/dist/ui/chatbot-popup/options-list-screen/option-card.d.ts +0 -5
- package/src/.DS_Store +0 -0
- package/src/assets/icons/arrowRight.svg +0 -3
- package/src/assets/icons/chat.svg +0 -4
- package/src/assets/icons/close.svg +0 -1
- package/src/assets/icons/closeCircle.svg +0 -3
- package/src/assets/icons/closeCirclePrimary.svg +0 -4
- package/src/assets/icons/envelope.svg +0 -3
- package/src/assets/icons/paperclip.svg +0 -3
- package/src/assets/icons/threeDots.svg +0 -3
- package/src/assets/icons/user.svg +0 -3
- package/src/assets/icons/x.svg +0 -4
- package/src/assets/logoColors.svg +0 -5
- package/src/assets/logo_ai.svg +0 -14
- package/src/assets/thinking-logo.svg +0 -3
- package/src/components/ui/header.tsx +0 -22
- package/src/ui/chatbot-popup/home-screen/card.tsx +0 -33
- package/src/ui/chatbot-popup/home-screen/chat-now-card.tsx +0 -36
- package/src/ui/chatbot-popup/home-screen/index.tsx +0 -44
- package/src/ui/chatbot-popup/options-list-screen/expanded-option.tsx +0 -37
- package/src/ui/chatbot-popup/options-list-screen/option-card.tsx +0 -31
- /package/src/assets/{icons/seperator.svg → seperator.svg} +0 -0
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import React, { useRef, useState, useCallback } from 'react';
|
|
2
2
|
import axios from 'axios';
|
|
3
|
-
import { Button } from '@/components';
|
|
4
3
|
import { ImagePreviewDialog } from '@/components/ui';
|
|
5
|
-
import
|
|
6
|
-
import PaperclipIcon from './../../../assets/icons/paperclip.svg';
|
|
7
|
-
import XIcon from './../../../assets/icons/x.svg';
|
|
4
|
+
import MaterialSymbolsCloseSmallOutlineRounded from '~icons/material-symbols/close-small-outline-rounded'
|
|
8
5
|
import { useLocalTranslation } from '../../../useLocalTranslation';
|
|
9
6
|
import { presignUpload } from '@/core/api';
|
|
7
|
+
import SolarPlain2BoldDuotone from '~icons/solar/plain-2-bold-duotone'
|
|
8
|
+
import SolarPaperclipBoldDuotone from '~icons/solar/paperclip-bold-duotone'
|
|
10
9
|
|
|
11
10
|
interface SelectedFileDto {
|
|
12
11
|
file: File;
|
|
@@ -21,12 +20,13 @@ interface ChatWindowFooterProps {
|
|
|
21
20
|
setInputMessage: (e: string) => void;
|
|
22
21
|
handleSendMessage: (attachmentIds: string[]) => void;
|
|
23
22
|
isLoading: boolean;
|
|
23
|
+
isChatClosed?: boolean;
|
|
24
24
|
onEnsureSession: () => Promise<string>;
|
|
25
25
|
sessionId?: string | null; // Pass existing sessionId to avoid creating new sessions
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
const ChatWindowFooter: React.FC<ChatWindowFooterProps> = (props) => {
|
|
29
|
-
const { t,
|
|
29
|
+
const { t, i18n } = useLocalTranslation();
|
|
30
30
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
31
31
|
const [selectedFiles, setSelectedFiles] = useState<SelectedFileDto[]>([]);
|
|
32
32
|
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
|
@@ -74,8 +74,8 @@ const ChatWindowFooter: React.FC<ChatWindowFooterProps> = (props) => {
|
|
|
74
74
|
}, []);
|
|
75
75
|
|
|
76
76
|
const handleSendMessageWithAttachments = useCallback(async () => {
|
|
77
|
-
// Prevent sending if already loading
|
|
78
|
-
if (props.isLoading || isSending) {
|
|
77
|
+
// Prevent sending if already loading or chat was closed from chat screen
|
|
78
|
+
if (props.isLoading || props.isChatClosed || isSending) {
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
81
|
|
|
@@ -127,7 +127,7 @@ const ChatWindowFooter: React.FC<ChatWindowFooterProps> = (props) => {
|
|
|
127
127
|
);
|
|
128
128
|
|
|
129
129
|
// Get presigned URL
|
|
130
|
-
const presignResponse = await presignUpload(sessionId, fileDto.file, i18n.language);
|
|
130
|
+
const presignResponse = await presignUpload(sessionId, fileDto.file, i18n.language as 'ar' | 'en');
|
|
131
131
|
|
|
132
132
|
// Upload file to presigned URL using axios
|
|
133
133
|
const uploadResponse = await axios.put(presignResponse.uploadUrl, fileDto.file, {
|
|
@@ -190,7 +190,7 @@ const ChatWindowFooter: React.FC<ChatWindowFooterProps> = (props) => {
|
|
|
190
190
|
console.error('[ChatWindowFooter] Error sending message:', error);
|
|
191
191
|
setIsSending(false);
|
|
192
192
|
}
|
|
193
|
-
}, [selectedFiles, props, i18n.language, isSending]);
|
|
193
|
+
}, [selectedFiles, props, i18n.language as 'ar' | 'en', isSending]);
|
|
194
194
|
|
|
195
195
|
// Check if any files are currently uploading
|
|
196
196
|
const hasUploadingFiles = selectedFiles.some((f) => f.uploading);
|
|
@@ -200,7 +200,8 @@ const ChatWindowFooter: React.FC<ChatWindowFooterProps> = (props) => {
|
|
|
200
200
|
|
|
201
201
|
// Allow sending if there's text OR files selected (files will be uploaded on send)
|
|
202
202
|
const hasContentToSend = props.inputMessage.trim() !== '' || selectedFiles.length > 0;
|
|
203
|
-
const isSendDisabled =
|
|
203
|
+
const isSendDisabled =
|
|
204
|
+
props.isLoading || props.isChatClosed || isSending || !hasContentToSend || hasUploadingFiles || hasFileErrors;
|
|
204
205
|
const showLoading = props.isLoading || isSending || hasUploadingFiles;
|
|
205
206
|
|
|
206
207
|
const handleKeyDown = useCallback(
|
|
@@ -216,85 +217,83 @@ const ChatWindowFooter: React.FC<ChatWindowFooterProps> = (props) => {
|
|
|
216
217
|
);
|
|
217
218
|
|
|
218
219
|
return (
|
|
219
|
-
<footer className='babylai
|
|
220
|
+
<footer className='babylai:flex babylai:flex-col babylai:gap-2 babylai:mx-4 babylai:md:mx-4 babylai:mb-2'>
|
|
220
221
|
{selectedFiles.length > 0 && (
|
|
221
|
-
<div className='babylai
|
|
222
|
+
<div className='babylai:flex babylai:gap-2 babylai:flex-wrap babylai:p-2 babylai:bg-card babylai:rounded-lg'>
|
|
222
223
|
{selectedFiles.map((file) => (
|
|
223
|
-
<div key={file.previewUrl} className='babylai
|
|
224
|
+
<div key={file.previewUrl} className='babylai:relative babylai:group'>
|
|
224
225
|
<img
|
|
225
226
|
src={file.previewUrl}
|
|
226
227
|
alt='Preview'
|
|
227
|
-
className='babylai
|
|
228
|
+
className='babylai:w-16 babylai:h-16 babylai:object-cover babylai:rounded-lg babylai:border babylai:border-black-white-200 babylai:cursor-pointer babylai:hover:opacity-80 babylai:transition-opacity'
|
|
228
229
|
onClick={() => setPreviewImage(file.previewUrl)}
|
|
229
230
|
role='button'
|
|
230
231
|
aria-label='Click to preview image'
|
|
231
232
|
/>
|
|
232
233
|
{file.uploading && (
|
|
233
|
-
<div className='babylai
|
|
234
|
-
<div className='babylai
|
|
234
|
+
<div className='babylai:absolute babylai:inset-0 babylai:flex babylai:items-center babylai:justify-center babylai:bg-black babylai:bg-opacity-50 babylai:rounded-lg'>
|
|
235
|
+
<div className='babylai:animate-spin babylai:rounded-full babylai:h-6 babylai:w-6 babylai:border-2 babylai:border-white babylai:border-t-transparent'></div>
|
|
235
236
|
</div>
|
|
236
237
|
)}
|
|
237
238
|
{file.error && (
|
|
238
|
-
<div className='babylai
|
|
239
|
-
<span className='babylai
|
|
239
|
+
<div className='babylai:absolute babylai:inset-0 babylai:flex babylai:items-center babylai:justify-center babylai:bg-red-500 babylai:bg-opacity-70 babylai:rounded-lg'>
|
|
240
|
+
<span className='babylai:text-white babylai:text-xs'>Error</span>
|
|
240
241
|
</div>
|
|
241
242
|
)}
|
|
242
243
|
<button
|
|
243
244
|
onClick={() => handleRemoveFile(file.previewUrl)}
|
|
244
|
-
className='babylai
|
|
245
|
+
className='babylai:absolute babylai:-top-2 babylai:-right-2 babylai:bg-destructive babylai:text-white babylai:rounded-full babylai:w-5 babylai:h-5 babylai:flex babylai:items-center babylai:justify-center babylai:cursor-pointer'
|
|
245
246
|
type='button'
|
|
246
247
|
aria-label='Remove image'
|
|
247
248
|
>
|
|
248
|
-
<
|
|
249
|
+
<MaterialSymbolsCloseSmallOutlineRounded className='babylai:w-3 babylai:h-3' />
|
|
249
250
|
</button>
|
|
250
251
|
</div>
|
|
251
252
|
))}
|
|
252
253
|
</div>
|
|
253
254
|
)}
|
|
254
255
|
|
|
255
|
-
<div className='babylai
|
|
256
|
+
<div className='babylai:flex babylai:items-center babylai:gap-2 babylai:relative babylai:rounded-full babylai:bg-card babylai:py-1 babylai:px-2 babylai:md:py-3 babylai:md:px-4'>
|
|
256
257
|
<input
|
|
257
258
|
type='file'
|
|
258
259
|
ref={fileInputRef}
|
|
259
260
|
onChange={handleFileSelect}
|
|
260
261
|
accept='image/*'
|
|
261
262
|
multiple
|
|
262
|
-
className='babylai
|
|
263
|
+
className='babylai:hidden'
|
|
263
264
|
/>
|
|
264
|
-
<
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
265
|
+
<div className='babylai:border-e babylai:border-border babylai:pe-2'>
|
|
266
|
+
<button
|
|
267
|
+
onClick={handleAttachClick}
|
|
268
|
+
disabled={props.isLoading || props.isChatClosed}
|
|
269
|
+
className='babylai:flex babylai:items-center babylai:justify-center babylai:rounded-full babylai:w-8 babylai:h-8 babylai:cursor-pointer babylai:bg-secondary babylai:text-muted-foreground babylai:hover:text-primary-500 babylai:transition-colors babylai:disabled:opacity-50 babylai:disabled:cursor-not-allowed'
|
|
270
|
+
type='button'
|
|
271
|
+
aria-label='Attach image'
|
|
272
|
+
>
|
|
273
|
+
<SolarPaperclipBoldDuotone className='babylai:w-4 babylai:h-4' />
|
|
274
|
+
</button>
|
|
275
|
+
</div>
|
|
274
276
|
<input
|
|
275
277
|
type='text'
|
|
276
278
|
value={props.inputMessage}
|
|
277
279
|
onChange={(e) => props.setInputMessage(e.target.value)}
|
|
278
280
|
onKeyDown={handleKeyDown}
|
|
279
281
|
placeholder={t('homeSdk.placeholder')}
|
|
280
|
-
|
|
282
|
+
disabled={props.isChatClosed}
|
|
283
|
+
className='babylai:flex-1 babylai:py-2 babylai:px-2 babylai:bg-transparent babylai:outline-none babylai:text-sm babylai:border-none babylai:text-card-foreground babylai:disabled:opacity-50 babylai:disabled:cursor-not-allowed'
|
|
281
284
|
/>
|
|
282
|
-
<
|
|
283
|
-
variant='default'
|
|
284
|
-
size='icon'
|
|
285
|
+
<button
|
|
285
286
|
onClick={handleSendMessageWithAttachments}
|
|
286
|
-
disabled={isSendDisabled}
|
|
287
|
-
className='babylai
|
|
287
|
+
disabled={isSendDisabled || props.isChatClosed}
|
|
288
|
+
className='babylai:rounded-full babylai:bg-primary-500 babylai:hover:bg-primary-600 babylai:w-8 babylai:h-8 babylai:p-0 babylai:flex babylai:items-center babylai:justify-center babylai:disabled:opacity-50 babylai:text-white babylai:cursor-pointer babylai:disabled:cursor-not-allowed'
|
|
288
289
|
type='button'
|
|
289
290
|
>
|
|
290
291
|
{showLoading ? (
|
|
291
|
-
<div className='babylai
|
|
292
|
+
<div className='babylai:inline-block babylai:animate-spin babylai:rounded-full babylai:h-4 babylai:w-4 babylai:aspect-square babylai:border-2 babylai:border-white babylai:border-t-transparent babylai:box-border'></div>
|
|
292
293
|
) : (
|
|
293
|
-
<
|
|
294
|
-
className={`babylai-w-4 babylai-h-4 babylai-flex-shrink-0 ${dir === 'rtl' ? 'babylai-rotate-270' : ''}`}
|
|
295
|
-
/>
|
|
294
|
+
<SolarPlain2BoldDuotone className="babylai:w-4 babylai:h-4" />
|
|
296
295
|
)}
|
|
297
|
-
</
|
|
296
|
+
</button>
|
|
298
297
|
</div>
|
|
299
298
|
|
|
300
299
|
{/* Image Preview Dialog */}
|
|
@@ -1,81 +1,48 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import { useLocalTranslation } from "../../../useLocalTranslation";
|
|
1
|
+
import React from "react";
|
|
2
|
+
import ActionButton from "./action-button";
|
|
3
|
+
import SolarAltArrowLeftLinear from '~icons/solar/alt-arrow-left-linear'
|
|
4
|
+
import IcRoundMinus from '~icons/ic/round-minus'
|
|
5
|
+
import MaterialSymbolsCloseSmallOutlineRounded from '~icons/material-symbols/close-small-outline-rounded'
|
|
7
6
|
|
|
8
7
|
interface ChatWindowHeaderProps {
|
|
9
|
-
isShowList: boolean;
|
|
10
|
-
setIsShowList?: (isShowList: boolean) => void;
|
|
11
|
-
showChat: boolean;
|
|
12
|
-
onClose: () => void;
|
|
13
8
|
handleBack: () => void;
|
|
14
9
|
handleEndChat: () => void;
|
|
15
|
-
|
|
10
|
+
handleMinimize: () => void;
|
|
11
|
+
optionTitle: string;
|
|
16
12
|
}
|
|
17
13
|
|
|
18
|
-
const ChatWindowHeader: React.FC<ChatWindowHeaderProps> = (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
const ChatWindowHeader: React.FC<ChatWindowHeaderProps> = ({
|
|
15
|
+
handleBack,
|
|
16
|
+
handleEndChat,
|
|
17
|
+
handleMinimize,
|
|
18
|
+
optionTitle,
|
|
19
|
+
}) => {
|
|
22
20
|
|
|
23
21
|
return (
|
|
24
|
-
<header className="
|
|
25
|
-
<div className="babylai
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
>
|
|
32
|
-
<ArrowRight
|
|
33
|
-
className={`babylai-w-3 babylai-h-3 ${isRTL ? "" : "babylai-rotate-180"} babylai-text-primary-500 dark:babylai-text-white`}
|
|
34
|
-
/>
|
|
35
|
-
</Button>
|
|
36
|
-
<div className="babylai-relative">
|
|
37
|
-
<Button
|
|
38
|
-
variant="rounded-icon"
|
|
39
|
-
size="icon"
|
|
40
|
-
className="!babylai-bg-primary-100 dark:!babylai-bg-storm-dust-950"
|
|
41
|
-
onClick={() => setShowMenu(!showMenu)}
|
|
42
|
-
>
|
|
43
|
-
<ThreeDots className="babylai-w-3.5 babylai-h-1 babylai-text-primary-500 dark:babylai-text-white" />
|
|
44
|
-
</Button>
|
|
22
|
+
<header className="bg-header babylai:flex babylai:items-center babylai:justify-between babylai:p-6 babylai:border-b babylai:border-black-white-200">
|
|
23
|
+
<div className="babylai:flex babylai:items-center babylai:gap-2">
|
|
24
|
+
<ActionButton
|
|
25
|
+
onClick={handleBack}
|
|
26
|
+
icon={<SolarAltArrowLeftLinear />}
|
|
27
|
+
ariaLabel="Back"
|
|
28
|
+
/>
|
|
45
29
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
aria-orientation="vertical"
|
|
52
|
-
>
|
|
53
|
-
<Button
|
|
54
|
-
role="menuitem"
|
|
55
|
-
variant="link"
|
|
56
|
-
onClick={(e) => {
|
|
57
|
-
e.stopPropagation();
|
|
58
|
-
props.handleEndChat();
|
|
59
|
-
setShowMenu(false);
|
|
60
|
-
}}
|
|
61
|
-
>
|
|
62
|
-
{t("homeSdk.endChat")}
|
|
63
|
-
</Button>
|
|
64
|
-
</div>
|
|
65
|
-
</div>
|
|
66
|
-
)}
|
|
67
|
-
</div>
|
|
30
|
+
<ActionButton
|
|
31
|
+
onClick={handleEndChat}
|
|
32
|
+
icon={<MaterialSymbolsCloseSmallOutlineRounded />}
|
|
33
|
+
ariaLabel="End Chat"
|
|
34
|
+
/>
|
|
68
35
|
</div>
|
|
69
36
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
37
|
+
<h2 className="babylai:text-lg babylai:font-semibold babylai:ml-auto babylai:text-card-foreground">
|
|
38
|
+
{optionTitle}
|
|
39
|
+
</h2>
|
|
40
|
+
|
|
41
|
+
<ActionButton
|
|
42
|
+
onClick={handleMinimize}
|
|
43
|
+
icon={<IcRoundMinus />}
|
|
44
|
+
ariaLabel="Minimize"
|
|
45
|
+
/>
|
|
79
46
|
</header>
|
|
80
47
|
);
|
|
81
48
|
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import React, { useState, useCallback } from 'react';
|
|
2
|
+
import { ReviewProps } from '@/lib/types';
|
|
3
|
+
import { Rating } from '@/ui/review-dialog/rating';
|
|
4
|
+
import { useLocalTranslation } from '@/useLocalTranslation';
|
|
5
|
+
import LogoIcon from '@/assets/logo.svg';
|
|
6
|
+
|
|
7
|
+
interface InChatReviewProps {
|
|
8
|
+
onSubmit: (payload: ReviewProps) => void | Promise<void>;
|
|
9
|
+
onDone?: () => void;
|
|
10
|
+
isSubmitting?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const InChatReview: React.FC<InChatReviewProps> = ({ onSubmit, isSubmitting = false }) => {
|
|
14
|
+
const { t } = useLocalTranslation();
|
|
15
|
+
const [rating, setRating] = useState<number>(0);
|
|
16
|
+
const [comment, setComment] = useState<string>('');
|
|
17
|
+
|
|
18
|
+
const hasRating = rating >= 1 && rating <= 5;
|
|
19
|
+
|
|
20
|
+
const handleRatingChange = useCallback((val: number) => {
|
|
21
|
+
setRating(val);
|
|
22
|
+
}, []);
|
|
23
|
+
|
|
24
|
+
const handleSubmit = useCallback(() => {
|
|
25
|
+
void onSubmit({ rating, comment: comment.trim() });
|
|
26
|
+
}, [rating, comment, onSubmit]);
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<section className="babylai:mb-4 babylai:flex babylai:justify-start">
|
|
30
|
+
<div className="babylai:shrink-0 babylai:me-3 babylai:w-8 babylai:h-8 babylai:rounded-full babylai:flex babylai:items-center babylai:justify-center babylai:bg-primary">
|
|
31
|
+
<LogoIcon className="babylai:w-4 babylai:h-4 babylai:text-primary" />
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<div className="babylai:bg-card babylai:max-w-[80%] babylai:p-5 babylai:rounded-3xl babylai:text-sm babylai:flex babylai:flex-col babylai:gap-2.5 babylai:text-start">
|
|
35
|
+
<h2 className='babylai:text-card-foreground'>
|
|
36
|
+
{t('homeSdk.InChatReview.title')}
|
|
37
|
+
</h2>
|
|
38
|
+
<p className="babylai:font-normal babylai:text-muted-foreground">
|
|
39
|
+
{t('homeSdk.InChatReview.description')}
|
|
40
|
+
</p>
|
|
41
|
+
|
|
42
|
+
<section className='babylai:bg-muted babylai:rounded-3xl babylai:p-3'>
|
|
43
|
+
<Rating value={rating} onChange={handleRatingChange} size="md" />
|
|
44
|
+
</section>
|
|
45
|
+
|
|
46
|
+
{hasRating && (
|
|
47
|
+
<>
|
|
48
|
+
<p className="babylai:text-card-foreground">{t('homeSdk.InChatReview.follow_up')}</p>
|
|
49
|
+
|
|
50
|
+
<textarea
|
|
51
|
+
className="babylai:w-full babylai:bg-secondary babylai:border babylai:border-black-white-200 babylai:rounded-xl babylai:text-card-foreground babylai:text-sm babylai:p-3 babylai:resize-vertical babylai:min-h-20 babylai:disabled:opacity-50 babylai:disabled:cursor-not-allowed babylai:disabled:bg-secondary"
|
|
52
|
+
rows={4}
|
|
53
|
+
placeholder={t('homeSdk.InChatReview.note_placeholder')}
|
|
54
|
+
value={comment}
|
|
55
|
+
onChange={(e) => setComment(e.target.value)}
|
|
56
|
+
aria-label={t('homeSdk.InChatReview.note_placeholder')}
|
|
57
|
+
/>
|
|
58
|
+
|
|
59
|
+
<button
|
|
60
|
+
onClick={handleSubmit}
|
|
61
|
+
disabled={isSubmitting}
|
|
62
|
+
className="babylai:bg-primary babylai:border babylai:border-primary babylai:cursor-pointer babylai:disabled:bg-black-white-300 babylai:disabled:border-black-white-300 babylai:disabled:cursor-not-allowed babylai:disabled:text-white babylai:duration-200 babylai:ease-out babylai:flex babylai:gap-1 babylai:items-center babylai:justify-center babylai:p-3 babylai:relative babylai:rounded-2xl babylai:text-white babylai:text-xl babylai:transition-all babylai:w-full"
|
|
63
|
+
>
|
|
64
|
+
{isSubmitting ? (
|
|
65
|
+
<>
|
|
66
|
+
<span
|
|
67
|
+
className="babylai:inline-block babylai:animate-spin babylai:rounded-full babylai:h-4 babylai:w-4 babylai:border-2 babylai:border-white babylai:border-t-transparent babylai:box-border"
|
|
68
|
+
aria-hidden
|
|
69
|
+
/>
|
|
70
|
+
{t('homeSdk.InChatReview.submit_button')}
|
|
71
|
+
</>
|
|
72
|
+
) : (
|
|
73
|
+
t('homeSdk.InChatReview.submit_button')
|
|
74
|
+
)}
|
|
75
|
+
</button>
|
|
76
|
+
</>
|
|
77
|
+
)}
|
|
78
|
+
</div>
|
|
79
|
+
</section>
|
|
80
|
+
);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
InChatReview.displayName = 'InChatReview';
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import AgentResponse from '@/components/ui/agent-response/agent-response';
|
|
2
2
|
import { ImageAttachment, ImagePreviewDialog } from '@/components/ui';
|
|
3
|
-
import { Message } from '@/lib/types';
|
|
3
|
+
import { Message, ReviewProps } from '@/lib/types';
|
|
4
4
|
import ChatWindowFooter from '@/ui/chatbot-popup/chat-window-screen/footer';
|
|
5
5
|
import React, { useEffect, useRef, useState, useCallback, useMemo } from 'react';
|
|
6
|
-
import
|
|
7
|
-
import Seperator from './../../../assets/icons/seperator.svg';
|
|
6
|
+
import Seperator from './../../../assets/seperator.svg';
|
|
8
7
|
import LogoIcon from './../../../assets/logo.svg';
|
|
9
|
-
import HumanIcon from './../../../assets/icons/user.svg';
|
|
10
8
|
import { presignDownload } from '@/core/api';
|
|
11
9
|
import { useLocalTranslation } from '../../../useLocalTranslation';
|
|
10
|
+
import PoweredBy from '@/ui/powered-by';
|
|
11
|
+
import { TypingIndicator } from './typing-indicator';
|
|
12
|
+
import { InChatReview } from './in-chat-review';
|
|
13
|
+
import SolarUserBold from '~icons/solar/user-bold'
|
|
12
14
|
|
|
13
15
|
interface ChatWindowProps {
|
|
14
16
|
onSendMessage: (message: string, attachmentIds: string[]) => void;
|
|
@@ -16,8 +18,12 @@ interface ChatWindowProps {
|
|
|
16
18
|
messages: Message[];
|
|
17
19
|
assistantStatus: string;
|
|
18
20
|
needsAgent: boolean;
|
|
19
|
-
isAblyConnected: boolean;
|
|
20
21
|
sessionId?: string | null;
|
|
22
|
+
isChatClosed?: boolean;
|
|
23
|
+
inChatReviewSessionId?: string | null;
|
|
24
|
+
isSubmittingReview?: boolean;
|
|
25
|
+
onInChatReviewSubmit?: (payload: ReviewProps) => void | Promise<void>;
|
|
26
|
+
onInChatReviewDone?: () => void;
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
// Memoize individual message component to prevent unnecessary re-renders
|
|
@@ -39,12 +45,13 @@ const MessageComponent = React.memo(
|
|
|
39
45
|
}) => {
|
|
40
46
|
const isFirstInSequence = index === 0 || messages[index - 1].senderType !== message.senderType;
|
|
41
47
|
const isFirstHumanAgentMessage = index === firstHumanAgentIndex && message.senderType === 2;
|
|
42
|
-
const textDirection = message.senderType === 1 ? 'babylai
|
|
48
|
+
const textDirection = message.senderType === 1 ? 'babylai:justify-end' : 'babylai:justify-start';
|
|
43
49
|
|
|
44
50
|
const handleImageClick = useCallback(
|
|
45
51
|
(clickedIndex: number) => {
|
|
46
52
|
// Use attachmentUrls if available (from Ably), otherwise use attachmentIds (user-sent)
|
|
47
|
-
const attachments =
|
|
53
|
+
const attachments =
|
|
54
|
+
message.attachmentUrls?.length ? message.attachmentUrls : (message.attachmentIds || []);
|
|
48
55
|
if (attachments.length > 0) {
|
|
49
56
|
onImageClick(attachments, clickedIndex);
|
|
50
57
|
}
|
|
@@ -55,32 +62,31 @@ const MessageComponent = React.memo(
|
|
|
55
62
|
return (
|
|
56
63
|
<div key={message.id}>
|
|
57
64
|
{isFirstHumanAgentMessage && (
|
|
58
|
-
<div className='babylai
|
|
59
|
-
<Seperator className='babylai
|
|
65
|
+
<div className='babylai:flex babylai:justify-center babylai:items-center babylai:my-4'>
|
|
66
|
+
<Seperator className='babylai:w-full babylai:text-primary-500' />
|
|
60
67
|
</div>
|
|
61
68
|
)}
|
|
62
|
-
<div className={`babylai
|
|
69
|
+
<div className={`babylai:mb-4 babylai:flex ${textDirection}`}>
|
|
63
70
|
{isFirstInSequence && (message.senderType === 3 || message.senderType === 2) && (
|
|
64
|
-
<div className='babylai
|
|
71
|
+
<div className='babylai:shrink-0 babylai:me-3'>
|
|
65
72
|
<div
|
|
66
|
-
className={`babylai
|
|
67
|
-
|
|
68
|
-
}`}
|
|
73
|
+
className={`babylai:w-8 babylai:h-8 babylai:rounded-full babylai:flex babylai:items-center babylai:justify-center ${message?.senderType === 3 ? 'babylai:bg-primary' : 'babylai:bg-black-white-50'
|
|
74
|
+
}`}
|
|
69
75
|
>
|
|
70
76
|
{message?.senderType === 3 ? (
|
|
71
|
-
<LogoIcon className='babylai
|
|
77
|
+
<LogoIcon className='babylai:w-4 babylai:h-4 babylai:text-primary' />
|
|
72
78
|
) : (
|
|
73
|
-
<
|
|
79
|
+
<SolarUserBold className='babylai:w-4 babylai:h-4 babylai:text-primary' />
|
|
74
80
|
)}
|
|
75
81
|
</div>
|
|
76
82
|
</div>
|
|
77
83
|
)}
|
|
78
|
-
{!isFirstInSequence && <div className='babylai
|
|
84
|
+
{!isFirstInSequence && <div className='babylai:shrink-0 babylai:me-3 babylai:w-8'></div>}
|
|
79
85
|
|
|
80
|
-
<div className='babylai
|
|
86
|
+
<div className='babylai:flex babylai:flex-col babylai:gap-2'>
|
|
81
87
|
{/* Display attachment URLs (from Ably) */}
|
|
82
88
|
{message.attachmentUrls && message.attachmentUrls.length > 0 && (
|
|
83
|
-
<div className='babylai
|
|
89
|
+
<div className='babylai:flex babylai:flex-row babylai:flex-wrap babylai:gap-2 babylai:max-w-full'>
|
|
84
90
|
{message.attachmentUrls.map((attachmentUrl, imgIndex) => (
|
|
85
91
|
<ImageAttachment
|
|
86
92
|
key={attachmentUrl}
|
|
@@ -93,7 +99,7 @@ const MessageComponent = React.memo(
|
|
|
93
99
|
)}
|
|
94
100
|
{/* Display attachment IDs (user-sent messages) */}
|
|
95
101
|
{message.attachmentIds && message.attachmentIds.length > 0 && (
|
|
96
|
-
<div className='babylai
|
|
102
|
+
<div className='babylai:flex babylai:flex-row babylai:flex-wrap babylai:gap-2 babylai:max-w-full'>
|
|
97
103
|
{message.attachmentIds.map((attachmentId, imgIndex) => (
|
|
98
104
|
<ImageAttachment
|
|
99
105
|
key={attachmentId}
|
|
@@ -122,26 +128,6 @@ const MessageComponent = React.memo(
|
|
|
122
128
|
|
|
123
129
|
MessageComponent.displayName = 'MessageComponent';
|
|
124
130
|
|
|
125
|
-
// Memoize typing indicator component
|
|
126
|
-
const TypingIndicator = React.memo(({ firstHumanAgentIndex }: { firstHumanAgentIndex: number }) => {
|
|
127
|
-
if (firstHumanAgentIndex !== -1) return null;
|
|
128
|
-
|
|
129
|
-
return (
|
|
130
|
-
<div className='babylai-mb-4 babylai-flex'>
|
|
131
|
-
<div className='babylai-flex-shrink-0 babylai-me-3'>
|
|
132
|
-
<div className='babylai-w-8 babylai-h-8 babylai-rounded-full babylai-flex babylai-items-center babylai-justify-center'>
|
|
133
|
-
<img src={LoadingGif} alt='Loading' className='babylai-w-8 babylai-h-8' />
|
|
134
|
-
</div>
|
|
135
|
-
</div>
|
|
136
|
-
<div className='babylai-max-w-[80%] babylai-rounded-2xl babylai-p-4 babylai-bg-white dark:!babylai-bg-storm-dust-900'>
|
|
137
|
-
<p className='babylai-text-sm babylai-opacity-70 dark:babylai-text-white babylai-m-0'>...</p>
|
|
138
|
-
</div>
|
|
139
|
-
</div>
|
|
140
|
-
);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
TypingIndicator.displayName = 'TypingIndicator';
|
|
144
|
-
|
|
145
131
|
export const ChatWindow = React.memo(
|
|
146
132
|
({
|
|
147
133
|
onSendMessage,
|
|
@@ -150,6 +136,11 @@ export const ChatWindow = React.memo(
|
|
|
150
136
|
assistantStatus = 'loading',
|
|
151
137
|
needsAgent,
|
|
152
138
|
sessionId,
|
|
139
|
+
isChatClosed = false,
|
|
140
|
+
inChatReviewSessionId,
|
|
141
|
+
isSubmittingReview = false,
|
|
142
|
+
onInChatReviewSubmit,
|
|
143
|
+
onInChatReviewDone,
|
|
153
144
|
}: ChatWindowProps) => {
|
|
154
145
|
const { i18n } = useLocalTranslation();
|
|
155
146
|
const [inputMessage, setInputMessage] = useState('');
|
|
@@ -249,7 +240,7 @@ export const ChatWindow = React.memo(
|
|
|
249
240
|
return Promise.resolve(null);
|
|
250
241
|
}
|
|
251
242
|
|
|
252
|
-
return presignDownload(fileId, i18n.language)
|
|
243
|
+
return presignDownload(fileId, i18n.language as 'ar' | 'en')
|
|
253
244
|
.then((response) => {
|
|
254
245
|
if (!response || !response.downloadUrl) {
|
|
255
246
|
return null;
|
|
@@ -322,13 +313,26 @@ export const ChatWindow = React.memo(
|
|
|
322
313
|
));
|
|
323
314
|
}, [messages, firstHumanAgentIndex, scrollToBottom, handleImageClick]);
|
|
324
315
|
|
|
316
|
+
const showInChatReview = !!inChatReviewSessionId && !!onInChatReviewSubmit;
|
|
317
|
+
|
|
325
318
|
return (
|
|
326
319
|
<>
|
|
327
|
-
<div className='babylai
|
|
320
|
+
<div className='babylai:flex-1 babylai:overflow-y-auto babylai:p-4 babylai:h-full'>
|
|
328
321
|
{messagesListWithGallery}
|
|
329
322
|
|
|
330
323
|
{assistantStatus === 'typing' && <TypingIndicator firstHumanAgentIndex={firstHumanAgentIndex} />}
|
|
331
324
|
|
|
325
|
+
{showInChatReview && (
|
|
326
|
+
<>
|
|
327
|
+
<Seperator className="babylai:my-6 babylai:text-black-white-300" />
|
|
328
|
+
<InChatReview
|
|
329
|
+
onSubmit={onInChatReviewSubmit}
|
|
330
|
+
onDone={onInChatReviewDone}
|
|
331
|
+
isSubmitting={isSubmittingReview}
|
|
332
|
+
/>
|
|
333
|
+
</>
|
|
334
|
+
)}
|
|
335
|
+
|
|
332
336
|
<div ref={messagesEndRef} />
|
|
333
337
|
</div>
|
|
334
338
|
|
|
@@ -337,10 +341,13 @@ export const ChatWindow = React.memo(
|
|
|
337
341
|
handleSendMessage={handleSendMessage}
|
|
338
342
|
setInputMessage={setInputMessage}
|
|
339
343
|
isLoading={isLoading}
|
|
344
|
+
isChatClosed={isChatClosed}
|
|
340
345
|
onEnsureSession={onEnsureSession}
|
|
341
346
|
sessionId={sessionId}
|
|
342
347
|
/>
|
|
343
348
|
|
|
349
|
+
<PoweredBy />
|
|
350
|
+
|
|
344
351
|
{/* Gallery Preview Dialog */}
|
|
345
352
|
{galleryState.isOpen && galleryState.imageUrls.length > 0 && (
|
|
346
353
|
<ImagePreviewDialog
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import LoadingGif from '../../../assets/animatedLogo.gif';
|
|
3
|
+
|
|
4
|
+
interface TypingIndicatorProps {
|
|
5
|
+
firstHumanAgentIndex: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const TypingIndicator = React.memo(({ firstHumanAgentIndex }: TypingIndicatorProps) => {
|
|
9
|
+
if (firstHumanAgentIndex !== -1) return null;
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<div className='babylai:mb-4 babylai:flex'>
|
|
13
|
+
<div className='babylai:shrink-0 babylai:me-3'>
|
|
14
|
+
<div className='babylai:w-8 babylai:h-8 babylai:rounded-full babylai:flex babylai:items-center babylai:justify-center'>
|
|
15
|
+
<img src={LoadingGif} alt='Loading' className='babylai:w-8 babylai:h-8' />
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
<div className='babylai:max-w-[80%] babylai:rounded-2xl babylai:p-4 babylai:bg-card'>
|
|
19
|
+
<p className='babylai:text-sm babylai:opacity-70 babylai:text-muted-foreground babylai:m-0'>...</p>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
TypingIndicator.displayName = 'TypingIndicator';
|
|
26
|
+
|
|
27
|
+
export default TypingIndicator;
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
import Header from '../options-list-screen/header'
|
|
2
3
|
|
|
3
4
|
interface ChatBotErrorScreenProps {
|
|
4
5
|
onClose: () => void
|
|
5
6
|
error: string
|
|
6
|
-
logoUrl?: string
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
const ChatBotErrorScreen: React.FC<ChatBotErrorScreenProps> = (props) => {
|
|
10
10
|
return (
|
|
11
|
-
<div className="babylai
|
|
12
|
-
<div className="babylai
|
|
13
|
-
<Header
|
|
14
|
-
<div className="babylai
|
|
15
|
-
<div className="babylai
|
|
11
|
+
<div className="babylai:w-full babylai:h-full babylai:bg-secondary babylai:rounded-3xl babylai:shadow-lg babylai:z-50 babylai:flex babylai:flex-col">
|
|
12
|
+
<div className="babylai:h-full babylai:rounded-3xl babylai:flex babylai:flex-col babylai:gap-4 babylai:md:gap-8">
|
|
13
|
+
<Header onMinimize={props.onClose} />
|
|
14
|
+
<div className="babylai:flex babylai:flex-col babylai:items-center babylai:justify-center babylai:w-full babylai:h-full babylai:px-6 babylai:py-16 babylai:md:py-28">
|
|
15
|
+
<div className="babylai:text-secondary-foreground babylai:text-lg">Error: {props.error}</div>
|
|
16
16
|
</div>
|
|
17
17
|
</div>
|
|
18
18
|
</div>
|