From 3bd3bead6462f5c1980a6b4a1769d818bdbb9267 Mon Sep 17 00:00:00 2001 From: Giddh's Black Tiger Date: Sat, 20 Jun 2026 12:32:52 +0530 Subject: [PATCH 1/6] chore: add port 3001 configuration to package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d894f90e..5c642e85 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev --turbopack", + "dev": "next dev -p 3001 --turbopack", "build": "next build", "start": "next start", "lint": "next lint", From f299f371d7651ed9d4a594105d28fffe9d167cdf Mon Sep 17 00:00:00 2001 From: Giddh's Black Tiger Date: Sat, 20 Jun 2026 17:26:27 +0530 Subject: [PATCH 2/6] refactor: add QuickActionMenu and fix z-index issues --- components/FormComponent.tsx | 2 +- .../Interface-Chatbot/ChatbotDrawer.tsx | 100 ++++++++--- .../Interface-Chatbot/ChatbotHeader.tsx | 70 +++++++- .../Interface-Chatbot/InterfaceChatbot.css | 2 +- .../Interface-Chatbot/QuickActionsMenu.tsx | 169 ++++++++++++++++++ 5 files changed, 311 insertions(+), 32 deletions(-) create mode 100644 components/Interface-Chatbot/QuickActionsMenu.tsx diff --git a/components/FormComponent.tsx b/components/FormComponent.tsx index 98713c5d..c20c884e 100644 --- a/components/FormComponent.tsx +++ b/components/FormComponent.tsx @@ -138,7 +138,7 @@ function FormComponent({ chatSessionId }: FormComponentProps) { if (!open && !showWidgetForm) return null; if (!open && showWidgetForm) return (
setOpen(true)} style={{ background: `linear-gradient(to right, ${backgroundColor}, ${backgroundColor}CC)`, diff --git a/components/Interface-Chatbot/ChatbotDrawer.tsx b/components/Interface-Chatbot/ChatbotDrawer.tsx index 1a036ae8..15eda6b4 100644 --- a/components/Interface-Chatbot/ChatbotDrawer.tsx +++ b/components/Interface-Chatbot/ChatbotDrawer.tsx @@ -1,8 +1,8 @@ 'use client'; import { lighten } from "@mui/material"; -import { AlignLeft, ChevronRight, SquarePen, Users, X } from "lucide-react"; -import { useContext, useEffect, useMemo } from "react"; +import { AlignLeft, ChevronRight, SquarePen, Users } from "lucide-react"; +import { useContext, useEffect, useMemo, useState } from "react"; import { useDispatch } from "react-redux"; // API and Services @@ -16,6 +16,7 @@ import { useReduxStateManagement } from "../Chatbot/hooks/useReduxManagement"; // Redux Actions import { setDataInAppInfoReducer } from "@/store/appInfo/appInfoSlice"; +import { setDataInDraftReducer } from "@/store/draftData/draftDataSlice"; import { setThreads } from "@/store/interface/interfaceSlice"; // Utils and Types @@ -25,6 +26,8 @@ import { useColor } from "../Chatbot/hooks/useColor"; import { useScreenSize } from "../Chatbot/hooks/useScreenSize"; import { MessageContext } from "./InterfaceChatbot"; import { useOnSendHello } from "../Chatbot/hooks/useHelloIntegration"; +import { emitEventToParent } from "@/utils/emitEventsToParent/emitEventsToParent"; +import QuickActionsMenu from "./QuickActionsMenu"; const createRandomId = () => Math.random().toString(36).substring(2, 15); @@ -75,9 +78,13 @@ const ChatbotDrawer = ({ tagline, hideCloseButton, voice_call_widget, - show_msg91 + show_msg91, + isChatbotMinimized, + isMobileSDK, + isFullScreen } = useCustomSelector((state) => { const show_close_button = state.Hello?.[chatSessionId]?.helloConfig?.show_close_button + const helloFullScreen = state.Hello?.[chatSessionId]?.helloConfig?.fullScreen return { subThreadList: state.Interface?.[chatSessionId]?.interfaceContext?.[bridgeName]?.threadList?.[threadId] || [], teamsList: state.Hello?.[chatSessionId]?.widgetInfo?.teams || [], @@ -87,7 +94,10 @@ const ChatbotDrawer = ({ tagline: state.Hello?.[chatSessionId]?.widgetInfo?.tagline || '', hideCloseButton: typeof show_close_button === 'boolean' ? !show_close_button : state.appInfo?.[tabSessionId]?.hideCloseButton || false, voice_call_widget: state.Hello?.[chatSessionId]?.widgetInfo?.voice_call_widget || false, - show_msg91: state.Hello?.[chatSessionId]?.widgetInfo?.show_msg91 || false + show_msg91: state.Hello?.[chatSessionId]?.widgetInfo?.show_msg91 || false, + isChatbotMinimized: state.draftData?.isChatbotMinimized || false, + isMobileSDK: state.Hello?.[chatSessionId]?.helloConfig?.isMobileSDK || false, + isFullScreen: (helloFullScreen === true || helloFullScreen === 'true') ?? false }; }); @@ -401,18 +411,70 @@ const ChatbotDrawer = ({ window.parent.postMessage({ type: "CLOSE_CHATBOT" }, "*"); }; - const CloseButton = useMemo(() => { - if (hideCloseButton === true || hideCloseButton === "true" || !isSmallScreen) return null; + const handleMinimizeChatbot = (value: boolean) => { + dispatch(setDataInDraftReducer({ isChatbotMinimized: value })); + }; + + const [fullScreen, setFullScreen] = useState(false); + + const toggleFullScreen = (enter: boolean) => { + if (!window?.parent) return; + setFullScreen(enter); + const message = enter + ? { type: "ENTER_FULL_SCREEN_CHATBOT" } + : { type: "EXIT_FULL_SCREEN_CHATBOT" }; + window.parent.postMessage(message, "*"); + }; + + const handleToggleMinimize = () => { + if (!isChatbotMinimized && fullScreen) { + toggleFullScreen(false); + } + handleMinimizeChatbot(!isChatbotMinimized); + if (!isChatbotMinimized) { + emitEventToParent('MINIMIZE_CHATBOT'); + } else { + toggleFullScreen(false); + } + }; + + // Quick Actions dropdown for the drawer header + const canMinimize = isHelloUser && !isMobileSDK; + const canFullScreen = !isMobileSDK && !isFullScreen; + + const DrawerQuickActionsMenu = useMemo(() => { + if (!isToggledrawer) return null; return ( -
- -
+ toggleFullScreen(!fullScreen)} + onNewConversation={handleCreateNewSubThread} + onClose={() => handleCloseChatbot()} + triggerClassName="p-2 hover:bg-gray-200 rounded-full transition-colors icn" + menuClassName="absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none z-[9999] py-1" + useIconColor + /> ); - }, [hideCloseButton, handleCloseChatbot]); + }, [ + isToggledrawer, + isHelloUser, + isChatbotMinimized, + fullScreen, + canMinimize, + canFullScreen, + hideCloseButton, + handleToggleMinimize, + toggleFullScreen, + handleCreateNewSubThread, + handleCloseChatbot + ]); return (
@@ -456,17 +518,7 @@ const ChatbotDrawer = ({ )}
- {isToggledrawer && !isHelloUser && ( -
- -
- )} - {isHelloUser && CloseButton} + {isToggledrawer && DrawerQuickActionsMenu}
diff --git a/components/Interface-Chatbot/ChatbotHeader.tsx b/components/Interface-Chatbot/ChatbotHeader.tsx index 1b328b13..7d7ed99d 100644 --- a/components/Interface-Chatbot/ChatbotHeader.tsx +++ b/components/Interface-Chatbot/ChatbotHeader.tsx @@ -29,6 +29,7 @@ import { emitEventToParent } from "@/utils/emitEventsToParent/emitEventsToParent import { createRandomId, DEFAULT_AI_SERVICE_MODALS, ParamsEnums } from "@/utils/enums"; import { useChatActions } from "../Chatbot/hooks/useChatActions"; import { ChatbotContext } from "../context"; +import QuickActionsMenu from "./QuickActionsMenu"; import "./InterfaceChatbot.css"; export function ChatbotHeaderPreview() { @@ -609,6 +610,62 @@ const ChatbotHeader: React.FC = ({ preview = false, chatSess ); }, [isHelloUser, isChatbotMinimized, fullScreen, toggleFullScreen]) + // Expand button for the collapsed header — un-minimizes the chat back to default size + const ExpandButton = useMemo(() => { + if (!isChatbotMinimized) return null; + return ( +
{ e.stopPropagation(); handleToggleMinimize(); }} + > + +
+ ); + }, [isChatbotMinimized, handleToggleMinimize]) + + // Determine which quick-action items are available + const hasMinimizeAction = !!MinimizeButton; + const hasFullScreenAction = !!ScreenSizeToggleButton && !isFullScreen; + const hasExitFullScreenAction = !!ScreenSizeToggleButton && isFullScreen; + const hasCloseAction = !!CloseButton; + const hasNewConversationAction = !!CreateThreadButton; + + const showQuickActions = ( + hasMinimizeAction || + hasFullScreenAction || + hasExitFullScreenAction || + hasCloseAction || + hasNewConversationAction + ); + + const QuickActionsMenuComponent = useMemo(() => ( + toggleFullScreen(!fullScreen)} + onNewConversation={handleCreateNewSubThread} + onClose={handleCloseChatbot} + position={isChatbotMinimized ? 'top' : 'bottom'} + /> + ), [ + isChatbotMinimized, + fullScreen, + hasMinimizeAction, + hasFullScreenAction, + hasExitFullScreenAction, + hasCloseAction, + hasNewConversationAction, + handleToggleMinimize, + toggleFullScreen, + handleCreateNewSubThread, + handleCloseChatbot + ]) + return isChatbotMinimized ?
@@ -616,8 +673,8 @@ const ChatbotHeader: React.FC = ({ preview = false, chatSess {HeaderTitleSection}
-
- {MinimizeButton} +
e.stopPropagation()}> + {ExpandButton} {CloseButton}
@@ -654,10 +711,11 @@ const ChatbotHeader: React.FC = ({ preview = false, chatSess ))} - {!isFullScreen &&
- {ScreenSizeToggleButton} - {(isMobileSDK || !isHelloUser) ? CloseButton : MinimizeButton} -
} + {showQuickActions && ( +
+ {QuickActionsMenuComponent} +
+ )}
diff --git a/components/Interface-Chatbot/InterfaceChatbot.css b/components/Interface-Chatbot/InterfaceChatbot.css index 6dc00a92..2eabfd2b 100644 --- a/components/Interface-Chatbot/InterfaceChatbot.css +++ b/components/Interface-Chatbot/InterfaceChatbot.css @@ -36,7 +36,7 @@ padding: 5px !important; border-radius: 50px !important; cursor: pointer !important; - z-index: 999999 !important; + z-index: 99 !important; pointer-events: auto !important; background-color: var(--down-btn-bg-color, #333) !important; color: var(--down-btn-text-color, white) !important; diff --git a/components/Interface-Chatbot/QuickActionsMenu.tsx b/components/Interface-Chatbot/QuickActionsMenu.tsx new file mode 100644 index 00000000..f96d3f14 --- /dev/null +++ b/components/Interface-Chatbot/QuickActionsMenu.tsx @@ -0,0 +1,169 @@ +'use client'; + +import { EllipsisVertical, Maximize2, Minimize2, Minus, Plus, X } from "lucide-react"; +import React, { useEffect, useMemo, useRef, useState } from "react"; + +export interface QuickActionsMenuProps { + /** Open state (controlled). If omitted, the component manages its own state. */ + open?: boolean; + onOpenChange?: (open: boolean) => void; + + isChatbotMinimized?: boolean; + fullScreen?: boolean; + + showMinimize?: boolean; + showFullScreen?: boolean; + showNewConversation?: boolean; + showClose?: boolean; + + onMinimize?: () => void; + onToggleFullScreen?: () => void; + onNewConversation?: () => void; + onClose?: (e?: React.MouseEvent) => void; + + /** Extra class for the trigger button. */ + triggerClassName?: string; + /** Extra class for the menu panel. */ + menuClassName?: string; + /** Icon size for the trigger. */ + triggerIconSize?: number; + /** Apply the `var(--icon-color)` color to the trigger icon. */ + useIconColor?: boolean; + /** Where to anchor the menu relative to the trigger. Defaults to "bottom". */ + position?: "top" | "bottom"; +} + +const QuickActionsMenu: React.FC = ({ + open: controlledOpen, + onOpenChange, + isChatbotMinimized = false, + fullScreen = false, + showMinimize = false, + showFullScreen = false, + showNewConversation = false, + showClose = false, + onMinimize, + onToggleFullScreen, + onNewConversation, + onClose, + triggerClassName = "cursor-pointer p-2 rounded-full hover:bg-gray-200 transition-colors icn", + menuClassName = "absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none z-50 py-1", + triggerIconSize = 22, + useIconColor = false, + position = "bottom", +}) => { + const [internalOpen, setInternalOpen] = useState(false); + const isControlled = controlledOpen !== undefined; + const open = isControlled ? !!controlledOpen : internalOpen; + + const setOpen = (next: boolean) => { + if (!isControlled) setInternalOpen(next); + onOpenChange?.(next); + }; + + const menuRef = useRef(null); + + useEffect(() => { + if (!open) return; + const handleClickOutside = (event: MouseEvent) => { + if (menuRef.current && !menuRef.current.contains(event.target as Node)) { + setOpen(false); + } + }; + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [open]); + + const closeMenu = () => setOpen(false); + + const triggerIconProps = useIconColor ? { color: "var(--icon-color)" as const } : {}; + + const trigger = useMemo(() => ( + + // eslint-disable-next-line react-hooks/exhaustive-deps + ), [open, triggerClassName, triggerIconSize, useIconColor]); + + return ( +
+ {trigger} + + {open && ( +
+ {showMinimize && ( + + )} + + {showFullScreen && ( + + )} + + {showNewConversation && ( + + )} + + {showClose && ( + + )} +
+ )} +
+ ); +}; + +export default QuickActionsMenu; From 277b5920203facdba4afff38fbe4bdac8e47dc4f Mon Sep 17 00:00:00 2001 From: Giddh's Black Tiger Date: Sat, 20 Jun 2026 20:05:22 +0530 Subject: [PATCH 3/6] style: improve sidebar/drawer layout --- .../Interface-Chatbot/ChatbotDrawer.tsx | 68 ++++++++++++------- public/chat-widget-style.css | 2 +- public/chatbot-style.css | 2 +- public/rag.css | 2 +- 4 files changed, 46 insertions(+), 28 deletions(-) diff --git a/components/Interface-Chatbot/ChatbotDrawer.tsx b/components/Interface-Chatbot/ChatbotDrawer.tsx index 15eda6b4..e4fdaf0a 100644 --- a/components/Interface-Chatbot/ChatbotDrawer.tsx +++ b/components/Interface-Chatbot/ChatbotDrawer.tsx @@ -1,7 +1,7 @@ 'use client'; import { lighten } from "@mui/material"; -import { AlignLeft, ChevronRight, SquarePen, Users } from "lucide-react"; +import { AlignLeft, ChevronRight, SquarePen, Users, Phone, Send } from "lucide-react"; import { useContext, useEffect, useMemo, useState } from "react"; import { useDispatch } from "react-redux"; @@ -243,7 +243,9 @@ const ChatbotDrawer = ({ ), [subThreadList, subThreadId, handleChangeSubThread]); const TeamsList = useMemo(() => ( -
+ <> + {((channelList?.length > 0 && channelList.some((thread: any) => thread?.id)) || teamsList?.length > 0) && ( +
{/* Conversations Section */} {(channelList || []).length > 0 && channelList.some((thread: any) => thread?.id) && (
@@ -342,16 +344,12 @@ const ChatbotDrawer = ({ )} {/* Teams Section */} + {(teamsList || []).length > 0 && (

Talk to our experts

- {teamsList.length === 0 ? ( -
- -
- ) : (
{teamsList.map((team: any, index: number) => (
))}
+
+
+ )} +
+ )} + + {/* Voice Call Section */} + {voice_call_widget && ( +
+

Talk to Our Teams

+
+ + {/*Send Message button in case of no team assign */} + { (teamsList || []).length === 0 && ( + )}
- - {voice_call_widget &&
-

Need specialized help?

-

Our teams are ready to assist you with any questions

- -
} -
+ )} + ), [ channelList, teamsList, @@ -439,7 +459,7 @@ const ChatbotDrawer = ({ }; // Quick Actions dropdown for the drawer header - const canMinimize = isHelloUser && !isMobileSDK; + const canMinimize = false; //isHelloUser && !isMobileSDK; const canFullScreen = !isMobileSDK && !isFullScreen; const DrawerQuickActionsMenu = useMemo(() => { @@ -524,10 +544,8 @@ const ChatbotDrawer = ({
{/* Content area with overflow handling - the scrollbar will appear at the edge */} -
-
+
{!isHelloUser ? DrawerList : TeamsList} -
{/* Footer with branding - always stays at bottom */} diff --git a/public/chat-widget-style.css b/public/chat-widget-style.css index f401ce79..0961c549 100644 --- a/public/chat-widget-style.css +++ b/public/chat-widget-style.css @@ -66,7 +66,7 @@ z-index: 2147483647; display: none; box-sizing: border-box; - border-radius: 12px; + border-radius: 16px; overflow: hidden; border: 1px solid #cecece; box-shadow: rgba(15, 15, 15, 0.08) 0px 5px 40px 0px; diff --git a/public/chatbot-style.css b/public/chatbot-style.css index 964a92a4..7aeed2ea 100644 --- a/public/chatbot-style.css +++ b/public/chatbot-style.css @@ -63,7 +63,7 @@ z-index: 9999; display: none; box-sizing: border-box; - border-radius: 12px; + border-radius: 16px; overflow: hidden; border: 1px solid #cecece; } diff --git a/public/rag.css b/public/rag.css index 8077266e..6e348e6e 100644 --- a/public/rag.css +++ b/public/rag.css @@ -436,7 +436,7 @@ height: 90vh; max-width: 1200px; max-height: 800px; - border-radius: 12px; + border-radius: 16px; box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); overflow: hidden; From 9820f98a5e1706f1a1c91a24fff7f024f33c17e5 Mon Sep 17 00:00:00 2001 From: Giddh's Black Tiger Date: Mon, 22 Jun 2026 19:33:18 +0530 Subject: [PATCH 4/6] Working in Sidebar improvement --- .../Chatbot/hooks/useReduxManagement.ts | 49 ++++++----- .../Interface-Chatbot/ChatbotDrawer.tsx | 83 ++++++++++++++++--- .../Interface-Chatbot/ChatbotHeader.tsx | 7 +- .../Interface-Chatbot/QuickActionsMenu.tsx | 18 +--- .../embeddingScriptEventHandler.ts | 28 ++++++- store/hello/helloReducer.ts | 9 +- 6 files changed, 134 insertions(+), 60 deletions(-) diff --git a/components/Chatbot/hooks/useReduxManagement.ts b/components/Chatbot/hooks/useReduxManagement.ts index 0eb50c7b..b3d9981a 100644 --- a/components/Chatbot/hooks/useReduxManagement.ts +++ b/components/Chatbot/hooks/useReduxManagement.ts @@ -34,25 +34,36 @@ export const useReduxStateManagement = ({ currentTeamId, isDefaultNavigateToChatScreen, overrideChannelId - } = useCustomSelector((state) => ({ - interfaceContextData: state.Interface?.[chatSessionId]?.interfaceContext?.variables, - isHelloUser: state.draftData?.isHelloUser || false, - uuid: state.Hello?.[chatSessionId]?.channelListData?.uuid, - unique_id: state.Hello?.[chatSessionId]?.channelListData?.unique_id, - presence_channel: state.Hello?.[chatSessionId]?.channelListData?.presence_channel, - team_id: state.Hello?.[chatSessionId]?.widgetInfo?.team?.[0]?.id, - isDefaultNavigateToChatScreen: isDefaultNavigateToChatScreenFn(state, chatSessionId), - chat_id: state.Hello?.[chatSessionId]?.Channel?.id, - channelId: state.Hello?.[chatSessionId]?.Channel?.channel || null, - mode: state.Hello?.[chatSessionId]?.mode || [], - selectedAiServiceAndModal: state.Interface?.[chatSessionId]?.selectedAiServiceAndModal || null, - unique_id_hello: state?.Hello?.[chatSessionId]?.helloConfig?.unique_id, - widgetToken: state?.Hello?.[chatSessionId]?.helloConfig?.widgetToken, - currentChatId: state?.appInfo?.[tabSessionId]?.currentChatId, - currentChannelId: state?.appInfo?.[tabSessionId]?.currentChannelId, - currentTeamId: state?.appInfo?.[tabSessionId]?.currentTeamId, - overrideChannelId: state?.appInfo?.[tabSessionId]?.overrideChannelId, - })); + } = useCustomSelector((state) => { + const channels = state.Hello?.[chatSessionId]?.channelListData?.channels || []; + const fallbackChat = (() => { + if (!channels?.length) return null; + const openChats = channels + .filter((ch: any) => !ch.is_closed) + .sort((a: any, b: any) => (b.last_message?.timetoken || 0) - (a.last_message?.timetoken || 0)); + return openChats[0] || channels[0]; + })(); + + return { + interfaceContextData: state.Interface?.[chatSessionId]?.interfaceContext?.variables, + isHelloUser: state.draftData?.isHelloUser || false, + uuid: state.Hello?.[chatSessionId]?.channelListData?.uuid, + unique_id: state.Hello?.[chatSessionId]?.channelListData?.unique_id, + presence_channel: state.Hello?.[chatSessionId]?.channelListData?.presence_channel, + team_id: state.Hello?.[chatSessionId]?.widgetInfo?.team?.[0]?.id, + isDefaultNavigateToChatScreen: isDefaultNavigateToChatScreenFn(state, chatSessionId), + chat_id: state.Hello?.[chatSessionId]?.Channel?.id, + channelId: state.Hello?.[chatSessionId]?.Channel?.channel || null, + mode: state.Hello?.[chatSessionId]?.mode || [], + selectedAiServiceAndModal: state.Interface?.[chatSessionId]?.selectedAiServiceAndModal || null, + unique_id_hello: state?.Hello?.[chatSessionId]?.helloConfig?.unique_id, + widgetToken: state?.Hello?.[chatSessionId]?.helloConfig?.widgetToken, + currentChatId: state?.appInfo?.[tabSessionId]?.currentChatId || fallbackChat?.id, + currentChannelId: state?.appInfo?.[tabSessionId]?.currentChannelId || fallbackChat?.channel, + currentTeamId: state?.appInfo?.[tabSessionId]?.currentTeamId || fallbackChat?.team_id, + overrideChannelId: state?.appInfo?.[tabSessionId]?.overrideChannelId, + }; + }); return { interfaceContextData, diff --git a/components/Interface-Chatbot/ChatbotDrawer.tsx b/components/Interface-Chatbot/ChatbotDrawer.tsx index e4fdaf0a..91d1339d 100644 --- a/components/Interface-Chatbot/ChatbotDrawer.tsx +++ b/components/Interface-Chatbot/ChatbotDrawer.tsx @@ -1,7 +1,7 @@ 'use client'; import { lighten } from "@mui/material"; -import { AlignLeft, ChevronRight, SquarePen, Users, Phone, Send } from "lucide-react"; +import { AlignLeft, ChevronRight, SquarePen, Users, Phone, Send, X } from "lucide-react"; import { useContext, useEffect, useMemo, useState } from "react"; import { useDispatch } from "react-redux"; @@ -67,6 +67,8 @@ const ChatbotDrawer = ({ const { currentChatId, currentTeamId, currentChannelId } = useReduxStateManagement({ chatSessionId, tabSessionId }); const { callState } = useCallUI(); const sendMessageToHello = useOnSendHello(); + const [showAllChannels, setShowAllChannels] = useState(false); + const [showAllTeams, setShowAllTeams] = useState(false); // Consolidated Redux state selection const { @@ -101,7 +103,25 @@ const ChatbotDrawer = ({ }; }); + const VISIBLE_ITEMS_COUNT = 3; + const filteredChannels = (channelList || []).filter( + (channel: any) => channel?.id + ); + + const closedChatsCount = filteredChannels.filter( + (channel: any) => channel?.is_closed + ).length; + + const displayedChannels = showAllChannels + ? filteredChannels + : filteredChannels.slice(0, VISIBLE_ITEMS_COUNT); + + const displayedTeams = showAllTeams + ? teamsList + : teamsList.slice(0, VISIBLE_ITEMS_COUNT); + useEffect(() => { + console.log("filteredChannels", filteredChannels); if (chatSessionId) { setToggleDrawer(true); } @@ -243,9 +263,9 @@ const ChatbotDrawer = ({ ), [subThreadList, subThreadId, handleChangeSubThread]); const TeamsList = useMemo(() => ( - <> - {((channelList?.length > 0 && channelList.some((thread: any) => thread?.id)) || teamsList?.length > 0) && ( -
+ <> + {((channelList?.length > 0 && channelList.some((thread: any) => thread?.id)) || teamsList?.length > 0) && ( +
{/* Conversations Section */} {(channelList || []).length > 0 && channelList.some((thread: any) => thread?.id) && (
@@ -253,9 +273,7 @@ const ChatbotDrawer = ({

Continue Conversations

- {channelList - .filter((channel: any) => channel?.id) - .map((channel: any, index: number) => ( + { displayedChannels.map((channel: any, index: number) => (
))} + {filteredChannels.length > VISIBLE_ITEMS_COUNT && ( +
+ +
+ )}
)} @@ -351,7 +386,7 @@ const ChatbotDrawer = ({
- {teamsList.map((team: any, index: number) => ( + {displayedTeams.map((team: any, index: number) => (
))} + + {teamsList.length > VISIBLE_ITEMS_COUNT && ( +
+ +
+ )}
@@ -463,6 +513,7 @@ const ChatbotDrawer = ({ const canFullScreen = !isMobileSDK && !isFullScreen; const DrawerQuickActionsMenu = useMemo(() => { + if (fullScreen || isFullScreen) return null; if (!isToggledrawer) return null; return ( @@ -472,11 +523,9 @@ const ChatbotDrawer = ({ showMinimize={canMinimize} showFullScreen={canFullScreen} showNewConversation={!isHelloUser} - showClose={!hideCloseButton} onMinimize={handleToggleMinimize} onToggleFullScreen={() => toggleFullScreen(!fullScreen)} onNewConversation={handleCreateNewSubThread} - onClose={() => handleCloseChatbot()} triggerClassName="p-2 hover:bg-gray-200 rounded-full transition-colors icn" menuClassName="absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none z-[9999] py-1" useIconColor @@ -487,13 +536,12 @@ const ChatbotDrawer = ({ isHelloUser, isChatbotMinimized, fullScreen, + isFullScreen, canMinimize, canFullScreen, - hideCloseButton, handleToggleMinimize, toggleFullScreen, handleCreateNewSubThread, - handleCloseChatbot ]); return ( @@ -537,8 +585,17 @@ const ChatbotDrawer = ({

{tagline}

)}
-
+
{isToggledrawer && DrawerQuickActionsMenu} + {isToggledrawer && !hideCloseButton && ( + + )}
diff --git a/components/Interface-Chatbot/ChatbotHeader.tsx b/components/Interface-Chatbot/ChatbotHeader.tsx index 7d7ed99d..8bd2ca51 100644 --- a/components/Interface-Chatbot/ChatbotHeader.tsx +++ b/components/Interface-Chatbot/ChatbotHeader.tsx @@ -627,14 +627,12 @@ const ChatbotHeader: React.FC = ({ preview = false, chatSess const hasMinimizeAction = !!MinimizeButton; const hasFullScreenAction = !!ScreenSizeToggleButton && !isFullScreen; const hasExitFullScreenAction = !!ScreenSizeToggleButton && isFullScreen; - const hasCloseAction = !!CloseButton; const hasNewConversationAction = !!CreateThreadButton; const showQuickActions = ( hasMinimizeAction || hasFullScreenAction || hasExitFullScreenAction || - hasCloseAction || hasNewConversationAction ); @@ -645,11 +643,9 @@ const ChatbotHeader: React.FC = ({ preview = false, chatSess showMinimize={hasMinimizeAction} showFullScreen={hasFullScreenAction || hasExitFullScreenAction} showNewConversation={hasNewConversationAction} - showClose={hasCloseAction} onMinimize={handleToggleMinimize} onToggleFullScreen={() => toggleFullScreen(!fullScreen)} onNewConversation={handleCreateNewSubThread} - onClose={handleCloseChatbot} position={isChatbotMinimized ? 'top' : 'bottom'} /> ), [ @@ -658,12 +654,10 @@ const ChatbotHeader: React.FC = ({ preview = false, chatSess hasMinimizeAction, hasFullScreenAction, hasExitFullScreenAction, - hasCloseAction, hasNewConversationAction, handleToggleMinimize, toggleFullScreen, handleCreateNewSubThread, - handleCloseChatbot ]) return isChatbotMinimized ? @@ -716,6 +710,7 @@ const ChatbotHeader: React.FC = ({ preview = false, chatSess {QuickActionsMenuComponent} )} + {CloseButton} diff --git a/components/Interface-Chatbot/QuickActionsMenu.tsx b/components/Interface-Chatbot/QuickActionsMenu.tsx index f96d3f14..339e3d5a 100644 --- a/components/Interface-Chatbot/QuickActionsMenu.tsx +++ b/components/Interface-Chatbot/QuickActionsMenu.tsx @@ -1,6 +1,6 @@ 'use client'; -import { EllipsisVertical, Maximize2, Minimize2, Minus, Plus, X } from "lucide-react"; +import { EllipsisVertical, Maximize2, Minimize2, Minus, Plus } from "lucide-react"; import React, { useEffect, useMemo, useRef, useState } from "react"; export interface QuickActionsMenuProps { @@ -14,12 +14,10 @@ export interface QuickActionsMenuProps { showMinimize?: boolean; showFullScreen?: boolean; showNewConversation?: boolean; - showClose?: boolean; onMinimize?: () => void; onToggleFullScreen?: () => void; onNewConversation?: () => void; - onClose?: (e?: React.MouseEvent) => void; /** Extra class for the trigger button. */ triggerClassName?: string; @@ -41,11 +39,9 @@ const QuickActionsMenu: React.FC = ({ showMinimize = false, showFullScreen = false, showNewConversation = false, - showClose = false, onMinimize, onToggleFullScreen, onNewConversation, - onClose, triggerClassName = "cursor-pointer p-2 rounded-full hover:bg-gray-200 transition-colors icn", menuClassName = "absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none z-50 py-1", triggerIconSize = 22, @@ -148,18 +144,6 @@ const QuickActionsMenu: React.FC = ({ New conversation )} - - {showClose && ( - - )} )} diff --git a/hooks/HELLO/eventHandlers/embeddingScript/embeddingScriptEventHandler.ts b/hooks/HELLO/eventHandlers/embeddingScript/embeddingScriptEventHandler.ts index 89721bb7..f210caa3 100644 --- a/hooks/HELLO/eventHandlers/embeddingScript/embeddingScriptEventHandler.ts +++ b/hooks/HELLO/eventHandlers/embeddingScript/embeddingScriptEventHandler.ts @@ -1,12 +1,12 @@ import { ThemeContext } from "@/components/AppWrapper"; import { useSendMessageToHello } from "@/components/Chatbot/hooks/useHelloIntegration"; -import { addDomainToHello, saveClientDetails } from "@/config/helloApi"; +import { addDomainToHello, getAllChannels, initializeHelloChat, saveClientDetails } from "@/config/helloApi"; import { CBManger } from "@/hooks/coBrowser/CBManger"; import { EmbeddingScriptEventRegistryInstance } from "@/hooks/CORE/eventHandlers/embeddingScript/embeddingScriptEventHandler"; import { setDataInAppInfoReducer } from "@/store/appInfo/appInfoSlice"; import { setToggleDrawer } from "@/store/chat/chatSlice"; import { setDataInDraftReducer, setVariablesForHelloBot } from "@/store/draftData/draftDataSlice"; -import { setHelloClientInfo, setHelloConfig, setHelloKeysData, setWidgetInfo } from "@/store/hello/helloSlice"; +import { setHelloClientInfo, setHelloConfig, setHelloKeysData, setWidgetInfo, setChannelListData } from "@/store/hello/helloSlice"; import { setDataInInterfaceRedux } from "@/store/interface/interfaceSlice"; import { GetSessionStorageData, SetSessionStorage } from "@/utils/ChatbotUtility"; import { useCustomSelector } from "@/utils/deepCheckSelector"; @@ -176,9 +176,31 @@ const useHandleHelloEmbeddingScriptEvents = (eventHandler: EmbeddingScriptEventR } }; - function handleChatbotVisibility(isChatbotOpen = false, id = "") { + const isFetchingHelloData = useRef(false); + + async function handleChatbotVisibility(isChatbotOpen = false, id = "") { dispatch(setDataInAppInfoReducer({ isChatbotOpen })) dispatch(setDataInDraftReducer({ isChatbotMinimized: false })) + if (isChatbotOpen) { + try { + if (isFetchingHelloData.current) return; + isFetchingHelloData.current = true; + const [channelsData, widgetInfo] = await Promise.all([ + getAllChannels(), + initializeHelloChat() + ]); + if (channelsData) { + dispatch(setChannelListData(channelsData)); + } + if (widgetInfo) { + dispatch(setWidgetInfo(widgetInfo)); + } + } catch (error) { + console.error("Failed to fetch chatbot data on open:", error); + } finally { + isFetchingHelloData.current = false; + } + } if (id) { // Create a mock MessageEvent to pass to handleShowTicket const mockEvent = { diff --git a/store/hello/helloReducer.ts b/store/hello/helloReducer.ts index ead35c38..8ce38e03 100644 --- a/store/hello/helloReducer.ts +++ b/store/hello/helloReducer.ts @@ -71,10 +71,15 @@ export const reducers: ValidateSliceCaseReducers< setChannelListData(state, action: actionType) { const chatSessionId = action.urlData?.chatSessionId if (chatSessionId) { + const channels = action.payload?.channels || []; + const sortedChannels = [...channels].sort((a: any, b: any) => { + if (a.is_closed === b.is_closed) return 0; + return a.is_closed ? 1 : -1; + }); state[chatSessionId] = { ...state[chatSessionId], - channelListData: action.payload, - Channel: action.payload?.channels?.[0] + channelListData: { ...action.payload, channels: sortedChannels }, + Channel: sortedChannels[0] }; } }, From 26301a747b7bd8ac9745049492d71249e3ebaa11 Mon Sep 17 00:00:00 2001 From: Giddh's Black Tiger Date: Tue, 23 Jun 2026 20:19:22 +0530 Subject: [PATCH 5/6] Working on sidebar design and New Design --- app/globals.css | 2 +- components/Chatbot/hooks/useColor.ts | 14 +++- .../Interface-Chatbot/ChatbotDrawer.tsx | 72 +++++++++++-------- public/chat-widget-local.js | 15 ++-- public/chat-widget-style.css | 16 +++-- utils/themeUtility.js | 18 +++++ utils/utilities.js | 6 +- 7 files changed, 94 insertions(+), 49 deletions(-) diff --git a/app/globals.css b/app/globals.css index a58bdeec..c365b2aa 100644 --- a/app/globals.css +++ b/app/globals.css @@ -9,7 +9,7 @@ --foreground: #171717; --message-card-background: #f0f0f0; --icon-color: #555555; - --drawer-color: #f2f2f2; + --drawer-color: #ffffff; } [data-theme="dark"] { diff --git a/components/Chatbot/hooks/useColor.ts b/components/Chatbot/hooks/useColor.ts index 9b7ad0ce..8a139aa9 100644 --- a/components/Chatbot/hooks/useColor.ts +++ b/components/Chatbot/hooks/useColor.ts @@ -1,10 +1,18 @@ -import { isColorLight } from "@/utils/themeUtility"; +import { getPrimaryGradientBg, isColorLight } from "@/utils/themeUtility"; import { useTheme } from "@mui/material"; export const useColor = () => { const theme = useTheme(); const backgroundColor = theme.palette.primary.main; - const textColor = isColorLight(backgroundColor) ? "black" : "white"; + const isLight = isColorLight(backgroundColor); + const textColor = isLight ? "black" : "white"; - return { backgroundColor, textColor } + return { + backgroundColor, + textColor, + primaryBgColor: backgroundColor, + primaryTextColor: textColor, + foregroundColor: textColor, + primaryGradientBg: getPrimaryGradientBg(backgroundColor), + } } \ No newline at end of file diff --git a/components/Interface-Chatbot/ChatbotDrawer.tsx b/components/Interface-Chatbot/ChatbotDrawer.tsx index 91d1339d..78fb9705 100644 --- a/components/Interface-Chatbot/ChatbotDrawer.tsx +++ b/components/Interface-Chatbot/ChatbotDrawer.tsx @@ -49,7 +49,7 @@ const ChatbotDrawer = ({ threadId }: ChatbotDrawerProps) => { const dispatch = useDispatch(); - const { backgroundColor, textColor } = useColor(); + const { backgroundColor, textColor, primaryGradientBg } = useColor(); // Context hooks const { messageRef } = useContext(MessageContext); @@ -121,7 +121,6 @@ const ChatbotDrawer = ({ : teamsList.slice(0, VISIBLE_ITEMS_COUNT); useEffect(() => { - console.log("filteredChannels", filteredChannels); if (chatSessionId) { setToggleDrawer(true); } @@ -270,7 +269,7 @@ const ChatbotDrawer = ({ {(channelList || []).length > 0 && channelList.some((thread: any) => thread?.id) && (
-

Continue Conversations

+

Continue Conversations

{ displayedChannels.map((channel: any, index: number) => ( @@ -382,20 +381,31 @@ const ChatbotDrawer = ({ {(teamsList || []).length > 0 && (
-

Talk to our experts

+

Talk to our experts

{displayedTeams.map((team: any, index: number) => (
handleChangeTeam(team?.id)} >
-
- {team?.icon || } +
+
+ {team?.name?.charAt(0)?.toUpperCase() || (team?.icon || )} +
+ {team?.widget_unread_count > 0 && ( + + {team?.widget_unread_count} + + )}
+
{team?.name}
@@ -430,7 +440,7 @@ const ChatbotDrawer = ({ {/* Voice Call Section */} {voice_call_widget && (
-

Talk to Our Teams

+

Talk to Our Teams

-
+

{Name ? `Hello ${Name.split(' ')[0]}` : 'Hello There!'}

@@ -601,30 +611,32 @@ const ChatbotDrawer = ({
{/* Content area with overflow handling - the scrollbar will appear at the edge */} -
+
{!isHelloUser ? DrawerList : TeamsList}
{/* Footer with branding - always stays at bottom */} -
-
- {isHelloUser && show_msg91 ? ( - <> - Powered by - - MSG91 - - - ) : !isHelloUser ? ( - <> - Powered by - - GTWY - - - ) : null} + {(isHelloUser && show_msg91) || !isHelloUser ? ( +
+
+ {isHelloUser && show_msg91 ? ( + <> + Powered by + + MSG91 + + + ) : ( + <> + Powered by + + GTWY + + + )} +
-
+ ) : null}
diff --git a/public/chat-widget-local.js b/public/chat-widget-local.js index 9ea03fb7..760758da 100644 --- a/public/chat-widget-local.js +++ b/public/chat-widget-local.js @@ -166,13 +166,14 @@ const imgElement = document.createElement('div'); imgElement.id = this.elements.chatbotIconImage; imgElement.innerHTML = ` - - - - - - - +
+ +
`; chatBotIcon.appendChild(imgElement); diff --git a/public/chat-widget-style.css b/public/chat-widget-style.css index 0961c549..b71d6498 100644 --- a/public/chat-widget-style.css +++ b/public/chat-widget-style.css @@ -14,7 +14,6 @@ /* background-color: #3d7bef !important; */ text-align: center; align-content: center; - color: white; font-size: 18px; /* cursor: pointer; */ z-index: 99999 !important; @@ -80,8 +79,8 @@ [id$="-hello-chatbot-icon-image"] { background-color: none !important; object-fit: contain; - height: 60px !important; - width: 60px !important; + height: 48px !important; + width: 48px !important; margin: 8px 0px 0px 2px !important; box-sizing: border-box !important; float: right; @@ -97,7 +96,7 @@ /* background-color: transparent; */ object-fit: contain; /* cursor: pointer; */ - z-index: 9999 !important; + z-index: 2147483003 !important; height: auto; width: auto; box-sizing: border-box !important; @@ -107,6 +106,11 @@ right: 18px !important; } +#chatbot-logo { + display: inherit; + place-items: inherit; +} + /* Starter Question Styles */ .hello-starter-question { z-index: 999999; @@ -279,8 +283,8 @@ } .chatbot-icon-interfaceEmbed { - width: 60px !important; - height: 60px !important; + width: 48px !important; + height: 48px !important; cursor: pointer; object-fit: contain; } diff --git a/utils/themeUtility.js b/utils/themeUtility.js index bee75cdb..741b8806 100644 --- a/utils/themeUtility.js +++ b/utils/themeUtility.js @@ -15,4 +15,22 @@ export function isColorLight(color) { // Return true if the color is light, otherwise false return brightness > 128; +} + +export function getPrimaryGradientBg(primaryColor) { + const canvas = document.createElement("canvas"); + canvas.width = 1; + canvas.height = 1; + const ctx = canvas.getContext("2d"); + ctx.fillStyle = primaryColor; + ctx.fillRect(0, 0, 1, 1); + const [r, g, b] = ctx.getImageData(0, 0, 1, 1).data; + + // Mix white with primary at 8%, 14% and 20% for a smooth 3-stop gradient + const light = (c, mix) => Math.round(255 + (c - 255) * mix); + const r1 = light(r, 0.08), g1 = light(g, 0.08), b1 = light(b, 0.08); + const r2 = light(r, 0.14), g2 = light(g, 0.14), b2 = light(b, 0.14); + const r3 = light(r, 0.20), g3 = light(g, 0.20), b3 = light(b, 0.20); + + return `linear-gradient(150deg, rgb(${r1}, ${g1}, ${b1}) 0%, rgb(${r2}, ${g2}, ${b2}) 50%, rgb(${r3}, ${g3}, ${b3}) 100%)`; } \ No newline at end of file diff --git a/utils/utilities.js b/utils/utilities.js index 60c2c04f..4f9664c1 100644 --- a/utils/utilities.js +++ b/utils/utilities.js @@ -46,8 +46,10 @@ export const generateNewId = (length = 8) => { }; export const generateChannelId = (companyId = '') => { - const uuid = uuidv4().replace(/-/g, ''); - return `ch-comp-${companyId}.${uuid}`; + // Backend regex: ^ch-comp-(\d+)\.([0-9a-f]{32})$ + const numericCompanyId = String(companyId).replace(/\D/g, ''); + const uuid = uuidv4().replace(/-/g, '').toLowerCase(); + return `ch-comp-${numericCompanyId}.${uuid}`; }; function getDomain() { From ee28e7eeb700a8b30a4b6b0cf3fda46d961b1ef6 Mon Sep 17 00:00:00 2001 From: Divyanshu Shrivastava Date: Wed, 24 Jun 2026 00:07:39 +0530 Subject: [PATCH 6/6] refactor(chat): improve channel ID generation logic - Standardize channel IDs to 32-hex format - Implement stricter validation for channel IDs - Ensure new chats trigger on voice call initiation - Sync companyId state via Redux management - Fix legacy ObjectId persistence issues --- .../Chatbot/hooks/useHelloIntegration.ts | 60 ++++++++++++++----- .../Chatbot/hooks/useReduxManagement.ts | 26 ++++++-- .../Interface-Chatbot/ChatbotDrawer.tsx | 36 +++++------ index.html | 7 +++ utils/utilities.js | 1 + 5 files changed, 88 insertions(+), 42 deletions(-) create mode 100644 index.html diff --git a/components/Chatbot/hooks/useHelloIntegration.ts b/components/Chatbot/hooks/useHelloIntegration.ts index 6f5dd6cc..98666111 100644 --- a/components/Chatbot/hooks/useHelloIntegration.ts +++ b/components/Chatbot/hooks/useHelloIntegration.ts @@ -61,13 +61,18 @@ export const useFetchHelloPreviousHistory = () => { const { setChatsLoading } = useChatActions(); const { setHelloMessages } = useHelloMessages(); - const { uuid, currentChannelId } = useReduxStateManagement({ + const { uuid, currentChannelId, companyId } = useReduxStateManagement({ chatSessionId, tabSessionId: useHelloContext().tabSessionId }); return useCallback((dynamicChannelId?: string) => { - const channelId = dynamicChannelId || currentChannelId; + // Backend regex: ^ch-comp-(\d+)\.([0-9a-f]{32})$ — must be 32 hex chars. + // Some code paths persist a 24-char ObjectId (k_clientId/a_clientId), so + // regenerate whenever the stored id does not match. + const storedChannel = (dynamicChannelId || currentChannelId) || ''; + const isValid = /^[0-9a-f]{32}$/.test(String(storedChannel).split('.')[1] || ''); + const channelId = isValid ? storedChannel : generateChannelId(companyId); if (!channelId || !uuid) return; setChatsLoading(true); @@ -92,7 +97,7 @@ export const useFetchHelloPreviousHistory = () => { .finally(() => { setChatsLoading(false); }); - }, [currentChannelId, uuid, setChatsLoading, setHelloMessages, globalDispatch]); + }, [currentChannelId, uuid, companyId, setChatsLoading, setHelloMessages, globalDispatch]); }; export const useGetMoreHelloChats = () => { @@ -101,7 +106,7 @@ export const useGetMoreHelloChats = () => { const { setChatsLoading } = useChatActions(); const { addHelloMessage } = useHelloMessages(); - const { uuid, currentChannelId } = useReduxStateManagement({ + const { uuid, currentChannelId, companyId } = useReduxStateManagement({ chatSessionId, tabSessionId: useHelloContext().tabSessionId }); @@ -112,10 +117,14 @@ export const useGetMoreHelloChats = () => { })); return useCallback(() => { - if (!currentChannelId || !uuid || !hasMoreMessages) return; + // Backend regex: ^ch-comp-(\d+)\.([0-9a-f]{32})$ — must be 32 hex chars. + const storedChannel = currentChannelId || ''; + const isValid = /^[0-9a-f]{32}$/.test(String(storedChannel).split('.')[1] || ''); + const channelId = isValid ? storedChannel : generateChannelId(companyId); + if (!channelId || !uuid || !hasMoreMessages) return; setChatsLoading(true); - getHelloChatHistoryApi(currentChannelId, skip) + getHelloChatHistoryApi(channelId, skip) .then((response) => { const helloChats = response?.data?.data; if (Array.isArray(helloChats) && helloChats.length > 0) { @@ -136,7 +145,7 @@ export const useGetMoreHelloChats = () => { .finally(() => { setChatsLoading(false); }); - }, [currentChannelId, uuid, setChatsLoading, addHelloMessage, hasMoreMessages, skip, globalDispatch]); + }, [currentChannelId, uuid, companyId, setChatsLoading, addHelloMessage, hasMoreMessages, skip, globalDispatch]); }; export const useFetchChannels = () => { @@ -224,13 +233,20 @@ export const useOnSendHello = () => { try { - const channelIdToUse = newChannelId || currentChannelId || overrideChannelId; - const chatIdToUse = overrideChatId || currentChatId; - const teamIdToUse = overrideTeamId || currentTeamId; + const channelIdToUse = newChannelId !== undefined ? newChannelId : (currentChannelId || overrideChannelId); + const chatIdToUse = overrideChatId !== undefined ? overrideChatId : currentChatId; + const teamIdToUse = overrideTeamId !== undefined ? overrideTeamId : currentTeamId; - let workingChannelId = channelIdToUse; - if (!chatIdToUse && !channelIdToUse) { - workingChannelId = generateChannelId(companyId); + // Backend regex: ^ch-comp-(\d+)\.([0-9a-f]{32})$ — must be 32 hex chars. + // Some code paths persist a 24-char ObjectId (k_clientId/a_clientId), so + // regenerate whenever the chosen channel id does not match. + const isChannelHexValid = (id: string) => + /^[0-9a-f]{32}$/.test(String(id || '').split('.')[1] || ''); + + let workingChannelId = isChannelHexValid(channelIdToUse) + ? channelIdToUse + : generateChannelId(companyId); + if (!chatIdToUse && (!channelIdToUse || !isChannelHexValid(channelIdToUse))) { dispatch(setDataInAppInfoReducer({ subThreadId: workingChannelId })); @@ -303,10 +319,24 @@ export const useOnSendHello = () => { } const data = await sendMessageToHelloApi(message, attachments, channelDetail, chatIdToUse, helloVariables, voiceCall, demo_widget, widget_msg_id, repliedOn); if (data && (!chatIdToUse || !channelIdToUse || demo_widget)) { + // Prefer the locally-generated 32-char channel id (matches backend regex + // ^ch-comp-(\d+)\.([0-9a-f]{32})$) over the backend's echoed channel, + // which may contain a 24-char ObjectId suffix and would be rejected by + // /get-history/. Fall back to the backend's echo only if it already has + // a 32-char hex suffix. + const backendChannel = data?.['channel']; + const channelToPersist = (() => { + if (workingChannelId) return workingChannelId; + if (backendChannel) { + const suffix = String(backendChannel).split('.')[1]; + if (suffix && /^[0-9a-f]{32}$/.test(suffix)) return backendChannel; + } + return generateChannelId(companyId); + })(); dispatch(setDataInAppInfoReducer({ - subThreadId: data?.['channel'], + subThreadId: channelToPersist, currentChatId: data?.['id'], - currentChannelId: data?.['channel'], + currentChannelId: channelToPersist, overrideChannelId: "" })); // no need to append user message again this time diff --git a/components/Chatbot/hooks/useReduxManagement.ts b/components/Chatbot/hooks/useReduxManagement.ts index b3d9981a..f2e4b114 100644 --- a/components/Chatbot/hooks/useReduxManagement.ts +++ b/components/Chatbot/hooks/useReduxManagement.ts @@ -33,7 +33,8 @@ export const useReduxStateManagement = ({ currentChannelId, currentTeamId, isDefaultNavigateToChatScreen, - overrideChannelId + overrideChannelId, + companyId } = useCustomSelector((state) => { const channels = state.Hello?.[chatSessionId]?.channelListData?.channels || []; const fallbackChat = (() => { @@ -44,6 +45,13 @@ export const useReduxStateManagement = ({ return openChats[0] || channels[0]; })(); + // Treat "" / null / undefined as a deliberate "start fresh" signal, + // not as a fallback trigger. Only fall back when explicitly cleared. + const isExplicitlyEmpty = (v: any) => v === '' || v === null || v === undefined; + const appInfoChannelId = state?.appInfo?.[tabSessionId]?.currentChannelId; + const appInfoChatId = state?.appInfo?.[tabSessionId]?.currentChatId; + const appInfoTeamId = state?.appInfo?.[tabSessionId]?.currentTeamId; + return { interfaceContextData: state.Interface?.[chatSessionId]?.interfaceContext?.variables, isHelloUser: state.draftData?.isHelloUser || false, @@ -58,10 +66,17 @@ export const useReduxStateManagement = ({ selectedAiServiceAndModal: state.Interface?.[chatSessionId]?.selectedAiServiceAndModal || null, unique_id_hello: state?.Hello?.[chatSessionId]?.helloConfig?.unique_id, widgetToken: state?.Hello?.[chatSessionId]?.helloConfig?.widgetToken, - currentChatId: state?.appInfo?.[tabSessionId]?.currentChatId || fallbackChat?.id, - currentChannelId: state?.appInfo?.[tabSessionId]?.currentChannelId || fallbackChat?.channel, - currentTeamId: state?.appInfo?.[tabSessionId]?.currentTeamId || fallbackChat?.team_id, + currentChannelId: isExplicitlyEmpty(appInfoChannelId) + ? '' + : (appInfoChannelId ?? fallbackChat?.channel ?? ''), + currentChatId: isExplicitlyEmpty(appInfoChatId) + ? '' + : (appInfoChatId ?? fallbackChat?.id ?? ''), + currentTeamId: isExplicitlyEmpty(appInfoTeamId) + ? '' + : (appInfoTeamId ?? fallbackChat?.team_id ?? ''), overrideChannelId: state?.appInfo?.[tabSessionId]?.overrideChannelId, + companyId: state.Hello?.[chatSessionId]?.widgetInfo?.company_id || '', }; }); @@ -82,6 +97,7 @@ export const useReduxStateManagement = ({ currentChatId, currentChannelId, currentTeamId, - overrideChannelId + overrideChannelId, + companyId }; }; \ No newline at end of file diff --git a/components/Interface-Chatbot/ChatbotDrawer.tsx b/components/Interface-Chatbot/ChatbotDrawer.tsx index 78fb9705..f9ade415 100644 --- a/components/Interface-Chatbot/ChatbotDrawer.tsx +++ b/components/Interface-Chatbot/ChatbotDrawer.tsx @@ -194,38 +194,30 @@ const ChatbotDrawer = ({ }; const handleVoiceCall = async () => { - // If no channel is selected, pick the most recent (first valid) channel just for this action - let overrideChannelId; - let overrideChatId; + // Voice call should always start a FRESH chat — never reuse an existing + // chat_id. We only use the team selection (if any) to route the call. let overrideTeamId; - if (!currentChannelId && Array.isArray(channelList) && channelList.length > 0 && channelList?.[0]?.id) { - const firstValid = channelList.find((ch: any) => ch?.id); - if (firstValid) { - overrideChannelId = firstValid?.channel; - overrideChatId = firstValid?.id; - dispatch( - setDataInAppInfoReducer({ - subThreadId: firstValid?.channel, - currentChannelId: firstValid?.channel, - currentChatId: firstValid?.id, - currentTeamId: firstValid?.team_id, - }) - ); - } - } else if (teamsList?.length > 0) { - const firstValid = teamsList[0] + if (Array.isArray(teamsList) && teamsList.length > 0) { + const firstValid = teamsList[0]; if (firstValid) { + overrideTeamId = firstValid?.id; dispatch( setDataInAppInfoReducer({ currentTeamId: firstValid?.id, }) ); - overrideTeamId = firstValid?.id; } } if (isSmallScreen) setToggleDrawer(false); - // pass overrides so sendMessageToHello uses latest values in the same tick - const data = await sendMessageToHello('', '', true, overrideChannelId || currentChannelId, overrideChatId || currentChatId, overrideTeamId || currentTeamId); + // Force a fresh chat by clearing chat_id / channel_id before sending. + dispatch(setDataInAppInfoReducer({ + subThreadId: '', + currentChannelId: '', + currentChatId: '', + overrideChannelId: '', + })); + // Pass empty chatId/channelId overrides so sendMessageToHello creates a new chat. + const data = await sendMessageToHello('', '', true, '', '', overrideTeamId || currentTeamId); helloVoiceService.initiateCall(data?.['call_jwt_token'] || ''); }; diff --git a/index.html b/index.html new file mode 100644 index 00000000..18d10658 --- /dev/null +++ b/index.html @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/utils/utilities.js b/utils/utilities.js index 4f9664c1..f3263b4d 100644 --- a/utils/utilities.js +++ b/utils/utilities.js @@ -49,6 +49,7 @@ export const generateChannelId = (companyId = '') => { // Backend regex: ^ch-comp-(\d+)\.([0-9a-f]{32})$ const numericCompanyId = String(companyId).replace(/\D/g, ''); const uuid = uuidv4().replace(/-/g, '').toLowerCase(); + console.log("Hero: ", uuid, `ch-comp-${numericCompanyId}.${uuid}`); return `ch-comp-${numericCompanyId}.${uuid}`; };