andy-widget-react 2.0.6 → 2.1.0

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.
@@ -74,6 +74,10 @@ export interface AndyChatProps {
74
74
  welcomeMessage?: string;
75
75
  /** Optional custom label for pill-style bubble (overrides default) */
76
76
  bubbleLabel?: string;
77
+ /** Optional UI language for the widget's own chrome, overrides the Agent's defaultLang */
78
+ lang?: 'en' | 'es';
79
+ /** Optional starter questions (overrides API value) */
80
+ predefinedQuestions?: PredefinedQuestion[];
77
81
  /** Optional theme ('light' | 'dark' | 'auto') */
78
82
  theme?: 'light' | 'dark' | 'auto';
79
83
  /** Optional position ('left' | 'right') */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "andy-widget-react",
3
- "version": "2.0.6",
3
+ "version": "2.1.0",
4
4
  "description": "Official React Widget for embeddable Andy agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -67,6 +67,6 @@
67
67
  "react-dom": "^18.0.0 || ^19.0.0"
68
68
  },
69
69
  "dependencies": {
70
- "andy-widget-core": "2.0.6"
70
+ "andy-widget-core": "2.1.0"
71
71
  }
72
72
  }
package/src/AndyChat.tsx CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  FOOTER_TEXT,
19
19
  getFooterUrl,
20
20
  getDefaultBubbleLabel,
21
+ getUiStrings,
21
22
  getKeyboardDetector,
22
23
  updateKeyboardHeight,
23
24
  isMobileDevice
