@@ -30,6 +30,10 @@ type ForumDraft = {
3030 defaultSubscribed : boolean ;
3131 mandatoryForNewAgents : boolean ;
3232} ;
33+ type DirectConversationDraft = {
34+ agentAId : string ;
35+ agentBId : string ;
36+ } ;
3337type LiveConversationSession = {
3438 id : string ;
3539 conversationId : string ;
@@ -65,6 +69,11 @@ const emptyForumDraft: ForumDraft = {
6569 mandatoryForNewAgents : false ,
6670} ;
6771
72+ const emptyDirectConversationDraft : DirectConversationDraft = {
73+ agentAId : "" ,
74+ agentBId : "" ,
75+ } ;
76+
6877function forumSlugFromName ( name : string ) {
6978 return name
7079 . toLowerCase ( )
@@ -690,31 +699,102 @@ function ForumSpecDetails({ spec }: { spec: ForumCreationSpec }) {
690699function DirectMessages ( {
691700 state,
692701 liveSessions,
702+ createConversationDraft,
693703 expandedIds,
704+ isCreateConversationOpen,
694705 readMessageIds,
695706 drafts,
707+ onCreateConversation,
708+ onCreateConversationDraft,
696709 onToggle,
710+ onToggleCreateConversation,
697711 onDraft,
698712 onReply,
699713 onStartLive,
700714 onStopLive,
701715} : {
702716 state : AgentCommsState ;
703717 liveSessions : LiveConversationSession [ ] ;
718+ createConversationDraft : DirectConversationDraft ;
704719 expandedIds : Set < string > ;
720+ isCreateConversationOpen : boolean ;
705721 readMessageIds : Record < string , string | undefined > ;
706722 drafts : Record < string , string > ;
723+ onCreateConversation : ( ) => void ;
724+ onCreateConversationDraft : ( draft : DirectConversationDraft ) => void ;
707725 onToggle : ( conversationId : string ) => void ;
726+ onToggleCreateConversation : ( ) => void ;
708727 onDraft : ( conversationId : string , value : string ) => void ;
709728 onReply : ( conversationId : string ) => void ;
710729 onStartLive : ( conversationId : string ) => void ;
711730 onStopLive : ( sessionId : string ) => void ;
712731} ) {
732+ const approvedAgents = state . agents . filter ( ( agent ) => agent . status === "approved" ) ;
713733 return (
714734 < div className = "view-stack" >
715735 < div className = "section-title" >
716- < h2 > Direct messages</ h2 >
736+ < div >
737+ < h2 > Direct messages</ h2 >
738+ < p className = "section-subtitle" > Create a pair conversation before starting live mode.</ p >
739+ </ div >
740+ < button className = "section-action" type = "button" onClick = { onToggleCreateConversation } >
741+ < Plus aria-hidden = "true" />
742+ Create pair
743+ </ button >
717744 </ div >
745+ { isCreateConversationOpen ? (
746+ < form
747+ className = "direct-create-panel"
748+ onSubmit = { ( event ) => {
749+ event . preventDefault ( ) ;
750+ onCreateConversation ( ) ;
751+ } }
752+ >
753+ < label >
754+ First agent
755+ < select
756+ onChange = { ( event ) =>
757+ onCreateConversationDraft ( { ...createConversationDraft , agentAId : event . target . value } )
758+ }
759+ value = { createConversationDraft . agentAId }
760+ >
761+ < option value = "" > Choose an approved agent</ option >
762+ { approvedAgents . map ( ( agent ) => (
763+ < option key = { agent . id } value = { agent . id } >
764+ { agent . handle }
765+ </ option >
766+ ) ) }
767+ </ select >
768+ </ label >
769+ < label >
770+ Second agent
771+ < select
772+ onChange = { ( event ) =>
773+ onCreateConversationDraft ( { ...createConversationDraft , agentBId : event . target . value } )
774+ }
775+ value = { createConversationDraft . agentBId }
776+ >
777+ < option value = "" > Choose an approved agent</ option >
778+ { approvedAgents . map ( ( agent ) => (
779+ < option key = { agent . id } value = { agent . id } disabled = { agent . id === createConversationDraft . agentAId } >
780+ { agent . handle }
781+ </ option >
782+ ) ) }
783+ </ select >
784+ </ label >
785+ < button
786+ type = "submit"
787+ disabled = {
788+ ! createConversationDraft . agentAId ||
789+ ! createConversationDraft . agentBId ||
790+ createConversationDraft . agentAId === createConversationDraft . agentBId
791+ }
792+ >
793+ < Plus aria-hidden = "true" />
794+ Create conversation
795+ </ button >
796+ </ form >
797+ ) : null }
718798 < div className = "conversation-list" >
719799 { state . directConversations . map ( ( item ) => {
720800 const messages = state . directMessages . filter ( ( message ) => message . conversationId === item . id ) ;
@@ -1224,6 +1304,8 @@ export function App() {
12241304 const [ selectedForumId , setSelectedForumId ] = useState < string | null > ( null ) ;
12251305 const [ isCreateForumOpen , setCreateForumOpen ] = useState ( false ) ;
12261306 const [ createForumDraft , setCreateForumDraft ] = useState < ForumDraft > ( emptyForumDraft ) ;
1307+ const [ isCreateConversationOpen , setCreateConversationOpen ] = useState ( false ) ;
1308+ const [ createConversationDraft , setCreateConversationDraft ] = useState < DirectConversationDraft > ( emptyDirectConversationDraft ) ;
12271309 const [ selectedProfileAgentId , setSelectedProfileAgentId ] = useState < string | null > ( null ) ;
12281310 const [ expandedThreadIds , setExpandedThreadIds ] = useState < Set < string > > ( ( ) => new Set ( ) ) ;
12291311 const [ threadDrafts , setThreadDrafts ] = useState < Record < string , string > > ( { } ) ;
@@ -1655,6 +1737,32 @@ export function App() {
16551737 }
16561738 } ;
16571739
1740+ const createDirectConversation = async ( ) => {
1741+ if ( ! createConversationDraft . agentAId || ! createConversationDraft . agentBId ) {
1742+ setActionStatus ( "Choose two approved agents." ) ;
1743+ return ;
1744+ }
1745+ if ( createConversationDraft . agentAId === createConversationDraft . agentBId ) {
1746+ setActionStatus ( "Choose two different agents." ) ;
1747+ return ;
1748+ }
1749+ try {
1750+ const payload = await operatorRequest ( "direct-conversations" , {
1751+ method : "POST" ,
1752+ body : JSON . stringify ( createConversationDraft ) ,
1753+ } ) ;
1754+ await refreshOperatorData ( ) ;
1755+ setCreateConversationDraft ( emptyDirectConversationDraft ) ;
1756+ setCreateConversationOpen ( false ) ;
1757+ if ( payload . conversation ?. id ) {
1758+ setExpandedConversationIds ( ( current ) => new Set ( [ ...current , payload . conversation . id ] ) ) ;
1759+ }
1760+ setActionStatus ( payload . existing ? "Direct conversation already exists." : "Direct conversation created." ) ;
1761+ } catch ( error ) {
1762+ setActionStatus ( error instanceof Error ? error . message : "Direct conversation creation failed." ) ;
1763+ }
1764+ } ;
1765+
16581766 const toggleSetValue = ( setter : Dispatch < SetStateAction < Set < string > > > , id : string ) => {
16591767 setter ( ( current ) => {
16601768 const next = new Set ( current ) ;
@@ -1879,16 +1987,21 @@ export function App() {
18791987 ) : null }
18801988 { view === "direct" ? (
18811989 < DirectMessages
1990+ createConversationDraft = { createConversationDraft }
18821991 drafts = { conversationDrafts }
18831992 expandedIds = { expandedConversationIds }
1993+ isCreateConversationOpen = { isCreateConversationOpen }
18841994 liveSessions = { liveSessions }
1995+ onCreateConversation = { createDirectConversation }
1996+ onCreateConversationDraft = { setCreateConversationDraft }
18851997 onDraft = { ( conversationId , value ) =>
18861998 setConversationDrafts ( ( current ) => ( { ...current , [ conversationId ] : value } ) )
18871999 }
18882000 onReply = { replyToConversation }
18892001 onStartLive = { startLiveConversation }
18902002 onStopLive = { stopLiveConversation }
18912003 onToggle = { toggleConversation }
2004+ onToggleCreateConversation = { ( ) => setCreateConversationOpen ( ( current ) => ! current ) }
18922005 readMessageIds = { readConversationMessageIds }
18932006 state = { state }
18942007 />
0 commit comments