/* eslint-disable react-refresh/only-export-components */ import { useState, useMemo } from 'react' import type { ColumnDef } from '@tanstack/react-table' import { Music } from 'lucide-react' import { useTranslation } from 'react-i18next' import { Button } from '@/components/ui/button' import { StatusBadge } from '@/components/status-badge' import { TASK_ACTIONS, TASK_STATUS } from '../../constants' import { taskActionMapper, taskStatusMapper, taskPlatformMapper, } from '../../lib/mappers' import type { TaskLog } from '../../types' import { AudioPreviewDialog, type AudioClip, } from '../dialogs/audio-preview-dialog' import { FailReasonDialog } from '../dialogs/fail-reason-dialog' import { createTimestampColumn, createDurationColumn, createChannelColumn, createProgressColumn, } from './column-helpers' function parseTaskData(data: unknown): unknown[] { if (Array.isArray(data)) return data if (typeof data === 'string') { try { const parsed = JSON.parse(data) return Array.isArray(parsed) ? parsed : [] } catch { return [] } } return [] } function AudioPreviewCell({ log }: { log: TaskLog }) { const { t } = useTranslation() const [open, setOpen] = useState(false) const clips = useMemo(() => { const data = parseTaskData(log.data) return data.filter( (c) => c && typeof c === 'object' && (c as Record).audio_url ) }, [log.data]) if (clips.length === 0) return null return ( <> ) } export function useTaskLogsColumns(isAdmin: boolean): ColumnDef[] { const { t } = useTranslation() const columns: ColumnDef[] = [ createTimestampColumn({ accessorKey: 'submit_time', title: t('Submit Time'), unit: 'seconds', }), createTimestampColumn({ accessorKey: 'finish_time', title: t('Finish Time'), unit: 'seconds', }), createDurationColumn({ submitTimeKey: 'submit_time', finishTimeKey: 'finish_time', unit: 'seconds', headerLabel: t('Duration'), }), ] // Channel (admin only) if (isAdmin) { columns.push(createChannelColumn({ headerLabel: t('Channel') })) } columns.push( // Platform { accessorKey: 'platform', header: t('Platform'), cell: ({ row }) => { const platform = row.getValue('platform') as string return ( ) }, meta: { label: t('Platform') }, }, // Type/Action { accessorKey: 'action', header: t('Type'), cell: ({ row }) => { const action = row.getValue('action') as string return ( ) }, meta: { label: t('Type') }, }, // Task ID { accessorKey: 'task_id', header: t('Task ID'), cell: ({ row }) => { const taskId = row.getValue('task_id') as string return ( ) }, meta: { label: t('Task ID'), mobileHidden: true }, }, // Status { accessorKey: 'status', header: t('Status'), cell: ({ row }) => { const status = row.getValue('status') as string return ( ) }, meta: { label: t('Status') }, }, createProgressColumn({ headerLabel: t('Progress') }), // Result/Fail Reason - Combined column { accessorKey: 'fail_reason', header: t('Details'), cell: function DetailsCell({ row }) { const log = row.original const failReason = row.getValue('fail_reason') as string const status = log.status const [dialogOpen, setDialogOpen] = useState(false) // Suno audio preview const isSunoSuccess = log.platform === 'suno' && status === TASK_STATUS.SUCCESS if (isSunoSuccess) { const data = parseTaskData(log.data) if ( data.some( (c) => c && typeof c === 'object' && (c as Record).audio_url ) ) { return } } // For video generation tasks that succeeded, fail_reason contains the result URL const isVideoTask = log.action === TASK_ACTIONS.GENERATE || log.action === TASK_ACTIONS.TEXT_GENERATE || log.action === TASK_ACTIONS.FIRST_TAIL_GENERATE || log.action === TASK_ACTIONS.REFERENCE_GENERATE || log.action === TASK_ACTIONS.REMIX_GENERATE const isSuccess = status === TASK_STATUS.SUCCESS const isUrl = failReason?.startsWith('http') // If success and is a URL, show as result link if (isSuccess && isVideoTask && isUrl) { const videoUrl = `/v1/videos/${log.task_id}/content` return ( {t('Click to preview video')} ) } // Otherwise, show fail reason (if any) using the existing dialog if (!failReason) { return - } return ( <> ) }, meta: { label: t('Details') }, size: 200, maxSize: 220, } ) return columns }