diff --git a/frontend/src/components/assistant/ConcludeSessionModal.tsx b/frontend/src/components/assistant/ConcludeSessionModal.tsx index c32e71cd..3f7609a0 100644 --- a/frontend/src/components/assistant/ConcludeSessionModal.tsx +++ b/frontend/src/components/assistant/ConcludeSessionModal.tsx @@ -11,6 +11,9 @@ import { ClipboardList, Sparkles, AlertTriangle, + FileText, + User, + Mail, } from 'lucide-react' import { cn } from '@/lib/utils' import { MarkdownContent } from '@/components/ui/MarkdownContent' @@ -76,6 +79,7 @@ export function ConcludeSessionModal({ const [error, setError] = useState(null) const [streaming, setStreaming] = useState(false) const [streamError, setStreamError] = useState(null) + const [generatingUpdate, setGeneratingUpdate] = useState(false) const summaryRef = useRef('') // Reset state when modal opens @@ -90,6 +94,7 @@ export function ConcludeSessionModal({ setError(null) setStreaming(false) setStreamError(null) + setGeneratingUpdate(false) summaryRef.current = '' } }, [isOpen]) @@ -142,10 +147,9 @@ export function ConcludeSessionModal({ }) }, ) - } else if (outcome === 'escalated') { - setSummary('Session escalated. Ticket notes will be generated when the session is resolved.') } else { - setSummary('Session paused. Progress saved — you can resume anytime.') + // For paused/escalated: don't set summary yet — show status update options + setSummary('') } } catch { setError('Failed to conclude session. Please try again.') @@ -176,6 +180,25 @@ export function ConcludeSessionModal({ onClose() } + const handleGenerateStatusUpdate = async (audience: 'ticket_notes' | 'client_update' | 'email_draft') => { + if (!sessionId) return + setGeneratingUpdate(true) + try { + const context = outcome === 'escalated' ? 'escalation' : 'status' + const result = await aiSessionsApi.generateStatusUpdate(sessionId, { + audience, + length: 'detailed', + context, + }) + setSummary(result.content) + setCopied(false) + } catch { + setSummary('Failed to generate status update. You can copy the conversation from the chat.') + } finally { + setGeneratingUpdate(false) + } + } + if (!isOpen) return null const selectedOutcome = OUTCOMES.find(o => o.value === outcome) @@ -354,42 +377,115 @@ export function ConcludeSessionModal({ )} - {/* Generated ticket notes */} -
-
- - - Ticket Notes - - {streaming && ( - - )} -
+ {/* Resolved: streamed ticket notes */} + {outcome === 'resolved' && ( +
+
+ + + Ticket Notes + + {streaming && ( + + )} +
- {/* Streaming content or skeleton */} - {summary ? ( + {summary ? ( +
+ +
+ ) : streaming ? ( +
+
+
+
+
+
+
+
+ ) : streamError ? ( +
+ + {streamError} +
+ ) : null} +
+ )} + + {/* Paused/Escalated: status update options */} + {(outcome === 'paused' || outcome === 'escalated') && !summary && !generatingUpdate && ( +
+

+ {outcome === 'paused' + ? 'Session paused. Generate a status update to share progress.' + : 'Session escalated. Generate an update to document the handoff.'} +

+
+ + + +
+
+ )} + + {/* Paused/Escalated: generating spinner */} + {(outcome === 'paused' || outcome === 'escalated') && generatingUpdate && ( +
+ +

Generating status update...

+
+ )} + + {/* Paused/Escalated: generated result */} + {(outcome === 'paused' || outcome === 'escalated') && summary && !generatingUpdate && ( +
+
+ + + Status Update + +
- ) : streaming ? ( -
-
-
-
-
-
-
-
- ) : streamError ? ( -
- - {streamError} -
- ) : null} -
+
+ )}
)}
@@ -451,9 +547,17 @@ export function ConcludeSessionModal({ Resume in New Chat )} + {(outcome === 'paused' || outcome === 'escalated') && summary && !generatingUpdate && ( + + )}
- {summary && !streaming && ( + {summary && !streaming && !generatingUpdate && ( + <> + + + )} {!showTaskLane && (activeQuestions.length > 0 || activeActions.length > 0) && (
)