diff --git a/src/components/VapiWidget.tsx b/src/components/VapiWidget.tsx index 78fa194..e984b9d 100644 --- a/src/components/VapiWidget.tsx +++ b/src/components/VapiWidget.tsx @@ -55,6 +55,7 @@ const VapiWidget: React.FC = ({ emptyHybridMessage = 'Use voice or text to communicate', // deprecated // Chat configuration chatFirstMessage, + chatEndMessage, firstChatMessage, // deprecated chatPlaceholder, // Voice configuration @@ -79,6 +80,7 @@ const VapiWidget: React.FC = ({ const [isExpanded, setIsExpanded] = useState(false); const [hasConsent, setHasConsent] = useState(false); const [chatInput, setChatInput] = useState(''); + const [showEndScreen, setShowEndScreen] = useState(false); const conversationEndRef = useRef(null); const inputRef = useRef(null); @@ -109,6 +111,8 @@ const VapiWidget: React.FC = ({ const effectiveOnVoiceStart = onVoiceStart ?? onCallStart; const effectiveOnVoiceEnd = onVoiceEnd ?? onCallEnd; const effectiveChatPlaceholder = chatPlaceholder ?? 'Type your message...'; + const effectiveChatEndMessage = + chatEndMessage ?? 'This chat has ended. Thank you.'; const vapi = useVapiWidget({ mode, @@ -253,6 +257,7 @@ const VapiWidget: React.FC = ({ const handleReset = () => { vapi.clearConversation(); + setShowEndScreen(false); if (vapi.voice.isCallActive) { vapi.voice.endCall(); @@ -267,6 +272,34 @@ const VapiWidget: React.FC = ({ } }; + const handleChatComplete = async () => { + try { + await vapi.chat.sendMessage('Ending chat...', true); + setShowEndScreen(true); + } finally { + setChatInput(''); + } + }; + + const handleStartNewChat = () => { + vapi.clearConversation(); + setShowEndScreen(false); + if (mode === 'chat' || mode === 'hybrid') { + setTimeout(() => { + inputRef.current?.focus(); + }, 100); + } + }; + + const handleCloseWidget = () => { + if (showEndScreen) { + vapi.clearConversation(); + setShowEndScreen(false); + setChatInput(''); + } + setIsExpanded(false); + }; + const handleFloatingButtonClick = () => { setIsExpanded(true); }; @@ -316,6 +349,38 @@ const VapiWidget: React.FC = ({ }; const renderConversationArea = () => { + if (showEndScreen) { + return ( +
+
+ {effectiveChatEndMessage} +
+
+ + +
+
+ ); + } // Chat mode: always show conversation messages if (mode === 'chat') { return renderConversationMessages(); @@ -380,6 +445,9 @@ const VapiWidget: React.FC = ({ }; const renderControls = () => { + if (showEndScreen) { + return null; + } if (mode === 'voice') { return ( = ({ isTyping={vapi.chat.isTyping} hasActiveConversation={vapi.conversation.length > 0} mainLabel={effectiveTextWidgetTitle} - onClose={() => setIsExpanded(false)} + onClose={handleCloseWidget} onReset={handleReset} + onChatComplete={handleChatComplete} + showEndChatButton={!showEndScreen} colors={colors} styles={styles} /> diff --git a/src/components/types.ts b/src/components/types.ts index 7b38806..63aec12 100644 --- a/src/components/types.ts +++ b/src/components/types.ts @@ -44,6 +44,7 @@ export interface VapiWidgetProps { // Chat Configuration chatFirstMessage?: string; chatPlaceholder?: string; + chatEndMessage?: string; // Voice Configuration voiceShowTranscript?: boolean; @@ -153,6 +154,8 @@ export interface WidgetHeaderProps { mainLabel: string; onClose: () => void; onReset: () => void; + onChatComplete: () => void; + showEndChatButton?: boolean; colors: ColorScheme; styles: StyleConfig; } diff --git a/src/components/widget/WidgetHeader.tsx b/src/components/widget/WidgetHeader.tsx index 588a0bd..0ab2938 100644 --- a/src/components/widget/WidgetHeader.tsx +++ b/src/components/widget/WidgetHeader.tsx @@ -13,6 +13,8 @@ const WidgetHeader: React.FC = ({ mainLabel, onClose, onReset, + onChatComplete, + showEndChatButton, colors, styles, }) => { @@ -68,6 +70,15 @@ const WidgetHeader: React.FC = ({
+ {showEndChatButton !== false && ( + + )}