Improve MineGeo exploration and mapping features
This commit is contained in:
148
components/PasswordForm.tsx
Normal file
148
components/PasswordForm.tsx
Normal file
@@ -0,0 +1,148 @@
|
||||
"use client";
|
||||
import { useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { createBrowserClient } from "@supabase/ssr";
|
||||
import { passwordUpdateSchema } from "@/shared/account";
|
||||
|
||||
export function PasswordForm({ recovery = false }: { recovery?: boolean }) {
|
||||
const [current, setCurrent] = useState(""),
|
||||
[password, setPassword] = useState(""),
|
||||
[confirmation, setConfirmation] = useState(""),
|
||||
[nonce, setNonce] = useState(""),
|
||||
[needsNonce, setNeedsNonce] = useState(false),
|
||||
[error, setError] = useState(""),
|
||||
[message, setMessage] = useState(""),
|
||||
[busy, setBusy] = useState(false);
|
||||
const router = useRouter();
|
||||
async function submit(event: React.FormEvent) {
|
||||
event.preventDefault();
|
||||
setError("");
|
||||
setMessage("");
|
||||
const body = {
|
||||
password,
|
||||
...(!recovery ? { currentPassword: current } : {}),
|
||||
...(needsNonce ? { nonce } : {}),
|
||||
};
|
||||
const valid = passwordUpdateSchema.safeParse(body);
|
||||
if (!valid.success) {
|
||||
setError(valid.error.issues[0].message);
|
||||
return;
|
||||
}
|
||||
if (password !== confirmation) {
|
||||
setError("Your passwords do not match.");
|
||||
return;
|
||||
}
|
||||
setBusy(true);
|
||||
try {
|
||||
const response = await fetch("/api/v1/auth/password", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(valid.data),
|
||||
signal: AbortSignal.timeout(30000),
|
||||
});
|
||||
const data = await response.json();
|
||||
if (!response.ok) {
|
||||
if (data.needsReauthentication) {
|
||||
const client = createBrowserClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,
|
||||
);
|
||||
const { error } = await client.auth.reauthenticate();
|
||||
if (error) throw error;
|
||||
setNeedsNonce(true);
|
||||
setMessage(
|
||||
"Check your email for a verification code, then enter it below to finish.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
throw new Error(data.error || "Could not update your password");
|
||||
}
|
||||
setCurrent("");
|
||||
setPassword("");
|
||||
setConfirmation("");
|
||||
setNonce("");
|
||||
setNeedsNonce(false);
|
||||
setMessage(data.message);
|
||||
if (recovery) {
|
||||
router.replace("/settings?password=updated");
|
||||
router.refresh();
|
||||
}
|
||||
} catch (e) {
|
||||
setError((e as Error).message);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<form onSubmit={submit} aria-busy={busy} className="password-form">
|
||||
{!recovery && (
|
||||
<label className="field">
|
||||
Current password
|
||||
<input
|
||||
name="currentPassword"
|
||||
type="password"
|
||||
autoComplete="current-password"
|
||||
value={current}
|
||||
onChange={(e) => setCurrent(e.target.value)}
|
||||
required
|
||||
disabled={busy}
|
||||
/>
|
||||
</label>
|
||||
)}
|
||||
<label className="field">
|
||||
New password
|
||||
<input
|
||||
name="newPassword"
|
||||
type="password"
|
||||
autoComplete="new-password"
|
||||
minLength={12}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
disabled={busy}
|
||||
/>
|
||||
<small>
|
||||
At least 12 characters. Use a unique password or passphrase.
|
||||
</small>
|
||||
</label>
|
||||
<label className="field">
|
||||
Confirm new password
|
||||
<input
|
||||
name="confirmPassword"
|
||||
type="password"
|
||||
autoComplete="new-password"
|
||||
value={confirmation}
|
||||
onChange={(e) => setConfirmation(e.target.value)}
|
||||
required
|
||||
disabled={busy}
|
||||
/>
|
||||
</label>
|
||||
{needsNonce && (
|
||||
<label className="field">
|
||||
Email verification code
|
||||
<input
|
||||
inputMode="numeric"
|
||||
autoComplete="one-time-code"
|
||||
value={nonce}
|
||||
onChange={(e) => setNonce(e.target.value)}
|
||||
required
|
||||
disabled={busy}
|
||||
/>
|
||||
</label>
|
||||
)}
|
||||
{error && (
|
||||
<p role="alert" className="error-message">
|
||||
{error}
|
||||
</p>
|
||||
)}
|
||||
{message && (
|
||||
<p role="status" className="success-message">
|
||||
{message}
|
||||
</p>
|
||||
)}
|
||||
<button className="button primary" disabled={busy}>
|
||||
{busy ? "Updating…" : recovery ? "Set new password" : "Update password"}
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user