@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.
Files changed (103) hide show
  1. package/README.md +0 -3
  2. package/dist/components/shared/Button/button.d.ts +3 -3
  3. package/dist/components/ui/index.d.ts +0 -1
  4. package/dist/core/AblyService.d.ts +4 -1
  5. package/dist/core/api.d.ts +4 -4
  6. package/dist/index.css +1 -1
  7. package/dist/index.esm.js +16040 -15655
  8. package/dist/index.esm.js.map +1 -1
  9. package/dist/index.js +16043 -15664
  10. package/dist/index.js.map +1 -1
  11. package/dist/lib/custom-hooks/useActionHandler.d.ts +8 -0
  12. package/dist/lib/index.d.ts +1 -0
  13. package/dist/lib/theme-utils.d.ts +0 -9
  14. package/dist/lib/types.d.ts +6 -3
  15. package/dist/services.esm.js +9588 -9443
  16. package/dist/services.esm.js.map +1 -1
  17. package/dist/services.js +9582 -9439
  18. package/dist/services.js.map +1 -1
  19. package/dist/ui/chatbot-popup/active-chat-actions.d.ts +7 -0
  20. package/dist/ui/chatbot-popup/chat-window-screen/action-button.d.ts +10 -0
  21. package/dist/ui/chatbot-popup/chat-window-screen/footer.d.ts +1 -0
  22. package/dist/ui/chatbot-popup/chat-window-screen/header.d.ts +2 -5
  23. package/dist/ui/chatbot-popup/chat-window-screen/in-chat-review.d.ts +9 -0
  24. package/dist/ui/chatbot-popup/chat-window-screen/index.d.ts +7 -3
  25. package/dist/ui/chatbot-popup/chat-window-screen/typing-indicator.d.ts +6 -0
  26. package/dist/ui/chatbot-popup/error-screen/index.d.ts +0 -1
  27. package/dist/ui/chatbot-popup/loading-screen/index.d.ts +0 -2
  28. package/dist/ui/chatbot-popup/options-list-screen/company-card.d.ts +9 -0
  29. package/dist/ui/chatbot-popup/options-list-screen/header.d.ts +1 -2
  30. package/dist/ui/chatbot-popup/options-list-screen/helpscreen-intro.d.ts +6 -0
  31. package/dist/ui/chatbot-popup/options-list-screen/helpscreen-list.d.ts +10 -0
  32. package/dist/ui/chatbot-popup/options-list-screen/helpscreen-option.d.ts +9 -0
  33. package/dist/ui/chatbot-popup/options-list-screen/index.d.ts +1 -4
  34. package/dist/ui/help-center.d.ts +2 -5
  35. package/dist/ui/help-popup.d.ts +16 -7
  36. package/dist/ui/review-dialog/index.d.ts +2 -1
  37. package/package.json +35 -29
  38. package/src/components/shared/Button/button.tsx +11 -18
  39. package/src/components/shared/Card/card.tsx +8 -8
  40. package/src/components/ui/agent-response/agent-response.tsx +4 -4
  41. package/src/components/ui/image-attachment.tsx +8 -8
  42. package/src/components/ui/image-preview-dialog.tsx +41 -41
  43. package/src/components/ui/index.ts +0 -1
  44. package/src/core/AblyService.ts +60 -23
  45. package/src/core/api.ts +9 -7
  46. package/src/globals.css +216 -50
  47. package/src/lib/custom-hooks/useActionHandler.ts +102 -0
  48. package/src/lib/index.ts +1 -0
  49. package/src/lib/theme-utils.ts +1 -33
  50. package/src/lib/types.ts +7 -4
  51. package/src/locales/ar.json +16 -6
  52. package/src/locales/en.json +16 -6
  53. package/src/types/icons.d.ts +6 -0
  54. package/src/ui/chatbot-popup/active-chat-actions.tsx +39 -0
  55. package/src/ui/chatbot-popup/chat-window-screen/action-button.tsx +37 -0
  56. package/src/ui/chatbot-popup/chat-window-screen/footer.tsx +42 -43
  57. package/src/ui/chatbot-popup/chat-window-screen/header.tsx +34 -67
  58. package/src/ui/chatbot-popup/chat-window-screen/in-chat-review.tsx +83 -0
  59. package/src/ui/chatbot-popup/chat-window-screen/index.tsx +49 -42
  60. package/src/ui/chatbot-popup/chat-window-screen/typing-indicator.tsx +27 -0
  61. package/src/ui/chatbot-popup/error-screen/index.tsx +7 -7
  62. package/src/ui/chatbot-popup/loading-screen/index.tsx +6 -17
  63. package/src/ui/chatbot-popup/options-list-screen/company-card.tsx +37 -0
  64. package/src/ui/chatbot-popup/options-list-screen/header.tsx +12 -31
  65. package/src/ui/chatbot-popup/options-list-screen/helpscreen-intro.tsx +32 -0
  66. package/src/ui/chatbot-popup/options-list-screen/helpscreen-list.tsx +48 -0
  67. package/src/ui/chatbot-popup/options-list-screen/helpscreen-option.tsx +38 -0
  68. package/src/ui/chatbot-popup/options-list-screen/index.tsx +44 -38
  69. package/src/ui/confirmation-modal/index.tsx +27 -12
  70. package/src/ui/floating-message.tsx +8 -7
  71. package/src/ui/help-button.tsx +5 -5
  72. package/src/ui/help-center.tsx +95 -58
  73. package/src/ui/help-popup.tsx +114 -91
  74. package/src/ui/powered-by.tsx +49 -7
  75. package/src/ui/review-dialog/index.tsx +48 -65
  76. package/src/ui/review-dialog/rating.tsx +7 -7
  77. package/dist/components/ui/header.d.ts +0 -6
  78. package/dist/ui/chatbot-popup/home-screen/card.d.ts +0 -6
  79. package/dist/ui/chatbot-popup/home-screen/chat-now-card.d.ts +0 -5
  80. package/dist/ui/chatbot-popup/home-screen/index.d.ts +0 -7
  81. package/dist/ui/chatbot-popup/options-list-screen/expanded-option.d.ts +0 -7
  82. package/dist/ui/chatbot-popup/options-list-screen/option-card.d.ts +0 -5
  83. package/src/.DS_Store +0 -0
  84. package/src/assets/icons/arrowRight.svg +0 -3
  85. package/src/assets/icons/chat.svg +0 -4
  86. package/src/assets/icons/close.svg +0 -1
  87. package/src/assets/icons/closeCircle.svg +0 -3
  88. package/src/assets/icons/closeCirclePrimary.svg +0 -4
  89. package/src/assets/icons/envelope.svg +0 -3
  90. package/src/assets/icons/paperclip.svg +0 -3
  91. package/src/assets/icons/threeDots.svg +0 -3
  92. package/src/assets/icons/user.svg +0 -3
  93. package/src/assets/icons/x.svg +0 -4
  94. package/src/assets/logoColors.svg +0 -5
  95. package/src/assets/logo_ai.svg +0 -14
  96. package/src/assets/thinking-logo.svg +0 -3
  97. package/src/components/ui/header.tsx +0 -22
  98. package/src/ui/chatbot-popup/home-screen/card.tsx +0 -33
  99. package/src/ui/chatbot-popup/home-screen/chat-now-card.tsx +0 -36
  100. package/src/ui/chatbot-popup/home-screen/index.tsx +0 -44
  101. package/src/ui/chatbot-popup/options-list-screen/expanded-option.tsx +0 -37
  102. package/src/ui/chatbot-popup/options-list-screen/option-card.tsx +0 -31
  103. /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 EnvelopeIcon from './../../../assets/icons/envelope.svg';
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, dir, i18n } = useLocalTranslation();
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 = props.isLoading || isSending || !hasContentToSend || hasUploadingFiles || hasFileErrors;
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-flex babylai-flex-col babylai-gap-2 babylai-mx-4 md:babylai-mx-6'>
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-flex babylai-gap-2 babylai-flex-wrap babylai-p-2 babylai-bg-white dark:!babylai-bg-storm-dust-900 babylai-rounded-lg'>
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-relative babylai-group'>
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-w-16 babylai-h-16 babylai-object-cover babylai-rounded-lg babylai-border babylai-border-black-white-200 babylai-cursor-pointer hover:babylai-opacity-80 babylai-transition-opacity'
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-absolute babylai-inset-0 babylai-flex babylai-items-center babylai-justify-center babylai-bg-black babylai-bg-opacity-50 babylai-rounded-lg'>
234
- <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>
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-absolute babylai-inset-0 babylai-flex babylai-items-center babylai-justify-center babylai-bg-red-500 babylai-bg-opacity-70 babylai-rounded-lg'>
239
- <span className='babylai-text-white babylai-text-xs'>Error</span>
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-absolute -babylai-top-2 -babylai-right-2 babylai-bg-red-500 babylai-text-white babylai-rounded-full babylai-w-5 babylai-h-5 babylai-flex babylai-items-center babylai-justify-center babylai-opacity-0 group-hover:babylai-opacity-100 babylai-transition-opacity'
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
- <XIcon className='babylai-w-3 babylai-h-3' />
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-flex babylai-items-center babylai-gap-2 babylai-relative babylai-rounded-full babylai-bg-white dark:!babylai-bg-storm-dust-900 babylai-py-1 babylai-px-2 md:babylai-py-3 md:babylai-px-4'>
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-hidden'
263
+ className='babylai:hidden'
263
264
  />
