@aslaluroba/help-center-react 3.2.5 → 3.2.7

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 (97) 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/api.d.ts +4 -4
  5. package/dist/index.css +1 -1
  6. package/dist/index.esm.js +15543 -15136
  7. package/dist/index.esm.js.map +1 -1
  8. package/dist/index.js +15546 -15145
  9. package/dist/index.js.map +1 -1
  10. package/dist/lib/theme-utils.d.ts +0 -9
  11. package/dist/lib/types.d.ts +6 -3
  12. package/dist/services.esm.js +9474 -9222
  13. package/dist/services.esm.js.map +1 -1
  14. package/dist/services.js +9472 -9222
  15. package/dist/services.js.map +1 -1
  16. package/dist/ui/chatbot-popup/active-chat-actions.d.ts +7 -0
  17. package/dist/ui/chatbot-popup/chat-window-screen/action-button.d.ts +10 -0
  18. package/dist/ui/chatbot-popup/chat-window-screen/footer.d.ts +1 -0
  19. package/dist/ui/chatbot-popup/chat-window-screen/header.d.ts +2 -5
  20. package/dist/ui/chatbot-popup/chat-window-screen/in-chat-review.d.ts +9 -0
  21. package/dist/ui/chatbot-popup/chat-window-screen/index.d.ts +7 -3
  22. package/dist/ui/chatbot-popup/chat-window-screen/typing-indicator.d.ts +6 -0
  23. package/dist/ui/chatbot-popup/error-screen/index.d.ts +0 -1
  24. package/dist/ui/chatbot-popup/loading-screen/index.d.ts +0 -2
  25. package/dist/ui/chatbot-popup/options-list-screen/company-card.d.ts +9 -0
  26. package/dist/ui/chatbot-popup/options-list-screen/header.d.ts +1 -2
  27. package/dist/ui/chatbot-popup/options-list-screen/helpscreen-intro.d.ts +6 -0
  28. package/dist/ui/chatbot-popup/options-list-screen/helpscreen-list.d.ts +10 -0
  29. package/dist/ui/chatbot-popup/options-list-screen/helpscreen-option.d.ts +9 -0
  30. package/dist/ui/chatbot-popup/options-list-screen/index.d.ts +1 -4
  31. package/dist/ui/help-center.d.ts +2 -5
  32. package/dist/ui/help-popup.d.ts +16 -7
  33. package/dist/ui/review-dialog/index.d.ts +2 -1
  34. package/package.json +35 -29
  35. package/src/components/shared/Button/button.tsx +11 -18
  36. package/src/components/shared/Card/card.tsx +8 -8
  37. package/src/components/ui/agent-response/agent-response.tsx +4 -4
  38. package/src/components/ui/image-attachment.tsx +8 -8
  39. package/src/components/ui/image-preview-dialog.tsx +41 -41
  40. package/src/components/ui/index.ts +0 -1
  41. package/src/core/AblyService.ts +22 -17
  42. package/src/core/api.ts +9 -7
  43. package/src/globals.css +216 -50
  44. package/src/lib/theme-utils.ts +1 -33
  45. package/src/lib/types.ts +7 -4
  46. package/src/locales/ar.json +16 -6
  47. package/src/locales/en.json +16 -6
  48. package/src/types/icons.d.ts +6 -0
  49. package/src/ui/chatbot-popup/active-chat-actions.tsx +39 -0
  50. package/src/ui/chatbot-popup/chat-window-screen/action-button.tsx +37 -0
  51. package/src/ui/chatbot-popup/chat-window-screen/footer.tsx +42 -43
  52. package/src/ui/chatbot-popup/chat-window-screen/header.tsx +34 -67
  53. package/src/ui/chatbot-popup/chat-window-screen/in-chat-review.tsx +83 -0
  54. package/src/ui/chatbot-popup/chat-window-screen/index.tsx +49 -42
  55. package/src/ui/chatbot-popup/chat-window-screen/typing-indicator.tsx +27 -0
  56. package/src/ui/chatbot-popup/error-screen/index.tsx +7 -7
  57. package/src/ui/chatbot-popup/loading-screen/index.tsx +6 -17
  58. package/src/ui/chatbot-popup/options-list-screen/company-card.tsx +37 -0
  59. package/src/ui/chatbot-popup/options-list-screen/header.tsx +12 -31
  60. package/src/ui/chatbot-popup/options-list-screen/helpscreen-intro.tsx +32 -0
  61. package/src/ui/chatbot-popup/options-list-screen/helpscreen-list.tsx +48 -0
  62. package/src/ui/chatbot-popup/options-list-screen/helpscreen-option.tsx +38 -0
  63. package/src/ui/chatbot-popup/options-list-screen/index.tsx +44 -38
  64. package/src/ui/confirmation-modal/index.tsx +27 -12
  65. package/src/ui/floating-message.tsx +8 -7
  66. package/src/ui/help-button.tsx +5 -5
  67. package/src/ui/help-center.tsx +71 -59
  68. package/src/ui/help-popup.tsx +114 -91
  69. package/src/ui/powered-by.tsx +49 -7
  70. package/src/ui/review-dialog/index.tsx +48 -65
  71. package/src/ui/review-dialog/rating.tsx +7 -7
  72. package/dist/components/ui/header.d.ts +0 -6
  73. package/dist/ui/chatbot-popup/home-screen/card.d.ts +0 -6
  74. package/dist/ui/chatbot-popup/home-screen/chat-now-card.d.ts +0 -5
  75. package/dist/ui/chatbot-popup/home-screen/index.d.ts +0 -7
  76. package/dist/ui/chatbot-popup/options-list-screen/expanded-option.d.ts +0 -7
  77. package/dist/ui/chatbot-popup/options-list-screen/option-card.d.ts +0 -5
  78. package/src/assets/icons/arrowRight.svg +0 -3
  79. package/src/assets/icons/chat.svg +0 -4
  80. package/src/assets/icons/close.svg +0 -1
  81. package/src/assets/icons/closeCircle.svg +0 -3
  82. package/src/assets/icons/closeCirclePrimary.svg +0 -4
  83. package/src/assets/icons/envelope.svg +0 -3
  84. package/src/assets/icons/paperclip.svg +0 -3
  85. package/src/assets/icons/threeDots.svg +0 -3
  86. package/src/assets/icons/user.svg +0 -3
  87. package/src/assets/icons/x.svg +0 -4
  88. package/src/assets/logoColors.svg +0 -5
  89. package/src/assets/logo_ai.svg +0 -14
  90. package/src/assets/thinking-logo.svg +0 -3
  91. package/src/components/ui/header.tsx +0 -22
  92. package/src/ui/chatbot-popup/home-screen/card.tsx +0 -33
  93. package/src/ui/chatbot-popup/home-screen/chat-now-card.tsx +0 -36
  94. package/src/ui/chatbot-popup/home-screen/index.tsx +0 -44
  95. package/src/ui/chatbot-popup/options-list-screen/expanded-option.tsx +0 -37
  96. package/src/ui/chatbot-popup/options-list-screen/option-card.tsx +0 -31
  97. /package/src/assets/{icons/seperator.svg → seperator.svg} +0 -0
