diff --git a/ui/litellm-dashboard/src/components/common_components/MemberTable.tsx b/ui/litellm-dashboard/src/components/common_components/MemberTable.tsx
index 7af2e442b90..611f414a64e 100644
--- a/ui/litellm-dashboard/src/components/common_components/MemberTable.tsx
+++ b/ui/litellm-dashboard/src/components/common_components/MemberTable.tsx
@@ -22,6 +22,9 @@ export interface MemberTableProps {
/** When true, renders top-right pagination controls instead of the default antd bottom pagination. */
withPagination?: boolean;
defaultPageSize?: number;
+ /** Controlled current page (optional). When provided, pair with onPageChange. */
+ currentPage?: number;
+ onPageChange?: (page: number) => void;
}
export default function MemberTable({
@@ -38,10 +41,15 @@ export default function MemberTable({
loading,
withPagination = false,
defaultPageSize = 50,
+ currentPage: controlledPage,
+ onPageChange,
}: MemberTableProps) {
- const [page, setPage] = useState(1);
+ const [internalPage, setInternalPage] = useState(1);
const [pageSize, setPageSize] = useState(defaultPageSize);
+ const page = controlledPage ?? internalPage;
+ const setPage = onPageChange ?? setInternalPage;
+
const total = members.length;
const totalPages = Math.max(1, Math.ceil(total / pageSize));
const safePage = Math.min(page, totalPages);
diff --git a/ui/litellm-dashboard/src/components/team/TeamMemberTab.test.tsx b/ui/litellm-dashboard/src/components/team/TeamMemberTab.test.tsx
index a040cbe19ca..2a67cac1203 100644
--- a/ui/litellm-dashboard/src/components/team/TeamMemberTab.test.tsx
+++ b/ui/litellm-dashboard/src/components/team/TeamMemberTab.test.tsx
@@ -403,12 +403,35 @@ describe("TeamMembersComponent", () => {
const roleSelect = screen.getByRole("combobox");
await user.click(roleSelect);
+ // "Admin" filter: only user2 (role: "admin") should remain
await user.click(screen.getByText("Admin"));
expect(screen.getAllByText("user2@test.com").length).toBeGreaterThanOrEqual(1);
expect(screen.queryByText("user1@test.com")).not.toBeInTheDocument();
});
+ it("should filter to non-admin members when Non-admin is selected", async () => {
+ const user = userEvent.setup();
+ renderWithProviders(
+ ,
+ );
+
+ const roleSelect = screen.getByRole("combobox");
+ await user.click(roleSelect);
+ // "Non-admin" filter: only user1 (role: "member") should remain
+ await user.click(screen.getByText("Non-admin"));
+
+ expect(screen.getAllByText("user1@test.com").length).toBeGreaterThanOrEqual(1);
+ expect(screen.queryByText("user2@test.com")).not.toBeInTheDocument();
+ });
+
it("should show all members when search is cleared", async () => {
const user = userEvent.setup();
renderWithProviders(
@@ -430,7 +453,7 @@ describe("TeamMembersComponent", () => {
expect(screen.getAllByText("user2@test.com").length).toBeGreaterThanOrEqual(1);
});
-
+ it("should hide action buttons when canEditTeam is false", () => {
renderWithProviders(
(null);
+ const [memberTablePage, setMemberTablePage] = useState(1);
+
+ // Reset to page 1 when filter/search changes — without remounting MemberTable
+ useEffect(() => { setMemberTablePage(1); }, [searchText, roleFilter]);
// O(1) lookup instead of O(n) find() per member per column
const membershipsMap = useMemo(
@@ -46,7 +50,12 @@ export default function TeamMemberTab({
const filteredMembers = useMemo(() => {
const q = searchText.trim().toLowerCase();
return teamData.team_info.members_with_roles.filter((m) => {
- if (roleFilter && m.role?.toLowerCase() !== roleFilter) return false;
+ if (roleFilter) {
+ const role = m.role?.toLowerCase() ?? "";
+ const isAdmin = role === "admin" || role === "org_admin";
+ if (roleFilter === "admin" && !isAdmin) return false;
+ if (roleFilter === "non-admin" && isAdmin) return false;
+ }
if (!q) return true;
return (
m.user_email?.toLowerCase().includes(q) ||
@@ -226,16 +235,17 @@ export default function TeamMemberTab({
style={{ width: 160 }}
options={[
{ value: "admin", label: "Admin" },
- { value: "user", label: "User" },
+ { value: "non-admin", label: "Non-admin" },
]}
onChange={(v) => setRoleFilter(v ?? null)}
/>
{
const membership = membershipsMap.get(record.user_id ?? "");
const enhancedMember = {