Files
biiproject-kit-v2/resources/js/Pages/TwoFactor/Challenge.tsx
T

81 lines
4.3 KiB
TypeScript

import React, { useState } from 'react';
import { Head, useForm } from '@inertiajs/react';
export default function TwoFactorChallenge() {
const [useRecovery, setUseRecovery] = useState(false);
const { data, setData, post, processing, errors } = useForm({ code: '' });
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
post(route('two-factor.verify'), { preserveScroll: true });
};
return (
<div className="min-h-screen bg-[#E3EBE8] flex items-center justify-center p-4">
<Head title="Two-Factor Authentication" />
<div className="w-full max-w-sm">
{/* Logo */}
<div className="text-center mb-8">
<div className="inline-flex items-center justify-center w-14 h-14 rounded-2xl bg-[#3D4E4B] mb-4">
<svg className="w-7 h-7 text-[#D4A017]" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z" />
</svg>
</div>
<h1 className="text-xl font-black text-[#3D4E4B] tracking-tight">Two-Factor Authentication</h1>
<p className="text-sm text-gray-500 font-medium mt-1">
{useRecovery ? 'Enter a recovery code to continue' : 'Enter the 6-digit code from your authenticator app'}
</p>
</div>
<div className="bg-white rounded-2xl shadow-sm border border-gray-100 p-8">
<form onSubmit={handleSubmit} className="space-y-5">
<div>
<label className="block text-xs font-bold text-gray-500 uppercase tracking-widest mb-2">
{useRecovery ? 'Recovery Code' : 'Authentication Code'}
</label>
<input
type="text"
inputMode={useRecovery ? 'text' : 'numeric'}
maxLength={useRecovery ? 21 : 6}
value={data.code}
onChange={e => setData('code', e.target.value)}
autoFocus
className={`w-full h-12 border rounded-xl px-4 text-center font-mono font-bold text-lg tracking-[0.4em] outline-none transition-all
${errors.code ? 'border-red-300 bg-red-50' : 'border-gray-200 focus:border-[#3D4E4B] focus:ring-2 focus:ring-[#3D4E4B]/10'}`}
placeholder={useRecovery ? 'xxxxxxxxxx-xxxxxxxxxx' : '000000'}
/>
{errors.code && (
<p className="text-xs text-red-500 font-semibold mt-1.5">{errors.code}</p>
)}
</div>
<button
type="submit"
disabled={processing || data.code.length < (useRecovery ? 5 : 6)}
className="w-full h-11 bg-[#3D4E4B] text-white text-sm font-bold rounded-xl hover:bg-[#2D3A38] transition-all disabled:opacity-60"
>
{processing ? 'Verifying...' : 'Continue'}
</button>
</form>
<div className="mt-6 text-center">
<button
onClick={() => { setUseRecovery(!useRecovery); setData('code', ''); }}
className="text-xs font-bold text-[#3D4E4B] hover:underline"
>
{useRecovery ? 'Use authenticator code instead' : 'Use a recovery code'}
</button>
</div>
</div>
<div className="mt-6 text-center">
<a href="/login" className="text-xs font-semibold text-gray-400 hover:text-[#3D4E4B]">
Back to login
</a>
</div>
</div>
</div>
);
}