174 lines
7.7 KiB
TypeScript
174 lines
7.7 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { Upload, CheckCircle2, Circle } from 'lucide-react';
|
|
import TrimModal from './TrimModal';
|
|
|
|
const LANGUAGES = [
|
|
{ code: 'ar', name: 'Arabic', group: 'A' },
|
|
{ code: 'zh', name: 'Chinese', group: 'C' },
|
|
{ code: 'yue', name: 'Cantonese', group: 'C' },
|
|
{ code: 'cs', name: 'Czech', group: 'C' },
|
|
{ code: 'zh-TW', name: 'Traditional Chinese', group: 'T' },
|
|
{ code: 'th', name: 'Thai', group: 'T' },
|
|
{ code: 'tr', name: 'Turkey', group: 'T' },
|
|
{ code: 'nl', name: 'Dutch', group: 'D' },
|
|
{ code: 'en', name: 'English', group: 'E' },
|
|
{ code: 'ru', name: 'Russian', group: 'R' },
|
|
{ code: 'ro', name: 'Romanian', group: 'R' },
|
|
{ code: 'ja', name: 'Japanese', group: 'J' },
|
|
{ code: 'ko', name: 'Korean', group: 'K' },
|
|
{ code: 'ms', name: 'Malay', group: 'M' },
|
|
{ code: 'fr', name: 'French', group: 'F' },
|
|
];
|
|
|
|
export default function UploadScreen({ onUpload }: { onUpload: (file: File, lang: string, startTime?: number, endTime?: number) => void }) {
|
|
const [mode, setMode] = useState<'editing' | 'simple'>('editing');
|
|
const [selectedLang, setSelectedLang] = useState('en');
|
|
const [showTrimModal, setShowTrimModal] = useState(false);
|
|
const [tempFile, setTempFile] = useState<File | null>(null);
|
|
|
|
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
if (e.target.files && e.target.files[0]) {
|
|
setTempFile(e.target.files[0]);
|
|
setShowTrimModal(true);
|
|
}
|
|
};
|
|
|
|
const handleTrimConfirm = (file: File, startTime: number, endTime: number) => {
|
|
setShowTrimModal(false);
|
|
const langName = LANGUAGES.find(l => l.code === selectedLang)?.name || 'English';
|
|
onUpload(file, langName, startTime, endTime);
|
|
};
|
|
|
|
return (
|
|
<div className="max-w-6xl mx-auto p-8 flex gap-8 h-screen items-center">
|
|
{/* Left: Upload Area */}
|
|
<div className="flex-1 bg-white rounded-lg shadow-sm border border-gray-200 p-8 flex flex-col items-center justify-center min-h-[400px]">
|
|
<div className="w-full h-full border-2 border-dashed border-gray-300 rounded-lg flex flex-col items-center justify-center bg-gray-50 relative">
|
|
<input
|
|
type="file"
|
|
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
|
|
accept="video/mp4,video/quicktime,video/webm"
|
|
onChange={handleFileChange}
|
|
/>
|
|
<Upload className="w-16 h-16 text-gray-400 mb-4" />
|
|
<p className="text-gray-600 mb-6">Click to upload or drag files here</p>
|
|
<button className="bg-[#52c41a] hover:bg-[#46a616] text-white px-8 py-3 rounded-md font-medium flex items-center gap-2 transition-colors w-full max-w-md justify-center pointer-events-none">
|
|
<Upload className="w-5 h-5" />
|
|
Upload Video
|
|
</button>
|
|
</div>
|
|
<p className="text-sm text-gray-500 mt-4 w-full text-left">
|
|
Supported formats: MP4/MOV/WEBM. Maximum file size is 500MB.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Right: Settings */}
|
|
<div className="w-[400px] flex flex-col gap-6">
|
|
{/* Mode Selection */}
|
|
<div className="flex gap-4">
|
|
<div
|
|
className={`flex-1 p-4 rounded-lg border-2 cursor-pointer transition-colors ${
|
|
mode === 'editing' ? 'border-blue-500 bg-blue-50' : 'border-gray-200 hover:border-blue-200'
|
|
}`}
|
|
onClick={() => setMode('editing')}
|
|
>
|
|
<div className="flex items-center gap-2 mb-1">
|
|
{mode === 'editing' ? (
|
|
<CheckCircle2 className="w-5 h-5 text-blue-500" />
|
|
) : (
|
|
<Circle className="w-5 h-5 text-gray-300" />
|
|
)}
|
|
<span className="font-semibold text-gray-800">Editing Mode</span>
|
|
</div>
|
|
<p className="text-xs text-gray-500 ml-7">Supports secondary editing and more precise translation</p>
|
|
</div>
|
|
<div
|
|
className={`flex-1 p-4 rounded-lg border-2 cursor-pointer transition-colors ${
|
|
mode === 'simple' ? 'border-blue-500 bg-blue-50' : 'border-gray-200 hover:border-blue-200'
|
|
}`}
|
|
onClick={() => setMode('simple')}
|
|
>
|
|
<div className="flex items-center gap-2 mb-1">
|
|
{mode === 'simple' ? (
|
|
<CheckCircle2 className="w-5 h-5 text-blue-500" />
|
|
) : (
|
|
<Circle className="w-5 h-5 text-gray-300" />
|
|
)}
|
|
<span className="font-semibold text-gray-800">Simple Mode</span>
|
|
</div>
|
|
<p className="text-xs text-gray-500 ml-7">One-click video translation for beginners</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Language Selection */}
|
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6 flex-1 flex flex-col">
|
|
<h3 className="font-semibold text-gray-800 mb-1">Select Translation Language</h3>
|
|
<p className="text-xs text-gray-500 mb-4">AI detect video source language automatically.</p>
|
|
|
|
{/* Alphabet Tabs */}
|
|
<div className="flex gap-4 border-b border-gray-100 pb-2 mb-4 text-sm text-gray-500 overflow-x-auto">
|
|
<button className="font-medium text-blue-600 border-b-2 border-blue-600 pb-2 -mb-[9px]">Popular</button>
|
|
<button className="hover:text-gray-800">ABC</button>
|
|
<button className="hover:text-gray-800">DEF</button>
|
|
<button className="hover:text-gray-800">GHI</button>
|
|
<button className="hover:text-gray-800">JKL</button>
|
|
<button className="hover:text-gray-800">MN</button>
|
|
<button className="hover:text-gray-800">OPQ</button>
|
|
<button className="hover:text-gray-800">RST</button>
|
|
<button className="hover:text-gray-800">UVW</button>
|
|
<button className="hover:text-gray-800">XYZ</button>
|
|
</div>
|
|
|
|
{/* Language List */}
|
|
<div className="flex-1 overflow-y-auto pr-2 custom-scrollbar">
|
|
{['A', 'C', 'T', 'D', 'E', 'R', 'J', 'K', 'M', 'F'].map((letter) => (
|
|
<div key={letter} className="flex border-b border-gray-100 py-3 last:border-0">
|
|
<div className="w-8 text-green-600 font-medium">{letter}</div>
|
|
<div className="flex flex-wrap gap-x-6 gap-y-2 flex-1">
|
|
{LANGUAGES.filter((l) => l.group === letter).map((lang) => (
|
|
<button
|
|
key={lang.code}
|
|
className={`text-sm hover:text-blue-600 transition-colors ${
|
|
selectedLang === lang.code
|
|
? 'bg-green-600 text-white px-2 py-0.5 rounded'
|
|
: lang.code === 'zh' || lang.code === 'yue' || lang.code === 'ja' || lang.code === 'ko'
|
|
? 'text-orange-500'
|
|
: 'text-gray-700'
|
|
}`}
|
|
onClick={() => setSelectedLang(lang.code)}
|
|
>
|
|
{lang.name}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<button
|
|
className={`w-full py-3 rounded-md font-medium mt-4 transition-colors ${
|
|
tempFile
|
|
? 'bg-[#52c41a] hover:bg-[#46a616] text-white'
|
|
: 'bg-gray-200 text-gray-400 cursor-not-allowed'
|
|
}`}
|
|
onClick={() => {
|
|
if (tempFile) setShowTrimModal(true);
|
|
}}
|
|
disabled={!tempFile}
|
|
>
|
|
Generate Translated Video
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{showTrimModal && tempFile && (
|
|
<TrimModal
|
|
file={tempFile}
|
|
onClose={() => setShowTrimModal(false)}
|
|
onConfirm={handleTrimConfirm}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|