import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { addAccount, type TotpAccount } from '../services/db'; import { parseOtpAuthUri } from '../services/uriParser'; export function TotpForm() { const navigate = useNavigate(); const [name, setName] = useState(''); const [issuer, setIssuer] = useState(''); const [secret, setSecret] = useState(''); const [digits, setDigits] = useState<6 | 7 | 8>(6); const [algorithm, setAlgorithm] = useState<'SHA1' | 'SHA256' | 'SHA512'>('SHA1'); const [period, setPeriod] = useState(30); const [uri, setUri] = useState(''); const [error, setError] = useState(null); const [isSubmitting, setIsSubmitting] = useState(false); const isFormDisabled = uri.startsWith('otpauth://'); const isUriDisabled = secret.length > 0; const handleUriChange = (value: string) => { const trimmedValue = value.trim(); setUri(trimmedValue); if (trimmedValue.startsWith('otpauth://')) { try { const parsed = parseOtpAuthUri(trimmedValue); setName(parsed.name || ''); setIssuer(parsed.issuer || ''); setSecret(parsed.secret || ''); setAlgorithm(parsed.algorithm || 'SHA1'); setDigits(parsed.digits || 6); setPeriod(parsed.period || 30); setError(null); } catch (err) { setError(err instanceof Error ? err.message : 'Invalid URI'); // Clear form if URI is invalid setName(''); setIssuer(''); setSecret(''); } } else if (trimmedValue === '') { // Clear form if URI is cleared setName(''); setIssuer(''); setSecret(''); setError(null); } }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!name || !secret) { setError('Name and Secret Key are required.'); return; } setError(null); setIsSubmitting(true); const newAccount: Omit = { name, issuer, secret, digits, period, algorithm, }; try { await addAccount({ ...newAccount, createdAt: new Date() }); navigate('/'); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to save the account.'); setIsSubmitting(false); } }; const baseInputStyles = "block w-full px-1 py-2 bg-transparent border-0 border-b-2 border-slate-300 dark:border-slate-600 focus:outline-none focus:ring-0 focus:border-indigo-500 transition-colors text-slate-900 dark:text-slate-100 placeholder:text-slate-400 dark:placeholder:text-slate-500"; const disabledInputStyles = "disabled:opacity-50 disabled:cursor-not-allowed disabled:border-slate-200 dark:disabled:border-slate-700"; const inputStyles = `${baseInputStyles} ${disabledInputStyles}`; const selectStyles = `${inputStyles} dark:[color-scheme:dark]`; return (
{/* URI Input Card */}