Create Organisation Fix
This commit is contained in:
@@ -35,11 +35,13 @@ import {
|
||||
Hash,
|
||||
ChevronDown,
|
||||
ChevronRight,
|
||||
ChevronLeft,
|
||||
User as UserIcon,
|
||||
RotateCcw,
|
||||
} from 'lucide-react';
|
||||
import { CreateUserModal } from '../components/users/CreateUserModal';
|
||||
import { UserSecurityModal } from '../components/users/UserSecurityModal';
|
||||
import { ObfuscatedIdNumber } from '../components/ui/ObfuscatedIdNumber';
|
||||
import { useRbac } from '../auth/rbac';
|
||||
import {
|
||||
getUsers,
|
||||
@@ -127,6 +129,13 @@ export const SettingsPage: React.FC = () => {
|
||||
const [isUserDetailModalOpen, setIsUserDetailModalOpen] = useState(false);
|
||||
const [isCreateUserModalOpen, setIsCreateUserModalOpen] = useState(false);
|
||||
|
||||
const [userPage, setUserPage] = useState(1);
|
||||
const [userPageSize, setUserPageSize] = useState(10);
|
||||
|
||||
React.useEffect(() => {
|
||||
setUserPage(1);
|
||||
}, [userEmailSearch, userStatusFilter, userRoleFilter]);
|
||||
|
||||
const userFilter: UserFilterRequest = {
|
||||
email: userEmailSearch.trim() || undefined,
|
||||
status: userStatusFilter !== 'all' ? userStatusFilter : undefined,
|
||||
@@ -145,7 +154,7 @@ export const SettingsPage: React.FC = () => {
|
||||
refetch: refetchUsers,
|
||||
} = useQuery({
|
||||
queryKey: ['users', userStatusFilter, userEmailSearch],
|
||||
queryFn: () => getUsers(1, 100, userFilter),
|
||||
queryFn: () => getUsers(1, 1000, userFilter),
|
||||
});
|
||||
|
||||
const filteredUsers = useMemo(() => {
|
||||
@@ -489,6 +498,13 @@ export const SettingsPage: React.FC = () => {
|
||||
role: (u: User) => getUserRoleBadge(u.role).label,
|
||||
});
|
||||
|
||||
const totalUsers = sortedUsers.length;
|
||||
const totalPages = Math.max(1, Math.ceil(totalUsers / userPageSize));
|
||||
const paginatedUsers = React.useMemo(() => {
|
||||
const start = (userPage - 1) * userPageSize;
|
||||
return sortedUsers.slice(start, start + userPageSize);
|
||||
}, [sortedUsers, userPage, userPageSize]);
|
||||
|
||||
const {
|
||||
sortedData: sortedInvites,
|
||||
sortColumn: inviteSortCol,
|
||||
@@ -842,7 +858,7 @@ export const SettingsPage: React.FC = () => {
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
sortedUsers.map((u) => {
|
||||
paginatedUsers.map((u) => {
|
||||
const badge = getActivityStatusBadge(u.status);
|
||||
return (
|
||||
<tr key={u.id} className="hover:bg-slate-800/40 transition-colors">
|
||||
@@ -853,7 +869,7 @@ export const SettingsPage: React.FC = () => {
|
||||
{u.id}
|
||||
</td>
|
||||
<td className="py-3.5 px-4 font-mono text-slate-300 text-[11px]">
|
||||
{u.idNumber || <span className="text-slate-600">—</span>}
|
||||
<ObfuscatedIdNumber idNumber={u.idNumber} />
|
||||
</td>
|
||||
<td className="py-3.5 px-4">
|
||||
<UserRoleBadge role={u.role} />
|
||||
@@ -915,6 +931,64 @@ export const SettingsPage: React.FC = () => {
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Pagination Controls */}
|
||||
{totalUsers > 0 && (
|
||||
<div className="flex flex-col sm:flex-row items-center justify-between gap-3 px-5 py-3 border-t border-slate-800/80 bg-slate-900/40 text-xs text-slate-400">
|
||||
<div className="flex items-center gap-2">
|
||||
<span>
|
||||
Showing <strong className="text-white font-mono">{Math.min((userPage - 1) * userPageSize + 1, totalUsers)}</strong> to{' '}
|
||||
<strong className="text-white font-mono">{Math.min(userPage * userPageSize, totalUsers)}</strong> of{' '}
|
||||
<strong className="text-white font-mono">{totalUsers}</strong> users
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="text-slate-400">Per page:</span>
|
||||
<select
|
||||
value={userPageSize}
|
||||
onChange={(e) => {
|
||||
setUserPageSize(Number(e.target.value));
|
||||
setUserPage(1);
|
||||
}}
|
||||
className="bg-slate-900 border border-slate-700/80 rounded-lg px-2 py-1 text-xs text-white focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||||
>
|
||||
<option value={10}>10</option>
|
||||
<option value={25}>25</option>
|
||||
<option value={50}>50</option>
|
||||
<option value={100}>100</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setUserPage((p) => Math.max(1, p - 1))}
|
||||
disabled={userPage <= 1}
|
||||
className="p-1 rounded-lg border border-slate-800 bg-slate-900 text-slate-300 hover:bg-slate-800 disabled:opacity-40 disabled:cursor-not-allowed transition-colors"
|
||||
title="Previous page"
|
||||
>
|
||||
<ChevronLeft className="w-4 h-4" />
|
||||
</button>
|
||||
|
||||
<span className="px-2 font-mono text-slate-300">
|
||||
{userPage} / {totalPages}
|
||||
</span>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setUserPage((p) => Math.min(totalPages, p + 1))}
|
||||
disabled={userPage >= totalPages}
|
||||
className="p-1 rounded-lg border border-slate-800 bg-slate-900 text-slate-300 hover:bg-slate-800 disabled:opacity-40 disabled:cursor-not-allowed transition-colors"
|
||||
title="Next page"
|
||||
>
|
||||
<ChevronRight className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user