Some checks failed
Deploy to Cloudflare Workers / deploy (push) Has been cancelled
138 lines
4.0 KiB
TypeScript
138 lines
4.0 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import Link from "next/link";
|
|
import { useRouter, useSearchParams } from "next/navigation";
|
|
import { createClient } from "@/lib/supabase/client";
|
|
import { formatAuthNetworkError } from "@/lib/supabase/env";
|
|
import {
|
|
type PortalRole,
|
|
PORTAL_ROUTES,
|
|
loginPathForRole,
|
|
} from "@/lib/auth/roles";
|
|
import { Button } from "@/components/ui/button";
|
|
import { PasswordInput } from "@/components/ui/password-input";
|
|
import { Label } from "@/components/ui/label";
|
|
|
|
function parsePortal(value: string | null): PortalRole {
|
|
return value === "league_master" ? "league_master" : "manager";
|
|
}
|
|
|
|
export function ResetPasswordForm() {
|
|
const router = useRouter();
|
|
const searchParams = useSearchParams();
|
|
const portal = parsePortal(searchParams.get("portal"));
|
|
const urlError = searchParams.get("error");
|
|
|
|
const [password, setPassword] = useState("");
|
|
const [confirm, setConfirm] = useState("");
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(urlError);
|
|
const [done, setDone] = useState(false);
|
|
|
|
const loginHref = loginPathForRole(portal);
|
|
|
|
async function handleSubmit(e: React.FormEvent) {
|
|
e.preventDefault();
|
|
setError(null);
|
|
|
|
if (password.length < 6) {
|
|
setError("Password must be at least 6 characters.");
|
|
return;
|
|
}
|
|
if (password !== confirm) {
|
|
setError("Passwords do not match.");
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
try {
|
|
const supabase = createClient();
|
|
const { error: updateError } = await supabase.auth.updateUser({
|
|
password,
|
|
});
|
|
if (updateError) {
|
|
setError(updateError.message);
|
|
return;
|
|
}
|
|
|
|
const {
|
|
data: { user },
|
|
} = await supabase.auth.getUser();
|
|
|
|
if (user) {
|
|
const { data: profile } = await supabase
|
|
.from("profiles")
|
|
.select("portal_role")
|
|
.eq("id", user.id)
|
|
.single();
|
|
|
|
const role = (profile?.portal_role as PortalRole) ?? portal;
|
|
if (role !== portal) {
|
|
setError(
|
|
`This account is a ${role === "league_master" ? "League Master" : "Team Manager"} account. Use the correct reset link or sign in at the other portal.`
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
setDone(true);
|
|
setTimeout(() => {
|
|
router.push(PORTAL_ROUTES[portal]);
|
|
router.refresh();
|
|
}, 1500);
|
|
} catch (err) {
|
|
setError(formatAuthNetworkError(err));
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
|
|
if (done) {
|
|
return (
|
|
<div className="space-y-4 text-center">
|
|
<p className="text-sm text-emerald-400">Password updated successfully.</p>
|
|
<p className="text-xs text-muted-foreground">Redirecting to your dashboard…</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="password">New password</Label>
|
|
<PasswordInput
|
|
id="password"
|
|
value={password}
|
|
onChange={(e) => setPassword(e.target.value)}
|
|
required
|
|
minLength={6}
|
|
autoComplete="new-password"
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="confirm">Confirm password</Label>
|
|
<PasswordInput
|
|
id="confirm"
|
|
value={confirm}
|
|
onChange={(e) => setConfirm(e.target.value)}
|
|
required
|
|
minLength={6}
|
|
autoComplete="new-password"
|
|
/>
|
|
</div>
|
|
{error && (
|
|
<p className="whitespace-pre-line text-sm text-red-400">{error}</p>
|
|
)}
|
|
<Button type="submit" className="w-full" disabled={loading}>
|
|
{loading ? "Saving…" : "Set new password"}
|
|
</Button>
|
|
<p className="text-center text-sm text-muted-foreground">
|
|
<Link href={loginHref} className="hover:underline">
|
|
Back to sign in
|
|
</Link>
|
|
</p>
|
|
</form>
|
|
);
|
|
}
|