ameerazam08's picture
Upload 4 files
68fa3e4 verified
Raw
History Blame Contribute Delete
7.05 kB
import React, { useRef, useState } from 'react';
import { ArrowRight, KeyRound, User, Languages } from 'lucide-react';
interface CodeInputProps {
onComplete: (code: string, name?: string, language?: string) => void;
isLoading?: boolean;
}
const LANGUAGES = [
{ label: 'No Dubbing (Original)', value: '' },
{ label: 'Spanish', value: 'Spanish' },
{ label: 'French', value: 'French' },
{ label: 'German', value: 'German' },
{ label: 'Hindi', value: 'Hindi' },
{ label: 'Japanese', value: 'Japanese' },
{ label: 'Chinese (Mandarin)', value: 'Chinese' },
{ label: 'Portuguese', value: 'Portuguese' },
];
export const CodeInput: React.FC<CodeInputProps> = ({ onComplete, isLoading }) => {
const [code, setCode] = useState<string[]>(new Array(6).fill(''));
const [name, setName] = useState('');
const [language, setLanguage] = useState('');
const inputsRef = useRef<(HTMLInputElement | null)[]>([]);
const handleChange = (index: number, value: string) => {
if (!/^\d*$/.test(value)) return;
const newCode = [...code];
// Take only the last character if user somehow pastes or types multiple
newCode[index] = value.slice(-1);
setCode(newCode);
if (value && index < 5) {
inputsRef.current[index + 1]?.focus();
}
};
const handleKeyDown = (index: number, e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Backspace' && !code[index] && index > 0) {
inputsRef.current[index - 1]?.focus();
}
};
const handlePaste = (e: React.ClipboardEvent) => {
e.preventDefault();
const pastedData = e.clipboardData.getData('text').replace(/\D/g, '').split('');
if (pastedData.length > 0) {
const newCode = [...code];
pastedData.slice(0, 6).forEach((digit, i) => {
newCode[i] = digit;
});
setCode(newCode);
const lastIndex = Math.min(pastedData.length - 1, 5);
inputsRef.current[lastIndex]?.focus();
}
};
const handleSubmit = (e?: React.FormEvent) => {
e?.preventDefault();
const codeStr = code.join('');
if (codeStr.length === 6) {
onComplete(codeStr, name || 'Guest', language);
}
};
return (
<form onSubmit={handleSubmit} className="w-full max-w-md flex flex-col items-center space-y-8 animate-fade-in">
<div className="text-center space-y-2">
<div className="bg-cyan-500/10 w-16 h-16 rounded-2xl flex items-center justify-center mx-auto mb-6 border border-cyan-500/20 shadow-[0_0_30px_-5px_rgba(6,182,212,0.3)]">
<KeyRound className="w-8 h-8 text-cyan-400" />
</div>
<h2 className="text-3xl font-bold text-white tracking-tight">Connect with Code</h2>
<p className="text-zinc-400 text-sm">Enter details to join the secure channel.</p>
</div>
<div className="w-full space-y-4">
<div className="w-full relative group">
<div className="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
<User className="h-5 w-5 text-zinc-500 group-focus-within:text-cyan-400 transition-colors" />
</div>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Your Name (Optional)"
className="w-full bg-zinc-900/50 border-2 border-zinc-800 text-white pl-11 pr-4 py-3.5 rounded-xl focus:outline-none focus:border-cyan-500/50 focus:bg-zinc-900/80 transition-all placeholder:text-zinc-600"
/>
</div>
<div className="w-full relative group">
<div className="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
<Languages className="h-5 w-5 text-zinc-500 group-focus-within:text-cyan-400 transition-colors" />
</div>
<select
value={language}
onChange={(e) => setLanguage(e.target.value)}
className="w-full bg-zinc-900/50 border-2 border-zinc-800 text-white pl-11 pr-4 py-3.5 rounded-xl focus:outline-none focus:border-cyan-500/50 focus:bg-zinc-900/80 transition-all appearance-none cursor-pointer"
>
{LANGUAGES.map(lang => (
<option key={lang.value} value={lang.value} className="bg-zinc-900 text-white">
{lang.label}
</option>
))}
</select>
<div className="absolute inset-y-0 right-0 pr-4 flex items-center pointer-events-none">
<svg className="w-4 h-4 text-zinc-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 9l-7 7-7-7" /></svg>
</div>
</div>
</div>
<div className="flex gap-2 sm:gap-3 justify-center" onPaste={handlePaste}>
{code.map((digit, idx) => (
<input
key={idx}
ref={el => inputsRef.current[idx] = el}
type="text"
inputMode="numeric"
maxLength={1}
autoFocus={idx === 0}
value={digit}
onChange={(e) => handleChange(idx, e.target.value)}
onKeyDown={(e) => handleKeyDown(idx, e)}
disabled={isLoading}
className={`w-10 h-12 sm:w-12 sm:h-14 text-center text-xl sm:text-2xl font-mono font-bold rounded-xl border-2 transition-all duration-200 outline-none
${digit
? 'border-cyan-500 bg-cyan-950/30 text-cyan-100 shadow-[0_0_15px_-3px_rgba(6,182,212,0.3)]'
: 'border-zinc-800 bg-zinc-900/50 text-zinc-500 focus:border-zinc-600 focus:bg-zinc-900'
}
disabled:opacity-50 disabled:cursor-not-allowed
`}
/>
))}
</div>
<button
type="submit"
disabled={code.some(d => d === '') || isLoading}
className="group relative flex items-center justify-center gap-2 px-8 py-3 w-full rounded-xl font-semibold text-white bg-gradient-to-r from-cyan-600 to-blue-600 hover:from-cyan-500 hover:to-blue-500 disabled:from-zinc-800 disabled:to-zinc-800 disabled:text-zinc-500 disabled:cursor-not-allowed transition-all duration-300 shadow-lg shadow-cyan-900/20"
>
{isLoading ? (
<span className="flex items-center gap-2">
<svg className="animate-spin h-5 w-5 text-white/80" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Connecting...
</span>
) : (
<>
Start Secure Call <ArrowRight className="w-4 h-4 group-hover:translate-x-1 transition-transform" />
</>
)}
</button>
</form>
);
};