@aslaluroba/help-center-react 3.2.10 → 3.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css +1 -1
- package/dist/index.esm.js +307 -163
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +307 -163
- package/dist/index.js.map +1 -1
- package/dist/ui/chatbot-popup/options-list-screen/helpscreen-list.d.ts +1 -0
- package/dist/ui/chatbot-popup/options-list-screen/index.d.ts +1 -0
- package/dist/ui/help-popup.d.ts +3 -1
- package/package.json +1 -1
- package/src/components/ui/agent-response/agent-response.tsx +1 -1
- package/src/globals.css +29 -2
- package/src/locales/ar.json +1 -1
- package/src/locales/en.json +1 -1
- package/src/ui/chatbot-popup/chat-window-screen/in-chat-review.tsx +38 -6
- package/src/ui/chatbot-popup/chat-window-screen/typing-indicator.tsx +8 -4
- package/src/ui/chatbot-popup/options-list-screen/helpscreen-list.tsx +15 -7
- package/src/ui/chatbot-popup/options-list-screen/helpscreen-option.tsx +11 -1
- package/src/ui/chatbot-popup/options-list-screen/index.tsx +3 -0
- package/src/ui/confirmation-modal/index.tsx +34 -30
- package/src/ui/help-center.tsx +23 -5
- package/src/ui/help-popup.tsx +35 -15
- package/src/ui/review-dialog/index.tsx +95 -43
- package/src/ui/review-dialog/rating.tsx +4 -3
|
@@ -3,6 +3,7 @@ interface OptionsListScreenProps {
|
|
|
3
3
|
helpScreen: HelpScreenData | null;
|
|
4
4
|
handleStartChat: (option: Option) => void;
|
|
5
5
|
handleMinimize: () => void;
|
|
6
|
+
hasActiveSession?: boolean;
|
|
6
7
|
}
|
|
7
8
|
declare const OptionsListScreen: React.FC<OptionsListScreenProps>;
|
|
8
9
|
export default OptionsListScreen;
|
package/dist/ui/help-popup.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { HelpScreenData, Message, Option, ReviewProps } from '@/lib/types';
|
|
2
2
|
type HelpPopupProps = {
|
|
3
3
|
isOpen: boolean;
|
|
4
|
+
isClosing?: boolean;
|
|
4
5
|
onClose: () => void;
|
|
6
|
+
onCloseAnimationEnd?: () => void;
|
|
5
7
|
helpScreen: HelpScreenData | null;
|
|
6
8
|
status: string;
|
|
7
9
|
error: string | null;
|
|
@@ -30,5 +32,5 @@ type HelpPopupProps = {
|
|
|
30
32
|
onReviewDialogSubmit?: (payload: ReviewProps) => void | Promise<void>;
|
|
31
33
|
onReviewDialogClose?: () => void;
|
|
32
34
|
};
|
|
33
|
-
declare const HelpPopup: ({ isOpen, onClose, helpScreen, status, error, onStartChat, onSendMessage, onEnsureSession, onEndChat, messages, assistantStatus, needsAgent, sessionId, isChatClosed, selectedOption, setSelectedOption, inChatReviewSessionId, onInChatReviewSubmit, onInChatReviewDone, navigateToOptionsListAfterReview, onNavigatedToOptionsList, isReviewDialogOpen, reviewSessionId, isSubmittingReview, onReviewDialogSubmit, onReviewDialogClose, }: HelpPopupProps) => import("react/jsx-runtime").JSX.Element;
|
|
35
|
+
declare const HelpPopup: ({ isOpen, isClosing, onClose, onCloseAnimationEnd, helpScreen, status, error, onStartChat, onSendMessage, onEnsureSession, onEndChat, messages, assistantStatus, needsAgent, sessionId, isChatClosed, selectedOption, setSelectedOption, inChatReviewSessionId, onInChatReviewSubmit, onInChatReviewDone, navigateToOptionsListAfterReview, onNavigatedToOptionsList, isReviewDialogOpen, reviewSessionId, isSubmittingReview, onReviewDialogSubmit, onReviewDialogClose, }: HelpPopupProps) => import("react/jsx-runtime").JSX.Element;
|
|
34
36
|
export default HelpPopup;
|
package/package.json
CHANGED
|
@@ -35,7 +35,7 @@ const AgentResponse = ({ senderType, messageContent, messageId, onType }: AgentR
|
|
|
35
35
|
components={{
|
|
36
36
|
p: ({ node, ...props }: { node?: Element; [key: string]: any }) => (
|
|
37
37
|
<p
|
|
38
|
-
className='babylai:m-0 babylai:leading-
|
|
38
|
+
className='babylai:m-0 babylai:leading-snug babylai:text-sm babylai:font-sans babylai:wrap-break-word babylai:text-start'
|
|
39
39
|
{...props}
|
|
40
40
|
/>
|
|
41
41
|
),
|
package/src/globals.css
CHANGED
|
@@ -136,6 +136,33 @@
|
|
|
136
136
|
transform: translateY(-10px);
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
|
+
|
|
140
|
+
@keyframes typing-dot {
|
|
141
|
+
0%,
|
|
142
|
+
60%,
|
|
143
|
+
100% {
|
|
144
|
+
opacity: 0.35;
|
|
145
|
+
transform: scale(0.85);
|
|
146
|
+
}
|
|
147
|
+
30% {
|
|
148
|
+
opacity: 1;
|
|
149
|
+
transform: scale(1);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* Typing indicator dots - staggered pulse animation */
|
|
155
|
+
.babylai-typing-dot {
|
|
156
|
+
animation: typing-dot 1.4s ease-in-out infinite;
|
|
157
|
+
}
|
|
158
|
+
.babylai-typing-dot:nth-child(1) {
|
|
159
|
+
animation-delay: 0ms;
|
|
160
|
+
}
|
|
161
|
+
.babylai-typing-dot:nth-child(2) {
|
|
162
|
+
animation-delay: 0.2s;
|
|
163
|
+
}
|
|
164
|
+
.babylai-typing-dot:nth-child(3) {
|
|
165
|
+
animation-delay: 0.4s;
|
|
139
166
|
}
|
|
140
167
|
|
|
141
168
|
/* Re-scope primary theme vars on widget root so primaryColor prop (inline style) wins over :root.
|
|
@@ -192,7 +219,7 @@
|
|
|
192
219
|
}
|
|
193
220
|
|
|
194
221
|
/* Dark theme colors */
|
|
195
|
-
@media (prefers-color-scheme: dark) {
|
|
222
|
+
/* @media (prefers-color-scheme: dark) {
|
|
196
223
|
:root {
|
|
197
224
|
--babylai-black-white-50: #000000;
|
|
198
225
|
--babylai-black-white-100: #050505;
|
|
@@ -219,7 +246,7 @@
|
|
|
219
246
|
--babylai-border: var(--babylai-black-white-200);
|
|
220
247
|
--babylai-ring: var(--babylai-primary-color);
|
|
221
248
|
}
|
|
222
|
-
}
|
|
249
|
+
} */
|
|
223
250
|
|
|
224
251
|
.bg-header {
|
|
225
252
|
background: linear-gradient(171deg,
|
package/src/locales/ar.json
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"comment_placeholder": "اكتب تعليقك هنا...",
|
|
23
23
|
"submit_button": "إرسال التقييم",
|
|
24
24
|
"rate_button": "تقييم",
|
|
25
|
-
"comment_error": "يجب أن يكون التعليق
|
|
25
|
+
"comment_error": "يجب أن يكون الحقل التعليق نصًا بحد أقصى 500 حرف.",
|
|
26
26
|
"rating_error": "يجب أن يكون التقييم بين 1 و5.",
|
|
27
27
|
"skip_button": "تخطي"
|
|
28
28
|
},
|
package/src/locales/en.json
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"comment_placeholder": "Write your comment here...",
|
|
23
23
|
"submit_button": "Submit Review",
|
|
24
24
|
"rate_button": "Rate",
|
|
25
|
-
"comment_error": "Comment must be
|
|
25
|
+
"comment_error": "The field Comment must be a string with a maximum length of 500.",
|
|
26
26
|
"rating_error": "Rating must be between 1 and 5.",
|
|
27
27
|
"skip_button": "Skip"
|
|
28
28
|
},
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import React, { useState, useCallback } from 'react';
|
|
2
2
|
import { ReviewProps } from '@/lib/types';
|
|
3
|
+
import { cn } from '@/lib/utils';
|
|
3
4
|
import { Rating } from '@/ui/review-dialog/rating';
|
|
4
5
|
import { useLocalTranslation } from '@/useLocalTranslation';
|
|
5
6
|
import LogoIcon from '@/assets/logo.svg';
|
|
6
7
|
import { Button } from '@/components';
|
|
7
8
|
|
|
9
|
+
const COMMENT_MAX_LENGTH = 500;
|
|
10
|
+
|
|
8
11
|
interface InChatReviewProps {
|
|
9
12
|
onSubmit: (payload: ReviewProps) => void | Promise<void>;
|
|
10
13
|
onDone?: () => void;
|
|
@@ -15,6 +18,7 @@ export const InChatReview: React.FC<InChatReviewProps> = ({ onSubmit, isSubmitti
|
|
|
15
18
|
const { t } = useLocalTranslation();
|
|
16
19
|
const [rating, setRating] = useState<number>(0);
|
|
17
20
|
const [comment, setComment] = useState<string>('');
|
|
21
|
+
const [commentError, setCommentError] = useState<string | null>(null);
|
|
18
22
|
|
|
19
23
|
const hasRating = rating >= 1 && rating <= 5;
|
|
20
24
|
|
|
@@ -22,9 +26,23 @@ export const InChatReview: React.FC<InChatReviewProps> = ({ onSubmit, isSubmitti
|
|
|
22
26
|
setRating(val);
|
|
23
27
|
}, []);
|
|
24
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
|
+
|
|
25
37
|
const handleSubmit = useCallback(() => {
|
|
26
|
-
|
|
27
|
-
|
|
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]);
|
|
28
46
|
|
|
29
47
|
return (
|
|
30
48
|
<section className="babylai:mb-4 babylai:flex babylai:justify-start">
|
|
@@ -36,7 +54,7 @@ export const InChatReview: React.FC<InChatReviewProps> = ({ onSubmit, isSubmitti
|
|
|
36
54
|
<h2 className='babylai:text-card-foreground'>
|
|
37
55
|
{t('homeSdk.InChatReview.title')}
|
|
38
56
|
</h2>
|
|
39
|
-
<p className="babylai:font-normal babylai:text-muted-foreground">
|
|
57
|
+
<p className="babylai:font-normal babylai:text-muted-foreground babylai:leading-snug">
|
|
40
58
|
{t('homeSdk.InChatReview.description')}
|
|
41
59
|
</p>
|
|
42
60
|
|
|
@@ -46,16 +64,30 @@ export const InChatReview: React.FC<InChatReviewProps> = ({ onSubmit, isSubmitti
|
|
|
46
64
|
|
|
47
65
|
{hasRating && (
|
|
48
66
|
<>
|
|
49
|
-
<p className="babylai:text-card-foreground">{t('homeSdk.InChatReview.follow_up')}</p>
|
|
67
|
+
<p className="babylai:text-card-foreground babylai:leading-snug">{t('homeSdk.InChatReview.follow_up')}</p>
|
|
50
68
|
|
|
51
69
|
<textarea
|
|
52
|
-
className=
|
|
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
|
+
)}
|
|
53
74
|
rows={4}
|
|
75
|
+
maxLength={COMMENT_MAX_LENGTH}
|
|
54
76
|
placeholder={t('homeSdk.InChatReview.note_placeholder')}
|
|
55
77
|
value={comment}
|
|
56
|
-
onChange={
|
|
78
|
+
onChange={handleCommentChange}
|
|
57
79
|
aria-label={t('homeSdk.InChatReview.note_placeholder')}
|
|
80
|
+
aria-invalid={!!commentError}
|
|
81
|
+
aria-describedby={commentError ? 'in-chat-review-comment-error' : undefined}
|
|
58
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>
|
|
59
91
|
|
|
60
92
|
<Button
|
|
61
93
|
variant='default'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import
|
|
2
|
+
import LogoIcon from '../../../assets/logo.svg';
|
|
3
3
|
|
|
4
4
|
interface TypingIndicatorProps {
|
|
5
5
|
firstHumanAgentIndex: number;
|
|
@@ -11,12 +11,16 @@ export const TypingIndicator = React.memo(({ firstHumanAgentIndex }: TypingIndic
|
|
|
11
11
|
return (
|
|
12
12
|
<div className='babylai:mb-4 babylai:flex'>
|
|
13
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
|
-
<
|
|
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
16
|
</div>
|
|
17
17
|
</div>
|
|
18
18
|
<div className='babylai:max-w-[80%] babylai:rounded-2xl babylai:p-4 babylai:bg-card'>
|
|
19
|
-
<p className='babylai:text-sm babylai:
|
|
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>
|
|
20
24
|
</div>
|
|
21
25
|
</div>
|
|
22
26
|
);
|
|
@@ -3,12 +3,14 @@ import { Option } from '@/lib/types';
|
|
|
3
3
|
import HelpscreenOption from '@/ui/chatbot-popup/options-list-screen/helpscreen-option';
|
|
4
4
|
import { useLocalTranslation } from '@/useLocalTranslation';
|
|
5
5
|
import { Button } from '@/components';
|
|
6
|
+
import SolarPlain2BoldDuotone from '~icons/solar/plain-2-bold-duotone'
|
|
6
7
|
|
|
7
8
|
interface HelpscreenListProps {
|
|
8
9
|
options: Option[] | undefined;
|
|
9
10
|
selectedOption: Option | null;
|
|
10
11
|
onToggleOption: (option: Option) => void;
|
|
11
12
|
onStartChat: () => void;
|
|
13
|
+
showChatNowButton?: boolean;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
const HelpscreenList: React.FC<HelpscreenListProps> = ({
|
|
@@ -16,6 +18,7 @@ const HelpscreenList: React.FC<HelpscreenListProps> = ({
|
|
|
16
18
|
selectedOption,
|
|
17
19
|
onToggleOption,
|
|
18
20
|
onStartChat,
|
|
21
|
+
showChatNowButton = true,
|
|
19
22
|
}) => {
|
|
20
23
|
const { t } = useLocalTranslation();
|
|
21
24
|
|
|
@@ -31,13 +34,18 @@ const HelpscreenList: React.FC<HelpscreenListProps> = ({
|
|
|
31
34
|
/>
|
|
32
35
|
))}
|
|
33
36
|
</div>
|
|
34
|
-
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
{showChatNowButton && (
|
|
38
|
+
<div className="babylai:sticky babylai:bottom-0 babylai:z-10">
|
|
39
|
+
<Button
|
|
40
|
+
variant='default'
|
|
41
|
+
onClick={onStartChat}
|
|
42
|
+
disabled={!selectedOption}
|
|
43
|
+
>
|
|
44
|
+
{t('homeSdk.chatNow')}
|
|
45
|
+
<SolarPlain2BoldDuotone className="babylai:w-6 babylai:h-6" />
|
|
46
|
+
</Button>
|
|
47
|
+
</div>
|
|
48
|
+
)}
|
|
41
49
|
</section>
|
|
42
50
|
);
|
|
43
51
|
};
|
|
@@ -15,17 +15,27 @@ const HelpscreenOption: React.FC<HelpscreenOptionProps> = ({
|
|
|
15
15
|
}) => {
|
|
16
16
|
return (
|
|
17
17
|
<div
|
|
18
|
+
role="button"
|
|
19
|
+
tabIndex={0}
|
|
18
20
|
className={cn(
|
|
19
21
|
'babylai:flex babylai:flex-col babylai:gap-2 babylai:p-6 babylai:rounded-3xl babylai:text-start babylai:border babylai:border-black-white-200 babylai:bg-card babylai:cursor-pointer',
|
|
22
|
+
'babylai:transition-all babylai:duration-200 babylai:ease-out',
|
|
23
|
+
'babylai:active:scale-[0.98] babylai:active:opacity-95',
|
|
20
24
|
isSelected && 'babylai:ring babylai:ring-primary-500 babylai:shadow-md'
|
|
21
25
|
)}
|
|
22
26
|
onClick={onClick}
|
|
27
|
+
onKeyDown={(e) => {
|
|
28
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
29
|
+
e.preventDefault();
|
|
30
|
+
onClick();
|
|
31
|
+
}
|
|
32
|
+
}}
|
|
23
33
|
>
|
|
24
34
|
<h2 className="babylai:text-base! babylai:font-semibold! babylai:text-card-foreground" dir="auto">
|
|
25
35
|
{option.title}
|
|
26
36
|
</h2>
|
|
27
37
|
{option.paragraphs.map((paragraph, index) => (
|
|
28
|
-
<p key={index} className="babylai:text-sm babylai:text-muted-foreground" dir="auto">
|
|
38
|
+
<p key={index} className="babylai:text-sm babylai:text-muted-foreground babylai:leading-snug" dir="auto">
|
|
29
39
|
{paragraph}
|
|
30
40
|
</p>
|
|
31
41
|
))}
|
|
@@ -10,12 +10,14 @@ interface OptionsListScreenProps {
|
|
|
10
10
|
helpScreen: HelpScreenData | null;
|
|
11
11
|
handleStartChat: (option: Option) => void;
|
|
12
12
|
handleMinimize: () => void;
|
|
13
|
+
hasActiveSession?: boolean;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
const OptionsListScreen: React.FC<OptionsListScreenProps> = ({
|
|
16
17
|
helpScreen,
|
|
17
18
|
handleStartChat,
|
|
18
19
|
handleMinimize,
|
|
20
|
+
hasActiveSession = false,
|
|
19
21
|
}) => {
|
|
20
22
|
const [selectedOption, setSelectedOption] = useState<Option | null>(null);
|
|
21
23
|
|
|
@@ -56,6 +58,7 @@ const OptionsListScreen: React.FC<OptionsListScreenProps> = ({
|
|
|
56
58
|
selectedOption={selectedOption}
|
|
57
59
|
onToggleOption={handleToggleExpandOption}
|
|
58
60
|
onStartChat={handleStartChatWithSelected}
|
|
61
|
+
showChatNowButton={!hasActiveSession}
|
|
59
62
|
/>
|
|
60
63
|
</div >
|
|
61
64
|
<PoweredBy />
|
|
@@ -3,6 +3,7 @@ import { useLocalTranslation } from "@/useLocalTranslation";
|
|
|
3
3
|
import SolarChatRoundUnreadBoldDuotone from '~icons/solar/chat-round-unread-bold-duotone'
|
|
4
4
|
import SolarCloseCircleLineDuotone from '~icons/solar/close-circle-line-duotone'
|
|
5
5
|
import SolarPlain2BoldDuotone from '~icons/solar/plain-2-bold-duotone'
|
|
6
|
+
import PoweredBy from "../powered-by";
|
|
6
7
|
|
|
7
8
|
interface ConfirmationModalProps {
|
|
8
9
|
title: string;
|
|
@@ -17,39 +18,42 @@ const ConfirmationModal = ({ title, message, onCancel, onConfirm }: Confirmation
|
|
|
17
18
|
return (
|
|
18
19
|
<div className='babylai:absolute babylai:inset-0 babylai:z-50 babylai:flex babylai:items-end babylai:rounded-3xl babylai:overflow-hidden'>
|
|
19
20
|
<div className='babylai:absolute babylai:inset-0 babylai:bg-black/60' onClick={onCancel}></div>
|
|
20
|
-
<div className='babylai:flex babylai:flex-col babylai:bg-card babylai:rounded-2xl babylai:
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
>
|
|
25
|
-
<SolarCloseCircleLineDuotone className="babylai:w-6 babylai:h-6" />
|
|
26
|
-
</button>
|
|
27
|
-
|
|
28
|
-
<section className="babylai:flex babylai:items-center babylai:justify-center babylai:border-b babylai:border-black-white-200 babylai:pb-6 babylai:mb-6">
|
|
29
|
-
<div className="babylai:flex babylai:items-center babylai:justify-center babylai:w-20 babylai:h-20 babylai:rounded-full babylai:p-3 babylai:bg-primary/15 babylai:text-primary">
|
|
30
|
-
<SolarChatRoundUnreadBoldDuotone className="babylai:w-16 babylai:h-16" />
|
|
31
|
-
</div>
|
|
32
|
-
</section>
|
|
33
|
-
|
|
34
|
-
<h3 className='babylai:text-2xl! babylai:text-center babylai:font-bold! babylai:mb-2! babylai:text-card-foreground'>{title}</h3>
|
|
35
|
-
|
|
36
|
-
<p className='babylai:text-sm babylai:text-center babylai:text-muted-foreground'>{message}</p>
|
|
37
|
-
|
|
38
|
-
<div className='babylai:flex babylai:justify-between babylai:gap-3 babylai:mt-6'>
|
|
39
|
-
<Button
|
|
40
|
-
onClick={onConfirm}
|
|
41
|
-
variant='secondary'
|
|
42
|
-
>
|
|
43
|
-
{t('homeSdk.ConfirmationModal.confirmation_button')}
|
|
44
|
-
</Button>
|
|
45
|
-
<Button
|
|
21
|
+
<div className='babylai:flex babylai:flex-col babylai:bg-card babylai:rounded-2xl babylai:w-full babylai:z-50 babylai:shadow-lg'>
|
|
22
|
+
<div className='babylai:flex babylai:flex-col babylai:p-6 babylai:pb-5 babylai:w-full'>
|
|
23
|
+
<button className="babylai:border-0 babylai:p-0 babylai:flex babylai:bg-transparent babylai:cursor-pointer babylai:mb-6 babylai:ms-auto babylai:text-card-foreground"
|
|
24
|
+
type='button'
|
|
46
25
|
onClick={onCancel}
|
|
47
|
-
variant='default'
|
|
48
26
|
>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
27
|
+
<SolarCloseCircleLineDuotone className="babylai:w-7 babylai:h-7" />
|
|
28
|
+
</button>
|
|
29
|
+
|
|
30
|
+
<section className="babylai:flex babylai:items-center babylai:justify-center babylai:border-b babylai:border-black-white-200 babylai:pb-6 babylai:mb-6">
|
|
31
|
+
<div className="babylai:flex babylai:items-center babylai:justify-center babylai:w-20 babylai:h-20 babylai:rounded-full babylai:p-3 babylai:bg-primary/15 babylai:text-primary">
|
|
32
|
+
<SolarChatRoundUnreadBoldDuotone className="babylai:w-16 babylai:h-16" />
|
|
33
|
+
</div>
|
|
34
|
+
</section>
|
|
35
|
+
|
|
36
|
+
<h3 className='babylai:text-2xl! babylai:text-center babylai:font-bold! babylai:mb-2! babylai:text-card-foreground'>{title}</h3>
|
|
37
|
+
|
|
38
|
+
<p className='babylai:text-sm babylai:text-center babylai:text-muted-foreground'>{message}</p>
|
|
39
|
+
|
|
40
|
+
<div className='babylai:flex babylai:justify-between babylai:gap-3 babylai:mt-6'>
|
|
41
|
+
<Button
|
|
42
|
+
onClick={onCancel}
|
|
43
|
+
variant='secondary'
|
|
44
|
+
>
|
|
45
|
+
{t('homeSdk.ConfirmationModal.cancel_button')}
|
|
46
|
+
<SolarPlain2BoldDuotone className="babylai:w-6 babylai:h-6" />
|
|
47
|
+
</Button>
|
|
48
|
+
<Button
|
|
49
|
+
onClick={onConfirm}
|
|
50
|
+
variant='default'
|
|
51
|
+
>
|
|
52
|
+
{t('homeSdk.ConfirmationModal.confirmation_button')}
|
|
53
|
+
</Button>
|
|
54
|
+
</div>
|
|
52
55
|
</div>
|
|
56
|
+
<PoweredBy />
|
|
53
57
|
</div>
|
|
54
58
|
</div>
|
|
55
59
|
);
|
package/src/ui/help-center.tsx
CHANGED
|
@@ -36,6 +36,7 @@ const HelpCenterContent = ({
|
|
|
36
36
|
}: HelpCenterProps) => {
|
|
37
37
|
const { t } = useLocalTranslation();
|
|
38
38
|
const [isOpen, setIsOpen] = useState(false);
|
|
39
|
+
const [isClosing, setIsClosing] = useState(false);
|
|
39
40
|
const [showArrowAnimation, setShowArrowAnimation] = useState(showArrow);
|
|
40
41
|
const [helpScreenData, setHelpScreenData] = useState<HelpScreenData | null>(null);
|
|
41
42
|
const [status, setStatus] = useState('idle');
|
|
@@ -57,10 +58,25 @@ const HelpCenterContent = ({
|
|
|
57
58
|
const actionHandler = useActionHandler();
|
|
58
59
|
|
|
59
60
|
const handleTogglePopup = () => {
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
if (isOpen) {
|
|
62
|
+
setIsClosing(true);
|
|
63
|
+
setIsOpen(false);
|
|
64
|
+
setShowArrowAnimation(true);
|
|
65
|
+
} else {
|
|
66
|
+
setIsOpen(true);
|
|
67
|
+
setShowArrowAnimation(false);
|
|
68
|
+
}
|
|
62
69
|
};
|
|
63
70
|
|
|
71
|
+
const handleClosePopup = useCallback(() => {
|
|
72
|
+
setIsClosing(true);
|
|
73
|
+
setIsOpen(false);
|
|
74
|
+
}, []);
|
|
75
|
+
|
|
76
|
+
const handlePopupCloseAnimationEnd = useCallback(() => {
|
|
77
|
+
setIsClosing(false);
|
|
78
|
+
}, []);
|
|
79
|
+
|
|
64
80
|
const handleCloseArrowAnimation = () => {
|
|
65
81
|
setShowArrowAnimation(false);
|
|
66
82
|
};
|
|
@@ -360,16 +376,18 @@ const HelpCenterContent = ({
|
|
|
360
376
|
|
|
361
377
|
return (
|
|
362
378
|
<div className='babylai-theme-root babylai:mb-4' style={themeStyles}>
|
|
363
|
-
{showArrowAnimation && !isOpen && (
|
|
379
|
+
{showArrowAnimation && !isOpen && !isClosing && (
|
|
364
380
|
<FloatingMessage onClose={handleCloseArrowAnimation} message={messageLabel || t('homeSdk.needAssistance')} />
|
|
365
381
|
)}
|
|
366
382
|
|
|
367
383
|
<HelpButton onClick={handleTogglePopup} />
|
|
368
384
|
|
|
369
|
-
{isOpen && (
|
|
385
|
+
{(isOpen || isClosing) && (
|
|
370
386
|
<HelpPopup
|
|
371
387
|
isOpen={isOpen}
|
|
372
|
-
|
|
388
|
+
isClosing={isClosing}
|
|
389
|
+
onClose={handleClosePopup}
|
|
390
|
+
onCloseAnimationEnd={handlePopupCloseAnimationEnd}
|
|
373
391
|
helpScreen={helpScreenData}
|
|
374
392
|
status={status}
|
|
375
393
|
error={error}
|
package/src/ui/help-popup.tsx
CHANGED
|
@@ -6,13 +6,18 @@ import OptionsListScreen from '@/ui/chatbot-popup/options-list-screen';
|
|
|
6
6
|
import { ActiveChatActions } from '@/ui/chatbot-popup/active-chat-actions';
|
|
7
7
|
import { useEffect, useRef, useState, useCallback, useMemo } from 'react';
|
|
8
8
|
import { HelpScreenData, Message, Option, ReviewProps } from '@/lib/types';
|
|
9
|
+
import { cn } from '@/lib/utils';
|
|
9
10
|
import { useLocalTranslation } from '../useLocalTranslation';
|
|
10
11
|
import ConfirmationModal from './confirmation-modal';
|
|
11
12
|
import ReviewDialog from './review-dialog';
|
|
12
13
|
|
|
14
|
+
const POPUP_ANIMATION_DURATION_MS = 250;
|
|
15
|
+
|
|
13
16
|
type HelpPopupProps = {
|
|
14
17
|
isOpen: boolean;
|
|
18
|
+
isClosing?: boolean;
|
|
15
19
|
onClose: () => void;
|
|
20
|
+
onCloseAnimationEnd?: () => void;
|
|
16
21
|
helpScreen: HelpScreenData | null;
|
|
17
22
|
status: string;
|
|
18
23
|
error: string | null;
|
|
@@ -42,7 +47,9 @@ type HelpPopupProps = {
|
|
|
42
47
|
|
|
43
48
|
const HelpPopup = ({
|
|
44
49
|
isOpen,
|
|
50
|
+
isClosing = false,
|
|
45
51
|
onClose,
|
|
52
|
+
onCloseAnimationEnd,
|
|
46
53
|
helpScreen,
|
|
47
54
|
status,
|
|
48
55
|
error,
|
|
@@ -73,12 +80,28 @@ const HelpPopup = ({
|
|
|
73
80
|
const [endChatConfirmation, setEndChatConfirmation] = useState<boolean>(false);
|
|
74
81
|
const [startNewChatConfirmation, setStartNewChatConfirmation] = useState<boolean>(false);
|
|
75
82
|
const [tempSelectedOption, setTempSelectedOption] = useState<Option | null>(null);
|
|
83
|
+
const [isEntering, setIsEntering] = useState(true);
|
|
76
84
|
|
|
77
85
|
const chatBoxRef = useRef<HTMLDivElement>(null);
|
|
78
86
|
const prevIsOpenRef = useRef(false);
|
|
79
87
|
const { t } = useLocalTranslation();
|
|
80
88
|
const messagesRef = useRef<Message[]>([]);
|
|
81
89
|
|
|
90
|
+
// Enter animation: transition from initial state to visible
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
const id = requestAnimationFrame(() => {
|
|
93
|
+
requestAnimationFrame(() => setIsEntering(false));
|
|
94
|
+
});
|
|
95
|
+
return () => cancelAnimationFrame(id);
|
|
96
|
+
}, []);
|
|
97
|
+
|
|
98
|
+
// Exit animation: notify parent to unmount after transition
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
if (!isClosing || !onCloseAnimationEnd) return;
|
|
101
|
+
const tId = setTimeout(onCloseAnimationEnd, POPUP_ANIMATION_DURATION_MS);
|
|
102
|
+
return () => clearTimeout(tId);
|
|
103
|
+
}, [isClosing, onCloseAnimationEnd]);
|
|
104
|
+
|
|
82
105
|
// Memoize the current messages to prevent unnecessary re-renders
|
|
83
106
|
const memoizedMessages = useMemo(() => {
|
|
84
107
|
return messages;
|
|
@@ -202,6 +225,7 @@ const HelpPopup = ({
|
|
|
202
225
|
helpScreen={helpScreen}
|
|
203
226
|
handleStartChat={handleStartChat}
|
|
204
227
|
handleMinimize={handleMinimize}
|
|
228
|
+
hasActiveSession={!!sessionId}
|
|
205
229
|
/>
|
|
206
230
|
);
|
|
207
231
|
}, [
|
|
@@ -277,15 +301,19 @@ const HelpPopup = ({
|
|
|
277
301
|
}
|
|
278
302
|
}, [messages, scrollToBottom]);
|
|
279
303
|
|
|
304
|
+
const popupBaseClasses =
|
|
305
|
+
'babylai:fixed babylai:inset-auto babylai:max-w-sm babylai:h-[calc(100vh-220px)] babylai:max-h-[800px] babylai:overflow-auto babylai:w-full babylai:bg-secondary babylai:mb-4 babylai:bottom-24 babylai:right-4 babylai:rounded-3xl babylai:shadow-lg babylai:z-50 babylai:flex babylai:flex-col';
|
|
306
|
+
const popupAnimationClasses = cn(
|
|
307
|
+
'babylai:transition-all babylai:duration-[250ms] babylai:ease-out',
|
|
308
|
+
(isEntering || isClosing) && 'babylai:opacity-0 babylai:scale-[0.96] babylai:translate-y-2',
|
|
309
|
+
!isEntering && !isClosing && 'babylai:opacity-100 babylai:scale-100 babylai:translate-y-0'
|
|
310
|
+
);
|
|
311
|
+
|
|
280
312
|
// EARLY RETURNS MUST COME AFTER ALL HOOKS
|
|
281
313
|
// Early returns for performance - moved after all hooks
|
|
282
314
|
if (status === 'loading' && !helpScreen) {
|
|
283
315
|
return (
|
|
284
|
-
<div
|
|
285
|
-
className='babylai:fixed babylai:inset-auto babylai:max-w-sm babylai:h-[calc(100vh-220px)] babylai:max-h-[800px]
|
|
286
|
-
babylai:overflow-auto babylai:w-full babylai:bg-secondary babylai:mb-4
|
|
287
|
-
babylai:bottom-24 babylai:right-4 babylai:rounded-3xl babylai:shadow-lg babylai:z-50 babylai:flex babylai:flex-col'
|
|
288
|
-
>
|
|
316
|
+
<div className={cn(popupBaseClasses, popupAnimationClasses)}>
|
|
289
317
|
<ChatBotLoadingScreen onClose={onClose} />
|
|
290
318
|
</div>
|
|
291
319
|
);
|
|
@@ -293,22 +321,14 @@ babylai:bottom-24 babylai:right-4 babylai:rounded-3xl babylai:shadow-lg babylai:
|
|
|
293
321
|
|
|
294
322
|
if (error) {
|
|
295
323
|
return (
|
|
296
|
-
<div
|
|
297
|
-
className='babylai:fixed babylai:inset-auto babylai:max-w-sm babylai:h-[calc(100vh-220px)] babylai:max-h-[800px]
|
|
298
|
-
babylai:overflow-auto babylai:w-full babylai:bg-secondary babylai:mb-4
|
|
299
|
-
babylai:bottom-24 babylai:right-4 babylai:rounded-3xl babylai:shadow-lg babylai:z-50 babylai:flex babylai:flex-col'
|
|
300
|
-
>
|
|
324
|
+
<div className={cn(popupBaseClasses, popupAnimationClasses)}>
|
|
301
325
|
<ChatBotErrorScreen onClose={onClose} error={error || ''} />
|
|
302
326
|
</div>
|
|
303
327
|
);
|
|
304
328
|
}
|
|
305
329
|
|
|
306
330
|
return (
|
|
307
|
-
<div
|
|
308
|
-
className='babylai:fixed babylai:inset-auto babylai:max-w-sm babylai:h-[calc(100vh-220px)] babylai:max-h-[800px]
|
|
309
|
-
babylai:overflow-auto babylai:w-full babylai:bg-secondary babylai:mb-4
|
|
310
|
-
babylai:bottom-24 babylai:right-4 babylai:rounded-3xl babylai:shadow-lg babylai:z-50 babylai:flex babylai:flex-col'
|
|
311
|
-
>
|
|
331
|
+
<div className={cn(popupBaseClasses, popupAnimationClasses)}>
|
|
312
332
|
<div className='babylai:h-full babylai:rounded-3xl babylai:flex babylai:flex-col babylai:relative'
|
|
313
333
|
>
|
|
314
334
|
{activeChatButton}
|