152 lines
4.8 KiB
TypeScript
152 lines
4.8 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useState, Suspense } from "react"
|
|
import { useRouter, useSearchParams } from "next/navigation"
|
|
import { Button } from "@/components/ui/button"
|
|
|
|
function ResetPasswordForm() {
|
|
const router = useRouter()
|
|
const searchParams = useSearchParams()
|
|
const token = searchParams.get("token")
|
|
const email = searchParams.get("email")
|
|
|
|
const [password, setPassword] = useState("")
|
|
const [confirmPassword, setConfirmPassword] = useState("")
|
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
|
const [error, setError] = useState<string | null>(null)
|
|
const [success, setSuccess] = useState(false)
|
|
|
|
useEffect(() => {
|
|
if (!token || !email) {
|
|
setError("Invalid or missing reset link")
|
|
}
|
|
}, [token, email])
|
|
|
|
const handleSubmit = async () => {
|
|
setError(null)
|
|
|
|
if (!password || !confirmPassword) {
|
|
setError("Please enter and confirm your new password")
|
|
return
|
|
}
|
|
|
|
if (password.length < 8) {
|
|
setError("Password must be at least 8 characters")
|
|
return
|
|
}
|
|
|
|
if (password !== confirmPassword) {
|
|
setError("Passwords do not match")
|
|
return
|
|
}
|
|
|
|
setIsSubmitting(true)
|
|
try {
|
|
const res = await fetch("/api/auth/reset-password", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ token, email, password }),
|
|
})
|
|
|
|
const data = await res.json()
|
|
if (!res.ok) {
|
|
setError(data.error || "Failed to reset password")
|
|
return
|
|
}
|
|
|
|
setSuccess(true)
|
|
// Redirect to login after 3 seconds
|
|
setTimeout(() => {
|
|
router.push("/login")
|
|
}, 3000)
|
|
} catch {
|
|
setError("Failed to reset password")
|
|
} finally {
|
|
setIsSubmitting(false)
|
|
}
|
|
}
|
|
|
|
if (success) {
|
|
return (
|
|
<div className="min-h-screen bg-slate-950 text-slate-100 flex items-center justify-center p-4">
|
|
<div className="w-full max-w-md border border-green-800 rounded-xl bg-green-900/20 p-6 text-center">
|
|
<div className="w-12 h-12 rounded-full bg-green-500/20 flex items-center justify-center mx-auto mb-4">
|
|
<svg className="w-6 h-6 text-green-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
|
</svg>
|
|
</div>
|
|
<h1 className="text-xl font-semibold text-white mb-2">Password Reset!</h1>
|
|
<p className="text-slate-300 mb-4">
|
|
Your password has been successfully reset.
|
|
</p>
|
|
<p className="text-sm text-slate-400">
|
|
Redirecting to login...
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen bg-slate-950 text-slate-100 flex items-center justify-center p-4">
|
|
<div className="w-full max-w-md border border-slate-800 rounded-xl bg-slate-900/70 p-6">
|
|
<h1 className="text-2xl font-semibold text-white mb-2">Reset Password</h1>
|
|
<p className="text-sm text-slate-400 mb-6">
|
|
Enter your new password below.
|
|
</p>
|
|
|
|
{error && (
|
|
<div className="mb-4 p-3 bg-red-900/30 border border-red-700 rounded-lg">
|
|
<p className="text-sm text-red-400">{error}</p>
|
|
</div>
|
|
)}
|
|
|
|
<div className="space-y-4">
|
|
<div>
|
|
<label className="block text-sm text-slate-300 mb-1">New Password</label>
|
|
<input
|
|
type="password"
|
|
value={password}
|
|
onChange={(e) => setPassword(e.target.value)}
|
|
className="w-full px-3 py-2 bg-slate-800 border border-slate-700 rounded-lg text-white"
|
|
placeholder="At least 8 characters"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm text-slate-300 mb-1">Confirm Password</label>
|
|
<input
|
|
type="password"
|
|
value={confirmPassword}
|
|
onChange={(e) => setConfirmPassword(e.target.value)}
|
|
className="w-full px-3 py-2 bg-slate-800 border border-slate-700 rounded-lg text-white"
|
|
placeholder="Re-enter password"
|
|
/>
|
|
</div>
|
|
|
|
<Button onClick={handleSubmit} className="w-full" disabled={isSubmitting || !token || !email}>
|
|
{isSubmitting ? "Resetting..." : "Reset Password"}
|
|
</Button>
|
|
|
|
<Button onClick={() => router.push("/login")} variant="outline" className="w-full">
|
|
Back to Login
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default function ResetPasswordPage() {
|
|
return (
|
|
<Suspense fallback={
|
|
<div className="min-h-screen bg-slate-950 text-slate-100 flex items-center justify-center p-4">
|
|
<div className="text-center">
|
|
<p className="text-slate-400">Loading...</p>
|
|
</div>
|
|
</div>
|
|
}>
|
|
<ResetPasswordForm />
|
|
</Suspense>
|
|
)
|
|
} |