import { useState, useRef, useEffect } from 'react' import { useTranslation } from 'react-i18next' import { Card } from '@/shared/ui/card' import { Button } from '@/shared/ui/button' import { Input } from '@/shared/ui/input' import { Label } from '@/shared/ui/label' import { fetchJson, getCsrfHeaders } from '@/api/client' import { truncateErrorMessage } from '@/shared/lib/error-display' async function authorizeDevice(userCode: string): Promise { await fetchJson('/api/v1/device/authorize', { method: 'POST', headers: getCsrfHeaders({ 'Content-Type': 'application/json', }), body: JSON.stringify({ userCode }), }) } export function DeviceAuthPage() { const { t } = useTranslation() const [part1, setPart1] = useState('') const [part2, setPart2] = useState('') const [isSubmitting, setIsSubmitting] = useState(false) const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null) const input1Ref = useRef(null) const input2Ref = useRef(null) useEffect(() => { input1Ref.current?.focus() }, []) const handlePart1Change = (value: string) => { const cleaned = value.toUpperCase().replace(/[^A-Z0-9]/g, '').slice(0, 4) setPart1(cleaned) if (cleaned.length === 4) { input2Ref.current?.focus() } } const handlePart2Change = (value: string) => { const cleaned = value.toUpperCase().replace(/[^A-Z0-9]/g, '').slice(0, 4) setPart2(cleaned) } const handlePaste = (e: React.ClipboardEvent) => { e.preventDefault() const pasted = e.clipboardData.getData('text').toUpperCase().replace(/[^A-Z0-9]/g, '') if (pasted.length >= 8) { setPart1(pasted.slice(0, 4)) setPart2(pasted.slice(4, 8)) input2Ref.current?.focus() } else if (pasted.length > 0) { setPart1(pasted.slice(0, 4)) if (pasted.length > 4) { setPart2(pasted.slice(4)) } } } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() if (part1.length !== 4 || part2.length !== 4) { setMessage({ type: 'error', text: t('device.incompleteCode') }) return } const userCode = `${part1}-${part2}` setIsSubmitting(true) setMessage(null) try { await authorizeDevice(userCode) setMessage({ type: 'success', text: t('device.success') }) setPart1('') setPart2('') input1Ref.current?.focus() } catch (error) { setMessage({ type: 'error', text: truncateErrorMessage(error instanceof Error ? error.message : t('device.defaultError')) ?? t('device.defaultError'), }) } finally { setIsSubmitting(false) } } return (

{t('device.title')}

{t('device.subtitle')}

handlePart1Change(e.target.value)} onPaste={handlePaste} placeholder="XXXX" className="text-center text-2xl font-mono tracking-wider" maxLength={4} /> - handlePart2Change(e.target.value)} onPaste={handlePaste} placeholder="XXXX" className="text-center text-2xl font-mono tracking-wider" maxLength={4} />

{t('device.codeHint')}

{message && (
{message.text}
)}

{t('device.notice')}

) }