fix and update
This commit is contained in:
@@ -23,6 +23,8 @@ import { useToast } from '@/components/id/toast-provider';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { PinInput } from '@/components/ui/pin-input';
|
||||
import { isPinInputComplete } from '@/lib/pin-input';
|
||||
|
||||
import { OtpInput } from '@/components/ui/otp-input';
|
||||
|
||||
@@ -1369,7 +1371,7 @@ export default function LoginPage() {
|
||||
|
||||
<form onSubmit={handlePinSubmit} className="space-y-3">
|
||||
|
||||
<Input
|
||||
<PinInput
|
||||
|
||||
className="h-[58px] border-[#555762] bg-transparent text-center text-lg tracking-[0.4em] text-white placeholder:text-[#8f92a0]"
|
||||
|
||||
@@ -1377,7 +1379,7 @@ export default function LoginPage() {
|
||||
|
||||
value={pin}
|
||||
|
||||
onChange={(event) => setPin(event.target.value)}
|
||||
onChange={setPin}
|
||||
|
||||
required
|
||||
|
||||
@@ -1387,7 +1389,7 @@ export default function LoginPage() {
|
||||
|
||||
/>
|
||||
|
||||
<Button variant="white" size="lg" className="w-full rounded-[18px] text-base" disabled={isSubmitting}>
|
||||
<Button variant="white" size="lg" className="w-full rounded-[18px] text-base" disabled={isSubmitting || !isPinInputComplete(pin)}>
|
||||
|
||||
{isSubmitting ? 'Проверяем...' : 'Подтвердить PIN'}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import { useAuth } from '@/components/id/auth-provider';
|
||||
import { useToast } from '@/components/id/toast-provider';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { PinInput } from '@/components/ui/pin-input';
|
||||
import { isPinInputComplete } from '@/lib/pin-input';
|
||||
import { OtpInput } from '@/components/ui/otp-input';
|
||||
import { PhoneInput, phoneCountries, toE164 } from '@/components/ui/phone-input';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
@@ -166,16 +168,16 @@ export default function RegisterPage() {
|
||||
{step === 'pin' && pendingSessionId ? (
|
||||
<form onSubmit={handlePinSubmit} className="mt-8 space-y-3">
|
||||
<p className="text-center text-sm text-[#b9bdc9]">Установите PIN для завершения регистрации</p>
|
||||
<Input
|
||||
<PinInput
|
||||
className="h-[58px] border-[#555762] bg-transparent text-center text-lg tracking-[0.4em] text-white placeholder:text-[#8f92a0]"
|
||||
placeholder="PIN"
|
||||
value={pin}
|
||||
onChange={(event) => setPin(event.target.value)}
|
||||
onChange={setPin}
|
||||
required
|
||||
minLength={4}
|
||||
maxLength={6}
|
||||
/>
|
||||
<Button variant="white" size="lg" className="w-full rounded-[18px] text-base" disabled={isSubmitting}>
|
||||
<Button variant="white" size="lg" className="w-full rounded-[18px] text-base" disabled={isSubmitting || !isPinInputComplete(pin)}>
|
||||
{isSubmitting ? 'Проверяем...' : 'Подтвердить PIN'}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
@@ -23,8 +23,11 @@ import { useToast } from '@/components/id/toast-provider';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { PinInput } from '@/components/ui/pin-input';
|
||||
import { isPinInputComplete } from '@/lib/pin-input';
|
||||
import { OtpInput } from '@/components/ui/otp-input';
|
||||
import { ActiveDevice, ActiveSession, apiFetch, AUTH_SESSION_KEY, getApiErrorMessage, SignInEvent, TotpSetupResponse, TotpStatusResponse } from '@/lib/api';
|
||||
import { markPinIdleWatch } from '@/lib/pin-idle-storage';
|
||||
import { formatUserAgentLabel } from '@/lib/device-client';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { QRCodeSVG } from 'qrcode.react';
|
||||
@@ -51,8 +54,8 @@ const deviceIcons: Record<string, LucideIcon> = {
|
||||
|
||||
type DialogMode = 'pin' | 'password' | 'backupEmail' | 'backupPhone' | 'totpSetup' | 'totpDisable' | null;
|
||||
|
||||
const dialogConfig: Record<Exclude<DialogMode, null | 'totpSetup' | 'totpDisable'>, { title: string; placeholder: string; type: string }> = {
|
||||
pin: { title: 'Установить PIN-код', placeholder: 'PIN из 4-6 цифр', type: 'password' },
|
||||
const dialogConfig: Record<Exclude<DialogMode, null | 'totpSetup' | 'totpDisable'>, { title: string; placeholder: string; type?: string }> = {
|
||||
pin: { title: 'Установить PIN-код', placeholder: 'PIN из 4-6 цифр' },
|
||||
password: { title: 'Установить пароль', placeholder: 'Новый пароль (мин. 8 символов)', type: 'password' },
|
||||
backupEmail: { title: 'Резервная почта', placeholder: 'backup@example.com', type: 'email' },
|
||||
backupPhone: { title: 'Резервный телефон', placeholder: '+79991234567', type: 'tel' }
|
||||
@@ -336,6 +339,7 @@ export default function SecurityPage() {
|
||||
try {
|
||||
if (dialogMode === 'pin') {
|
||||
await apiFetch(`/security/users/${user.id}/pin/setup`, { method: 'POST', body: JSON.stringify({ pin: dialogValue.trim() }) }, token);
|
||||
markPinIdleWatch(user.id);
|
||||
showToast('PIN-код установлен');
|
||||
} else if (dialogMode === 'password') {
|
||||
await apiFetch(`/profile/users/${user.id}/password`, { method: 'POST', body: JSON.stringify({ password: dialogValue }) }, token);
|
||||
@@ -694,15 +698,29 @@ export default function SecurityPage() {
|
||||
<>
|
||||
<div className="flex items-center gap-3 rounded-2xl bg-[#f4f5f8] px-4">
|
||||
<ShieldCheck className="h-5 w-5 text-[#667085]" />
|
||||
<Input
|
||||
className="border-0 bg-transparent focus:ring-0"
|
||||
type={dialogConfig[dialogMode].type}
|
||||
placeholder={dialogConfig[dialogMode].placeholder}
|
||||
value={dialogValue}
|
||||
onChange={(event) => setDialogValue(event.target.value)}
|
||||
/>
|
||||
{dialogMode === 'pin' ? (
|
||||
<PinInput
|
||||
className="border-0 bg-transparent focus:ring-0"
|
||||
placeholder={dialogConfig.pin.placeholder}
|
||||
value={dialogValue}
|
||||
onChange={setDialogValue}
|
||||
maxLength={6}
|
||||
/>
|
||||
) : (
|
||||
<Input
|
||||
className="border-0 bg-transparent focus:ring-0"
|
||||
type={dialogMode === 'password' ? 'password' : 'text'}
|
||||
placeholder={dialogConfig[dialogMode].placeholder}
|
||||
value={dialogValue}
|
||||
onChange={(event) => setDialogValue(event.target.value)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<Button className="mt-5 w-full" onClick={submitDialog} disabled={isDialogSaving || !dialogValue.trim()}>
|
||||
<Button
|
||||
className="mt-5 w-full"
|
||||
onClick={submitDialog}
|
||||
disabled={isDialogSaving || (dialogMode === 'pin' ? !isPinInputComplete(dialogValue) : !dialogValue.trim())}
|
||||
>
|
||||
{isDialogSaving ? 'Сохраняем...' : 'Сохранить'}
|
||||
</Button>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user