diff --git a/src/app/api/download-metadata-csv/route.ts b/src/app/api/download-metadata-csv/route.ts new file mode 100644 index 0000000..2c28d29 --- /dev/null +++ b/src/app/api/download-metadata-csv/route.ts @@ -0,0 +1,53 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { downloadFilteredEntries } from '@/app/actions/download-filtered-entries'; +import { requireAdmin } from '@/lib/auth'; + +interface FilterParams { + speakerId?: string; + modelName?: string; + dialect?: string; + iteration?: number; + utteranceId?: string; +} + +export async function POST(request: NextRequest) { + try { + const resultAdmin = await requireAdmin(); + if (!resultAdmin.authenticated || !resultAdmin.admin) { + return NextResponse.json( + { error: 'Unauthorized' }, + { status: 401 } + ); + } + + const body = await request.json(); + const { datasetId, filters } = body; + + if (!datasetId) { + return NextResponse.json( + { error: 'datasetId is required' }, + { status: 400 } + ); + } + + // Get filtered entries data from server action + const result = await downloadFilteredEntries(datasetId, filters || {}); + + const fileName = `dataset-${datasetId}-metadata-${new Date().toISOString().split('T')[0]}.csv`; + + return new NextResponse(result.csvContent, { + status: 200, + headers: { + 'Content-Type': 'text/csv', + 'Content-Disposition': `attachment; filename="${fileName}"`, + 'Content-Length': Buffer.byteLength(result.csvContent).toString(), + }, + }); + } catch (error) { + console.error('Error downloading CSV:', error); + return NextResponse.json( + { error: 'Failed to download CSV file' }, + { status: 500 } + ); + } +} diff --git a/src/components/DatasetEntriesList.tsx b/src/components/DatasetEntriesList.tsx index e1a8dc7..fa33d7c 100644 --- a/src/components/DatasetEntriesList.tsx +++ b/src/components/DatasetEntriesList.tsx @@ -5,7 +5,7 @@ import Link from 'next/link'; import AudioPlayer from './AudioPlayer'; import { getDatasetEntries } from '@/app/actions/get-dataset-entries'; import { getFilterOptions } from '@/app/actions/get-filter-options'; -import { downloadFilteredEntriesAsZip } from '@/lib/download-utils'; +import { downloadFilteredEntriesAsZip, downloadFilteredEntriesAsCsv } from '@/lib/download-utils'; interface DatasetEntriesListProps { datasetId: number; @@ -45,6 +45,7 @@ export default function DatasetEntriesList({ const [entries, setEntries] = useState([]); const [loading, setLoading] = useState(false); const [downloading, setDownloading] = useState(false); + const [csvDownloading, setCsvDownloading] = useState(false); const [hasMore, setHasMore] = useState(true); const [page, setPage] = useState(1); const [total, setTotal] = useState(0); @@ -197,6 +198,26 @@ export default function DatasetEntriesList({ } } + async function handleDownloadFilteredCsv() { + setCsvDownloading(true); + try { + const filterParams = { + speakerId: filters.speakerId || undefined, + modelName: filters.modelName || undefined, + dialect: filters.dialect || undefined, + iteration: filters.iteration ? parseInt(filters.iteration, 10) : undefined, + utteranceId: filters.utteranceId || undefined, + }; + + await downloadFilteredEntriesAsCsv(datasetId, filterParams); + } catch (error) { + console.error('Error downloading CSV:', error); + alert('Failed to download CSV. Please try again.'); + } finally { + setCsvDownloading(false); + } + } + if (entries.length === 0 && !loading) { return (
@@ -207,6 +228,13 @@ export default function DatasetEntriesList({

Filters

+