"use client"; import { useState, useEffect } from "react"; import { UserGrid } from "@/components/users/UserGrid"; // import { Button } from "@/components/ui/button"; import { Card, CardHeader, CardContent } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { useUser } from "@clerk/nextjs"; import { getGymIdFromUser } from "@/lib/error-helpers"; import log from "@/lib/logger"; import { toast } from "@/lib/toast"; import { CreateUserModal } from "./CreateUserModal"; interface User { id: string; email: string; firstName: string; lastName: string; role: string; phone?: string; gymId?: string; createdAt: Date; isCheckedIn?: boolean; checkInTime?: Date; lastCheckInTime?: Date; checkInsThisWeek?: number; checkInsThisMonth?: number; client?: { id: string; membershipType: string; membershipStatus: string; joinDate: Date; lastVisit?: Date; }; } export function UserManagement() { const { user } = useUser(); const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); const [filter, setFilter] = useState("all"); const [selectedUser, setSelectedUser] = useState(null); const [isEditing, setIsEditing] = useState(false); const [isDeleting, setIsDeleting] = useState(false); const [createModalOpen, setCreateModalOpen] = useState(false); const [editForm, setEditForm] = useState<{ firstName: string; lastName: string; email: string; role: string; phone: string; gymId: string; } | null>(null); // Active gyms for dropdown const [gyms, setGyms] = useState>([]); // Load gyms when modal opens or refreshes useEffect(() => { if (isEditing) { (async () => { try { const res = await fetch("/api/gyms"); const data = await res.json(); if (Array.isArray(data)) { // map down to id and name to avoid extra payload use here setGyms(data.map((g: any) => ({ id: g.id, name: g.name }))); } else { setGyms([]); } } catch { setGyms([]); } })(); } }, [isEditing]); useEffect(() => { fetchUsers(); }, [filter]); const fetchUsers = async () => { setLoading(true); try { const ts = Date.now(); const url = filter === "all" ? `/api/users?ts=${ts}` : `/api/users?role=${filter}&ts=${ts}`; log.debug("Fetching users", { url }); const response = await fetch(url, { cache: "no-store" }); log.debug("Users fetch response", { ok: response.ok, status: response.status, }); const responseData = await response.json(); const data = responseData.data || responseData; // Handle both old and new API response formats log.debug("Received users data", { count: Array.isArray(data.users) ? data.users.length : 0, sample: data.users && data.users[0] ? { id: data.users[0].id, gymId: data.users[0].gymId, role: data.users[0].role, } : null, }); setUsers(data.users || []); } catch (error) { log.error("Failed to fetch users", error); } finally { setLoading(false); } }; const handleUserSelect = (user: User | null) => { setSelectedUser(user); }; const handleEditUser = (user: User) => { setSelectedUser(user); setEditForm({ firstName: user.firstName, lastName: user.lastName, email: user.email, role: user.role, phone: user.phone || "", gymId: user.gymId || "", }); setIsEditing(true); }; const handleDeleteUser = (user: User) => { setSelectedUser(user); setIsDeleting(true); }; const handleBulkDelete = async (users: User[]) => { if (users.length === 0) return; if (!confirm(`Are you sure you want to delete ${users.length} users?`)) return; try { const response = await fetch("/api/users", { method: "DELETE", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ids: users.map((u) => u.id) }), }); if (response.ok) { fetchUsers(); toast.success("Users deleted successfully"); } else { toast.error("Error deleting users"); } } catch (error) { log.error("Failed to delete users", error); } }; const handleExport = () => { const csvContent = [ [ "Name", "Email", "Role", "Phone", "Membership", "Status", "Join Date", "Last Visit", ], ...users.map((user) => [ `${user.firstName} ${user.lastName}`, user.email, user.role, user.phone || "", user.client?.membershipType || "", user.client?.membershipStatus || "", user.client?.joinDate || user.createdAt, user.client?.lastVisit || "", ]), ] .map((row) => row.join(",")) .join("\n"); const blob = new Blob([csvContent], { type: "text/csv" }); const url = window.URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `users_${new Date().toISOString().split("T")[0]}.csv`; a.click(); window.URL.revokeObjectURL(url); }; const handleRefresh = () => { fetchUsers(); }; const handleSaveEdit = async () => { if (!editForm) return; try { if (selectedUser) { // Update existing user const payload = { id: selectedUser.id, email: editForm.email, firstName: editForm.firstName, lastName: editForm.lastName, role: editForm.role, phone: editForm.phone, gymId: editForm.gymId === "" ? null : editForm.gymId, }; log.debug("Updating user", payload); const response = await fetch("/api/users", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); log.debug("User update response", { ok: response.ok, status: response.status, }); if (response.ok) { // Optimistically update local state so grid reflects changes immediately setUsers((prev) => prev.map((u) => u.id === selectedUser.id ? { ...u, email: editForm.email, firstName: editForm.firstName, lastName: editForm.lastName, role: editForm.role, phone: editForm.phone || undefined, gymId: editForm.gymId === "" ? undefined : editForm.gymId, } : u, ), ); setSelectedUser((prev) => prev ? { ...prev, email: editForm.email, firstName: editForm.firstName, lastName: editForm.lastName, role: editForm.role, phone: editForm.phone || undefined, gymId: editForm.gymId === "" ? undefined : editForm.gymId, } : prev, ); setIsEditing(false); setEditForm(null); // Still re-fetch from server to ensure consistency log.debug("Re-fetching users after successful edit"); fetchUsers(); toast.success("User updated successfully"); } else { const errText = await response.text().catch(() => ""); log.error("User update failed", new Error(errText), { status: response.status, }); toast.error("Error updating user"); } } else { // Create (Invite) new user const response = await fetch("/api/invitations", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ inviteeEmail: editForm.email, roleAssigned: editForm.role, gymId: editForm.gymId || undefined, }), }); if (response.ok) { setIsEditing(false); setEditForm(null); fetchUsers(); toast.success("Invitation sent successfully!"); } else { const errorData = await response.json(); toast.error(`Error sending invitation: ${errorData.error}`); } } } catch (error) { console.error(error); toast.error("An unexpected error occurred"); } }; const handleDeleteConfirm = async () => { if (!selectedUser) return; try { const response = await fetch(`/api/users?id=${selectedUser.id}`, { method: "DELETE", }); if (response.ok) { setIsDeleting(false); setSelectedUser(null); fetchUsers(); toast.success("User deleted successfully"); } else { toast.error("Error deleting user"); } } catch (error) { log.error("Failed to delete user", error); } }; return (

