first commit
This commit is contained in:
203
app/auth/password-reset/confirm/[uid]/[token]/page.tsx
Normal file
203
app/auth/password-reset/confirm/[uid]/[token]/page.tsx
Normal file
@@ -0,0 +1,203 @@
|
||||
"use client";
|
||||
|
||||
import { useState, FormEvent } from "react";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
|
||||
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || "http://localhost:8000/api/v1";
|
||||
|
||||
export default function PasswordResetConfirmPage() {
|
||||
const params = useParams();
|
||||
const router = useRouter();
|
||||
const [formData, setFormData] = useState({
|
||||
new_password: "",
|
||||
re_new_password: "",
|
||||
});
|
||||
const [error, setError] = useState("");
|
||||
const [fieldErrors, setFieldErrors] = useState<Record<string, string[]>>({});
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [success, setSuccess] = useState(false);
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
[e.target.name]: e.target.value,
|
||||
});
|
||||
// Clear field error when user starts typing
|
||||
if (fieldErrors[e.target.name]) {
|
||||
const newErrors = { ...fieldErrors };
|
||||
delete newErrors[e.target.name];
|
||||
setFieldErrors(newErrors);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
setError("");
|
||||
setFieldErrors({});
|
||||
setLoading(true);
|
||||
|
||||
const { uid, token } = params;
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/auth/users/reset_password_confirm/`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
uid,
|
||||
token,
|
||||
new_password: formData.new_password,
|
||||
re_new_password: formData.re_new_password,
|
||||
}),
|
||||
});
|
||||
|
||||
if (response.ok || response.status === 204) {
|
||||
setSuccess(true);
|
||||
setTimeout(() => {
|
||||
router.push("/auth/login");
|
||||
}, 3000);
|
||||
} else {
|
||||
const data = await response.json();
|
||||
if (data.new_password || data.re_new_password) {
|
||||
setFieldErrors(data);
|
||||
} else if (data.detail) {
|
||||
setError(data.detail);
|
||||
} else if (data.token) {
|
||||
setError("Şifre sıfırlama linki geçersiz veya süresi dolmuş.");
|
||||
} else {
|
||||
setError("Şifre sıfırlama başarısız. Lütfen bilgilerinizi kontrol edin.");
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
setError("Bir hata oluştu. Lütfen tekrar deneyin.");
|
||||
console.error("Password reset confirm error:", err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (success) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gray-50">
|
||||
<div className="max-w-md w-full space-y-8 p-8 bg-white rounded-lg shadow">
|
||||
<div className="text-center">
|
||||
<div className="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-green-100">
|
||||
<svg
|
||||
className="h-6 w-6 text-green-600"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M5 13l4 4L19 7"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h2 className="mt-6 text-center text-3xl font-bold text-gray-900">
|
||||
Şifre Sıfırlama Başarılı!
|
||||
</h2>
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Şifreniz başarıyla değiştirildi. Artık yeni şifrenizle giriş yapabilirsiniz.
|
||||
</p>
|
||||
<p className="mt-2 text-center text-sm text-gray-500">
|
||||
Giriş sayfasına yönlendiriliyorsunuz...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div className="max-w-md w-full space-y-8">
|
||||
<div>
|
||||
<h2 className="mt-6 text-center text-3xl font-bold text-gray-900">
|
||||
Yeni Şifre Belirle
|
||||
</h2>
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Hesabınız için yeni bir şifre oluşturun.
|
||||
</p>
|
||||
</div>
|
||||
<form className="mt-8 space-y-6" onSubmit={handleSubmit}>
|
||||
<div className="rounded-md shadow-sm space-y-4">
|
||||
<div>
|
||||
<label htmlFor="new_password" className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Yeni Şifre
|
||||
</label>
|
||||
<input
|
||||
id="new_password"
|
||||
name="new_password"
|
||||
type="password"
|
||||
autoComplete="new-password"
|
||||
required
|
||||
className={`appearance-none relative block w-full px-3 py-2 border ${
|
||||
fieldErrors.new_password ? "border-red-300" : "border-gray-300"
|
||||
} placeholder-gray-500 text-gray-900 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm`}
|
||||
placeholder="Minimum 8 karakter"
|
||||
value={formData.new_password}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
{fieldErrors.new_password && (
|
||||
<p className="mt-1 text-sm text-red-600">{fieldErrors.new_password[0]}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="re_new_password" className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Yeni Şifre Tekrar
|
||||
</label>
|
||||
<input
|
||||
id="re_new_password"
|
||||
name="re_new_password"
|
||||
type="password"
|
||||
autoComplete="new-password"
|
||||
required
|
||||
className={`appearance-none relative block w-full px-3 py-2 border ${
|
||||
fieldErrors.re_new_password ? "border-red-300" : "border-gray-300"
|
||||
} placeholder-gray-500 text-gray-900 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm`}
|
||||
placeholder="Şifrenizi tekrar girin"
|
||||
value={formData.re_new_password}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
{fieldErrors.re_new_password && (
|
||||
<p className="mt-1 text-sm text-red-600">{fieldErrors.re_new_password[0]}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="rounded-md bg-red-50 p-4">
|
||||
<p className="text-sm text-red-800">{error}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{loading ? "Şifre değiştiriliyor..." : "Şifreyi Değiştir"}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="text-center">
|
||||
<Link
|
||||
href="/auth/login"
|
||||
className="text-sm font-medium text-blue-600 hover:text-blue-500"
|
||||
>
|
||||
Giriş sayfasına dön
|
||||
</Link>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user