diff options
| author | soryu <soryu@soryu.co> | 2026-01-15 00:05:20 +0000 |
|---|---|---|
| committer | soryu <soryu@soryu.co> | 2026-01-15 01:30:02 +0000 |
| commit | b8035a7bc86dfb40af66f80e0564a41b8c6f7ba8 (patch) | |
| tree | 59223bc7b3ec88c5ced42ed77f419e9fc4501941 /makima/frontend/src/lib | |
| parent | eae8e698e89d7e5c8dc5bcdb2dcef61f25295515 (diff) | |
| download | soryu-b8035a7bc86dfb40af66f80e0564a41b8c6f7ba8.tar.gz soryu-b8035a7bc86dfb40af66f80e0564a41b8c6f7ba8.zip | |
feat(listen): add transcript analysis UI panel
Add UI integration for the transcript analysis feature:
- Add TranscriptSaved WebSocket message type to notify client when transcript is saved
- Create TranscriptAnalysisPanel component to display analysis results
- Shows requirements grouped by category, decisions, action items with priorities
- Displays speaker statistics and suggested contract name/description
- Provides buttons to create new contract or add to existing contract
- Update Listen page to show analysis panel as modal overlay after recording stops
- Update useWebSocket hook to handle transcriptSaved message
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'makima/frontend/src/lib')
| -rw-r--r-- | makima/frontend/src/lib/listenApi.ts | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/makima/frontend/src/lib/listenApi.ts b/makima/frontend/src/lib/listenApi.ts new file mode 100644 index 0000000..187ebe0 --- /dev/null +++ b/makima/frontend/src/lib/listenApi.ts @@ -0,0 +1,168 @@ +import { API_BASE } from './api'; +import { supabase } from './supabase'; + +// ============================================================================= +// Authentication helper (same pattern as api.ts) +// ============================================================================= + +/** Storage key for API key */ +const API_KEY_STORAGE_KEY = "makima_api_key"; + +/** Get stored API key from localStorage */ +function getStoredApiKey(): string | null { + if (typeof window === "undefined") return null; + return localStorage.getItem(API_KEY_STORAGE_KEY); +} + +/** Get auth headers for API requests */ +async function getAuthHeaders(): Promise<HeadersInit> { + const headers: HeadersInit = { + "Content-Type": "application/json", + }; + + // Try Supabase session first + if (supabase) { + const { data: { session } } = await supabase.auth.getSession(); + if (session?.access_token) { + headers["Authorization"] = `Bearer ${session.access_token}`; + return headers; + } + } + + // Fall back to API key if available + const apiKey = getStoredApiKey(); + if (apiKey) { + headers["X-Makima-API-Key"] = apiKey; + } + + return headers; +} + +// ============================================================================= +// Transcript Analysis Types +// ============================================================================= + +export interface TranscriptAnalysisResult { + requirements: Array<{ + text: string; + speaker: string; + timestamp: number; + confidence: number; + category?: string; + }>; + decisions: Array<{ + text: string; + speaker: string; + timestamp: number; + confidence: number; + context?: string; + }>; + actionItems: Array<{ + text: string; + speaker: string; + timestamp: number; + assignee?: string; + priority?: string; + }>; + keyTopics: string[]; + suggestedContractName?: string; + suggestedDescription?: string; + speakerSummary: Array<{ + speaker: string; + wordCount: number; + speakingTimeSeconds: number; + contributionPercentage: number; + }>; +} + +export interface AnalyzeResponse { + fileId: string; + analysis: TranscriptAnalysisResult; +} + +export interface CreateContractResponse { + contractId: string; + contractName: string; + filesCreated: Array<{ id: string; name: string; fileType: string }>; + tasksCreated: Array<{ id: string; name: string }>; +} + +export interface CreateContractOptions { + name?: string; + description?: string; + includeRequirements?: boolean; + includeDecisions?: boolean; + includeActionItems?: boolean; +} + +// ============================================================================= +// Listen API Functions +// ============================================================================= + +/** + * Analyze a transcript file to extract requirements, decisions, and action items. + */ +export async function analyzeTranscript(fileId: string): Promise<AnalyzeResponse> { + const response = await fetch(`${API_BASE}/api/v1/listen/analyze`, { + method: 'POST', + headers: await getAuthHeaders(), + body: JSON.stringify({ fileId }), + }); + + if (!response.ok) { + const error = await response.json().catch(() => ({ message: 'Failed to analyze transcript' })); + throw new Error(error.message || 'Failed to analyze transcript'); + } + + return response.json(); +} + +/** + * Create a contract from a transcript analysis. + */ +export async function createContractFromTranscript( + fileId: string, + options?: CreateContractOptions +): Promise<CreateContractResponse> { + const response = await fetch(`${API_BASE}/api/v1/listen/create-contract`, { + method: 'POST', + headers: await getAuthHeaders(), + body: JSON.stringify({ + fileId, + ...options, + }), + }); + + if (!response.ok) { + const error = await response.json().catch(() => ({ message: 'Failed to create contract' })); + throw new Error(error.message || 'Failed to create contract'); + } + + return response.json(); +} + +/** + * Update an existing contract with transcript analysis. + */ +export async function updateContractFromTranscript( + fileId: string, + contractId: string, + options?: Omit<CreateContractOptions, 'name'> +): Promise<CreateContractResponse> { + const response = await fetch(`${API_BASE}/api/v1/listen/update-contract`, { + method: 'POST', + headers: await getAuthHeaders(), + body: JSON.stringify({ + fileId, + contractId, + ...options, + }), + }); + + if (!response.ok) { + const error = await response.json().catch(() => ({ message: 'Failed to update contract' })); + throw new Error(error.message || 'Failed to update contract'); + } + + return response.json(); +} |