User Management

Showing {users.length} users {selectedUser && ( Selected: {selectedUser.firstName} {selectedUser.lastName} )}
handleUserSelect(user)} onEditUser={handleEditUser} onDeleteUser={handleDeleteUser} onBulkDelete={handleBulkDelete} loading={loading} /> {isEditing && editForm && (

{selectedUser ? "Edit User" : "Invite New User"}

{ e.preventDefault(); handleSaveEdit(); }} >
setEditForm({ ...editForm, firstName: e.target.value }) } className="w-full border border-gray-300 rounded px-3 py-2" required />
setEditForm({ ...editForm, lastName: e.target.value }) } className="w-full border border-gray-300 rounded px-3 py-2" required />
setEditForm({ ...editForm, email: e.target.value }) } className="w-full border border-gray-300 rounded px-3 py-2" required disabled={!!selectedUser} // Disable email edit for existing users if desired, or keep enabled />

Note: You can only assign roles lower than your own.

setEditForm({ ...editForm, phone: e.target.value }) } className="w-full border border-gray-300 rounded px-3 py-2" />

Select an active gym or proceed without a gym.

)} {isDeleting && selectedUser && (

Delete User

Are you sure you want to delete {selectedUser.firstName}{" "} {selectedUser.lastName}? This action cannot be undone.

)} {selectedUser && (

User Details

View Full Profile & Recommendations

Basic Information

Name:{" "} {selectedUser.firstName} {selectedUser.lastName}

Email:{" "} {selectedUser.email}

Phone:{" "} {selectedUser.phone || "N/A"}

Role:{" "} {selectedUser.role}

Joined:{" "} {new Date(selectedUser.createdAt).toLocaleDateString()}

{selectedUser.client && (

Client Information

Membership:{" "} {selectedUser.client.membershipType}

Status:{" "} {selectedUser.client.membershipStatus}

Member Since:{" "} {new Date( selectedUser.client.joinDate, ).toLocaleDateString()}

Last Visit:{" "} {selectedUser.client.lastVisit ? new Date( selectedUser.client.lastVisit, ).toLocaleDateString() : "Never"}

)}

Check-In Statistics

Last Check-In:{" "} {selectedUser.lastCheckInTime ? new Date(selectedUser.lastCheckInTime).toLocaleString() : "Never"}

This Week:{" "} {selectedUser.checkInsThisWeek || 0} check-ins

This Month:{" "} {selectedUser.checkInsThisMonth || 0} check-ins

)} fetchUsers()} />
); }