/*
* Copyright (C) 2025 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see .
*/
import {IconButton} from '@instructure/ui-buttons'
import {Flex} from '@instructure/ui-flex'
import {IconDownloadLine, IconRefreshLine, IconTrashLine} from '@instructure/ui-icons'
import {Text} from '@instructure/ui-text'
import {TruncateText} from '@instructure/ui-truncate-text'
import {View} from '@instructure/ui-view'
import {
type ClosedCaptionPanelTranslations,
defaultClosedCaptionPanelTranslations,
} from './translations'
/**
* Base props shared across all caption row states
*/
interface BaseCaptionRowProps {
captionName: string
deleteButtonRef?: (el: Element | null) => void
}
/**
* Props for a caption in processing state
*/
interface ProcessingCaptionRowProps extends BaseCaptionRowProps {
workflow_state: 'processing'
}
/**
* Props for a caption in failed state
*/
interface FailedCaptionRowProps extends BaseCaptionRowProps {
workflow_state: 'failed'
failedOperation?: 'upload' | 'delete' | 'asr'
asr?: boolean
onRetry?: () => void
onDelete?: () => void
}
/**
* Props for a caption in ready state
*/
interface UploadedCaptionRowProps extends BaseCaptionRowProps {
workflow_state: 'ready'
filename?: string
url?: string
onDelete?: () => void
isInherited?: boolean
}
/**
* Discriminated union of all caption row prop types
*/
export type CaptionRowProps = (
| ProcessingCaptionRowProps
| FailedCaptionRowProps
| UploadedCaptionRowProps
) & {translations?: ClosedCaptionPanelTranslations}
export function getStatusText(
workflow_state: CaptionRowProps['workflow_state'],
failedOperation?: 'upload' | 'delete' | 'asr',
asr?: boolean,
translations: ClosedCaptionPanelTranslations = defaultClosedCaptionPanelTranslations,
): string | undefined {
if (workflow_state === 'processing') {
return translations.processing()
}
if (workflow_state === 'failed') {
if (failedOperation === 'delete') return translations.deleteFailed()
if (failedOperation === 'asr' || asr) return translations.generationFailed()
return translations.uploadFailed()
}
return undefined
}
/**
* Displays a single caption row with status-specific UI
*/
export function CaptionRow(props: CaptionRowProps) {
const {workflow_state, captionName, translations = defaultClosedCaptionPanelTranslations} = props
const {failedOperation, asr} = props as FailedCaptionRowProps
const statusText = getStatusText(workflow_state, failedOperation, asr, translations)
const ariaLabel = statusText ? `${captionName}, ${statusText}` : captionName
return (
{/* Left side: Language and filename */}
{captionName}
{/* Right side: Status-specific content */}
{workflow_state === 'processing' && (
{statusText}
)}
{workflow_state === 'failed' && (
{statusText}
{props.onRetry && (
)}
{props.onDelete && (
)}
)}
{workflow_state === 'ready' && (
)}
{workflow_state === 'ready' && props.isInherited && (
{translations.inheritedCaptionsMessage()}
)}
)
}