Files
sbd2-frontend/src/pages/TTSPage.tsx

201 lines
5.6 KiB
TypeScript

import { AppLayout } from '@/components/AppLayout'
import { AppPagination } from '@/components/AppPagination'
import { CreateTTSDialog } from '@/components/tts/CreateTTSDialog'
import { TTSHeader } from '@/components/tts/TTSHeader'
import {
TTSEmpty,
TTSError,
TTSLoading,
} from '@/components/tts/TTSLoadingStates'
import { TTSTable } from '@/components/tts/TTSTable'
import {
type TTSResponse,
type TTSSortField,
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'
export function TTSPage() {
const [ttsHistory, setTTSHistory] = useState<TTSResponse[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
// Search and sorting state
const [searchQuery, setSearchQuery] = useState('')
const [sortBy, setSortBy] = useState<TTSSortField>('created_at')
const [sortOrder, setSortOrder] = useState<TTSSortOrder>('desc')
// Pagination state
const [currentPage, setCurrentPage] = useState(1)
const [totalPages, setTotalPages] = useState(1)
const [totalCount, setTotalCount] = useState(0)
const [pageSize, setPageSize] = useState(10)
// Create TTS dialog state
const [showCreateDialog, setShowCreateDialog] = useState(false)
// Debounce search query
const [debouncedSearchQuery, setDebouncedSearchQuery] = useState(searchQuery)
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedSearchQuery(searchQuery)
}, 300)
return () => clearTimeout(handler)
}, [searchQuery])
const fetchTTSHistory = useCallback(async () => {
try {
setLoading(true)
setError(null)
const response = await ttsService.getTTSHistory({
search: debouncedSearchQuery.trim() || undefined,
sort_by: sortBy,
sort_order: sortOrder,
page: currentPage,
limit: pageSize,
})
setTTSHistory(response.tts)
setTotalPages(response.total_pages)
setTotalCount(response.total)
} catch (err) {
const errorMessage =
err instanceof Error ? err.message : 'Failed to fetch TTS history'
setError(errorMessage)
toast.error(errorMessage)
} finally {
setLoading(false)
}
}, [debouncedSearchQuery, sortBy, sortOrder, currentPage, pageSize])
useEffect(() => {
fetchTTSHistory()
}, [fetchTTSHistory])
// Reset to page 1 when filters change
useEffect(() => {
if (currentPage !== 1) {
setCurrentPage(1)
}
}, [debouncedSearchQuery, sortBy, sortOrder, pageSize])
// Listen for TTS events to refresh the list
useEffect(() => {
const handleTTSCompleted = () => {
fetchTTSHistory()
}
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 () => {
// 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])
const handlePageChange = (page: number) => {
setCurrentPage(page)
}
const handlePageSizeChange = (size: number) => {
setPageSize(size)
setCurrentPage(1) // Reset to first page when changing page size
}
const handleTTSDeleted = (ttsId: number) => {
// Remove the deleted TTS from the current list
setTTSHistory(prev => prev.filter(tts => tts.id !== ttsId))
// Update total count
setTotalCount(prev => prev - 1)
// If current page is now empty and not the first page, go to previous page
const remainingOnCurrentPage = ttsHistory.length - 1
if (remainingOnCurrentPage === 0 && currentPage > 1) {
setCurrentPage(currentPage - 1)
}
// Refresh the full list to ensure accuracy
fetchTTSHistory()
}
const renderContent = () => {
if (loading) {
return <TTSLoading />
}
if (error) {
return <TTSError error={error} onRetry={fetchTTSHistory} />
}
if (!ttsHistory || ttsHistory.length === 0) {
return <TTSEmpty searchQuery={searchQuery} />
}
return (
<div className="space-y-4">
<TTSTable
ttsHistory={ttsHistory}
onTTSDeleted={handleTTSDeleted}
/>
<AppPagination
currentPage={currentPage}
totalPages={totalPages}
totalCount={totalCount}
pageSize={pageSize}
onPageChange={handlePageChange}
onPageSizeChange={handlePageSizeChange}
itemName="TTS generations"
/>
</div>
)
}
return (
<AppLayout
breadcrumb={{
items: [{ label: 'Dashboard', href: '/' }, { label: 'Text to Speech' }],
}}
>
<div className="flex-1 rounded-xl bg-muted/50 p-4">
<TTSHeader
searchQuery={searchQuery}
onSearchChange={setSearchQuery}
sortBy={sortBy}
onSortByChange={setSortBy}
sortOrder={sortOrder}
onSortOrderChange={setSortOrder}
onRefresh={fetchTTSHistory}
onCreateClick={() => setShowCreateDialog(true)}
loading={loading}
error={error}
ttsCount={totalCount}
/>
<CreateTTSDialog
open={showCreateDialog}
onOpenChange={setShowCreateDialog}
/>
{renderContent()}
</div>
</AppLayout>
)
}