"use client"; import { useEffect, useState } from "react"; interface User { id: string; username: string; email: string | null; role: string; createdAt: string; lastLoginAt: string | null; } export default function UsersPage() { const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [showAddModal, setShowAddModal] = useState(false); const [showEditModal, setShowEditModal] = useState(false); const [showDeleteModal, setShowDeleteModal] = useState(false); const [selectedUser, setSelectedUser] = useState(null); const [formData, setFormData] = useState({ username: "", email: "", password: "", role: "VIEWER", }); // Fetch users const fetchUsers = async () => { try { const response = await fetch("/api/users"); if (!response.ok) throw new Error("Failed to fetch users"); const data = await response.json(); setUsers(data.users); setError(null); } catch (err) { setError("Failed to load users"); console.error(err); } finally { setLoading(false); } }; useEffect(() => { fetchUsers(); }, []); // Handle Add User const handleAdd = async (e: React.FormEvent) => { e.preventDefault(); try { const response = await fetch("/api/users", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(formData), }); if (!response.ok) { const error = await response.json(); throw new Error(error.error || "Failed to create user"); } await fetchUsers(); setShowAddModal(false); setFormData({ username: "", email: "", password: "", role: "VIEWER" }); } catch (err: any) { alert(err.message || "Failed to create user"); } }; // Handle Edit User const handleEdit = async (e: React.FormEvent) => { e.preventDefault(); if (!selectedUser) return; try { const updateData: any = { username: formData.username, email: formData.email || null, role: formData.role, }; // Only include password if it's been changed if (formData.password) { updateData.password = formData.password; } const response = await fetch(`/api/users/${selectedUser.id}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(updateData), }); if (!response.ok) { const error = await response.json(); throw new Error(error.error || "Failed to update user"); } await fetchUsers(); setShowEditModal(false); setSelectedUser(null); setFormData({ username: "", email: "", password: "", role: "VIEWER" }); } catch (err: any) { alert(err.message || "Failed to update user"); } }; // Handle Delete User const handleDelete = async () => { if (!selectedUser) return; try { const response = await fetch(`/api/users/${selectedUser.id}`, { method: "DELETE", }); if (!response.ok) { const error = await response.json(); throw new Error(error.error || "Failed to delete user"); } await fetchUsers(); setShowDeleteModal(false); setSelectedUser(null); } catch (err: any) { alert(err.message || "Failed to delete user"); } }; // Resend welcome email const handleResendWelcome = async (user: User) => { if (!user.email) { alert('This user has no email address'); return; } if (!confirm(`Send welcome email to ${user.email}?`)) { return; } try { const response = await fetch('/api/admin/emails/send-test', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ template: 'welcome', email: user.email, }), }); if (!response.ok) { const error = await response.json(); throw new Error(error.error || 'Failed to send email'); } alert('Welcome email sent successfully'); } catch (err: any) { alert(err.message || 'Failed to send welcome email'); } }; // Send password reset const handleSendPasswordReset = async (user: User) => { if (!user.email) { alert('This user has no email address'); return; } if (!confirm(`Send password reset email to ${user.email}?`)) { return; } try { const response = await fetch('/api/auth/forgot-password', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: user.email }), }); if (!response.ok) { const error = await response.json(); throw new Error(error.error || 'Failed to send email'); } alert('Password reset email sent successfully'); } catch (err: any) { alert(err.message || 'Failed to send password reset email'); } }; // Open Edit Modal const openEditModal = (user: User) => { setSelectedUser(user); setFormData({ username: user.username, email: user.email || "", password: "", // Leave empty unless user wants to change it role: user.role, }); setShowEditModal(true); }; // Open Delete Modal const openDeleteModal = (user: User) => { setSelectedUser(user); setShowDeleteModal(true); }; if (loading) { return (

Loading users...

); } if (error) { return (

{error}

); } return (
{/* Header */}

User Management

{/* Users Grid */}
{users.map((user) => (
{user.role}

Username:{" "} {user.username}

Email:{" "} {user.email || "—"}

Created: {new Date(user.createdAt).toLocaleDateString()}

{user.lastLoginAt && (

Last login: {new Date(user.lastLoginAt).toLocaleString()}

)}
{/* Email Actions */} {user.email && (
)}
))}
{users.length === 0 && (

No users found. Create your first user!

)} {/* Add User Modal */} {showAddModal && (

Add New User

setFormData({ ...formData, username: e.target.value }) } className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" />
setFormData({ ...formData, email: e.target.value }) } className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" />
setFormData({ ...formData, password: e.target.value }) } className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" />
)} {/* Edit User Modal */} {showEditModal && selectedUser && (

Edit User

setFormData({ ...formData, username: e.target.value }) } className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" />
setFormData({ ...formData, email: e.target.value }) } className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" />
setFormData({ ...formData, password: e.target.value }) } className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" />

Leave empty to keep current password

)} {/* Delete User Modal */} {showDeleteModal && selectedUser && (

Delete User

Are you sure you want to delete user {selectedUser.username}? This action cannot be undone.

)}
); }