This commit is contained in:
Malini 2025-12-08 16:21:53 +05:30
parent df6e81a2af
commit 6ddc700600
2 changed files with 128 additions and 96 deletions

View File

@ -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}

View File

@ -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">