feat(video): add video panel component with error handling

Implement video panel with placeholder prompt input and video display area
Add error states for invalid and undefined video cases
Update i18n strings for video related messages
This commit is contained in:
icarus 2025-10-11 17:22:14 +08:00
parent 45bdea5301
commit ab2aa8380f
4 changed files with 72 additions and 3 deletions

View File

@ -4646,7 +4646,14 @@
"title": "Update"
},
"video": {
"title": "Video"
"error": {
"invalid": "Invalid video"
},
"prompt": {
"placeholder": "describes the video to generate"
},
"title": "Video",
"undefined": "No available video"
},
"warning": {
"missing_provider": "The supplier does not exist; reverted to the default supplier {{provider}}. This may cause issues."

View File

@ -0,0 +1,22 @@
import { CircleXIcon } from 'lucide-react'
import { useTranslation } from 'react-i18next'
export interface VideoProps {
video: null | undefined
}
export const Video = ({ video }: VideoProps) => {
const { t } = useTranslation()
return (
<div className="flex h-full w-full items-center justify-center rounded-2xl bg-foreground-200">
{video && <video></video>}
{video === undefined && t('video.undefined')}
{video === null && (
<div className="flex h-full w-full flex-col items-center justify-center rounded-2xl bg-danger-100">
<CircleXIcon size={64} className="fill-danger text-danger-100" />
<span className="font-bold text-2xl">{t('video.error.invalid')}</span>
</div>
)}
</div>
)
}

View File

@ -7,6 +7,7 @@ import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ProviderSetting } from './settings/ProviderSetting'
import { VideoPanel } from './VideoPanel'
export const VideoPage = () => {
const { t } = useTranslation()
@ -21,9 +22,9 @@ export const VideoPage = () => {
<ProviderSetting providerId={providerId} setProviderId={setProviderId} />
</div>
<Divider orientation="vertical" />
<div className="flex-1 p-2"></div>
<VideoPanel />
<Divider orientation="vertical" />
<div className="w-25"></div>
<div className="w-40"></div>
</div>
</div>
)

View File

@ -0,0 +1,39 @@
import { Button, Skeleton, Textarea } from '@heroui/react'
import { ArrowUp } from 'lucide-react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Video } from './Video'
export const VideoPanel = () => {
const { t } = useTranslation()
const [prompt, setPrompt] = useState('')
// TODO: get video job from api
const video = { success: false, data: undefined }
return (
<div className="flex flex-1 flex-col p-2">
<div className="m-8 flex-1">
<Skeleton className="h-full w-full rounded-2xl" classNames={{ content: 'h-full w-full' }} isLoaded={true}>
<Video video={video.data} />
</Skeleton>
</div>
<div className="relative">
<Textarea
label={t('common.prompt')}
placeholder={t('video.prompt.placeholder')}
value={prompt}
onValueChange={setPrompt}
isClearable
classNames={{ inputWrapper: 'pb-8' }}
/>
<Button
color="primary"
radius="full"
startContent={<ArrowUp size={16} className="text-primary-foreground" />}
isIconOnly
className="absolute right-1 bottom-1 h-6 w-6 min-w-0"
/>
</div>
</div>
)
}