'use client'; import { useState } from 'react'; import { useRouter } from 'next/navigation'; import { useTranslations } from 'next-intl'; import { signIn } from 'next-auth/react'; import { ArrowLeft, Delete, MapPin } from 'lucide-react'; import { trpc } from '@/lib/trpc/client'; interface Operator { id: string; email: string; } type PickerState = | { step: 'list' } | { step: 'pin'; operator: Operator } | { step: 'workstation'; operator: Operator }; const PIN_MIN = 4; const PIN_MAX = 6; function OperatorList({ operators, onSelect, t, }: { operators: Operator[]; onSelect: (op: Operator) => void; t: ReturnType>; }) { if (operators.length === 0) { return (

{t('noOperators')}

); } return (
{operators.map((op) => ( ))}
); } function PinPad({ operator, onBack, onSuccess, t, tc, }: { operator: Operator; onBack: () => void; onSuccess: () => void; t: ReturnType>; tc: ReturnType>; }) { const [digits, setDigits] = useState(''); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); function press(d: string) { if (digits.length >= PIN_MAX) return; setDigits((prev) => prev + d); setError(null); } function erase() { setDigits((prev) => prev.slice(0, -1)); setError(null); } async function submit() { if (digits.length < PIN_MIN || busy) return; setBusy(true); setError(null); try { const result = await signIn('credentials', { email: operator.email, pin: digits, redirect: false, }); if (result?.error) { setDigits(''); setError(t('invalidPin')); } else { // Authenticated — next step is binding to a workstation (badge-in). onSuccess(); } } catch { setDigits(''); setError(t('unexpectedError')); } finally { setBusy(false); } } const keys = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '', '0', 'del']; return (
{/* Header */}

{t('operatorSelected')}

{operator.email}

{/* PIN dots */}
{Array.from({ length: PIN_MAX }).map((_, i) => (
))}
{/* Error */} {error && (

{error}

)} {/* Numpad */}
{keys.map((key, idx) => { if (key === '') { return
; } if (key === 'del') { return ( ); } return ( ); })}
{/* Submit */}
); } function WorkstationStep({ operator, ts, }: { operator: Operator; ts: ReturnType>; }) { const router = useRouter(); const { data: workstations = [], isLoading } = trpc.workstation.list.useQuery(undefined, { staleTime: 60 * 60 * 1000, }); const startSession = trpc.operatorSession.start.useMutation({ onSuccess: () => { router.push('/'); router.refresh(); }, }); return (

{operator.email}

{ts('badgeInTitle')}

{ts('badgeInSubtitle')}

{isLoading ? (

{ts('loadingStations')}

) : workstations.length === 0 ? (

{ts('noStations')}

) : (
{workstations.map((ws) => ( ))}
)} {startSession.isPending && (

{ts('starting')}

)}
); } export function OperatorPicker({ operators }: { operators: Operator[] }) { const t = useTranslations('auth'); const tc = useTranslations('common'); const ts = useTranslations('session'); const [state, setState] = useState({ step: 'list' }); if (state.step === 'pin') { return ( setState({ step: 'list' })} onSuccess={() => setState({ step: 'workstation', operator: state.operator })} t={t} tc={tc} /> ); } if (state.step === 'workstation') { return ; } return ( setState({ step: 'pin', operator: op })} t={t} /> ); }