MY QUALITY - First iteration
This commit is contained in:
@@ -4,7 +4,8 @@ import { useState } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { signIn } from 'next-auth/react';
|
||||
import { ArrowLeft, Delete } from 'lucide-react';
|
||||
import { ArrowLeft, Delete, MapPin } from 'lucide-react';
|
||||
import { trpc } from '@/lib/trpc/client';
|
||||
|
||||
interface Operator {
|
||||
id: string;
|
||||
@@ -13,7 +14,8 @@ interface Operator {
|
||||
|
||||
type PickerState =
|
||||
| { step: 'list' }
|
||||
| { step: 'pin'; operator: Operator };
|
||||
| { step: 'pin'; operator: Operator }
|
||||
| { step: 'workstation'; operator: Operator };
|
||||
|
||||
const PIN_MIN = 4;
|
||||
const PIN_MAX = 6;
|
||||
@@ -50,15 +52,16 @@ function OperatorList({
|
||||
function PinPad({
|
||||
operator,
|
||||
onBack,
|
||||
onSuccess,
|
||||
t,
|
||||
tc,
|
||||
}: {
|
||||
operator: Operator;
|
||||
onBack: () => void;
|
||||
onSuccess: () => void;
|
||||
t: ReturnType<typeof useTranslations<'auth'>>;
|
||||
tc: ReturnType<typeof useTranslations<'common'>>;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const [digits, setDigits] = useState('');
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -88,8 +91,8 @@ function PinPad({
|
||||
setDigits('');
|
||||
setError(t('invalidPin'));
|
||||
} else {
|
||||
router.push('/');
|
||||
router.refresh();
|
||||
// Authenticated — next step is binding to a workstation (badge-in).
|
||||
onSuccess();
|
||||
}
|
||||
} catch {
|
||||
setDigits('');
|
||||
@@ -182,9 +185,68 @@ function PinPad({
|
||||
);
|
||||
}
|
||||
|
||||
function WorkstationStep({
|
||||
operator,
|
||||
ts,
|
||||
}: {
|
||||
operator: Operator;
|
||||
ts: ReturnType<typeof useTranslations<'session'>>;
|
||||
}) {
|
||||
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 (
|
||||
<div className="flex flex-col gap-6">
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground">{operator.email}</p>
|
||||
<h2 className="mt-1 text-xl font-bold tracking-tight">{ts('badgeInTitle')}</h2>
|
||||
<p className="mt-1 text-sm text-muted-foreground">{ts('badgeInSubtitle')}</p>
|
||||
</div>
|
||||
|
||||
{isLoading ? (
|
||||
<p className="text-sm text-muted-foreground">{ts('loadingStations')}</p>
|
||||
) : workstations.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground">{ts('noStations')}</p>
|
||||
) : (
|
||||
<div className="flex flex-col gap-3">
|
||||
{workstations.map((ws) => (
|
||||
<button
|
||||
key={ws.id}
|
||||
onClick={() => startSession.mutate({ workstationId: ws.id })}
|
||||
disabled={startSession.isPending}
|
||||
className="flex w-full items-center gap-3 rounded-xl border border-border bg-card px-6 py-5 text-left transition-colors hover:bg-accent active:scale-[0.98] disabled:opacity-50"
|
||||
>
|
||||
<MapPin className="h-5 w-5 shrink-0 text-primary" />
|
||||
<span>
|
||||
<span className="block text-base font-medium">
|
||||
{ws.code} — {ws.name}
|
||||
</span>
|
||||
<span className="block text-xs text-muted-foreground">{ws.area}</span>
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{startSession.isPending && (
|
||||
<p className="text-center text-sm text-muted-foreground">{ts('starting')}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function OperatorPicker({ operators }: { operators: Operator[] }) {
|
||||
const t = useTranslations('auth');
|
||||
const tc = useTranslations('common');
|
||||
const ts = useTranslations('session');
|
||||
const [state, setState] = useState<PickerState>({ step: 'list' });
|
||||
|
||||
if (state.step === 'pin') {
|
||||
@@ -192,12 +254,17 @@ export function OperatorPicker({ operators }: { operators: Operator[] }) {
|
||||
<PinPad
|
||||
operator={state.operator}
|
||||
onBack={() => setState({ step: 'list' })}
|
||||
onSuccess={() => setState({ step: 'workstation', operator: state.operator })}
|
||||
t={t}
|
||||
tc={tc}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (state.step === 'workstation') {
|
||||
return <WorkstationStep operator={state.operator} ts={ts} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<OperatorList
|
||||
operators={operators}
|
||||
|
||||
Reference in New Issue
Block a user