geo/frontend/app/(auth)/reset-password/page.tsx

148 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useState, useEffect, Suspense } from "react";
import { useSearchParams, useRouter } from "next/navigation";
import Link from "next/link";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { api } from "@/lib/api";
function ResetPasswordForm() {
const router = useRouter();
const searchParams = useSearchParams();
const token = searchParams.get("token");
const [newPassword, setNewPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(false);
const [countdown, setCountdown] = useState(3);
useEffect(() => {
if (success && countdown > 0) {
const timer = setTimeout(() => setCountdown((c) => c - 1), 1000);
return () => clearTimeout(timer);
}
if (success && countdown === 0) {
router.push("/login");
}
}, [success, countdown, router]);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError("");
if (newPassword !== confirmPassword) {
setError("两次输入的密码不一致");
return;
}
if (!token) {
setError("重置令牌无效或已过期");
return;
}
setLoading(true);
try {
await api.auth.resetPassword(token, newPassword);
setSuccess(true);
} catch (err: unknown) {
const message = err instanceof Error ? err.message : "重置失败";
setError(message);
} finally {
setLoading(false);
}
};
if (success) {
return (
<Card className="w-full max-w-md">
<CardHeader className="space-y-1">
<CardTitle className="text-2xl"></CardTitle>
<CardDescription>
{countdown}
</CardDescription>
</CardHeader>
<CardFooter>
<Link href="/login" className="text-sm text-primary hover:underline">
</Link>
</CardFooter>
</Card>
);
}
return (
<Card className="w-full max-w-md">
<CardHeader className="space-y-1">
<CardTitle className="text-2xl"></CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<form onSubmit={handleSubmit}>
<CardContent className="space-y-4">
{error && (
<p className="text-sm text-destructive">{error}</p>
)}
<div className="space-y-2">
<Label htmlFor="newPassword"></Label>
<Input
id="newPassword"
type="password"
placeholder="请输入新密码"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="confirmPassword"></Label>
<Input
id="confirmPassword"
type="password"
placeholder="请再次输入新密码"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
required
/>
</div>
</CardContent>
<CardFooter className="flex flex-col space-y-4">
<Button type="submit" className="w-full" disabled={loading}>
{loading ? "提交中..." : "重置密码"}
</Button>
<Link href="/login" className="text-sm text-primary hover:underline">
</Link>
</CardFooter>
</form>
</Card>
);
}
export default function ResetPasswordPage() {
return (
<Suspense
fallback={
<Card className="w-full max-w-md">
<CardHeader className="space-y-1">
<CardTitle className="text-2xl"></CardTitle>
<CardDescription>...</CardDescription>
</CardHeader>
</Card>
}
>
<ResetPasswordForm />
</Suspense>
);
}