fix(pwa): код двухфакторки уходит сам, а не ждёт кнопки
Второй шаг входа сотрудника не отправлялся вовсе: у поля с кодом не было ни обработки Enter, ни автоотправки, а «Готово» на клавиатуре телефона — это как раз Enter. Со стороны выглядело как «код не принят», хотя запрос на /auth/2fa не уходил ни разу (в логах бэкенда — только login с ответом «нужен второй шаг»). Теперь шесть цифр отправляются сразу, Enter тоже работает, поле получает фокус и autocomplete=one-time-code (подсказка кода из СМС/менеджера паролей). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -59,8 +59,9 @@ describe("DevLoginPage", () => {
|
||||
await userEvent.click(screen.getByRole("button", { name: "Войти" }));
|
||||
expect(useStaff.getState().token).toBeNull();
|
||||
|
||||
// Шестая цифра отправляет код сама: раньше форма ждала нажатия, а «Готово» на
|
||||
// клавиатуре телефона ничего не делало — выглядело как «код не принят».
|
||||
await userEvent.type(await screen.findByPlaceholderText("000000"), "123456");
|
||||
await userEvent.click(screen.getByRole("button", { name: "Подтвердить" }));
|
||||
expect(staffApi.staffLogin2fa).toHaveBeenCalledWith("partial", "123456");
|
||||
expect(useStaff.getState().token).toBe("staff-tok");
|
||||
});
|
||||
|
||||
@@ -42,11 +42,11 @@ export function DevLoginPage() {
|
||||
}
|
||||
}
|
||||
|
||||
async function submit2fa() {
|
||||
if (!partial || code.length < 6 || busy) return;
|
||||
async function submit2fa(value = code) {
|
||||
if (!partial || value.length < 6 || busy) return;
|
||||
setBusy(true);
|
||||
try {
|
||||
const r = await staffLogin2fa(partial, code);
|
||||
const r = await staffLogin2fa(partial, value);
|
||||
if (!r.access_token) throw new Error("no token");
|
||||
setStaff(r.access_token, username);
|
||||
setPartial(null);
|
||||
@@ -80,13 +80,23 @@ export function DevLoginPage() {
|
||||
<>
|
||||
<p className="text-sm mb-2">Код из приложения-аутентификатора.</p>
|
||||
<input
|
||||
autoFocus
|
||||
inputMode="numeric"
|
||||
autoComplete="one-time-code"
|
||||
value={code}
|
||||
onChange={(e) => setCode(e.target.value.replace(/\D/g, "").slice(0, 6))}
|
||||
onChange={(e) => {
|
||||
const next = e.target.value.replace(/\D/g, "").slice(0, 6);
|
||||
setCode(next);
|
||||
// Шесть цифр — код собран, ждать нажатия нечего: у TOTP всё равно
|
||||
// остаётся секунд тридцать. Раньше отправки не было ни по Enter, ни
|
||||
// по «Готово» на клавиатуре телефона, и это выглядело как «код не принят».
|
||||
if (next.length === 6) void submit2fa(next);
|
||||
}}
|
||||
onKeyDown={(e) => { if (e.key === "Enter") void submit2fa(); }}
|
||||
placeholder="000000"
|
||||
className="card w-full px-4 py-3 mb-3 text-base tracking-widest bg-white"
|
||||
/>
|
||||
<button className="btn-primary" disabled={busy || code.length < 6} onClick={submit2fa}>
|
||||
<button className="btn-primary" disabled={busy || code.length < 6} onClick={() => submit2fa()}>
|
||||
{busy ? <Spinner /> : "Подтвердить"}
|
||||
</button>
|
||||
<button className="btn-ghost mt-2" onClick={() => { setPartial(null); setCode(""); }}>
|
||||
|
||||
Reference in New Issue
Block a user