Files
next-dj/app/profile/page.tsx
Beyhan Oğur e881f38e4e first commit
2026-04-26 22:12:36 +03:00

276 lines
9.3 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 { useSession, signOut } from "next-auth/react";
import { useState, useEffect, FormEvent } from "react";
import { 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";
interface UserData {
id: number;
email: string;
first_name: string;
last_name: string;
is_active: boolean;
date_joined: string;
}
export default function ProfilePage() {
const { data: session, status } = useSession();
const router = useRouter();
const [userData, setUserData] = useState<UserData | null>(null);
const [formData, setFormData] = useState({
first_name: "",
last_name: "",
});
const [loading, setLoading] = useState(true);
const [updating, setUpdating] = useState(false);
const [error, setError] = useState("");
const [success, setSuccess] = useState(false);
useEffect(() => {
if (status === "unauthenticated") {
router.push("/auth/login");
}
}, [status, router]);
useEffect(() => {
const fetchUserData = async () => {
if (!session?.accessToken) return;
try {
const response = await fetch(`${API_BASE_URL}/auth/users/me/`, {
headers: {
Authorization: `Bearer ${session.accessToken}`,
},
});
if (response.ok) {
const data = await response.json();
setUserData(data);
setFormData({
first_name: data.first_name || "",
last_name: data.last_name || "",
});
} else if (response.status === 401) {
// Token expired, logout
signOut({ callbackUrl: "/auth/login" });
}
} catch (err) {
console.error("Error fetching user data:", err);
setError("Kullanıcı bilgileri yüklenemedi.");
} finally {
setLoading(false);
}
};
if (session?.accessToken) {
fetchUserData();
}
}, [session]);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setFormData({
...formData,
[e.target.name]: e.target.value,
});
};
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
setError("");
setSuccess(false);
setUpdating(true);
if (!session?.accessToken) return;
try {
const response = await fetch(`${API_BASE_URL}/auth/users/me/`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${session.accessToken}`,
},
body: JSON.stringify(formData),
});
if (response.ok) {
const data = await response.json();
setUserData(data);
setSuccess(true);
setTimeout(() => setSuccess(false), 3000);
} else if (response.status === 401) {
signOut({ callbackUrl: "/auth/login" });
} else {
const data = await response.json();
setError(data.detail || "Profil güncellenemedi.");
}
} catch (err) {
setError("Bir hata oluştu. Lütfen tekrar deneyin.");
console.error("Update profile error:", err);
} finally {
setUpdating(false);
}
};
const handleLogout = () => {
signOut({ callbackUrl: "/auth/login" });
};
if (status === "loading" || loading) {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="text-center">
<div className="inline-block animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
<p className="mt-4 text-gray-600">Yükleniyor...</p>
</div>
</div>
);
}
if (!session) {
return null;
}
return (
<div className="min-h-screen bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-3xl mx-auto">
<div className="bg-white shadow rounded-lg">
{/* Header */}
<div className="px-4 py-5 sm:px-6 border-b border-gray-200">
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold text-gray-900">Profil</h1>
<p className="mt-1 text-sm text-gray-500">
Hesap bilgilerinizi görüntüleyin ve güncelleyin.
</p>
</div>
<Link
href="/dashboard"
className="inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"
>
Dashboard
</Link>
</div>
</div>
{/* User Info */}
{userData && (
<div className="px-4 py-5 sm:p-6">
<dl className="grid grid-cols-1 gap-x-4 gap-y-6 sm:grid-cols-2">
<div className="sm:col-span-2">
<dt className="text-sm font-medium text-gray-500">Email</dt>
<dd className="mt-1 text-sm text-gray-900">{userData.email}</dd>
</div>
<div>
<dt className="text-sm font-medium text-gray-500">Üyelik Tarihi</dt>
<dd className="mt-1 text-sm text-gray-900">
{new Date(userData.date_joined).toLocaleDateString("tr-TR", {
year: "numeric",
month: "long",
day: "numeric",
})}
</dd>
</div>
<div>
<dt className="text-sm font-medium text-gray-500">Hesap Durumu</dt>
<dd className="mt-1">
<span
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
userData.is_active
? "bg-green-100 text-green-800"
: "bg-red-100 text-red-800"
}`}
>
{userData.is_active ? "Aktif" : "Pasif"}
</span>
</dd>
</div>
</dl>
</div>
)}
{/* Update Form */}
<div className="px-4 py-5 sm:p-6 border-t border-gray-200">
<h2 className="text-lg font-medium text-gray-900 mb-4">
Profil Bilgilerini Güncelle
</h2>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div>
<label
htmlFor="first_name"
className="block text-sm font-medium text-gray-700 mb-1"
>
Ad
</label>
<input
type="text"
name="first_name"
id="first_name"
className="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
value={formData.first_name}
onChange={handleChange}
/>
</div>
<div>
<label
htmlFor="last_name"
className="block text-sm font-medium text-gray-700 mb-1"
>
Soyad
</label>
<input
type="text"
name="last_name"
id="last_name"
className="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
value={formData.last_name}
onChange={handleChange}
/>
</div>
</div>
{success && (
<div className="rounded-md bg-green-50 p-4">
<p className="text-sm text-green-800">
Profil bilgileriniz başarıyla güncellendi!
</p>
</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={updating}
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium 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"
>
{updating ? "Güncelleniyor..." : "Profili Güncelle"}
</button>
</div>
</form>
</div>
{/* Logout Button */}
<div className="px-4 py-5 sm:p-6 border-t border-gray-200">
<button
onClick={handleLogout}
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
>
Çıkış Yap
</button>
</div>
</div>
</div>
</div>
);
}