@aslaluroba/help-center-react 3.2.17 → 3.2.18

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 (101) hide show
  1. package/dist/components/shared/Button/button.d.ts +1 -1
  2. package/dist/components/shared/Card/card.d.ts +1 -4
  3. package/dist/components/ui/agent-response/agent-response.d.ts +2 -1
  4. package/dist/index.css +1424 -1
  5. package/dist/index.d.ts +3 -3
  6. package/dist/index.esm.js +19194 -38923
  7. package/dist/index.esm.js.map +1 -1
  8. package/dist/index.js +19198 -38927
  9. package/dist/index.js.map +1 -1
  10. package/dist/lib/LanguageContext.d.ts +1 -1
  11. package/dist/lib/custom-hooks/useAblyConnection.d.ts +25 -0
  12. package/dist/lib/custom-hooks/useActionHandler.d.ts +1 -7
  13. package/dist/lib/custom-hooks/useChatSession.d.ts +37 -0
  14. package/dist/lib/custom-hooks/useMessageQueue.d.ts +16 -0
  15. package/dist/lib/custom-hooks/useReview.d.ts +14 -0
  16. package/dist/lib/index.d.ts +1 -2
  17. package/dist/services.d.ts +9 -6
  18. package/dist/services.esm.js +1 -14348
  19. package/dist/services.esm.js.map +1 -1
  20. package/dist/services.js +19 -14344
  21. package/dist/services.js.map +1 -1
  22. package/dist/ui/chatbot-popup/chat-window-screen/footer.d.ts +1 -1
  23. package/dist/ui/chatbot-popup/chat-window-screen/in-chat-review.d.ts +1 -1
  24. package/dist/ui/chatbot-popup/chat-window-screen/index.d.ts +2 -2
  25. package/dist/ui/chatbot-popup/options-list-screen/helpscreen-list.d.ts +1 -1
  26. package/dist/ui/chatbot-popup/options-list-screen/helpscreen-option.d.ts +1 -1
  27. package/dist/ui/chatbot-popup/options-list-screen/index.d.ts +1 -1
  28. package/dist/ui/help-center.d.ts +1 -1
  29. package/dist/ui/help-popup.d.ts +4 -27
  30. package/dist/ui/review-dialog/index.d.ts +1 -1
  31. package/package.json +12 -26
  32. package/postcss.config.js +5 -0
  33. package/rollup.config.mjs +34 -0
  34. package/dist/core/AblyService.d.ts +0 -16
  35. package/dist/core/ApiService.d.ts +0 -16
  36. package/dist/core/api.d.ts +0 -10
  37. package/dist/core/token-service.d.ts +0 -10
  38. package/dist/i18n.d.ts +0 -3
  39. package/dist/lib/config.d.ts +0 -18
  40. package/dist/lib/theme-utils.d.ts +0 -10
  41. package/dist/lib/types.d.ts +0 -145
  42. package/dist/lib/utils.d.ts +0 -2
  43. package/src/assets/animatedLogo.gif +0 -0
  44. package/src/assets/logo.svg +0 -5
  45. package/src/assets/seperator.svg +0 -5
  46. package/src/components/index.ts +0 -1
  47. package/src/components/shared/Button/button.tsx +0 -38
  48. package/src/components/shared/Button/index.ts +0 -1
  49. package/src/components/shared/Card/card.tsx +0 -44
  50. package/src/components/shared/Card/index.ts +0 -1
  51. package/src/components/shared/index.ts +0 -2
  52. package/src/components/ui/agent-response/agent-response.tsx +0 -57
  53. package/src/components/ui/agent-response/doc.md +0 -88
  54. package/src/components/ui/image-attachment.tsx +0 -119
  55. package/src/components/ui/image-preview-dialog.tsx +0 -400
  56. package/src/components/ui/index.ts +0 -3
  57. package/src/core/AblyService.ts +0 -243
  58. package/src/core/ApiService.ts +0 -116
  59. package/src/core/api.ts +0 -278
  60. package/src/core/token-service.ts +0 -35
  61. package/src/globals.css +0 -268
  62. package/src/i18n.ts +0 -21
  63. package/src/index.ts +0 -19
  64. package/src/lib/LanguageContext.tsx +0 -28
  65. package/src/lib/config.ts +0 -52
  66. package/src/lib/custom-hooks/useActionHandler.ts +0 -102
  67. package/src/lib/custom-hooks/useTypewriter.ts +0 -26
  68. package/src/lib/index.ts +0 -4
  69. package/src/lib/theme-utils.ts +0 -56
  70. package/src/lib/types.ts +0 -158
  71. package/src/lib/utils.ts +0 -6
  72. package/src/locales/ar.json +0 -45
  73. package/src/locales/en.json +0 -45
  74. package/src/services.ts +0 -14
  75. package/src/types/icons.d.ts +0 -6
  76. package/src/types/svg.d.ts +0 -5
  77. package/src/types.d.ts +0 -9
  78. package/src/ui/chatbot-popup/active-chat-actions.tsx +0 -39
  79. package/src/ui/chatbot-popup/chat-window-screen/action-button.tsx +0 -37
  80. package/src/ui/chatbot-popup/chat-window-screen/footer.tsx +0 -313
  81. package/src/ui/chatbot-popup/chat-window-screen/header.tsx +0 -53
  82. package/src/ui/chatbot-popup/chat-window-screen/in-chat-review.tsx +0 -116
  83. package/src/ui/chatbot-popup/chat-window-screen/index.tsx +0 -366
  84. package/src/ui/chatbot-popup/chat-window-screen/typing-indicator.tsx +0 -31
  85. package/src/ui/chatbot-popup/error-screen/index.tsx +0 -22
  86. package/src/ui/chatbot-popup/loading-screen/index.tsx +0 -21
  87. package/src/ui/chatbot-popup/options-list-screen/company-card.tsx +0 -39
  88. package/src/ui/chatbot-popup/options-list-screen/header.tsx +0 -23
  89. package/src/ui/chatbot-popup/options-list-screen/helpscreen-intro.tsx +0 -32
  90. package/src/ui/chatbot-popup/options-list-screen/helpscreen-list.tsx +0 -57
  91. package/src/ui/chatbot-popup/options-list-screen/helpscreen-option.tsx +0 -56
  92. package/src/ui/chatbot-popup/options-list-screen/index.tsx +0 -70
  93. package/src/ui/confirmation-modal/index.tsx +0 -62
  94. package/src/ui/floating-message.tsx +0 -29
  95. package/src/ui/help-button.tsx +0 -25
  96. package/src/ui/help-center.tsx +0 -448
  97. package/src/ui/help-popup.tsx +0 -367
  98. package/src/ui/powered-by.tsx +0 -62
  99. package/src/ui/review-dialog/index.tsx +0 -149
  100. package/src/ui/review-dialog/rating.tsx +0 -79
  101. package/src/useLocalTranslation.ts +0 -15