264
- <Button
265
- variant='ghost'
266
- size='icon'
267
- onClick={handleAttachClick}
268
- disabled={props.isLoading}
269
- className='babylai-rounded-full babylai-w-8 babylai-h-8 babylai-text-black-white-500 hover:babylai-text-primary-500 hover:babylai-bg-transparent'
270
- type='button'
271
- >
272
- <PaperclipIcon className='babylai-w-4 babylai-h-4' />
273
- </Button>
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
- className='babylai-flex-1 babylai-py-2 babylai-px-2 babylai-bg-transparent babylai-outline-none babylai-text-sm dark:babylai-text-white babylai-border-none'
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
- <Button
283
- variant='default'
284
- size='icon'
285
+ <button
285
286
  onClick={handleSendMessageWithAttachments}
286
- disabled={isSendDisabled}
287
- className='babylai-rounded-full babylai-bg-primary-500 babylai-hover:babylai-bg-purple-600 babylai-w-8 babylai-h-8 !babylai-p-0 babylai-flex babylai-items-center babylai-justify-center disabled:babylai-opacity-50'
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-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
+ <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
- <EnvelopeIcon
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
- </Button>
296
+ </button>
298
297
  </div>
299
298
 
300
299
  {/* Image Preview Dialog */}
@@ -1,81 +1,48 @@
1
- import Logo from "../../../assets/logo_ai.svg";
2
- import { Button } from "@/components";
3
- import React, { useState } from "react";
4
- import ArrowRight from "./../../../assets/icons/arrowRight.svg";
5
- import ThreeDots from "./../../../assets/icons/threeDots.svg";
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
- logoUrl?: string;
10
+ handleMinimize: () => void;
11
+ optionTitle: string;
16
12
  }
