# Call Transcripts UI Plan

**Date:** January 30, 2026  
**Priority:** HIGH (User visibility gap)  
**Effort:** 2-3 hours  
**Author:** Architect Agent

---

## Problem Statement

Call transcripts are captured and stored but **not viewable** in the UI after the call is logged. Users can only play audio recordings - there's no text view of what was said.

**User Impact:**
- Sales reps can't review previous call content before follow-ups
- Managers can't audit calls by reading (faster than listening)
- Context is lost when multiple reps handle the same lead

---

## Solution: Transcript Viewer

### Scope (0.1% Approach)
Focus on the simplest valuable addition:
1. ✅ Add expandable transcript section to call history items
2. ✅ Display dossier call summaries in a new card
3. ❌ NOT building dedicated call history page (overkill for 3-person team)
4. ❌ NOT building transcript search (future enhancement)

---

## Implementation Plan

### Step 1: Expand Call History Item
**File:** `admin/src/pages/LeadProfile.jsx`

**Current state (lines 784-820):**
```jsx
<p className="text-slate-600 line-clamp-2">
  {comm.metadata?.ai_summary?.summary || comm.content || (isCall ? comm.metadata?.outcome : '')}
</p>
{/* Recording Playback Button */}
{hasRecording && (
  <button onClick={() => handlePlayRecording(comm.id)}>...</button>
)}
```

**New state:**
```jsx
{/* Existing summary */}
<p className="text-slate-600 line-clamp-2">
  {comm.metadata?.ai_summary?.summary || comm.content || (isCall ? comm.metadata?.outcome : '')}
</p>

{/* NEW: Transcript toggle for calls */}
{isCall && comm.metadata?.transcript && (
  <TranscriptViewer
    transcript={comm.metadata.transcript}
    aiSummary={comm.metadata.ai_summary}
    recordingId={hasRecording ? comm.id : null}
    onPlayRecording={handlePlayRecording}
  />
)}
```

### Step 2: Create TranscriptViewer Component
**New file:** `admin/src/components/TranscriptViewer.jsx`

```jsx
import { useState } from 'react';
import { ChevronDown, ChevronUp, FileText, Play, Pause, Copy, Check } from 'lucide-react';

export default function TranscriptViewer({ 
  transcript, 
  aiSummary, 
  recordingId, 
  onPlayRecording 
}) {
  const [expanded, setExpanded] = useState(false);
  const [copied, setCopied] = useState(false);

  const copyTranscript = () => {
    navigator.clipboard.writeText(transcript);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };

  return (
    <div className="mt-2">
      {/* Toggle Button */}
      <button
        onClick={() => setExpanded(!expanded)}
        className="flex items-center gap-1.5 text-xs text-blue-600 hover:text-blue-800"
      >
        <FileText className="w-3.5 h-3.5" />
        {expanded ? 'Hide Transcript' : 'View Transcript'}
        {expanded ? <ChevronUp className="w-3 h-3" /> : <ChevronDown className="w-3 h-3" />}
      </button>

      {/* Expanded Content */}
      {expanded && (
        <div className="mt-2 p-3 bg-slate-50 rounded-lg border border-slate-200">
          {/* AI Summary Section */}
          {aiSummary && (
            <div className="mb-3 pb-3 border-b border-slate-200">
              <p className="text-xs font-semibold text-emerald-600 uppercase mb-1">AI Summary</p>
              <p className="text-sm text-slate-700">{aiSummary.summary}</p>
              
              {aiSummary.key_points?.length > 0 && (
                <ul className="mt-2 space-y-1">
                  {aiSummary.key_points.map((point, i) => (
                    <li key={i} className="text-xs text-slate-600 flex items-start gap-1">
                      <span className="text-emerald-500">•</span>
                      {point}
                    </li>
                  ))}
                </ul>
              )}
              
              {aiSummary.next_step && (
                <p className="mt-2 text-xs text-blue-600">
                  <strong>Next:</strong> {aiSummary.next_step}
                </p>
              )}
            </div>
          )}

          {/* Transcript Section */}
          <div>
            <div className="flex items-center justify-between mb-2">
              <p className="text-xs font-semibold text-slate-500 uppercase">Full Transcript</p>
              <button
                onClick={copyTranscript}
                className="flex items-center gap-1 text-xs text-slate-400 hover:text-slate-600"
              >
                {copied ? <Check className="w-3 h-3 text-green-500" /> : <Copy className="w-3 h-3" />}
                {copied ? 'Copied!' : 'Copy'}
              </button>
            </div>
            <div className="max-h-48 overflow-y-auto">
              <p className="text-sm text-slate-700 whitespace-pre-wrap">
                {transcript}
              </p>
            </div>
          </div>

          {/* Recording Playback */}
          {recordingId && (
            <button
              onClick={() => onPlayRecording(recordingId)}
              className="mt-3 flex items-center gap-2 px-3 py-1.5 bg-green-100 text-green-700 rounded-full text-xs font-medium hover:bg-green-200"
            >
              <Play className="w-3 h-3" />
              Play Recording
            </button>
          )}
        </div>
      )}
    </div>
  );
}
```

