import { memo } from 'react' import { useTranslation } from 'react-i18next' import type { Job } from '@/types/job' import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { Badge } from '@/components/ui/badge' import { Separator } from '@/components/ui/separator' import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from '@/components/ui/collapsible' import { ScrollArea } from '@/components/ui/scroll-area' import { AudioPlayer } from '@/components/AudioPlayer' import { ChevronDown, AlertCircle } from 'lucide-react' import { jobApi } from '@/lib/api' interface JobDetailDialogProps { job: Job | null open: boolean onOpenChange: (open: boolean) => void } const jobTypeBadgeVariant = { custom_voice: 'default' as const, voice_design: 'secondary' as const, voice_clone: 'outline' as const, } const formatTimestamp = (timestamp: string, locale: string) => { return new Date(timestamp).toLocaleString(locale, { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', }) } const JobDetailDialog = memo(({ job, open, onOpenChange }: JobDetailDialogProps) => { const { t, i18n } = useTranslation(['job', 'common']) if (!job) return null const jobTypeLabel = { custom_voice: t('job:typeCustomVoice'), voice_design: t('job:typeVoiceDesign'), voice_clone: t('job:typeVoiceClone'), } const getLanguageDisplay = (lang: string | undefined) => { if (!lang || lang === 'Auto') return t('job:autoDetect') return lang } const formatBooleanDisplay = (value: boolean | undefined) => { return value ? t('common:yes') : t('common:no') } const canPlay = job.status === 'completed' const audioUrl = canPlay ? jobApi.getAudioUrl(job.id, job.audio_url) : '' return (
{jobTypeLabel[job.type]} #{job.id} {formatTimestamp(job.created_at, i18n.language)}
{t('job:detailsDescription')}

{t('job:basicInfo')}

{job.type === 'custom_voice' && job.parameters?.speaker && (
{t('job:speaker')} {job.parameters.speaker}
)}
{t('job:language')} {getLanguageDisplay(job.parameters?.language)}
{job.type === 'voice_clone' && ( <>
{t('job:fastMode')} {formatBooleanDisplay(job.parameters?.x_vector_only_mode)}
{t('job:useCache')} {formatBooleanDisplay(job.parameters?.use_cache)}
)}

{t('job:synthesisText')}

{job.parameters?.text || {t('job:notSet')}}
{job.type === 'voice_design' && job.parameters?.instruct && ( <>

{t('job:voiceDescription')}

{job.parameters.instruct}
)} {job.type === 'custom_voice' && job.parameters?.instruct && ( <>

{t('job:emotionGuidance')}

{job.parameters.instruct}
)} {job.type === 'voice_clone' && ( <>

{t('job:referenceText')}

{job.parameters?.ref_text || {t('job:notProvided')}}
)} {t('job:advancedParameters')}
{job.parameters?.max_new_tokens !== undefined && (
{t('job:maxNewTokens')} {job.parameters.max_new_tokens}
)} {job.parameters?.temperature !== undefined && (
{t('job:temperature')} {job.parameters.temperature}
)} {job.parameters?.top_k !== undefined && (
{t('job:topK')} {job.parameters.top_k}
)} {job.parameters?.top_p !== undefined && (
{t('job:topP')} {job.parameters.top_p}
)} {job.parameters?.repetition_penalty !== undefined && (
{t('job:repetitionPenalty')} {job.parameters.repetition_penalty}
)}
{job.status === 'failed' && job.error_message && ( <>

{t('job:errorMessage')}

{job.error_message}

)} {canPlay && ( <>

{t('job:audioPlayback')}

)}
) }) JobDetailDialog.displayName = 'JobDetailDialog' export { JobDetailDialog }