AuthriX / frontend-react /src /components /UploadSection.tsx
Deepfake Authenticator
feat: replace vanilla frontend with React + Vite UI
70348ce
import { useCallback, useEffect, useRef, useState } from 'react';
import FileUploadInput from './FileUploadInput';
import InitiateButton from './InitiateButton';
interface UploadSectionProps {
onAnalyze: (file: File) => void;
onBack: () => void;
}
function fmtBytes(b: number) {
if (b < 1024) return b + ' B';
if (b < 1048576) return (b / 1024).toFixed(1) + ' KB';
return (b / 1048576).toFixed(1) + ' MB';
}
export default function UploadSection({ onAnalyze, onBack }: UploadSectionProps) {
const [file, setFile] = useState<File | null>(null);
const [dragging, setDragging] = useState(false);
const [time, setTime] = useState('--:--:--');
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
const t = setInterval(() => {
setTime(new Date().toLocaleTimeString('en-US', { hour12: false }));
}, 1000);
return () => clearInterval(t);
}, []);
const applyFile = useCallback((f: File) => {
if (!f.type.startsWith('video/')) return;
setFile(f);
}, []);
const onDrop = useCallback((e: React.DragEvent) => {
e.preventDefault();
setDragging(false);
const f = e.dataTransfer.files[0];
if (f) applyFile(f);
}, [applyFile]);
return (
<section className="relative z-10 flex flex-col items-center justify-center pt-28 pb-24 px-6 min-h-screen">
<div className="w-full max-w-3xl flex flex-col gap-8">
{/* Header */}
<div className="text-center space-y-2">
<h1
className="text-3xl font-semibold"
style={{
background: 'linear-gradient(135deg, #c084fc, #a855f7)',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
filter: 'drop-shadow(0 0 12px rgba(168,85,247,0.5))',
}}
>
SECURE INGEST PORTAL
</h1>
<p className="text-sm font-medium tracking-wider text-purple-300/50">
Awaiting encrypted payload via protocol AX-9.
</p>
</div>
{/* Drop zone outer */}
<div
className="relative backdrop-blur-2xl rounded-xl overflow-hidden transition-colors duration-500"
style={{
background: 'rgba(30,15,50,0.4)',
border: `1px solid ${dragging ? 'rgba(168,85,247,0.6)' : 'rgba(168,85,247,0.15)'}`,
boxShadow: 'inset 0 1px 1px rgba(255,255,255,0.04), 0 0 30px rgba(0,0,0,0.5)',
}}
>
<div className="scan-line" style={{ background: 'linear-gradient(to bottom, transparent 0%, rgba(168,85,247,0.12) 50%, transparent 100%)' }} />
{/* Drop zone inner */}
<div
onClick={() => {/* clicks handled by FileUploadInput */}}
onDragOver={e => { e.preventDefault(); setDragging(true); }}
onDragLeave={() => setDragging(false)}
onDrop={onDrop}
className="p-10 flex flex-col items-center justify-center border-2 border-dashed
m-4 rounded-lg transition-all duration-300 cursor-pointer min-h-[300px]"
style={{
borderColor: dragging ? 'rgba(168,85,247,0.7)' : 'rgba(88,28,135,0.4)',
background: dragging ? 'rgba(168,85,247,0.06)' : 'rgba(20,10,35,0.3)',
boxShadow: dragging ? '0 0 40px rgba(168,85,247,0.12) inset' : 'none',
}}
onMouseEnter={e => {
if (!dragging) {
(e.currentTarget as HTMLDivElement).style.borderColor = 'rgba(168,85,247,0.5)';
(e.currentTarget as HTMLDivElement).style.background = 'rgba(168,85,247,0.04)';
}
}}
onMouseLeave={e => {
if (!dragging) {
(e.currentTarget as HTMLDivElement).style.borderColor = 'rgba(88,28,135,0.4)';
(e.currentTarget as HTMLDivElement).style.background = 'rgba(20,10,35,0.3)';
}
}}
>
{!file ? (
<div className="flex flex-col items-center gap-6">
{/* Animated folder upload widget */}
<FileUploadInput onFile={applyFile} />
<h3 className="text-2xl font-semibold text-white text-center">
INITIALIZE UPLOAD
</h3>
<p className="text-base text-purple-300/50 text-center max-w-sm -mt-3">
Drag and drop your video file here, or use the button above.
</p>
<div className="flex items-center gap-2 font-bold text-[10px] text-purple-500/50 tracking-widest justify-center">
<span className="w-1.5 h-1.5 rounded-full bg-purple-500/50" />
ACCEPTED: MP4 · AVI · MOV · MKV · WebM
<span className="w-1.5 h-1.5 rounded-full bg-purple-500/50" />
</div>
</div>
) : (
<div className="w-full flex items-center gap-5">
<div
className="w-14 h-14 rounded-lg flex items-center justify-center flex-shrink-0"
style={{
background: 'rgba(124,58,237,0.15)',
border: '1px solid rgba(168,85,247,0.3)',
boxShadow: '0 0 20px rgba(124,58,237,0.15)',
}}
>
<span
className="material-symbols-outlined text-[28px]"
style={{ color: '#a855f7', fontVariationSettings: "'FILL' 1" }}
>
video_file
</span>
</div>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium tracking-wider text-white overflow-hidden text-ellipsis whitespace-nowrap">
{file.name}
</p>
<p className="font-bold text-[10px] text-purple-300/50 mt-1">{fmtBytes(file.size)}</p>
</div>
<button
onClick={e => { e.stopPropagation(); setFile(null); }}
className="p-2 rounded-sm transition-all text-purple-400/50 hover:text-red-400 hover:bg-red-400/10"
>
<span className="material-symbols-outlined text-[20px]">close</span>
</button>
</div>
)}
</div>
</div>
{/* Hidden input — used only by drag-and-drop path */}
<input
ref={inputRef}
type="file"
accept="video/*"
className="hidden"
onChange={e => e.target.files?.[0] && applyFile(e.target.files[0])}
/>
{/* Initiate analysis button */}
<InitiateButton onClick={() => file && onAnalyze(file)} disabled={!file} />
{/* Active queue */}
<div className="flex flex-col gap-4">
<div
className="flex items-center justify-between pb-2"
style={{ borderBottom: '1px solid rgba(168,85,247,0.12)' }}
>
<h4 className="font-bold text-[10px] uppercase tracking-widest flex items-center gap-2"
style={{ color: '#a855f7' }}>
<span
className="w-2 h-2"
style={{ background: '#a855f7', boxShadow: '0 0 8px rgba(168,85,247,0.8)' }}
/>
ACTIVE QUEUE
</h4>
<span className="font-mono text-[10px] text-purple-400/40">SYS_TIME: {time}</span>
</div>
<div className="grid grid-cols-1 gap-2">
{file ? (
<div
className="rounded p-4 flex items-center gap-3"
style={{
background: 'rgba(20,10,35,0.6)',
border: '1px solid rgba(124,58,237,0.25)',
}}
>
<span className="material-symbols-outlined text-[20px]" style={{ color: '#a855f7' }}>video_file</span>
<span className="text-sm font-medium tracking-wider text-white truncate">{file.name}</span>
<span
className="ml-auto font-bold text-[10px] uppercase tracking-widest"
style={{ color: '#a855f7' }}
>
QUEUED
</span>
</div>
) : (
<div
className="rounded p-4 flex items-center gap-3 opacity-40"
style={{
background: 'rgba(20,10,35,0.4)',
border: '1px solid rgba(88,28,135,0.2)',
}}
>
<span className="material-symbols-outlined text-[20px] text-purple-500/50">inbox</span>
<span className="text-sm font-medium tracking-wider text-purple-300/50">
No payload queued. Awaiting upload.
</span>
</div>
)}
</div>
</div>
{/* Back */}
<div className="text-center">
<button
onClick={onBack}
className="text-sm font-medium tracking-wider transition-colors flex items-center gap-2 mx-auto text-purple-400/40 hover:text-purple-300"
>
<span className="material-symbols-outlined text-[16px]">arrow_back</span>
Back to Command Center
</button>
</div>
</div>
</section>
);
}