'use client';
import { useEffect, useState } from 'react';
import { useSession } from 'next-auth/react';
import { useRouter } from 'next/navigation';
import { Users, Shield, Trash2, CheckCircle, XCircle, ArrowLeft } from 'lucide-react';
import Link from 'next/link';
interface User {
id: number;
username: string;
email: string;
first_name: string;
last_name: string;
dob: string;
gender: string;
expertise_level: string;
role: 'user' | 'admin';
is_active: boolean;
email_verified: boolean;
google_id: string | null;
created_at: string;
}
export default function UserManagement() {
const { data: session, status } = useSession();
const router = useRouter();
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [processing, setProcessing] = useState(false);
useEffect(() => {
if (status === 'unauthenticated') {
router.push('/login');
}
}, [status, router]);
useEffect(() => {
fetchUsers();
}, []);
const fetchUsers = async () => {
try {
setLoading(true);
const response = await fetch('/api/admin/users');
const data = await response.json();
if (data.success) {
setUsers(data.users);
} else {
setError(data.error);
}
} catch (err: any) {
setError('Failed to fetch users');
} finally {
setLoading(false);
}
};
const handleToggleActive = async (userId: number, currentStatus: boolean) => {
if (!confirm(`Are you sure you want to ${currentStatus ? 'deactivate' : 'activate'} this user?`)) {
return;
}
setProcessing(true);
try {
const response = await fetch('/api/admin/users', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
userId,
action: 'toggle_active',
value: !currentStatus,
}),
});
const data = await response.json();
if (data.success) {
await fetchUsers();
} else {
setError(data.error);
}
} catch (err: any) {
setError('Failed to update user');
} finally {
setProcessing(false);
}
};
const handleToggleRole = async (userId: number, currentRole: string) => {
const newRole = currentRole === 'admin' ? 'user' : 'admin';
if (!confirm(`Are you sure you want to change this user's role to ${newRole}?`)) {
return;
}
setProcessing(true);
try {
const response = await fetch('/api/admin/users', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
userId,
action: 'update_role',
value: newRole,
}),
});
const data = await response.json();
if (data.success) {
await fetchUsers();
} else {
setError(data.error);
}
} catch (err: any) {
setError('Failed to update user role');
} finally {
setProcessing(false);
}
};
const handleDeleteUser = async (userId: number, username: string) => {
if (!confirm(`Are you sure you want to delete user "${username}"? This action cannot be undone.`)) {
return;
}
setProcessing(true);
try {
const response = await fetch(`/api/admin/users?userId=${userId}`, {
method: 'DELETE',
});
const data = await response.json();
if (data.success) {
await fetchUsers();
} else {
setError(data.error);
}
} catch (err: any) {
setError('Failed to delete user');
} finally {
setProcessing(false);
}
};
if (status === 'loading' || loading) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-gray-600">Loading...</div>
</div>
);
}
return (
<div className="min-h-screen bg-gray-50 p-8">
<div className="max-w-7xl mx-auto">
<div className="mb-8">
<Link
href="/admin"
className="inline-flex items-center gap-2 text-blue-600 hover:text-blue-700 mb-4"
>
<ArrowLeft className="h-4 w-4" />
Back to Admin Dashboard
</Link>
<div className="flex items-center gap-3 mb-2">
<Users className="h-8 w-8 text-blue-600" />
<h1 className="text-3xl font-bold text-gray-900">User Management</h1>
</div>
<p className="text-gray-600">Manage user accounts and permissions</p>
</div>
{error && (
<div className="mb-6 p-4 bg-red-50 border border-red-200 rounded-lg text-red-700">
{error}
</div>
)}
<div className="bg-white rounded-lg shadow overflow-hidden">
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
User
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Email
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Expertise
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Status
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Role
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Auth
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Actions
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{users.map((user) => (
<tr key={user.id} className="hover:bg-gray-50">
<td className="px-6 py-4 whitespace-nowrap">
<div className="flex items-center">
<div>
<div className="text-sm font-medium text-gray-900">
{user.first_name} {user.last_name}
</div>
<div className="text-sm text-gray-500">@{user.username}</div>
</div>
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm text-gray-900">{user.email}</div>
{user.email_verified && (
<div className="text-xs text-green-600">Verified</div>
)}
</td>
<td className="px-6 py-4 whitespace-nowrap">
<span className="px-2 py-1 text-xs rounded-full bg-blue-100 text-blue-800">
{user.expertise_level} years
</span>
</td>
<td className="px-6 py-4 whitespace-nowrap">
{user.is_active ? (
<span className="px-2 py-1 text-xs rounded-full bg-green-100 text-green-800 flex items-center gap-1 w-fit">
<CheckCircle className="h-3 w-3" />
Active
</span>
) : (
<span className="px-2 py-1 text-xs rounded-full bg-red-100 text-red-800 flex items-center gap-1 w-fit">
<XCircle className="h-3 w-3" />
Inactive
</span>
)}
</td>
<td className="px-6 py-4 whitespace-nowrap">
<button
onClick={() => handleToggleRole(user.id, user.role)}
disabled={processing}
className={`px-2 py-1 text-xs rounded-full flex items-center gap-1 ${
user.role === 'admin'
? 'bg-purple-100 text-purple-800'
: 'bg-gray-100 text-gray-800'
} hover:opacity-80 disabled:opacity-50`}
>
{user.role === 'admin' && <Shield className="h-3 w-3" />}
{user.role}
</button>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{user.google_id ? 'Google' : 'Local'}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium space-x-2">
<button
onClick={() => handleToggleActive(user.id, user.is_active)}
disabled={processing}
className={`px-3 py-1 rounded text-white ${
user.is_active
? 'bg-yellow-600 hover:bg-yellow-700'
: 'bg-green-600 hover:bg-green-700'
} disabled:bg-gray-400`}
>
{user.is_active ? 'Deactivate' : 'Activate'}
</button>
<button
onClick={() => handleDeleteUser(user.id, user.username)}
disabled={processing}
className="px-3 py-1 rounded bg-red-600 text-white hover:bg-red-700 disabled:bg-gray-400"
>
<Trash2 className="h-4 w-4 inline" />
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
{users.length === 0 && (
<div className="text-center py-12 text-gray-500">
No users found
</div>
)}
</div>
</div>
</div>
);
}