summaryrefslogtreecommitdiff
path: root/makima/frontend/src/lib
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-01-15 00:05:20 +0000
committersoryu <soryu@soryu.co>2026-01-15 01:30:02 +0000
commitb8035a7bc86dfb40af66f80e0564a41b8c6f7ba8 (patch)
tree59223bc7b3ec88c5ced42ed77f419e9fc4501941 /makima/frontend/src/lib
parenteae8e698e89d7e5c8dc5bcdb2dcef61f25295515 (diff)
downloadsoryu-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.ts168
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();
+}