@@ -4,7 +4,6 @@
4
4
 
5
5
  export interface ThemeColors {
6
6
  primaryColor?: string;
7
- secondaryColor?: string;
8
7
  }
9
8
 
10
9
  /**
@@ -12,7 +11,7 @@ export interface ThemeColors {
12
11
  */
13
12
  export const getPrimaryColorStyles = (primaryColor?: string) => {
14
13
  if (!primaryColor) return {};
15
-
14
+
16
15
  return {
17
16
  '--babylai-primary-color': primaryColor,
18
17
  '--babylai-primary-color-100': lightenColor(primaryColor, 0.9),
@@ -28,37 +27,6 @@ export const getPrimaryColorStyles = (primaryColor?: string) => {
28
27
  } as React.CSSProperties;
29
28
  };
30
29
 
31
- /**
32
- * Generate dynamic styles for secondary color
33
- */
34
- export const getSecondaryColorStyles = (secondaryColor?: string) => {
35
- if (!secondaryColor) return {};
36
-
37
- return {
38
- '--babylai-secondary-color': secondaryColor,
39
- '--babylai-secondary-color-100': lightenColor(secondaryColor, 0.9),
40
- '--babylai-secondary-color-200': lightenColor(secondaryColor, 0.8),
41
- '--babylai-secondary-color-300': lightenColor(secondaryColor, 0.7),
42
- '--babylai-secondary-color-400': lightenColor(secondaryColor, 0.6),
43
- '--babylai-secondary-color-500': secondaryColor,
44
- '--babylai-secondary-color-600': darkenColor(secondaryColor, 0.1),
45
- '--babylai-secondary-color-700': darkenColor(secondaryColor, 0.2),
46
- '--babylai-secondary-color-800': darkenColor(secondaryColor, 0.3),
47
- '--babylai-secondary-color-900': darkenColor(secondaryColor, 0.4),
48
- '--babylai-secondary-color-950': darkenColor(secondaryColor, 0.5),
49
- } as React.CSSProperties;
50
- };
51
-
52
- /**
53
- * Get combined theme styles
54
- */
55
- export const getThemeStyles = (colors: ThemeColors): React.CSSProperties => {
56
- return {
57
- ...getPrimaryColorStyles(colors.primaryColor),
58
- ...getSecondaryColorStyles(colors.secondaryColor),
59
- };
60
- };
61
-
62
30
  /**
63
31
  * Lighten a color by a percentage
64
32
  */
package/src/lib/types.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  export interface HelpCenterProps extends React.HTMLAttributes<HTMLDivElement> {
2
2
  helpScreenId: string;
3
3
  primaryColor?: string;
4
- secondaryColor?: string;
5
- logoUrl?: string;
4
+ language?: 'ar' | 'en';
6
5
  }
7
6
 
8
7
  export interface Theme {
@@ -88,7 +87,11 @@ export interface HelpScreenData {
88
87
  tenant: {
89
88
  id: string;
90
89
  name: string;
91
- key: string;
90
+ key: string;
91
+ logoUrl: string;
92
+ settings: {
93
+ description: string;
94
+ };
92
95
  };
93
96
  title: string;
94
97
  description: string;
@@ -125,7 +128,7 @@ export interface Option {
125
128
  }
126
129
 
127
130
  export interface ReviewProps {
128
- comment: string;
131
+ comment?: string;
129
132
  rating: number;
130
133
  }
131
134
 
@@ -13,21 +13,31 @@
13
13
  "poweredBy": "مدعوم بواسطة",
14
14
 
15
15
  "ReviewDialog": {
16
- "title": "أضف تقييمك",
17
- "description": "نحن نقدر ملاحظاتك! يُرجى تخصيص لحظة لتقييم تجربتك ومشاركة آرائك في قسم التعليقات أدناه. يساعدنا تقييمك في تحسين خدماتنا ويساعد المستخدمين الآخرين في اتخاذ قرارات مستنيرة. شكرًا لك!",
16
+ "title": "نحن نقدر ملاحظاتك!",
17
+ "simple_title": "نود معرفة رأيك!",
18
+ "simple_description": "رأيك يساعدنا على التحسين وتقديم دعم أفضل. يُرجى تقييم تجربتك مع خدمتنا وإخبارنا كيف كان أداؤنا.",
19
+ "description": "رأيك يساعدنا على التحسين وتقديم دعم أفضل. يُرجى تقييم تجربتك مع خدمتنا وإخبارنا كيف كان أداؤنا.",
18
20
  "rating_label": "التقييم:",
19
21
  "comment_label": "التعليق:",
20
22
  "comment_placeholder": "اكتب تعليقك هنا...",
21
23
  "submit_button": "إرسال التقييم",
24
+ "rate_button": "تقييم",
22
25
  "comment_error": "يجب أن يكون التعليق بين 10 و500 حرف.",
23
26
  "rating_error": "يجب أن يكون التقييم بين 1 و5.",
24
27
  "skip_button": "تخطي"
25
28
  },
29
+ "InChatReview": {
30
+ "title": "كيف كانت محادثتك معنا اليوم؟",
31
+ "description": "نحب أن نعرف كيف كان أداؤنا - ملاحظاتك تساعدنا على جعل كل محادثة أفضل.",
32
+ "follow_up": "رائع! نحن سعداء أنك استمتعت بالدردشة معنا. هل يمكنك تقييم تجربتك الإجمالية لمساعدتنا على الحفاظ على ذلك؟",
33
+ "note_placeholder": "اكتب ملاحظتك",
34
+ "submit_button": "تقديم"
35
+ },
26
36
  "ConfirmationModal": {
27
- "title": "تأكيد",
28
- "message": "هل أنت متأكد أنك تريد إنهاء المحادثة؟",
29
- "confirmation_button": "إنهاء",
30
- "cancel_button": "إلغاء",
37
+ "title": "تريد المغادرة؟ 👋",
38
+ "message": "لا تقلق، يمكنك العودة في أي وقت. نحن دائما هنا إذا كنت تحتاج مساعدة أو لديك أسئلة.",
39
+ "confirmation_button": "إغلاق المحادثة",
40
+ "cancel_button": "استمرار",
31
41
  "endAndStartNewChatTitle": "إنهاء وبدء محادثة جديدة",
32
42
  "endAndStartNewChatMessage": "هل أنت متأكد أنك تريد إنهاء هذه المحادثة وبدء محادثة جديدة؟"
33
43
  }
@@ -13,21 +13,31 @@
13
13
  "poweredBy": "Powered by",
14
14
 
15
15
  "ReviewDialog": {
16
- "title": "Add your Review",
17
- "description": "We appreciate your feedback! Please take a moment to rate your experience and share your thoughts in the comment section below. Your review helps us improve our services and assists other users in making informed decisions. Thank you!",
16
+ "title": "We’d Love Your Feedback!",
17
+ "simple_title": "We'd Love Your Feedback!",
18
+ "simple_description": "Your opinion helps us improve and deliver better support. Please rate your experience with our service and let us know how we did.",
19
+ "description": "Your opinion helps us improve and deliver better support. Please rate your experience with our service and let us know how we did.",
18
20
  "rating_label": "Rating:",
19
21
  "comment_label": "Comment:",
20
22
  "comment_placeholder": "Write your comment here...",
21
23
  "submit_button": "Submit Review",
24
+ "rate_button": "Rate",
22
25
  "comment_error": "Comment must be between 10 and 500 characters.",
23
26
  "rating_error": "Rating must be between 1 and 5.",
24
27
  "skip_button": "Skip"
25
28
  },
29
+ "InChatReview": {
30
+ "title": "How was your conversation with us today?",
31
+ "description": "We'd love to know how we did - your feedback helps us make every chat even better.",
32
+ "follow_up": "Awesome! We're glad you enjoyed chatting with us. Could you rate your overall experience to help us keep it that way?",
33
+ "note_placeholder": "Write Your Note",
34
+ "submit_button": "Submit Review"
35
+ },
26
36
  "ConfirmationModal": {
27
- "title": "Confirmation",
28
- "message": "Are you sure you want to end the conversation?",
29
- "confirmation_button": "Confirm",
30
- "cancel_button": "Cancel",
37
+ "title": "Leaving so soon? 👋",
38
+ "message": "Don't worry, you can come back anytime. We're always here if you need help or have questions.",
39
+ "confirmation_button": "Close Chat",
40
+ "cancel_button": "Continue",
31
41
  "endAndStartNewChatTitle": "End and Start New Chat",
32
42
  "endAndStartNewChatMessage": "Are you sure you want to end the current conversation and start a new one?"
33
43
  }
@@ -0,0 +1,6 @@
1
+ declare module "~icons/*" {
2
+ import type { FC, SVGProps } from "react";
3
+ const Icon: FC<SVGProps<SVGSVGElement> & { title?: string }>;
4
+ export default Icon;
5
+ }
6
+
@@ -0,0 +1,39 @@
1
+ import { Button } from '@/components';
2
+ import { useLocalTranslation } from '@/useLocalTranslation';
3
+ import React from 'react';
4
+ import SolarPlain2BoldDuotone from '~icons/solar/plain-2-bold-duotone';
5
+
6
+ interface ActiveChatActionsProps {
7
+ onConfirm: () => void;
8
+ onCancel: () => void;
9
+ }
10
+
11
+ export const ActiveChatActions: React.FC<ActiveChatActionsProps> = ({
12
+ onConfirm,
13
+ onCancel,
14
+ }) => {
15
+ const { t } = useLocalTranslation();
16
+
17
+ return (
18
+ <section className="babylai:flex babylai:justify-between babylai:gap-3 babylai:px-4 babylai:py-6 babylai:absolute babylai:left-0 babylai:right-0 babylai:bottom-11 babylai:z-20 babylai:bg-linear-to-t babylai:from-card babylai:to-transparent babylai:from-[28.32%] babylai:to-[112.59%]">
19
+ <Button
20
+ onClick={onConfirm}
21
+ variant='secondary'
22
+ >
23
+ {t('homeSdk.ConfirmationModal.confirmation_button')}
24
+
25
+ </Button>
26
+ <Button
27
+ onClick={onCancel}
28
+ variant='default'
29
+ >
30
+ {t('homeSdk.ConfirmationModal.cancel_button')}
31
+ <SolarPlain2BoldDuotone className="babylai:w-6 babylai:h-6" />
32
+ </Button>
33
+ </section>
34
+ );
35
+ };
36
+
37
+ ActiveChatActions.displayName = 'ActiveChatActions';
38
+
39
+ export default ActiveChatActions;
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+ import { cn } from '@/lib/utils';
3
+
4
+ interface ActionButtonProps {
5
+ onClick: () => void;
6
+ icon?: React.ReactNode;
7
+ children?: React.ReactNode;
8
+ ariaLabel?: string;
9
+ className?: string;
10
+ }
11
+
12
+ const ActionButton: React.FC<ActionButtonProps> = ({
13
+ onClick,
14
+ icon,
15
+ children,
16
+ ariaLabel,
17
+ className,
18
+ }) => {
19
+ return (
20
+ <button
21
+ onClick={onClick}
22
+ className={cn(
23
+ 'babylai:bg-card babylai:text-card-foreground babylai:h-6 babylai:w-8 babylai:rounded-full babylai:flex babylai:items-center babylai:justify-center babylai:font-bold babylai:text-base babylai:ml-auto babylai:cursor-pointer',
24
+ className
25
+ )}
26
+ aria-label={ariaLabel}
27
+ type="button"
28
+ >
29
+ {icon}
30
+ {children}
31
+ </button>
32
+ );
33
+ };
34
+
35
+ ActionButton.displayName = 'ActionButton';
36
+
37
+ export default ActionButton;
@@ -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: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-3 babylai: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';