### Step 3: Add CallSummariesCard to Lead Profile
**New file:** `admin/src/components/CallSummariesCard.jsx`

Display the `lead_dossier.call_summaries` that are currently hidden:

```jsx
import { useState, useEffect } from 'react';
import { Phone, ChevronDown, ChevronUp, Calendar } from 'lucide-react';
import { dossierApi } from '../api/client';

export default function CallSummariesCard({ leadId }) {
  const [dossier, setDossier] = useState(null);
  const [expanded, setExpanded] = useState(false);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    if (leadId) {
      dossierApi.get(leadId)
        .then(setDossier)
        .catch(console.error)
        .finally(() => setLoading(false));
    }
  }, [leadId]);

  const callSummaries = dossier?.call_summaries || [];
  
  if (loading || callSummaries.length === 0) return null;

  return (
    <div className="bg-white rounded-2xl border border-slate-200 overflow-hidden">
      <button
        onClick={() => setExpanded(!expanded)}
        className="w-full flex items-center justify-between p-4"
      >
        <div className="flex items-center gap-3">
          <div className="p-2 bg-green-100 rounded-xl">
            <Phone className="w-5 h-5 text-green-600" />
          </div>
          <div className="text-left">
            <h3 className="font-bold text-slate-900">Call History</h3>
            <p className="text-xs text-green-600">{callSummaries.length} calls recorded</p>
          </div>
        </div>
        {expanded ? <ChevronUp className="w-5 h-5" /> : <ChevronDown className="w-5 h-5" />}
      </button>

      {expanded && (
        <div className="px-4 pb-4 space-y-3">
          {callSummaries.slice().reverse().map((call, i) => (
            <div key={i} className="p-3 bg-slate-50 rounded-xl">
              <div className="flex items-center justify-between mb-2">
                <span className={`px-2 py-0.5 text-xs font-semibold rounded-full ${
                  call.outcome === 'interested' ? 'bg-green-100 text-green-700' :
                  call.outcome === 'converted' ? 'bg-emerald-100 text-emerald-700' :
                  call.outcome === 'not_interested' ? 'bg-red-100 text-red-700' :
                  'bg-slate-100 text-slate-700'
                }`}>
                  {call.outcome}
                </span>
                <span className="text-xs text-slate-400">
                  {new Date(call.date).toLocaleDateString()}
                </span>
              </div>
              
              {call.key_points?.length > 0 && (
                <ul className="space-y-1">
                  {call.key_points.map((point, j) => (
                    <li key={j} className="text-sm text-slate-700">• {point}</li>
                  ))}
                </ul>
              )}
              
              {call.next_steps && (
                <p className="mt-2 text-xs text-blue-600">Next: {call.next_steps}</p>
              )}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}
```

### Step 4: Integrate into LeadProfile
**File:** `admin/src/pages/LeadProfile.jsx`

Add imports and place components:

```jsx
// Add import
import TranscriptViewer from '../components/TranscriptViewer';
import CallSummariesCard from '../components/CallSummariesCard';

// In the main content area, after RecentIntelCard:
<CallSummariesCard leadId={id} />
```

---

## File Changes Summary

| File | Action | Description |
|------|--------|-------------|
| `admin/src/components/TranscriptViewer.jsx` | CREATE | Expandable transcript display |
| `admin/src/components/CallSummariesCard.jsx` | CREATE | Dossier call summaries display |
| `admin/src/pages/LeadProfile.jsx` | MODIFY | Import + integrate new components |

---

## Testing Checklist

- [ ] Make a call, log it with transcript
- [ ] View Lead Profile → History section
- [ ] Click "View Transcript" → expands with full text
- [ ] AI Summary section shows key points + next step
- [ ] "Copy" button works
- [ ] Play Recording button works (if recording exists)
- [ ] CallSummariesCard shows past call outcomes
- [ ] Dark mode works for new components
- [ ] Mobile responsive at 390px width

---

## Future Enhancements (Parked)

1. **Transcript Search** - Search across all calls for keywords
2. **Audio Sync** - Highlight transcript as recording plays
3. **Call Timeline** - Full dedicated call history page
4. **Export** - Download transcripts as PDF/TXT
5. **Coaching Overlay** - Manager notes on transcripts

---

## Approval

**Ready for Builder:** ✅ Yes

**Blockers:** None - all data already exists, just needs UI

**Dependencies:** None - uses existing APIs
