'use client'; import { useState } from 'react'; import { FoxFileUpload } from '@fox/core/components/fox-file-upload'; import { FoxHelperText } from '@fox/core/components/fox-helper-text'; import { FILE_MODULE, allowedExtensions, fileModuleAccept, isAllowedFileExtension, } from '@/lib/domain/file-module'; const UPLOAD_ENDPOINT = `/contents/upload?module=${FILE_MODULE.vodContent.id}`; interface ContentVideoFieldProps { defaultFileId?: string | null; } /** * 영상 콘텐츠 — 고르는 즉시 스트리밍 라우트로 올리고 파일 ID만 폼에 싣는다. * * 영상은 Server Action의 multipart 한도(10MB)를 넘기 때문에 폼 제출에 파일을 실을 수 없다. * 업로드가 끝나기 전에 저장하면 영상 없이 저장되므로 그동안은 `videoUploading`을 실어 보내 * Server Action이 막게 한다. */ export function ContentVideoField({ defaultFileId }: ContentVideoFieldProps) { const [fileId, setFileId] = useState(defaultFileId ?? ''); const [isUploading, setIsUploading] = useState(false); const [error, setError] = useState(''); async function upload(picked: File[]) { const file = picked[0]; if (!file) { return; } setError(''); if (!isAllowedFileExtension(FILE_MODULE.vodContent, file.name)) { setError( `${allowedExtensions(FILE_MODULE.vodContent).join(', ')} 파일만 올릴 수 있습니다.` ); return; } const body = new FormData(); body.set('file', file); setIsUploading(true); try { const response = await fetch(UPLOAD_ENDPOINT, { method: 'POST', body }); const payload = (await response.json()) as { id?: string; message?: string }; if (!response.ok || !payload.id) { setError(payload.message ?? '영상을 업로드하지 못했습니다.'); return; } setFileId(payload.id); } catch { setError('영상을 업로드하지 못했습니다.'); } finally { setIsUploading(false); } } return ( <> setFileId('')} /> {isUploading && } {isUploading && ( )} {error && } ); }