diff --git a/src/components/tts/CreateTTSDialog.tsx b/src/components/tts/CreateTTSDialog.tsx index cdd0891..07f43c8 100644 --- a/src/components/tts/CreateTTSDialog.tsx +++ b/src/components/tts/CreateTTSDialog.tsx @@ -22,6 +22,7 @@ import { Badge } from '@/components/ui/badge' import { Label } from '@/components/ui/label' import { Loader2, Mic } from 'lucide-react' import { ttsService, type TTSProvider } from '@/lib/api/services/tts' +import { TTS_EVENTS, ttsEvents } from '@/lib/events' import { toast } from 'sonner' interface FormData { @@ -88,8 +89,8 @@ export function CreateTTSDialog({ open, onOpenChange }: CreateTTSDialogProps) { onOpenChange(false) handleReset() - // Trigger refresh of parent list if needed - window.dispatchEvent(new CustomEvent('tts-generated')) + // Emit event for new TTS created + ttsEvents.emit(TTS_EVENTS.TTS_CREATED, response.tts) } catch (error: any) { toast.error(error.response?.data?.detail || 'Failed to generate TTS') } finally { diff --git a/src/components/tts/TTSTable.tsx b/src/components/tts/TTSTable.tsx index ed7b3e3..cf41ed3 100644 --- a/src/components/tts/TTSTable.tsx +++ b/src/components/tts/TTSTable.tsx @@ -1,4 +1,4 @@ -import { Calendar, CheckCircle, Loader, Mic, Trash2, Volume2 } from 'lucide-react' +import { AlertCircle, Calendar, CheckCircle, Clock, Loader, Mic, Trash2, Volume2, XCircle } from 'lucide-react' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' @@ -57,22 +57,36 @@ export function TTSTable({ ttsHistory, onTTSDeleted }: TTSTableProps) { } const getStatusBadge = (tts: TTSResponse) => { - const isCompleted = tts.sound_id !== null - - if (isCompleted) { - return ( - - - Complete - - ) - } else { - return ( - - - Processing - - ) + switch (tts.status) { + case 'completed': + return ( + + + Completed + + ) + case 'processing': + return ( + + + Processing + + ) + case 'failed': + return ( + + + Failed + + ) + case 'pending': + default: + return ( + + + Pending + + ) } } @@ -83,8 +97,8 @@ export function TTSTable({ ttsHistory, onTTSDeleted }: TTSTableProps) { Text Provider - Status Options + Status Created Actions @@ -103,9 +117,6 @@ export function TTSTable({ ttsHistory, onTTSDeleted }: TTSTableProps) { {tts.provider.toUpperCase()} - - {getStatusBadge(tts)} - {Object.keys(tts.options).length > 0 ? (
@@ -119,6 +130,19 @@ export function TTSTable({ ttsHistory, onTTSDeleted }: TTSTableProps) { None )} + +
+ {getStatusBadge(tts)} + {tts.error && ( +
+ {tts.error} +
+ )} +
+
diff --git a/src/contexts/SocketContext.tsx b/src/contexts/SocketContext.tsx index a906f95..a20ae59 100644 --- a/src/contexts/SocketContext.tsx +++ b/src/contexts/SocketContext.tsx @@ -12,11 +12,13 @@ import { EXTRACTION_EVENTS, PLAYER_EVENTS, SOUND_EVENTS, + TTS_EVENTS, USER_EVENTS, authEvents, extractionEvents, playerEvents, soundEvents, + ttsEvents, userEvents, } from '../lib/events' import { extractionsService } from '../lib/api/services/extractions' @@ -128,10 +130,10 @@ export function SocketProvider({ children }: SocketProviderProps) { // Listen for extraction status updates newSocket.on('extraction_status_update', data => { const { extraction_id, status, title, error } = data - + // Emit local event for other components to listen to extractionEvents.emit(EXTRACTION_EVENTS.EXTRACTION_STATUS_UPDATED, data) - + // Handle specific status events switch (status) { case 'processing': @@ -158,6 +160,30 @@ export function SocketProvider({ children }: SocketProviderProps) { } }) + // Listen for TTS status updates + newSocket.on('tts_completed', data => { + const { tts_id, sound_id } = data + + // Emit local event for other components to listen to + ttsEvents.emit(TTS_EVENTS.TTS_COMPLETED, data) + + toast.success('TTS generation completed', { + duration: 3000, + }) + }) + + newSocket.on('tts_failed', data => { + const { tts_id, error } = data + + // Emit local event for other components to listen to + ttsEvents.emit(TTS_EVENTS.TTS_FAILED, data) + + toast.error('TTS generation failed', { + description: error, + duration: 5000, + }) + }) + return newSocket }, [user, fetchAndShowOngoingExtractions]) diff --git a/src/lib/api/services/tts.ts b/src/lib/api/services/tts.ts index 4fcd8e2..66e7e85 100644 --- a/src/lib/api/services/tts.ts +++ b/src/lib/api/services/tts.ts @@ -11,6 +11,8 @@ export interface TTSResponse { text: string provider: string options: Record + status: string + error: string | null sound_id: number | null user_id: number created_at: string diff --git a/src/lib/events.ts b/src/lib/events.ts index 524de72..8aa8232 100644 --- a/src/lib/events.ts +++ b/src/lib/events.ts @@ -37,6 +37,7 @@ export const playerEvents = new EventEmitter() export const soundEvents = new EventEmitter() export const userEvents = new EventEmitter() export const extractionEvents = new EventEmitter() +export const ttsEvents = new EventEmitter() // Auth event types export const AUTH_EVENTS = { @@ -69,3 +70,11 @@ export const EXTRACTION_EVENTS = { EXTRACTION_COMPLETED: 'extraction_completed', EXTRACTION_FAILED: 'extraction_failed', } as const + +// TTS event types +export const TTS_EVENTS = { + TTS_STATUS_UPDATED: 'tts_status_updated', + TTS_CREATED: 'tts_created', + TTS_COMPLETED: 'tts_completed', + TTS_FAILED: 'tts_failed', +} as const diff --git a/src/pages/TTSPage.tsx b/src/pages/TTSPage.tsx index 6c795a6..7913c78 100644 --- a/src/pages/TTSPage.tsx +++ b/src/pages/TTSPage.tsx @@ -14,6 +14,7 @@ import { type TTSSortOrder, ttsService, } from '@/lib/api/services/tts' +import { TTS_EVENTS, ttsEvents } from '@/lib/events' import { useCallback, useEffect, useState } from 'react' import { toast } from 'sonner' @@ -82,15 +83,30 @@ export function TTSPage() { } }, [debouncedSearchQuery, sortBy, sortOrder, pageSize]) - // Listen for TTS generation events to refresh the list + // Listen for TTS events to refresh the list useEffect(() => { - const handleTTSGenerated = () => { + const handleTTSCompleted = () => { fetchTTSHistory() } - window.addEventListener('tts-generated', handleTTSGenerated) + const handleTTSFailed = () => { + fetchTTSHistory() + } + + const handleTTSCreated = () => { + fetchTTSHistory() + } + + // Subscribe to TTS events + ttsEvents.on(TTS_EVENTS.TTS_COMPLETED, handleTTSCompleted) + ttsEvents.on(TTS_EVENTS.TTS_FAILED, handleTTSFailed) + ttsEvents.on(TTS_EVENTS.TTS_CREATED, handleTTSCreated) + return () => { - window.removeEventListener('tts-generated', handleTTSGenerated) + // Cleanup event listeners + ttsEvents.off(TTS_EVENTS.TTS_COMPLETED, handleTTSCompleted) + ttsEvents.off(TTS_EVENTS.TTS_FAILED, handleTTSFailed) + ttsEvents.off(TTS_EVENTS.TTS_CREATED, handleTTSCreated) } }, [fetchTTSHistory])