diff --git a/packages/web/src/components/modals/instanceSettingsPanels/UsersPanel.tsx b/packages/web/src/components/modals/instanceSettingsPanels/UsersPanel.tsx index 18c1f495..668a65b9 100644 --- a/packages/web/src/components/modals/instanceSettingsPanels/UsersPanel.tsx +++ b/packages/web/src/components/modals/instanceSettingsPanels/UsersPanel.tsx @@ -16,6 +16,13 @@ export function UsersPanel() { const [page, setPage] = useState(1); const pageSize = 50; + const [instanceFilter, setInstanceFilter] = useState(''); + const [roleFilter, setRoleFilter] = useState(''); + const [joinedAfter, setJoinedAfter] = useState(''); + const [joinedBefore, setJoinedBefore] = useState(''); + const [sortBy, setSortBy] = useState('newest'); + const [instances, setInstances] = useState([]); + // Confirm dialogs const [confirmAction, setConfirmAction] = useState<{ type: 'demote' | 'delete'; user: AdminUser } | null>(null); const [actionLoading, setActionLoading] = useState(false); @@ -25,11 +32,28 @@ export function UsersPanel() { const debounceRef = useRef>(); - const fetchUsers = useCallback(async (q: string, p: number, deleted: boolean) => { + useEffect(() => { + api.admin.listInstances().then(({ instances: inst }) => setInstances(inst)).catch(() => {}); + }, []); + + const filtersRef = useRef({ homeInstance: '', role: '', joinedAfter: '', joinedBefore: '', sort: 'newest' }); + filtersRef.current = { homeInstance: instanceFilter, role: roleFilter, joinedAfter, joinedBefore, sort: sortBy }; + + const fetchUsers = useCallback(async ( + q: string, p: number, deleted: boolean, + opts?: { homeInstance?: string; role?: string; joinedAfter?: string; joinedBefore?: string; sort?: string } + ) => { setLoading(true); setError(''); try { - const result = await api.admin.listUsers({ q: q || undefined, page: p, pageSize, showDeleted: deleted }); + const result = await api.admin.listUsers({ + q: q || undefined, page: p, pageSize, showDeleted: deleted, + homeInstance: opts?.homeInstance || undefined, + role: opts?.role || undefined, + joinedAfter: opts?.joinedAfter || undefined, + joinedBefore: opts?.joinedBefore || undefined, + sort: opts?.sort || undefined, + }); setData(result); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load users'); @@ -39,15 +63,15 @@ export function UsersPanel() { }, []); useEffect(() => { - fetchUsers(query, page, showDeleted); - }, [fetchUsers, page, showDeleted]); // eslint-disable-line react-hooks/exhaustive-deps + fetchUsers(query, page, showDeleted, filtersRef.current); + }, [fetchUsers, page, showDeleted, instanceFilter, roleFilter, joinedAfter, joinedBefore, sortBy]); // eslint-disable-line react-hooks/exhaustive-deps const handleSearchChange = (value: string) => { setQuery(value); if (debounceRef.current) clearTimeout(debounceRef.current); debounceRef.current = setTimeout(() => { setPage(1); - fetchUsers(value, 1, showDeleted); + fetchUsers(value, 1, showDeleted, filtersRef.current); }, 300); }; @@ -61,7 +85,7 @@ export function UsersPanel() { setError(''); try { await api.admin.setUserRole(user.id, true); - fetchUsers(query, page, showDeleted); + fetchUsers(query, page, showDeleted, filtersRef.current); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to update role'); } @@ -106,7 +130,7 @@ export function UsersPanel() { await api.admin.deleteUser(confirmAction.user.id); } setConfirmAction(null); - fetchUsers(query, page, showDeleted); + fetchUsers(query, page, showDeleted, filtersRef.current); } catch (err) { setError(err instanceof Error ? err.message : 'Action failed'); setConfirmAction(null); @@ -129,7 +153,7 @@ export function UsersPanel() { View and manage user accounts on this instance. - {/* Search + Show Deleted */} + {/* Search */}
-
+ + {/* Filters */} +
+ + + + + { setJoinedAfter(e.target.value); setPage(1); }} + className="input-search text-xs py-1" + title="Joined after" + /> + { setJoinedBefore(e.target.value); setPage(1); }} + className="input-search text-xs py-1" + title="Joined before" + /> + + + + + + {(instanceFilter || roleFilter || joinedAfter || joinedBefore || sortBy !== 'newest' || showDeleted || query) && ( + + )}
{/* Error */}