Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | 'use client' import {signIn, useSession} from 'next-auth/react' import {useRouter} from 'next/navigation' import {useState} from 'react' const MIN = 10 export default function ChangePassword({ hasPassword = true, }: { hasPassword?: boolean }) { const router = useRouter() const {data: session} = useSession() const [current, setCurrent] = useState('') const [next, setNext] = useState('') const [again, setAgain] = useState('') const [msg, setMsg] = useState<{ok: boolean; text: string} | null>(null) const [busy, setBusy] = useState(false) async function submit(e: React.FormEvent) { e.preventDefault() if (next !== again) return setMsg({ok: false, text: 'The new passwords don’t match.'}) if (next.length < MIN) return setMsg({ok: false, text: `Use at least ${MIN} characters.`}) setBusy(true) const r = await fetch('/api/account/password', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({current: hasPassword ? current : undefined, next}), }) if (!r.ok) { setBusy(false) return setMsg({ ok: false, text: (await r.json().catch(() => ({}))).error ?? 'Could not change it.', }) } // Changing the password ends every session, this one included: sign back in await signIn('credentials', { email: session?.user?.email, password: next, redirect: false, }) setBusy(false) setCurrent('') setNext('') setAgain('') setMsg({ ok: true, text: 'Changed. Your other devices will need the new password.', }) router.refresh() } const field = ( label: string, value: string, set: (v: string) => void, auto: string, ) => ( <label className="flex flex-col gap-1 text-sm text-muted"> {label} <input type="password" autoComplete={auto} required value={value} onChange={(e) => set(e.target.value)} className="min-h-12 rounded-lg border border-line-2 bg-panel px-3 font-mono text-base text-text" /> </label> ) return ( <section className="mt-8"> <h2 className="text-xl font-extrabold"> {hasPassword ? 'Change password' : 'Set a password'} </h2> {!hasPassword && ( <p className="mt-1 text-muted"> You sign in with Google. A password lets you sign in without it too. </p> )} <form onSubmit={submit} className="mt-5 flex flex-col gap-4"> {hasPassword && field('Current password', current, setCurrent, 'current-password')} {field( `New password (at least ${MIN} characters)`, next, setNext, 'new-password', )} {field('New password again', again, setAgain, 'new-password')} {msg && ( <p role="alert" className={`text-sm ${msg.ok ? 'text-good' : 'text-bad'}`} > {msg.text} </p> )} <button type="submit" disabled={busy} className="min-h-12 rounded-xl bg-accent font-extrabold text-on-accent disabled:opacity-50" > {busy ? 'Saving…' : 'Change password'} </button> </form> </section> ) } |