feat: overhaul session documentation, PSA notes, and client communications
- Reformat PSA resolution/escalation notes: clean single-line header, steps with engineer responses inline, remove duplicate timing blocks, remove AI confidence section, add follow-up recommendations - Standardize time display to decimal hours (e.g. 0.25 hrs) across all note formatters and status update context - Add follow_up_recommendations to SessionDocumentation schema and surface in SessionDocView; extracted from resolution suggestion steps - Add _build_what_we_know() helper: uses session.evidence_items when cockpit branch merges, falls back to deriving findings from steps - Fix option label lookup in generate_status_update (was passing raw machine values to AI instead of human-readable labels) - Add 'What We Know' section to status update ticket notes prompt - Improve _build_session_context in resolution_output_generator to include intake text and full step details instead of truncated chat - Add request_info audience type: client-facing information request that skips the length step and generates a numbered question list - Improve client_update and email_draft prompts with per-context guidance (status/resolution/escalation) and fix escalation subject line from 'Specialist Review' to 'Specialist Assistance' Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useState } from 'react'
|
||||
import { FileText, User, Mail, Zap, AlignLeft, Copy, Check, RotateCcw, ArrowLeftRight, Loader2 } from 'lucide-react'
|
||||
import { FileText, User, Mail, HelpCircle, Zap, AlignLeft, Copy, Check, RotateCcw, ArrowLeftRight, Loader2 } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { toast } from '@/lib/toast'
|
||||
import type { StatusUpdateAudience, StatusUpdateLength, StatusUpdateContext, StatusUpdateResponse } from '@/types/ai-session'
|
||||
@@ -12,10 +12,11 @@ interface StatusUpdateModalProps {
|
||||
hasPsaTicket?: boolean
|
||||
}
|
||||
|
||||
const AUDIENCES: { value: StatusUpdateAudience; icon: typeof FileText; label: string; description: string }[] = [
|
||||
const AUDIENCES: { value: StatusUpdateAudience; icon: typeof FileText; label: string; description: string; skipLength?: boolean }[] = [
|
||||
{ value: 'ticket_notes', icon: FileText, label: 'Ticket Notes', description: 'Technical, for your PSA' },
|
||||
{ value: 'client_update', icon: User, label: 'Client Update', description: 'Professional, non-technical' },
|
||||
{ value: 'email_draft', icon: Mail, label: 'Email Draft', description: 'Full email with subject line' },
|
||||
{ value: 'request_info', icon: HelpCircle, label: 'Request Information', description: 'Ask the client specific questions', skipLength: true },
|
||||
]
|
||||
|
||||
const LENGTHS: { value: StatusUpdateLength; icon: typeof Zap; label: string; description: string }[] = [
|
||||
@@ -38,9 +39,24 @@ export function StatusUpdateModal({ open, onClose, onGenerate, context = 'status
|
||||
escalation: 'Share Escalation',
|
||||
}
|
||||
|
||||
const handleAudienceSelect = (value: StatusUpdateAudience) => {
|
||||
const handleAudienceSelect = async (value: StatusUpdateAudience) => {
|
||||
setAudience(value)
|
||||
setStep('length')
|
||||
const opt = AUDIENCES.find(a => a.value === value)
|
||||
if (opt?.skipLength) {
|
||||
// Skip length selection — always concise for request_info
|
||||
setLength('quick')
|
||||
setStep('generating')
|
||||
try {
|
||||
const res = await onGenerate(value, 'quick', context)
|
||||
setResult(res)
|
||||
setStep('result')
|
||||
} catch {
|
||||
setStep('audience')
|
||||
setAudience(null)
|
||||
}
|
||||
} else {
|
||||
setStep('length')
|
||||
}
|
||||
}
|
||||
|
||||
const handleLengthSelect = async (value: StatusUpdateLength) => {
|
||||
@@ -170,7 +186,7 @@ export function StatusUpdateModal({ open, onClose, onGenerate, context = 'status
|
||||
<div className="flex flex-col items-center justify-center py-8 gap-3">
|
||||
<Loader2 size={24} className="animate-spin text-blue-400" />
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Generating {audience === 'email_draft' ? 'email draft' : audience === 'client_update' ? 'client update' : 'ticket notes'}...
|
||||
{audience === 'request_info' ? 'Drafting information request...' : audience === 'email_draft' ? 'Generating email draft...' : audience === 'client_update' ? 'Generating client update...' : 'Generating ticket notes...'}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user