@@ -1,53 +0,0 @@
1
- import React from "react";
2
- import { useLocalTranslation } from "@/useLocalTranslation";
3
- import { cn } from "@/lib/utils";
4
- import ActionButton from "./action-button";
5
- import SolarAltArrowLeftLinear from '~icons/solar/alt-arrow-left-linear'
6
- import IcRoundMinus from '~icons/ic/round-minus'
7
- import MaterialSymbolsCloseSmallOutlineRounded from '~icons/material-symbols/close-small-outline-rounded'
8
-
9
- interface ChatWindowHeaderProps {
10
- handleBack: () => void;
11
- handleEndChat: () => void;
12
- handleMinimize: () => void;
13
- optionTitle: string;
14
- }
15
-
16
- const ChatWindowHeader: React.FC<ChatWindowHeaderProps> = ({
17
- handleBack,
18
- handleEndChat,
19
- handleMinimize,
20
- optionTitle,
21
- }) => {
22
- const { i18n } = useLocalTranslation();
23
-
24
- return (
25
- <header className="bg-header babylai:flex babylai:items-center babylai:justify-between babylai:p-6 babylai:border-b babylai:border-black-white-200">
26
- <div className="babylai:flex babylai:items-center babylai:gap-2">
27
- <ActionButton
28
- onClick={handleBack}
29
- icon={<SolarAltArrowLeftLinear />}
30
- ariaLabel="Back"
31
- className={cn(i18n.language === 'ar' && 'babylai:rotate-180')}
32
- />
33
-
34
- <ActionButton
35
- onClick={handleEndChat}
36
- icon={<MaterialSymbolsCloseSmallOutlineRounded />}
37
- ariaLabel="End Chat"
38
- />
39
- </div>
40
-
41
- <h2 className="babylai:text-lg! babylai:font-semibold! babylai:text-card-foreground">
42
- {optionTitle}
43
- </h2>
44
-
45
- <ActionButton
46
- onClick={handleMinimize}
47
- icon={<IcRoundMinus />}
48
- ariaLabel="Minimize"
49
- />
50
- </header>
51
- );
52
- };
53
- export default ChatWindowHeader;
@@ -1,116 +0,0 @@
1
- import React, { useState, useCallback } from 'react';
2
- import { ReviewProps } from '@/lib/types';
3
- import { cn } from '@/lib/utils';
4
- import { Rating } from '@/ui/review-dialog/rating';
5
- import { useLocalTranslation } from '@/useLocalTranslation';
6
- import LogoIcon from '@/assets/logo.svg';
7
- import { Button } from '@/components';
8
-
9
- const COMMENT_MAX_LENGTH = 500;
10
-
11
- interface InChatReviewProps {
12
- onSubmit: (payload: ReviewProps) => void | Promise<void>;
13
- onDone?: () => void;
14
- isSubmitting?: boolean;
15
- }
16
-
17
- export const InChatReview: React.FC<InChatReviewProps> = ({ onSubmit, isSubmitting = false }) => {
18
- const { t } = useLocalTranslation();
19
- const [rating, setRating] = useState<number>(0);
20
- const [comment, setComment] = useState<string>('');
21
- const [commentError, setCommentError] = useState<string | null>(null);
22
-
23
- const hasRating = rating >= 1 && rating <= 5;
24
-
25
- const handleRatingChange = useCallback((val: number) => {
26
- setRating(val);
27
- }, []);
28
-
29
- const handleCommentChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {
30
- const value = e.target.value;
31
- setComment(value);
32
- if (commentError && value.length <= COMMENT_MAX_LENGTH) {
33
- setCommentError(null);
34
- }
35
- }, [commentError]);
36
-
37
- const handleSubmit = useCallback(() => {
38
- const trimmed = comment.trim();
39
- if (trimmed.length > COMMENT_MAX_LENGTH) {
40
- setCommentError(t('homeSdk.ReviewDialog.comment_error') || 'The field Comment must be a string with a maximum length of 500.');
41
- return;
42
- }
43
- setCommentError(null);
44
- void onSubmit({ rating, comment: trimmed });
45
- }, [rating, comment, onSubmit, t]);
46
-
47
- return (
48
- <section className="babylai:mb-4 babylai:flex babylai:justify-start">
49
- <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">
50
- <LogoIcon className="babylai:w-4 babylai:h-4 babylai:text-primary" />
51
- </div>
52
-
53
- <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">
54
- <h2 className='babylai:text-card-foreground'>
55
- {t('homeSdk.InChatReview.title')}
56
- </h2>
57
- <p className="babylai:font-normal babylai:text-muted-foreground babylai:leading-snug">
58
- {t('homeSdk.InChatReview.description')}
59
- </p>
60
-
61
- <section className='babylai:bg-muted babylai:rounded-3xl babylai:p-3'>
62
- <Rating value={rating} onChange={handleRatingChange} size="md" />
63
- </section>
64
-
65
- {hasRating && (
66
- <>
67
- <p className="babylai:text-card-foreground babylai:leading-snug">{t('homeSdk.InChatReview.follow_up')}</p>
68
-
69
- <textarea
70
- className={cn(
71
- 'babylai:resize-none babylai:w-full babylai:bg-secondary babylai:border 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',
72
- commentError ? 'babylai:border-destructive' : 'babylai:border-black-white-200'
73
- )}
74
- rows={4}
75
- maxLength={COMMENT_MAX_LENGTH}
76
- placeholder={t('homeSdk.InChatReview.note_placeholder')}
77
- value={comment}
78
- onChange={handleCommentChange}
79
- aria-label={t('homeSdk.InChatReview.note_placeholder')}
80
- aria-invalid={!!commentError}
81
- aria-describedby={commentError ? 'in-chat-review-comment-error' : undefined}
82
- />
83
- {commentError && (
84
- <p id="in-chat-review-comment-error" className="babylai:text-sm babylai:text-destructive" role="alert">
85
- {commentError}
86
- </p>
87
- )}
88
- <p className="babylai:text-xs babylai:text-muted-foreground">
89
- {comment.length}/{COMMENT_MAX_LENGTH}
90
- </p>
91
-
92
- <Button
93
- variant='default'
94
- onClick={handleSubmit}
95
- disabled={isSubmitting}
96
- >
97
- {isSubmitting ? (
98
- <>
99
- <span
100
- 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"
101
- aria-hidden
102
- />
103
- {t('homeSdk.InChatReview.submit_button')}
104
- </>
105
- ) : (
106
- t('homeSdk.InChatReview.submit_button')
107
- )}
108
- </Button>
109
- </>
110
- )}
111
- </div>
112
- </section>
113
- );
114
- };
115
-
116
- InChatReview.displayName = 'InChatReview';
@@ -1,366 +0,0 @@
1
- import AgentResponse from '@/components/ui/agent-response/agent-response';
2
- import { ImageAttachment, ImagePreviewDialog } from '@/components/ui';
3
- import { Message, ReviewProps } from '@/lib/types';
4
- import ChatWindowFooter from '@/ui/chatbot-popup/chat-window-screen/footer';
5
- import React, { useEffect, useRef, useState, useCallback, useMemo } from 'react';
6
- import Seperator from './../../../assets/seperator.svg';
7
- import LogoIcon from './../../../assets/logo.svg';
8
- import { presignDownload } from '@/core/api';
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'
14
-
15
- interface ChatWindowProps {
16
- onSendMessage: (message: string, attachmentIds: string[]) => void;
17
- onEnsureSession: () => Promise<string>;
18
- messages: Message[];
19
- assistantStatus: string;
20
- needsAgent: boolean;
21
- sessionId?: string | null;
22
- isChatClosed?: boolean;
23
- inChatReviewSessionId?: string | null;
24
- isSubmittingReview?: boolean;
25
- onInChatReviewSubmit?: (payload: ReviewProps) => void | Promise<void>;
26
- onInChatReviewDone?: () => void;
27
- }
28
-
29
- // Memoize individual message component to prevent unnecessary re-renders
30
- const MessageComponent = React.memo(
31
- ({
32
- message,
33
- index,
34
- messages,
35
- firstHumanAgentIndex,
36
- onType,
37
- onImageClick,
38
- }: {
39
- message: Message;
40
- index: number;
41
- messages: Message[];
42
- firstHumanAgentIndex: number;
43
- onType: () => void;
44
- onImageClick: (attachmentIdsOrUrls: string[], clickedIndex: number) => void;
45
- }) => {
46
- const isFirstInSequence = index === 0 || messages[index - 1].senderType !== message.senderType;
47
- const isFirstHumanAgentMessage = index === firstHumanAgentIndex && message.senderType === 2;
48
- const textDirection = message.senderType === 1 ? 'babylai:justify-end' : 'babylai:justify-start';
49
-
50
- const handleImageClick = useCallback(
51
- (clickedIndex: number) => {
52
- // Use attachmentUrls if available (from Ably), otherwise use attachmentIds (user-sent)
53
- const attachments =
54
- message.attachmentUrls?.length ? message.attachmentUrls : (message.attachmentIds || []);
55
- if (attachments.length > 0) {
56
- onImageClick(attachments, clickedIndex);
57
- }
58
- },
59
- [message.attachmentIds, message.attachmentUrls, onImageClick]
60
- );
61
-
62
- return (
63
- <div key={message.id}>
64
- {isFirstHumanAgentMessage && (
65
- <div className='babylai:flex babylai:justify-center babylai:items-center babylai:my-4'>
66
- <Seperator className='babylai:w-full babylai:text-primary-500' />
67
- </div>
68
- )}
69
- <div className={`babylai:mb-4 babylai:flex ${textDirection}`}>
70
- {isFirstInSequence && (message.senderType === 3 || message.senderType === 2) && (
71
- <div className='babylai:shrink-0 babylai:me-3'>
72
- <div
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
- }`}
75
- >
76
- {message?.senderType === 3 ? (
77
- <LogoIcon className='babylai:w-4 babylai:h-4 babylai:text-primary' />
78
- ) : (
79
- <SolarUserBold className='babylai:w-4 babylai:h-4 babylai:text-primary' />
80
- )}
81
- </div>
82
- </div>
83
- )}
84
- {!isFirstInSequence && <div className='babylai:shrink-0 babylai:me-3 babylai:w-8'></div>}
85
-
86
- <div className='babylai:flex babylai:flex-col babylai:gap-2'>
87
- {/* Display attachment URLs (from Ably) */}
88
- {message.attachmentUrls && message.attachmentUrls.length > 0 && (
89
- <div className='babylai:flex babylai:flex-row babylai:flex-wrap babylai:gap-2 babylai:max-w-full'>
90
- {message.attachmentUrls.map((attachmentUrl, imgIndex) => (
91
- <ImageAttachment
92
- key={attachmentUrl}
93
- imageUrl={attachmentUrl}
94
- enablePreview={false}
95
- onClick={() => handleImageClick(imgIndex)}
96
- />
97
- ))}
98
- </div>
99
- )}
100
- {/* Display attachment IDs (user-sent messages) */}
101
- {message.attachmentIds && message.attachmentIds.length > 0 && (
102
- <div className='babylai:flex babylai:flex-row babylai:flex-wrap babylai:gap-2 babylai:max-w-full'>
103
- {message.attachmentIds.map((attachmentId, imgIndex) => (
104
- <ImageAttachment
105
- key={attachmentId}
106
- fileId={attachmentId}
107
- enablePreview={false}
108
- onClick={() => handleImageClick(imgIndex)}
109
- />
110
- ))}
111
- </div>
112
- )}
113
- {/* Only show chat bubble if there's message content */}
114
- {message.messageContent && message.messageContent.trim() !== '' && (
115
- <AgentResponse
116
- messageContent={message.messageContent}
117
- senderType={message.senderType}
118
- messageId={message.id}
119
- onType={onType}
120
- />
121
- )}
122
- </div>
123
- </div>
124
- </div>
125
- );
126
- }
127
- );
128
-
129
- MessageComponent.displayName = 'MessageComponent';
130
-
131
- export const ChatWindow = React.memo(
132
- ({
133
- onSendMessage,
134
- onEnsureSession,
135
- messages,
136
- assistantStatus = 'loading',
137
- needsAgent,
138
- sessionId,
139
- isChatClosed = false,
140
- inChatReviewSessionId,
141
- isSubmittingReview = false,
142
- onInChatReviewSubmit,
143
- onInChatReviewDone,
144
- }: ChatWindowProps) => {
145
- const { i18n } = useLocalTranslation();
146
- const [inputMessage, setInputMessage] = useState('');
147
- const messagesEndRef = useRef<HTMLDivElement>(null);
148
- const scrollTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
149
- const lastMessageCountRef = useRef(messages.length);
150
- const [galleryState, setGalleryState] = useState<{
151
- isOpen: boolean;
152
- imageUrls: string[];
153
- initialIndex: number;
154
- }>({
155
- isOpen: false,
156
- imageUrls: [],
157
- initialIndex: 0,
158
- });
159
-
160
- // Debounced scroll to bottom function
161
- const scrollToBottom = useCallback(() => {
162
- if (scrollTimeoutRef.current) {
163
- clearTimeout(scrollTimeoutRef.current);
164
- }
165
-
166
- scrollTimeoutRef.current = setTimeout(() => {
167
- messagesEndRef.current?.scrollIntoView({
168
- behavior: 'smooth',
169
- block: 'end',
170
- });
171
- }, 100);
172
- }, []);
173
-
174
- // Only scroll when new messages are added or status changes
175
- useEffect(() => {
176
- if (messages.length !== lastMessageCountRef.current || assistantStatus === 'typing') {
177
- lastMessageCountRef.current = messages.length;
178
- scrollToBottom();
179
- }
180
- }, [messages.length, assistantStatus, scrollToBottom]);
181
-
182
- // Cleanup timeout on unmount
183
- useEffect(() => {
184
- return () => {
185
- if (scrollTimeoutRef.current) {
186
- clearTimeout(scrollTimeoutRef.current);
187
- }
188
- };
189
- }, []);
190
-
191
- const handleSendMessage = useCallback(
192
- (attachmentIds: string[]) => {
193
- // Allow sending if there's text OR attachments
194
- if (inputMessage.trim() || attachmentIds.length > 0) {
195
- onSendMessage(inputMessage, attachmentIds);
196
- setInputMessage('');
197
- }
198
- },
199
- [inputMessage, onSendMessage]
200
- );
201
-
202
- // Memoize the first human agent index calculation
203
- const firstHumanAgentIndex = useMemo(() => {
204
- return messages.findIndex((message) => message.senderType === 2);
205
- }, [messages]);
206
-
207
- // Handle image gallery opening
208
- const handleImageClick = useCallback(
209
- async (attachmentIdsOrUrls: string[], clickedIndex: number) => {
210
- if (!attachmentIdsOrUrls || attachmentIdsOrUrls.length === 0) {
211
- return;
212
- }
213
-
214
- try {
215
- // Check if the first item is a URL (starts with http:// or https://)
216
- // If so, they're all URLs from Ably and can be used directly
217
- const isUrl = attachmentIdsOrUrls[0]?.startsWith('http://') || attachmentIdsOrUrls[0]?.startsWith('https://');
218
-
219
- let imageUrls: string[];
220
-
221
- if (isUrl) {
222
- // These are already URLs from Ably, use them directly (no async needed)
223
- imageUrls = attachmentIdsOrUrls.filter((url): url is string => url !== null && url.length > 0);
224
-
225
- // Open gallery immediately with URLs
226
- if (imageUrls.length > 0) {
227
- const adjustedIndex = Math.max(0, Math.min(clickedIndex, imageUrls.length - 1));
228
- setGalleryState({
229
- isOpen: true,
230
- imageUrls,
231
- initialIndex: adjustedIndex,
232
- });
233
- }
234
- return; // Exit early since we don't need to fetch anything
235
- }
236
-
237
- // These are file IDs, need to fetch URLs using presignDownload
238
- const imageUrlPromises = attachmentIdsOrUrls.map((fileId) => {
239
- if (!fileId || typeof fileId !== 'string') {
240
- return Promise.resolve(null);
241
- }
242
-
243
- return presignDownload(fileId, i18n.language as 'ar' | 'en')
244
- .then((response) => {
245
- if (!response || !response.downloadUrl) {
246
- return null;
247
- }
248
- return response.downloadUrl;
249
- })
250
- .catch(() => {
251
- // Return null for failed downloads so we can filter them out
252
- return null;
253
- });
254
- });
255
-
256
- imageUrls = (await Promise.all(imageUrlPromises)).filter(
257
- (url): url is string => url !== null && url.length > 0
258
- );
259
-
260
- if (imageUrls.length === 0) {
261
- return;
262
- }
263
-
264
- // Adjust the initial index if some images failed to load
265
- const adjustedIndex = Math.max(0, Math.min(clickedIndex, imageUrls.length - 1));
266
-
267
- setGalleryState({
268
- isOpen: true,
269
- imageUrls,
270
- initialIndex: adjustedIndex,
271
- });
272
- } catch (error) {
273
- // Handle unexpected errors silently
274
- }
275
- },
276
- [i18n.language]
277
- );
278
-
279
- const handleCloseGallery = useCallback(() => {
280
- setGalleryState({
281
- isOpen: false,
282
- imageUrls: [],
283
- initialIndex: 0,
284
- });
285
- }, []);
286
-
287
- // Memoize loading state check
288
- // When a human agent has joined, don't disable based on assistantStatus
289
- const hasHumanAgent = useMemo(() => {
290
- return needsAgent || messages.some((msg) => msg.senderType === 2);
291
- }, [needsAgent, messages]);
292
-
293
- const isLoading = useMemo(() => {
294
- // If human agent has joined, don't disable file uploads based on assistantStatus
295
- if (hasHumanAgent) {
296
- return false;
297
- }
298
- return assistantStatus === 'typing' || assistantStatus === 'loading' || assistantStatus === 'error';
299
- }, [assistantStatus, hasHumanAgent]);
300
-
301
- // Memoize the message list to prevent unnecessary re-renders
302
- const messagesListWithGallery = useMemo(() => {
303
- return messages.map((message, index) => (
304
- <MessageComponent
305
- key={`${message.id}-${index}`}
306
- message={message}
307
- index={index}
308
- messages={messages}
309
- firstHumanAgentIndex={firstHumanAgentIndex}
310
- onType={scrollToBottom}
311
- onImageClick={handleImageClick}
312
- />
313
- ));
314
- }, [messages, firstHumanAgentIndex, scrollToBottom, handleImageClick]);
315
-
316
- const showInChatReview = !!inChatReviewSessionId && !!onInChatReviewSubmit;
317
-
318
- return (
319
- <>
320
- <div className='babylai:flex-1 babylai:overflow-y-auto babylai:p-4 babylai:h-full'>
321
- {messagesListWithGallery}
322
-
323
- {assistantStatus === 'typing' && <TypingIndicator firstHumanAgentIndex={firstHumanAgentIndex} />}
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
-
336
- <div ref={messagesEndRef} />
337
- </div>
338
-
339
- <ChatWindowFooter
340
- inputMessage={inputMessage}
341
- handleSendMessage={handleSendMessage}
342
- setInputMessage={setInputMessage}
343
- isLoading={isLoading}
344
- isChatClosed={isChatClosed}
345
- onEnsureSession={onEnsureSession}
346
- sessionId={sessionId}
347
- />
348
-
349
- <PoweredBy />
350
-
351
- {/* Gallery Preview Dialog */}
352
- {galleryState.isOpen && galleryState.imageUrls.length > 0 && (
353
- <ImagePreviewDialog
354
- imageUrls={galleryState.imageUrls}
355
- initialIndex={galleryState.initialIndex}
356
- isOpen={galleryState.isOpen}
357
- onClose={handleCloseGallery}
358
- alt='Image gallery preview'
359
- />
360
- )}
361
- </>
362
- );
363
- }
364
- );
365
-
366
- ChatWindow.displayName = 'ChatWindow';
@@ -1,31 +0,0 @@
1
- import React from 'react';
2
- import LogoIcon from '../../../assets/logo.svg';
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 babylai:bg-primary'>
15
- <LogoIcon className='babylai:w-4 babylai:h-4 babylai:text-primary' />
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:text-muted-foreground babylai:m-0 babylai:flex babylai:gap-0.5 babylai:items-center' aria-hidden="true">
20
- <span className="babylai-typing-dot babylai:inline-block babylai:w-1.5 babylai:h-1.5 babylai:rounded-full babylai:bg-current" />
21
- <span className="babylai-typing-dot babylai:inline-block babylai:w-1.5 babylai:h-1.5 babylai:rounded-full babylai:bg-current" />
22
- <span className="babylai-typing-dot babylai:inline-block babylai:w-1.5 babylai:h-1.5 babylai:rounded-full babylai:bg-current" />
23
- </p>
24
- </div>
25
- </div>
26
- );
27
- });
28
-
29
- TypingIndicator.displayName = 'TypingIndicator';
30
-
31
- export default TypingIndicator;
@@ -1,22 +0,0 @@
1
-
2
- import Header from '../options-list-screen/header'
3
-
4
- interface ChatBotErrorScreenProps {
5
- onClose: () => void
6
- error: string
7
- }
8
-
9
- const ChatBotErrorScreen: React.FC<ChatBotErrorScreenProps> = (props) => {
10
- return (
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-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-28">
15
- <div className="babylai:text-secondary-foreground babylai:text-lg">Error: {props.error}</div>
16
- </div>
17
- </div>
18
- </div>
19
- )
20
- }
21
-
22
- export default ChatBotErrorScreen
@@ -1,21 +0,0 @@
1
- import animatedLogo from '../../../assets/animatedLogo.gif'
2
- import Header from '../options-list-screen/header'
3
-
4
- interface ChatBotLoadingScreenProps {
5
- onClose: () => void
6
- }
7
-
8
- const ChatBotLoadingScreen: React.FC<ChatBotLoadingScreenProps> = (props) => {
9
- return (
10
- <div className="babylai:w-full babylai:h-full babylai:bg-secondary babylai:rounded-3xl babylai:shadow-lg babylai:flex babylai:flex-col">
11
- <div className="babylai:rounded-3xl babylai:h-full babylai:flex babylai:flex-col babylai:gap-4">
12
- <Header onMinimize={props.onClose} />
13
- <div className="babylai:flex babylai:flex-col babylai:items-center babylai:justify-center babylai:w-full babylai:h-full babylai:py-28">
14
- <img src={animatedLogo} alt="Animated Logo" className="babylai:w-20 babylai:h-20" />
15
- </div>
16
- </div>
17
- </div>
18
- )
19
- }
20
-
21
- export default ChatBotLoadingScreen
@@ -1,39 +0,0 @@
1
- import React from 'react';
2
-
3
- interface CompanyCardProps {
4
- logoUrl?: string;
5
- name?: string;
6
- description?: string;
7
- title?: string;
8
- }
9
-
10
- const CompanyCard: React.FC<CompanyCardProps> = ({
11
- logoUrl,
12
- name,
13
- description,
14
- title,
15
- }) => {
16
- return (
17
- <section className='babylai:border-b babylai:border-black-white-200'>
18
- <div className="babylai:flex babylai:items-center babylai:gap-3 babylai:p-6 babylai:mb-6 babylai:rounded-3xl babylai:border babylai:border-black-white-200 babylai:bg-card">
19
- {logoUrl && (
20
- <div className="babylai:shrink-0 babylai:w-12 babylai:h-12 babylai:rounded-md babylai:overflow-hidden">
21
- <img
22
- src={logoUrl}
23
- alt={title ? `${title} logo` : 'Company logo'}
24
- className="babylai:w-full babylai:h-full babylai:object-contain"
25
- />
26
- </div>
27
- )}
28
- <div className="babylai:flex-1 babylai:min-w-0 babylai:text-start">
29
- <h3 className="babylai:text-lg babylai:font-semibold babylai:mb-1 babylai:text-card-foreground" dir="auto">{name}</h3>
30
- <p className="babylai:text-sm babylai:text-muted-foreground" dir="auto">{description}</p>
31
- </div>
32
- </div>
33
- </section>
34
- );
35
- };
36
-
37
- CompanyCard.displayName = 'CompanyCard';
38
-
39
- export default CompanyCard;
@@ -1,23 +0,0 @@
1
- import ActionButton from '../chat-window-screen/action-button';
2
- import IcRoundMinus from '~icons/ic/round-minus'
3
-
4
- interface OptionsListHeaderProps {
5
- onMinimize: () => void;
6
- }
7
-
8
- const OptionsListHeader: React.FC<OptionsListHeaderProps> = ({
9
- onMinimize,
10
- }) => {
11
-
12
- return (
13
- <header className="bg-header babylai:flex babylai:items-center babylai:justify-end babylai:p-6 babylai:border-b babylai:border-black-white-200">
14
- <ActionButton
15
- onClick={onMinimize}
16
- icon={<IcRoundMinus />}
17
- ariaLabel="Minimize"
18
- />
19
- </header >
20
- );
21
- };
22
-
23
- export default OptionsListHeader;