17
13
 
18
- const ChatWindowHeader: React.FC<ChatWindowHeaderProps> = (props) => {
19
- const [showMenu, setShowMenu] = useState(false);
20
- const { t, dir } = useLocalTranslation();
21
- const isRTL = String(dir) === "rtl";
14
+ const ChatWindowHeader: React.FC<ChatWindowHeaderProps> = ({
15
+ handleBack,
16
+ handleEndChat,
17
+ handleMinimize,
18
+ optionTitle,
19
+ }) => {
22
20
 
23
21
  return (
24
- <header className="babylai-flex babylai-w-full babylai-items-center babylai-py-[18px] babylai-px-[18px] babylai-justify-between babylai-relative babylai-bg-black-white-50 dark:babylai-bg-storm-dust-900 babylai-rounded-t-3xl">
25
- <div className="babylai-flex babylai-items-center babylai-gap-2">
26
- <Button
27
- variant="rounded-icon"
28
- size="icon"
29
- className="!babylai-bg-primary-100 dark:!babylai-bg-storm-dust-950"
30
- onClick={props.handleBack}
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
- {showMenu && (
47
- <div className="babylai-absolute babylai-top-10 babylai-start-0 babylai-w-48 babylai-rounded-md babylai-shadow-lg babylai-bg-white dark:babylai-bg-storm-dust-950 babylai-ring-1 babylai-ring-black-white-700 babylai-ring-opacity-5 babylai-z-10">
48
- <div
49
- className="babylai-py-1"
50
- role="menu"
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
- {props.logoUrl ? (
71
- <img
72
- src={props.logoUrl}
73
- alt="BabylAI Logo"
74
- className="babylai-max-h-10 babylai-object-contain"
75
- />
76
- ) : (
77
- <Logo className="babylai-w-32 babylai-h-auto dark:babylai-text-white" />
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 LoadingGif from './../../../assets/animatedLogo.gif';
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-justify-end' : 'babylai-justify-start';
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 = message.attachmentUrls || [];
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-flex babylai-justify-center babylai-items-center babylai-my-4'>
59
- <Seperator className='babylai-w-full babylai-text-primary-500' />
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-mb-4 babylai-flex ${textDirection}`}>
69
+ <div className={`babylai:mb-4 babylai:flex ${textDirection}`}>
63
70
  {isFirstInSequence && (message.senderType === 3 || message.senderType === 2) && (
64
- <div className='babylai-flex-shrink-0 babylai-me-3'>
71
+ <div className='babylai:shrink-0 babylai:me-3'>
65
72
  <div
66
- className={`babylai-w-8 babylai-h-8 babylai-rounded-full babylai-flex babylai-items-center babylai-justify-center ${
67
- message?.senderType === 3 ? 'babylai-bg-primary' : 'babylai-bg-black-white-50'
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-w-4 babylai-h-4 babylai-text-primary' />
77
+ <LogoIcon className='babylai:w-4 babylai:h-4 babylai:text-primary' />
72
78
  ) : (
73
- <HumanIcon className='babylai-w-4 babylai-h-4 babylai-text-primary' />
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-flex-shrink-0 babylai-me-3 babylai-w-8'></div>}
84
+ {!isFirstInSequence && <div className='babylai:shrink-0 babylai:me-3 babylai:w-8'></div>}
79
85
 
80
- <div className='babylai-flex babylai-flex-col babylai-gap-2'>
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-flex babylai-flex-row babylai-flex-wrap babylai-gap-2 babylai-max-w-full'>
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-flex babylai-flex-row babylai-flex-wrap babylai-gap-2 babylai-max-w-full'>
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-flex-1 babylai-overflow-y-auto babylai-p-4 babylai-h-full'>
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
- import Header from '@/components/ui/header'
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-w-full babylai-h-full babylai-bg-black-white-50 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-py-6 md:babylai-py-8 babylai-px-6 babylai-gap-4 md:babylai-gap-8 babylai-bg-linear-to-b">
13
- <Header onClose={props.onClose} logoUrl={props.logoUrl} />
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 md:babylai-py-28">
15
- <div className="babylai-text-white babylai-text-lg">Error: {props.error}</div>
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>