"use client"; import { ArrowRightLeftIcon, ChevronLeft, ChevronRight, ChevronsUpDown, CircleUserRoundIcon, CircleXIcon, FilePlus2Icon, Menu, MoreHorizontalIcon, X, } from "lucide-react"; import { useEffect, useRef, useState } from "react"; import { toast } from "sonner"; import type { PanelImperativeHandle } from "react-resizable-panels"; import { ConversationCloseDialog } from "@/components/conversation-actions/close-dialog"; import { ConversationTransferDialog } from "@/components/conversation-actions/transfer-dialog"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { ResizableHandle, ResizablePanel, ResizablePanelGroup, } from "@/components/ui/resizable"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Sheet, SheetContent } from "@/components/ui/sheet"; import { useI18n } from "@/i18n/provider"; import { agentConversationFilterOptions, agentConversationSelectors, type AgentConversationFilterKey, useAgentConversationsStore, } from "@/lib/stores/agent-conversations"; import { CreateTicketFromConversationDialog } from "../../tickets/_components/create-ticket-from-conversation-dialog"; import { ChatPanel } from "./chat-panel"; import { ConversationInfoPanel } from "./conversation-info-panel"; import { ConversationList } from "./conversation-list"; const workbenchIconButtonClassName = "size-8 text-muted-foreground hover:bg-muted hover:text-foreground"; function getCustomerOnlineClassName(online?: boolean) { return online ? "border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-500/30 dark:bg-emerald-500/15 dark:text-emerald-300" : "border-border bg-muted text-muted-foreground"; } function getCustomerOnlineDotClassName(online?: boolean) { return online ? "bg-emerald-500" : "bg-muted-foreground/70"; } export function ConversationWorkbench() { const t = useI18n(); const conversation = useAgentConversationsStore( agentConversationSelectors.selectedConversation, ); const conversationFilter = useAgentConversationsStore( (state) => state.conversationFilter, ); const setConversationFilter = useAgentConversationsStore( (state) => state.setConversationFilter, ); const loadConversations = useAgentConversationsStore( (state) => state.loadConversations, ); const loadMessages = useAgentConversationsStore( (state) => state.loadMessages, ); const [sidebarCollapsed, setSidebarCollapsed] = useState(false); const [infoPanelCollapsed, setInfoPanelCollapsed] = useState(false); const [mobileMenuOpen, setMobileMenuOpen] = useState(false); const [mobileCustomerSheetOpen, setMobileCustomerSheetOpen] = useState(false); const [transferOpen, setTransferOpen] = useState(false); const [closeOpen, setCloseOpen] = useState(false); const [createTicketOpen, setCreateTicketOpen] = useState(false); const sidebarPanelRef = useRef(null); const infoPanelRef = useRef(null); const filterContainerRef = useRef(null); const filterMeasureRef = useRef(null); const [showFilterDropdown, setShowFilterDropdown] = useState(false); useEffect(() => { const container = filterContainerRef.current; const measure = filterMeasureRef.current; if (!container || !measure) { return; } const updateFilterMode = () => { setShowFilterDropdown(measure.scrollWidth > container.clientWidth); }; updateFilterMode(); const observer = new ResizeObserver(() => { updateFilterMode(); }); observer.observe(container); observer.observe(measure); return () => { observer.disconnect(); }; }, []); const currentFilterOption = agentConversationFilterOptions.find((opt) => opt.value === conversationFilter) ?? agentConversationFilterOptions[0]; const getFilterLabel = (labelKey: string) => t(labelKey); useEffect(() => { void loadConversations().catch((error) => { toast.error(error instanceof Error ? error.message : t("conversation.loadListFailed")); }); }, [loadConversations, conversationFilter, t]); async function handleConversationChanged(conversationId: number) { await loadConversations(); await loadMessages(conversationId, { forceLoading: false, reset: false, }); } const handleSidebarToggle = () => { const panel = sidebarPanelRef.current; if (!panel) { setSidebarCollapsed((current) => !current); return; } if (panel.isCollapsed()) { panel.expand(); setSidebarCollapsed(false); return; } panel.collapse(); setSidebarCollapsed(true); }; const handleInfoPanelToggle = () => { const panel = infoPanelRef.current; if (!panel) { setInfoPanelCollapsed((current) => !current); return; } if (panel.isCollapsed()) { panel.expand(); setInfoPanelCollapsed(false); return; } panel.collapse(); setInfoPanelCollapsed(true); }; const renderConversationSidebar = (opts?: { onListAfterSelect?: () => void }) => (
{showFilterDropdown ? ( } > {currentFilterOption ? getFilterLabel(currentFilterOption.labelKey) : t("conversation.filterPlaceholder")} setConversationFilter(value as AgentConversationFilterKey) } > {agentConversationFilterOptions.map((opt) => ( {getFilterLabel(opt.labelKey)} ))} ) : ( setConversationFilter(value as AgentConversationFilterKey) } className="min-w-0 flex-1 gap-0" > {agentConversationFilterOptions.map((opt) => ( {getFilterLabel(opt.labelKey)} ))} )}
); const workspaceContent = (
{conversation ? ( <> {t("conversation.customerAvatar")}

{conversation.customerName || t("conversation.customerFallback", { id: conversation.customerId || conversation.id, })}

{conversation.customerOnline ? t("conversation.customerOnline") : t("conversation.customerOffline")}

{t("conversation.channelNumber", { id: conversation.channelId || "-" })} {conversation.customerId ? ( <> / {t("conversation.linkedCustomer")} ) : null}

) : (

{t("conversation.workbenchTitle")}

{t("conversation.openMenuSelectConversation")}

{t("conversation.selectConversationFromSidebar")}

)}
} > setCreateTicketOpen(true)} disabled={!conversation} > {t("conversation.createTicket")} setTransferOpen(true)} disabled={!conversation || conversation.status !== 3} > {t("conversation.transferConversation")} setCloseOpen(true)} disabled={!conversation || conversation.status === 4} > {t("conversation.closeConversation")}
); return (
{mobileMenuOpen && (
); }