fixed
This commit is contained in:
parent
df6e81a2af
commit
6ddc700600
@ -123,68 +123,73 @@ const AdminUsers = () => {
|
|||||||
);
|
);
|
||||||
}, [users, searchTerm]);
|
}, [users, searchTerm]);
|
||||||
|
|
||||||
|
// Get current page data
|
||||||
|
const getCurrentPageData = () => {
|
||||||
|
const dataSource = searchTerm ? filteredUsers : users;
|
||||||
|
const startIndex = (currentPage - 1) * pageSize;
|
||||||
|
return dataSource.slice(startIndex, startIndex + pageSize);
|
||||||
|
};
|
||||||
|
|
||||||
// Fetch users with pagination
|
// Fetch users with pagination
|
||||||
useEffect(() => {
|
const fetchUsers = React.useCallback(async (search = searchTerm) => {
|
||||||
const fetchUsers = async () => {
|
try {
|
||||||
try {
|
setLoading(true);
|
||||||
setLoading(true);
|
const res = await getAdminUser(search);
|
||||||
// Pass currentPage and pageSize to the API if it supports pagination
|
|
||||||
// If not, we'll handle pagination client-side
|
|
||||||
const res = await getAdminUser(searchTerm);
|
|
||||||
|
|
||||||
const usersData = Array.isArray(res)
|
const usersData = Array.isArray(res)
|
||||||
? res
|
? res
|
||||||
: Array.isArray(res?.data)
|
: Array.isArray(res?.data)
|
||||||
? res.data
|
? res.data
|
||||||
: Array.isArray(res?.data?.data)
|
: Array.isArray(res?.data?.data)
|
||||||
? res.data.data
|
? res.data.data
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
if (!usersData) {
|
if (!usersData) {
|
||||||
showToast('Failed to load users', 'error');
|
showToast('Failed to load users', 'error');
|
||||||
setUsers([]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const mappedUsers = usersData.map((user) => {
|
|
||||||
const id = user.id ?? user._id ?? user.user_id ?? null;
|
|
||||||
const name = user.name ?? user.fullName ?? 'N/A';
|
|
||||||
const email = user.email ?? 'N/A';
|
|
||||||
const lastLoginRaw = user.updatedAt ?? user.last_login ?? user.lastLogin ?? null;
|
|
||||||
const lastLogin = lastLoginRaw
|
|
||||||
? new Date(lastLoginRaw).toLocaleString('en-GB', {
|
|
||||||
day: '2-digit',
|
|
||||||
month: 'short',
|
|
||||||
year: 'numeric',
|
|
||||||
hour: '2-digit',
|
|
||||||
minute: '2-digit',
|
|
||||||
})
|
|
||||||
: 'Never logged in';
|
|
||||||
const is_active =
|
|
||||||
user.is_active !== undefined ? !!user.is_active : user.active ?? true;
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
email,
|
|
||||||
lastLogin,
|
|
||||||
status: is_active ? 'Active' : 'Inactive',
|
|
||||||
is_active,
|
|
||||||
raw: user,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
setUsers(mappedUsers);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching admin users:', error);
|
|
||||||
showToast('Error fetching admin users', 'error');
|
|
||||||
setUsers([]);
|
setUsers([]);
|
||||||
} finally {
|
return;
|
||||||
setLoading(false);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
const mappedUsers = usersData.map((user) => {
|
||||||
|
const id = user.id ?? user._id ?? user.user_id ?? null;
|
||||||
|
const name = user.name ?? user.fullName ?? 'N/A';
|
||||||
|
const email = user.email ?? 'N/A';
|
||||||
|
const lastLoginRaw = user.updatedAt ?? user.last_login ?? user.lastLogin ?? null;
|
||||||
|
const lastLogin = lastLoginRaw
|
||||||
|
? new Date(lastLoginRaw).toLocaleString('en-GB', {
|
||||||
|
day: '2-digit',
|
||||||
|
month: 'short',
|
||||||
|
year: 'numeric',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
})
|
||||||
|
: 'Never logged in';
|
||||||
|
const is_active =
|
||||||
|
user.is_active !== undefined ? !!user.is_active : user.active ?? true;
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
lastLogin,
|
||||||
|
status: is_active ? 'Active' : 'Inactive',
|
||||||
|
is_active,
|
||||||
|
raw: user,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
setUsers(mappedUsers);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching admin users:', error);
|
||||||
|
showToast('Error fetching admin users', 'error');
|
||||||
|
setUsers([]);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, [searchTerm]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
fetchUsers();
|
fetchUsers();
|
||||||
}, [currentPage, searchTerm]); // Add currentPage and searchTerm as dependencies
|
}, [currentPage, fetchUsers]);
|
||||||
|
|
||||||
const totalUsers = searchTerm ? filteredUsers.length : users.length;
|
const totalUsers = searchTerm ? filteredUsers.length : users.length;
|
||||||
const activeUsers = (searchTerm ? filteredUsers : users).filter((user) => user.status === 'Active').length;
|
const activeUsers = (searchTerm ? filteredUsers : users).filter((user) => user.status === 'Active').length;
|
||||||
@ -356,7 +361,7 @@ const AdminUsers = () => {
|
|||||||
|
|
||||||
const success =
|
const success =
|
||||||
res?.status === "success" ||
|
res?.status === "success" ||
|
||||||
res?.code === 200 ||
|
res?.status === 200 ||
|
||||||
res?.data?.status === "success" ||
|
res?.data?.status === "success" ||
|
||||||
(!res.error && res);
|
(!res.error && res);
|
||||||
|
|
||||||
@ -705,6 +710,32 @@ const AdminUsers = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handlePageChange = (page) => {
|
||||||
|
setCurrentPage(page);
|
||||||
|
// Clear any row states when changing pages
|
||||||
|
setEditingRow(null);
|
||||||
|
setDeletingRow(null);
|
||||||
|
setResetUser(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSearch = (e) => {
|
||||||
|
setSearchTerm(e.target.value);
|
||||||
|
setCurrentPage(1); // Reset to first page on search
|
||||||
|
// Clear any row states
|
||||||
|
setEditingRow(null);
|
||||||
|
setDeletingRow(null);
|
||||||
|
setResetUser(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePageSizeChange = (size) => {
|
||||||
|
setPageSize(size);
|
||||||
|
setCurrentPage(1);
|
||||||
|
// Clear any row states when changing page size
|
||||||
|
setEditingRow(null);
|
||||||
|
setDeletingRow(null);
|
||||||
|
setResetUser(null);
|
||||||
|
};
|
||||||
|
|
||||||
const tableToolbar = (
|
const tableToolbar = (
|
||||||
<div className="px-6 py-4 flex flex-col lg:flex-row lg:items-center lg:justify-between gap-3 bg-white">
|
<div className="px-6 py-4 flex flex-col lg:flex-row lg:items-center lg:justify-between gap-3 bg-white">
|
||||||
<h3 className="text-sm font-medium text-[#232528] whitespace-nowrap">Admin Users</h3>
|
<h3 className="text-sm font-medium text-[#232528] whitespace-nowrap">Admin Users</h3>
|
||||||
@ -718,9 +749,9 @@ const AdminUsers = () => {
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
onChange={handleSearch}
|
||||||
placeholder="Search by name, email, or status"
|
placeholder="Search by name, email, or status"
|
||||||
className="h-10 w-[350px] rounded-lg border border-[#E6EAF5] pl-10 pr-4 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
className="h-10 w-[350px] rounded-lg border border-[#E6EAF5] pl-10 pr-4 text-sm focus:outline-none focus:ring-2 focus:ring-[#B68A35]"
|
||||||
/>
|
/>
|
||||||
{searchTerm && (
|
{searchTerm && (
|
||||||
<button
|
<button
|
||||||
@ -800,22 +831,17 @@ const AdminUsers = () => {
|
|||||||
beforeHeader={tableToolbar}
|
beforeHeader={tableToolbar}
|
||||||
pagination={{
|
pagination={{
|
||||||
currentPage,
|
currentPage,
|
||||||
onPageChange: setCurrentPage,
|
onPageChange: handlePageChange,
|
||||||
pageSize,
|
pageSize,
|
||||||
totalItems: searchTerm ? filteredUsers.length : users.length,
|
totalItems: searchTerm ? filteredUsers.length : users.length,
|
||||||
pageSizeOptions: [10, 20, 50, 100],
|
pageSizeOptions: [10, 20, 50, 100],
|
||||||
onPageSizeChange: (size) => {
|
onPageSizeChange: handlePageSizeChange
|
||||||
setPageSize(size);
|
|
||||||
setCurrentPage(1);
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
renderCell={(value, rowIndex, colIndex) => {
|
renderCell={(value, rowIndex, colIndex) => {
|
||||||
if (colIndex === 4) {
|
if (colIndex === 4) {
|
||||||
// Get the current data source based on search
|
// Get the current page data
|
||||||
const dataSource = searchTerm ? filteredUsers : users;
|
const currentPageData = getCurrentPageData();
|
||||||
// Calculate the actual index in the current data source based on pagination
|
const user = currentPageData[rowIndex];
|
||||||
const actualIndex = (currentPage - 1) * pageSize + rowIndex;
|
|
||||||
const user = dataSource[actualIndex];
|
|
||||||
if (!user) return null;
|
if (!user) return null;
|
||||||
|
|
||||||
const isActive = user.status === 'Active';
|
const isActive = user.status === 'Active';
|
||||||
@ -824,15 +850,8 @@ const AdminUsers = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="h-8 w-8 grid place-items-center rounded hover:bg-gray-50 cursor-pointer"
|
className="h-8 w-8 grid place-items-center rounded hover:bg-gray-50 cursor-pointer"
|
||||||
onClick={() => {
|
onClick={() => handleEdit(user.raw || user)}
|
||||||
// Make sure we're using the full user object from the current data source
|
title="Edit user"
|
||||||
const dataSource = searchTerm ? filteredUsers : users;
|
|
||||||
const actualIndex = (currentPage - 1) * pageSize + rowIndex;
|
|
||||||
const currentUser = dataSource[actualIndex];
|
|
||||||
if (currentUser) {
|
|
||||||
handleEdit(currentUser.raw || currentUser);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={editingRow?.id === user.id ? pencilActiveSrc : pencilInactiveSrc}
|
src={editingRow?.id === user.id ? pencilActiveSrc : pencilInactiveSrc}
|
||||||
@ -844,11 +863,14 @@ const AdminUsers = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="h-8 w-8 grid place-items-center rounded hover:bg-gray-50 cursor-pointer"
|
className="h-8 w-8 grid place-items-center rounded hover:bg-gray-50 cursor-pointer"
|
||||||
title="Delete"
|
title="Delete user"
|
||||||
onClick={() => handleDelete(rowIndex)}
|
onClick={() => {
|
||||||
|
setSelectedUser(user);
|
||||||
|
setShowDeleteModal(true);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={deletingRow === rowIndex ? trashActiveSrc : trashInactiveSrc}
|
src={deletingRow === user.id ? trashActiveSrc : trashInactiveSrc}
|
||||||
alt="Delete"
|
alt="Delete"
|
||||||
className="h-4 w-4"
|
className="h-4 w-4"
|
||||||
/>
|
/>
|
||||||
@ -881,19 +903,29 @@ const AdminUsers = () => {
|
|||||||
<AddAdminUsers
|
<AddAdminUsers
|
||||||
isOpen={isAddUserModalOpen}
|
isOpen={isAddUserModalOpen}
|
||||||
onClose={() => setIsAddUserModalOpen(false)}
|
onClose={() => setIsAddUserModalOpen(false)}
|
||||||
onSave={(newUser) => {
|
onSave={async (newUser) => {
|
||||||
setUsers((prev) => [
|
try {
|
||||||
...prev,
|
// First, optimistically update the UI
|
||||||
{
|
setUsers(prev => [
|
||||||
...newUser,
|
...prev,
|
||||||
id: newUser.id ?? `local-${Date.now()}`,
|
{
|
||||||
lastLogin: new Date().toLocaleString('en-GB'),
|
...newUser,
|
||||||
status: 'Active',
|
id: newUser.id ?? `local-${Date.now()}`,
|
||||||
is_active: true,
|
lastLogin: new Date().toLocaleString('en-GB'),
|
||||||
},
|
status: 'Active',
|
||||||
]);
|
is_active: true,
|
||||||
setIsAddUserModalOpen(false);
|
},
|
||||||
showToast('User added locally', 'success');
|
]);
|
||||||
|
|
||||||
|
// Then fetch fresh data to ensure consistency
|
||||||
|
await fetchUsers();
|
||||||
|
|
||||||
|
setIsAddUserModalOpen(false);
|
||||||
|
showToast('User added successfully', 'success');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error adding user:', error);
|
||||||
|
showToast('Error adding user', 'error');
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
passwordMinLength={8}
|
passwordMinLength={8}
|
||||||
passwordMaxLength={12}
|
passwordMaxLength={12}
|
||||||
|
|||||||
@ -2896,7 +2896,7 @@ const handleView = async (id) => {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}, 1000);
|
}, 3000);
|
||||||
|
|
||||||
return apiResponse?.data;
|
return apiResponse?.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -3461,13 +3461,13 @@ const handleImport = async () => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Full Page Loader */}
|
{/* Full Page Loader */}
|
||||||
{isRefreshing && (
|
{/* {isRefreshing && (
|
||||||
<div className="fixed inset-0 z-[9999] bg-white/80 flex flex-col items-center justify-center">
|
<div className="fixed inset-0 z-[9999] bg-white/80 flex flex-col items-center justify-center">
|
||||||
<div className="animate-spin rounded-full h-16 w-16 border-t-4 border-[#92722A]"></div>
|
<div className="animate-spin rounded-full h-16 w-16 border-t-4 border-[#92722A]"></div>
|
||||||
<p className="mt-4 text-lg font-medium text-gray-700">Updating data...</p>
|
<p className="mt-4 text-lg font-medium text-gray-700">Updating data...</p>
|
||||||
<p className="text-sm text-gray-500">Please wait while we refresh the page</p>
|
<p className="text-sm text-gray-500">Please wait while we refresh the page</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)} */}
|
||||||
|
|
||||||
{showDeleteConfirm && deletingProfile && (
|
{showDeleteConfirm && deletingProfile && (
|
||||||
<div className="fixed inset-0 z-50">
|
<div className="fixed inset-0 z-50">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user