@@ -74,6 +75,8 @@ const AndyChat: React.FC<AndyChatProps> = ({
74
75
  description,
75
76
  welcomeMessage,
76
77
  bubbleLabel,
78
+ lang,
79
+ predefinedQuestions,
77
80
  theme,
78
81
  orientation,
79
82
  brandColor,
@@ -84,6 +87,11 @@ const AndyChat: React.FC<AndyChatProps> = ({
84
87
  initialOpen = false
85
88
  }) => {
86
89
  const [config, setConfig] = useState<ChatbotConfig | null>(null)
90
+ // The visitor's language wins over the Agent's own default: a Spanish Agent
91
+ // embedded on an English page must still render English chrome.
92
+ const uiLang = lang || config?.defaultLang || 'en'
93
+ const strings = getUiStrings(uiLang)
94
+ const activeQuestions = predefinedQuestions?.length ? predefinedQuestions : config?.predefinedQuestions
87
95
  const [isOpen, setIsOpen] = useState(initialOpen)
88
96
  const [messages, setMessages] = useState<Message[]>([])
89
97
  const [isLoading, setIsLoading] = useState(false)
@@ -114,7 +122,7 @@ const AndyChat: React.FC<AndyChatProps> = ({
114
122
  pendingHandoffIdsRef.current = projection.pendingProjectionIds || []
115
123
  const projectedMessages = projection.messages.map(message => ({ id: message.id, content: message.content, sender: message.role === 'user' ? 'user' as const : 'bot' as const, timestamp: new Date(message.createdAt).toISOString(), isHuman: message.sentByHuman }))
116
124
  setMessages(projection.control === 'requested' || projection.control === 'human'
117
- ? [...projectedMessages, { id: 'handoff-waiting', content: config.defaultLang === 'es' ? 'Un miembro del equipo te responderá pronto.' : 'A team member will reply soon.', sender: 'bot', timestamp: new Date().toISOString(), isWaiting: true }]
125
+ ? [...projectedMessages, { id: 'handoff-waiting', content: strings.handoffWaiting, sender: 'bot', timestamp: new Date().toISOString(), isWaiting: true }]
118
126
  : projectedMessages)
119
127
  })
120
128
  handoffRef.current = handoff
@@ -326,12 +334,12 @@ const AndyChat: React.FC<AndyChatProps> = ({
326
334
  bubbleStyle: bubbleStyle || 'icon',
327
335
  bubbleLabel: bubbleLabel || null,
328
336
  useBrandColor: useBrandColor !== undefined ? useBrandColor : true,
329
- defaultLang: 'en',
337
+ defaultLang: uiLang,
330
338
  showBranding: true,
331
339
  showWelcomePopup: { enabled: false, seconds: 3, message: '' },
332
340
  collectUserInfo: { enabled: false, message: '' },
333
341
  welcomeMessage: welcomeMessage || 'Hi there! How can I help you today?',
334
- predefinedQuestions: [],
342
+ predefinedQuestions: predefinedQuestions || [],
335
343
  faqs: []
336
344
  }
337
345
  setConfig(previewConfig)
@@ -495,7 +503,7 @@ const AndyChat: React.FC<AndyChatProps> = ({
495
503
  setMessages([])
496
504
  }
497
505
 
498
- if (config?.predefinedQuestions && config.predefinedQuestions.length > 0) {
506
+ if (activeQuestions && activeQuestions.length > 0) {
499
507
  setShowPredefinedQuestions(true)
500
508
  }
501
509
  }
@@ -527,7 +535,7 @@ const AndyChat: React.FC<AndyChatProps> = ({
527
535
  setIsLoading(true)
528
536
  const response = await sendMessageToAPI(embedId!, sessionId, messageText)
529
537
  if (response.status === 'waiting') {
530
- setMessages(prev => prev.some(message => message.isWaiting) ? prev : [...prev, { id: 'handoff-waiting', content: config?.defaultLang === 'es' ? 'Un miembro del equipo te responderá pronto.' : 'A team member will reply soon.', sender: 'bot', timestamp: new Date().toISOString(), isWaiting: true }])
538
+ setMessages(prev => prev.some(message => message.isWaiting) ? prev : [...prev, { id: 'handoff-waiting', content: strings.handoffWaiting, sender: 'bot', timestamp: new Date().toISOString(), isWaiting: true }])
531
539
  return
532
540
  }
533
541
 
@@ -556,10 +564,7 @@ const AndyChat: React.FC<AndyChatProps> = ({
556
564
  console.error('AndyChat: Message request failed.')
557
565
  const errorMessage: Message = {
558
566
  id: timestamp + 3,
559
- content:
560
- config?.defaultLang === 'es'
561
- ? 'Lo siento, no pude responder. Inténtalo de nuevo.'
562
- : 'Sorry, I could not answer. Please try again.',
567
+ content: strings.sendFailure,
563
568
  sender: 'bot',
564
569
  timestamp: new Date().toISOString(),
565
570
  isError: true
@@ -704,7 +709,7 @@ const AndyChat: React.FC<AndyChatProps> = ({
704
709
 
705
710
  const isPillStyle = config?.bubbleStyle === 'pill'
706
711
  const displayLabel =
707
- bubbleLabel || config?.bubbleLabel || (isPillStyle ? getDefaultBubbleLabel(config?.defaultLang || 'en') : null)
712
+ bubbleLabel || config?.bubbleLabel || (isPillStyle ? getDefaultBubbleLabel(uiLang) : null)
708
713
  const bubbleLogoUrl = config?.logoUrl || CHAT_BUBBLE_DEFAULT_ICON_URL
709
714
 
710
715
  if (bubble) {
@@ -951,7 +956,7 @@ const AndyChat: React.FC<AndyChatProps> = ({
951
956
  )}
952
957
  </div>
953
958
  <span className='andy-bot-header__name'>
954
- {message.isHuman ? (config?.defaultLang === 'es' ? 'Equipo' : 'Team') : config?.name || 'Andy bot'}
959
+ {message.isHuman ? strings.teamName : config?.name || 'Andy bot'}
955
960
  </span>
956
961
  </div>
957
962
  <div className='andy-message__content'>
@@ -984,15 +989,15 @@ const AndyChat: React.FC<AndyChatProps> = ({
984
989
  </div>
985
990
 
986
991
  <div className='andy-chat-window__bottom'>
987
- {config?.predefinedQuestions &&
988
- config.predefinedQuestions.length > 0 &&
992
+ {activeQuestions &&
993
+ activeQuestions.length > 0 &&
989
994
  showPredefinedQuestions && (
990
995
  <div className='andy-predefined-questions' id='andy-predefined-questions'>
991
996
  <div className='andy-predefined-questions__header'>
992
- <p className='andy-predefined-questions__title'>What would you like to know?</p>
997
+ <p className='andy-predefined-questions__title'>{strings.predefinedQuestionsTitle}</p>
993
998
  </div>
994
999
  <div className='andy-predefined-questions__list'>
995
- {config.predefinedQuestions.map((q, index) => (
1000
+ {activeQuestions.map((q, index) => (
996
1001
  <button
997
1002
  key={index}
998
1003
  type='button'
@@ -1015,7 +1020,7 @@ const AndyChat: React.FC<AndyChatProps> = ({
1015
1020
  type='text'
1016
1021
  className='andy-input__field'
1017
1022
  id='andy-message-input'
1018
- placeholder={config ? 'Type your message...' : '...'}
1023
+ placeholder={config ? strings.inputPlaceholder : '...'}
1019
1024
  aria-label='Message input'
1020
1025
  onKeyDown={e => {
1021
1026
  if (e.key === 'Enter' && (e.target as HTMLInputElement).value.trim()) {
@@ -90,6 +90,10 @@ export interface AndyChatProps {
90
90
  welcomeMessage?: string
91
91
  /** Optional custom label for pill-style bubble (overrides default) */
92
92
  bubbleLabel?: string
93
+ /** Optional UI language for the widget's own chrome, overrides the Agent's defaultLang */
94
+ lang?: 'en' | 'es'
95
+ /** Optional starter questions (overrides API value) */
96
+ predefinedQuestions?: PredefinedQuestion[]
93
97
  /** Optional theme ('light' | 'dark' | 'auto') */
94
98
  theme?: 'light' | 'dark' | 'auto'
95
99
  /** Optional position ('left' | 'right') */