From 98b48d60a4338175282b17403b995284472ddc2c Mon Sep 17 00:00:00 2001 From: Brett Kolodny Date: Mon, 10 Mar 2025 20:30:33 +0000 Subject: [PATCH 01/10] feat: implement pagination for org members table --- site/src/api/api.ts | 17 ++ site/src/api/queries/organizations.ts | 22 +++ .../OrganizationMembersPage.tsx | 20 ++- .../OrganizationMembersPageView.tsx | 164 ++++++++++-------- 4 files changed, 146 insertions(+), 77 deletions(-) diff --git a/site/src/api/api.ts b/site/src/api/api.ts index 627ede80976c6..d9142dfa3612f 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -583,6 +583,23 @@ class ApiMethods { return response.data; }; + /** + * @param organization Can be the organization's ID or name + * @param options Pagination options + */ + getOrganizationPaginatedMembers = async ( + organization: string, + options?: TypesGen.Pagination + ) => { + const url = getURLWithSearchParams( + `/api/v2/organizations/${organization}/paginated-members`, + options + ); + const response = await this.axios.get(url); + + return response.data; + }; + /** * @param organization Can be the organization's ID or name */ diff --git a/site/src/api/queries/organizations.ts b/site/src/api/queries/organizations.ts index bca0bc6a72fff..23dac6ae66d78 100644 --- a/site/src/api/queries/organizations.ts +++ b/site/src/api/queries/organizations.ts @@ -2,9 +2,11 @@ import { API } from "api/api"; import type { CreateOrganizationRequest, GroupSyncSettings, + PaginatedMembersResponse, RoleSyncSettings, UpdateOrganizationRequest, } from "api/typesGenerated"; +import type { UsePaginatedQueryOptions } from "hooks/usePaginatedQuery"; import { type OrganizationPermissionName, type OrganizationPermissions, @@ -59,6 +61,26 @@ export const organizationMembersKey = (id: string) => [ "members", ]; +export function paginatedOrganizationMembers( + organizationName: string, + searchParams: URLSearchParams +): UsePaginatedQueryOptions { + return { + searchParams, + queryPayload: (params) => { + return { + limit: params.limit, + offset: params.offset, + }; + }, + queryKey: ({ payload }) => [ + ...organizationMembersKey(organizationName), + payload, + ], + queryFn: ({ payload }) => API.getOrganizationPaginatedMembers(organizationName, payload), + }; +} + export const organizationMembers = (id: string) => { return { queryFn: () => API.getOrganizationMembers(id), diff --git a/site/src/pages/OrganizationSettingsPage/OrganizationMembersPage.tsx b/site/src/pages/OrganizationSettingsPage/OrganizationMembersPage.tsx index ffa7b08b83742..69b83d32f25cf 100644 --- a/site/src/pages/OrganizationSettingsPage/OrganizationMembersPage.tsx +++ b/site/src/pages/OrganizationSettingsPage/OrganizationMembersPage.tsx @@ -3,7 +3,7 @@ import { getErrorMessage } from "api/errors"; import { groupsByUserIdInOrganization } from "api/queries/groups"; import { addOrganizationMember, - organizationMembers, + paginatedOrganizationMembers, removeOrganizationMember, updateOrganizationMemberRoles, } from "api/queries/organizations"; @@ -14,12 +14,13 @@ import { EmptyState } from "components/EmptyState/EmptyState"; import { displayError, displaySuccess } from "components/GlobalSnackbar/utils"; import { Stack } from "components/Stack/Stack"; import { useAuthenticated } from "contexts/auth/RequireAuth"; +import { usePaginatedQuery } from "hooks/usePaginatedQuery"; import { useOrganizationSettings } from "modules/management/OrganizationSettingsLayout"; import { RequirePermission } from "modules/permissions/RequirePermission"; import { type FC, useState } from "react"; import { Helmet } from "react-helmet-async"; import { useMutation, useQuery, useQueryClient } from "react-query"; -import { useParams } from "react-router-dom"; +import { useParams, useSearchParams } from "react-router-dom"; import { pageTitle } from "utils/page"; import { OrganizationMembersPageView } from "./OrganizationMembersPageView"; @@ -30,14 +31,18 @@ const OrganizationMembersPage: FC = () => { organization: string; }; const { organization, organizationPermissions } = useOrganizationSettings(); + const searchParamsResult = useSearchParams(); - const membersQuery = useQuery(organizationMembers(organizationName)); const organizationRolesQuery = useQuery(organizationRoles(organizationName)); const groupsByUserIdQuery = useQuery( groupsByUserIdInOrganization(organizationName), ); - const members = membersQuery.data?.map((member) => { + const membersQuery = usePaginatedQuery( + paginatedOrganizationMembers(organizationName, searchParamsResult[0]) + ); + + const members = membersQuery.data?.Members.map((member: OrganizationMemberWithUserData) => { const groups = groupsByUserIdQuery.data?.get(member.user_id) ?? []; return { ...member, groups }; }); @@ -76,6 +81,11 @@ const OrganizationMembersPage: FC = () => { ); } + const isLoading = + membersQuery.isLoading || + organizationRolesQuery.isLoading || + groupsByUserIdQuery.isLoading; + return ( <> {helmet} @@ -95,6 +105,8 @@ const OrganizationMembersPage: FC = () => { isUpdatingMemberRoles={updateMemberRolesMutation.isLoading} me={me} members={members} + isLoading={isLoading} + membersQuery={membersQuery} addMember={async (user: User) => { await addMemberMutation.mutateAsync(user.id); void membersQuery.refetch(); diff --git a/site/src/pages/OrganizationSettingsPage/OrganizationMembersPageView.tsx b/site/src/pages/OrganizationSettingsPage/OrganizationMembersPageView.tsx index 743e8a9381e15..95415f975efb7 100644 --- a/site/src/pages/OrganizationSettingsPage/OrganizationMembersPageView.tsx +++ b/site/src/pages/OrganizationSettingsPage/OrganizationMembersPageView.tsx @@ -18,7 +18,9 @@ import { MoreMenuTrigger, ThreeDotsButton, } from "components/MoreMenu/MoreMenu"; +import { PaginationContainer } from "components/PaginationWidget/PaginationContainer"; import { SettingsHeader } from "components/SettingsHeader/SettingsHeader"; +import { Loader } from "components/Loader/Loader"; import { Stack } from "components/Stack/Stack"; import { Table, @@ -28,6 +30,7 @@ import { TableRow, } from "components/Table/Table"; import { UserAutocomplete } from "components/UserAutocomplete/UserAutocomplete"; +import type { PaginationResultInfo } from "hooks/usePaginatedQuery"; import { TriangleAlert } from "lucide-react"; import { UserGroupsCell } from "pages/UsersPage/UsersTable/UserGroupsCell"; import { type FC, useState } from "react"; @@ -41,8 +44,12 @@ interface OrganizationMembersPageViewProps { error: unknown; isAddingMember: boolean; isUpdatingMemberRoles: boolean; + isLoading: boolean; me: User; members: Array | undefined; + membersQuery: PaginationResultInfo & { + isPreviousData: boolean; + }; addMember: (user: User) => Promise; removeMember: (member: OrganizationMemberWithUserData) => void; updateMemberRoles: ( @@ -64,8 +71,10 @@ export const OrganizationMembersPageView: FC< error, isAddingMember, isUpdatingMemberRoles, + isLoading, me, members, + membersQuery, addMember, removeMember, updateMemberRoles, @@ -92,80 +101,89 @@ export const OrganizationMembersPageView: FC< )} - - - - User - - - Roles - - - - - - Groups - - - - - - - - {members?.map((member) => ( - - - + ) : ( + +
+ + + User + + + Roles + + + + + + Groups + + + + + + + + {members?.map((member) => ( + + + + } + title={member.name || member.username} + subtitle={member.email} /> - } - title={member.name || member.username} - subtitle={member.email} - /> - - { - try { - await updateMemberRoles(member, roles); - displaySuccess("Roles updated successfully."); - } catch (error) { - displayError( - getErrorMessage(error, "Failed to update roles."), - ); - } - }} - /> - - - {member.user_id !== me.id && canEditMembers && ( - - - - - - removeMember(member)} - > - Remove - - - - )} - - - ))} - -
+ + { + try { + await updateMemberRoles(member, roles); + displaySuccess("Roles updated successfully."); + } catch (error) { + displayError( + getErrorMessage(error, "Failed to update roles."), + ); + } + }} + /> + + + {member.user_id !== me.id && canEditMembers && ( + + + + + + removeMember(member)} + > + Remove + + + + )} + + + ))} + + + + )} ); From 54a90679e4696a682210dbc33bb5a126e0de1aa0 Mon Sep 17 00:00:00 2001 From: Brett Kolodny Date: Tue, 11 Mar 2025 15:18:41 +0000 Subject: [PATCH 02/10] fix: use new endpoint in Autocomplete component --- codersdk/organizations.go | 9 ++++----- site/src/api/api.ts | 7 ++++--- site/src/api/queries/organizations.ts | 14 ++++++++------ site/src/api/typesGenerated.ts | 3 +-- .../UserAutocomplete/UserAutocomplete.tsx | 13 ++++++++----- .../OrganizationMembersPage.tsx | 16 +++++++++------- .../OrganizationMembersPageView.tsx | 2 +- 7 files changed, 35 insertions(+), 29 deletions(-) diff --git a/codersdk/organizations.go b/codersdk/organizations.go index e093f6f85594a..8a028d46e098c 100644 --- a/codersdk/organizations.go +++ b/codersdk/organizations.go @@ -82,14 +82,13 @@ type OrganizationMemberWithUserData struct { } type PaginatedMembersRequest struct { - OrganizationID uuid.UUID `table:"organization id" json:"organization_id" format:"uuid"` - Limit int `json:"limit,omitempty"` - Offset int `json:"offset,omitempty"` + Limit int `json:"limit,omitempty"` + Offset int `json:"offset,omitempty"` } type PaginatedMembersResponse struct { - Members []OrganizationMemberWithUserData - Count int `json:"count"` + Members []OrganizationMemberWithUserData `json:"members"` + Count int `json:"count"` } type CreateOrganizationRequest struct { diff --git a/site/src/api/api.ts b/site/src/api/api.ts index d9142dfa3612f..b6012335f93d8 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -589,13 +589,14 @@ class ApiMethods { */ getOrganizationPaginatedMembers = async ( organization: string, - options?: TypesGen.Pagination + options?: TypesGen.Pagination, ) => { const url = getURLWithSearchParams( `/api/v2/organizations/${organization}/paginated-members`, - options + options, ); - const response = await this.axios.get(url); + const response = + await this.axios.get(url); return response.data; }; diff --git a/site/src/api/queries/organizations.ts b/site/src/api/queries/organizations.ts index 23dac6ae66d78..798430c173912 100644 --- a/site/src/api/queries/organizations.ts +++ b/site/src/api/queries/organizations.ts @@ -2,6 +2,7 @@ import { API } from "api/api"; import type { CreateOrganizationRequest, GroupSyncSettings, + PaginatedMembersRequest, PaginatedMembersResponse, RoleSyncSettings, UpdateOrganizationRequest, @@ -63,21 +64,22 @@ export const organizationMembersKey = (id: string) => [ export function paginatedOrganizationMembers( organizationName: string, - searchParams: URLSearchParams -): UsePaginatedQueryOptions { + searchParams: URLSearchParams, +): UsePaginatedQueryOptions { return { searchParams, - queryPayload: (params) => { + queryPayload: ({ limit, offset }) => { return { - limit: params.limit, - offset: params.offset, + limit: limit, + offset: offset, }; }, queryKey: ({ payload }) => [ ...organizationMembersKey(organizationName), payload, ], - queryFn: ({ payload }) => API.getOrganizationPaginatedMembers(organizationName, payload), + queryFn: ({ payload }) => + API.getOrganizationPaginatedMembers(organizationName, payload), }; } diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 6fdfb5ea9d9a1..cd993e61db94a 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -1486,14 +1486,13 @@ export interface OrganizationSyncSettings { // From codersdk/organizations.go export interface PaginatedMembersRequest { - readonly organization_id: string; readonly limit?: number; readonly offset?: number; } // From codersdk/organizations.go export interface PaginatedMembersResponse { - readonly Members: readonly OrganizationMemberWithUserData[]; + readonly members: readonly OrganizationMemberWithUserData[]; readonly count: number; } diff --git a/site/src/components/UserAutocomplete/UserAutocomplete.tsx b/site/src/components/UserAutocomplete/UserAutocomplete.tsx index f5bfd109c4a5c..88ce8a6ff9795 100644 --- a/site/src/components/UserAutocomplete/UserAutocomplete.tsx +++ b/site/src/components/UserAutocomplete/UserAutocomplete.tsx @@ -3,12 +3,13 @@ import Autocomplete from "@mui/material/Autocomplete"; import CircularProgress from "@mui/material/CircularProgress"; import TextField from "@mui/material/TextField"; import { getErrorMessage } from "api/errors"; -import { organizationMembers } from "api/queries/organizations"; +import { paginatedOrganizationMembers } from "api/queries/organizations"; import { users } from "api/queries/users"; import type { OrganizationMemberWithUserData, User } from "api/typesGenerated"; import { Avatar } from "components/Avatar/Avatar"; import { AvatarData } from "components/Avatar/AvatarData"; import { useDebouncedFunction } from "hooks/debounce"; +import { usePaginatedQuery } from "hooks/usePaginatedQuery"; import { type ChangeEvent, type ComponentProps, @@ -69,18 +70,20 @@ export const MemberAutocomplete: FC = ({ }) => { const [filter, setFilter] = useState(); + const searchParams = new URLSearchParams(); + searchParams.append("limit", "0"); + // Currently this queries all members, as there is no pagination. - const membersQuery = useQuery({ - ...organizationMembers(organizationId), + const membersQuery = usePaginatedQuery({ + ...paginatedOrganizationMembers(organizationId, searchParams), enabled: filter !== undefined, - keepPreviousData: true, }); return ( error={membersQuery.error} isFetching={membersQuery.isFetching} setFilter={setFilter} - users={membersQuery.data} + users={membersQuery.data?.members} {...props} /> ); diff --git a/site/src/pages/OrganizationSettingsPage/OrganizationMembersPage.tsx b/site/src/pages/OrganizationSettingsPage/OrganizationMembersPage.tsx index 69b83d32f25cf..40fe42e5d98c8 100644 --- a/site/src/pages/OrganizationSettingsPage/OrganizationMembersPage.tsx +++ b/site/src/pages/OrganizationSettingsPage/OrganizationMembersPage.tsx @@ -39,13 +39,15 @@ const OrganizationMembersPage: FC = () => { ); const membersQuery = usePaginatedQuery( - paginatedOrganizationMembers(organizationName, searchParamsResult[0]) + paginatedOrganizationMembers(organizationName, searchParamsResult[0]), ); - const members = membersQuery.data?.Members.map((member: OrganizationMemberWithUserData) => { - const groups = groupsByUserIdQuery.data?.get(member.user_id) ?? []; - return { ...member, groups }; - }); + const members = membersQuery.data?.members.map( + (member: OrganizationMemberWithUserData) => { + const groups = groupsByUserIdQuery.data?.get(member.user_id) ?? []; + return { ...member, groups }; + }, + ); const addMemberMutation = useMutation( addOrganizationMember(queryClient, organizationName), @@ -81,8 +83,8 @@ const OrganizationMembersPage: FC = () => { ); } - const isLoading = - membersQuery.isLoading || + const isLoading = + membersQuery.isLoading || organizationRolesQuery.isLoading || groupsByUserIdQuery.isLoading; diff --git a/site/src/pages/OrganizationSettingsPage/OrganizationMembersPageView.tsx b/site/src/pages/OrganizationSettingsPage/OrganizationMembersPageView.tsx index 95415f975efb7..1b85583e8c374 100644 --- a/site/src/pages/OrganizationSettingsPage/OrganizationMembersPageView.tsx +++ b/site/src/pages/OrganizationSettingsPage/OrganizationMembersPageView.tsx @@ -11,6 +11,7 @@ import { ErrorAlert } from "components/Alert/ErrorAlert"; import { Avatar } from "components/Avatar/Avatar"; import { AvatarData } from "components/Avatar/AvatarData"; import { displayError, displaySuccess } from "components/GlobalSnackbar/utils"; +import { Loader } from "components/Loader/Loader"; import { MoreMenu, MoreMenuContent, @@ -20,7 +21,6 @@ import { } from "components/MoreMenu/MoreMenu"; import { PaginationContainer } from "components/PaginationWidget/PaginationContainer"; import { SettingsHeader } from "components/SettingsHeader/SettingsHeader"; -import { Loader } from "components/Loader/Loader"; import { Stack } from "components/Stack/Stack"; import { Table, From 1f6b13c7d9e3ccc29ce6721a87d1b88cf2ad06b9 Mon Sep 17 00:00:00 2001 From: Brett Kolodny Date: Tue, 11 Mar 2025 15:21:38 +0000 Subject: [PATCH 03/10] fix: remove unused code --- site/src/api/queries/organizations.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/site/src/api/queries/organizations.ts b/site/src/api/queries/organizations.ts index 798430c173912..0c988a7932abc 100644 --- a/site/src/api/queries/organizations.ts +++ b/site/src/api/queries/organizations.ts @@ -83,13 +83,6 @@ export function paginatedOrganizationMembers( }; } -export const organizationMembers = (id: string) => { - return { - queryFn: () => API.getOrganizationMembers(id), - queryKey: organizationMembersKey(id), - }; -}; - export const addOrganizationMember = (queryClient: QueryClient, id: string) => { return { mutationFn: (userId: string) => { From cfd11f6f945c9bdba8d8ac0e3a14d726538f6faf Mon Sep 17 00:00:00 2001 From: Brett Kolodny Date: Wed, 12 Mar 2025 14:07:49 +0000 Subject: [PATCH 04/10] feat: create un-paginated version of query --- site/src/api/queries/organizations.ts | 38 ++++++++++++++----- .../UserAutocomplete/UserAutocomplete.tsx | 14 +++---- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/site/src/api/queries/organizations.ts b/site/src/api/queries/organizations.ts index 0c988a7932abc..2dc0402d75484 100644 --- a/site/src/api/queries/organizations.ts +++ b/site/src/api/queries/organizations.ts @@ -62,10 +62,32 @@ export const organizationMembersKey = (id: string) => [ "members", ]; -export function paginatedOrganizationMembers( - organizationName: string, +/** + * Creates a query configuration to fetch all members of an organization. + * + * Unlike the paginated version, this function sets the `limit` parameter to 0, + * which instructs the API to return all organization members in a single request + * without pagination. + * + * @param id - The unique identifier of the organization + * @returns A query configuration object for use with React Query + * + * @see paginatedOrganizationMembers - For fetching members with pagination support + */ +export const organizationMembers = (id: string) => { + return { + queryFn: () => API.getOrganizationPaginatedMembers(id, { limit: 0 }), + queryKey: organizationMembersKey(id), + }; +}; + +export const paginatedOrganizationMembers = ( + id: string, searchParams: URLSearchParams, -): UsePaginatedQueryOptions { +): UsePaginatedQueryOptions< + PaginatedMembersResponse, + PaginatedMembersRequest +> => { return { searchParams, queryPayload: ({ limit, offset }) => { @@ -74,14 +96,10 @@ export function paginatedOrganizationMembers( offset: offset, }; }, - queryKey: ({ payload }) => [ - ...organizationMembersKey(organizationName), - payload, - ], - queryFn: ({ payload }) => - API.getOrganizationPaginatedMembers(organizationName, payload), + queryKey: ({ payload }) => [...organizationMembersKey(id), payload], + queryFn: ({ payload }) => API.getOrganizationPaginatedMembers(id, payload), }; -} +}; export const addOrganizationMember = (queryClient: QueryClient, id: string) => { return { diff --git a/site/src/components/UserAutocomplete/UserAutocomplete.tsx b/site/src/components/UserAutocomplete/UserAutocomplete.tsx index 88ce8a6ff9795..1fe997bdc5bbf 100644 --- a/site/src/components/UserAutocomplete/UserAutocomplete.tsx +++ b/site/src/components/UserAutocomplete/UserAutocomplete.tsx @@ -3,7 +3,10 @@ import Autocomplete from "@mui/material/Autocomplete"; import CircularProgress from "@mui/material/CircularProgress"; import TextField from "@mui/material/TextField"; import { getErrorMessage } from "api/errors"; -import { paginatedOrganizationMembers } from "api/queries/organizations"; +import { + organizationMembers, + paginatedOrganizationMembers, +} from "api/queries/organizations"; import { users } from "api/queries/users"; import type { OrganizationMemberWithUserData, User } from "api/typesGenerated"; import { Avatar } from "components/Avatar/Avatar"; @@ -70,13 +73,10 @@ export const MemberAutocomplete: FC = ({ }) => { const [filter, setFilter] = useState(); - const searchParams = new URLSearchParams(); - searchParams.append("limit", "0"); - - // Currently this queries all members, as there is no pagination. - const membersQuery = usePaginatedQuery({ - ...paginatedOrganizationMembers(organizationId, searchParams), + const membersQuery = useQuery({ + ...organizationMembers(organizationId), enabled: filter !== undefined, + keepPreviousData: true, }); return ( From fb15b46de776a6cce134a71469016407d22ba648 Mon Sep 17 00:00:00 2001 From: Brett Kolodny Date: Wed, 12 Mar 2025 14:40:24 +0000 Subject: [PATCH 05/10] wip --- site/src/components/UserAutocomplete/UserAutocomplete.tsx | 6 +----- .../OrganizationMembersPage.test.tsx | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/site/src/components/UserAutocomplete/UserAutocomplete.tsx b/site/src/components/UserAutocomplete/UserAutocomplete.tsx index 1fe997bdc5bbf..e375116cd2d22 100644 --- a/site/src/components/UserAutocomplete/UserAutocomplete.tsx +++ b/site/src/components/UserAutocomplete/UserAutocomplete.tsx @@ -3,16 +3,12 @@ import Autocomplete from "@mui/material/Autocomplete"; import CircularProgress from "@mui/material/CircularProgress"; import TextField from "@mui/material/TextField"; import { getErrorMessage } from "api/errors"; -import { - organizationMembers, - paginatedOrganizationMembers, -} from "api/queries/organizations"; +import { organizationMembers } from "api/queries/organizations"; import { users } from "api/queries/users"; import type { OrganizationMemberWithUserData, User } from "api/typesGenerated"; import { Avatar } from "components/Avatar/Avatar"; import { AvatarData } from "components/Avatar/AvatarData"; import { useDebouncedFunction } from "hooks/debounce"; -import { usePaginatedQuery } from "hooks/usePaginatedQuery"; import { type ChangeEvent, type ComponentProps, diff --git a/site/src/pages/OrganizationSettingsPage/OrganizationMembersPage.test.tsx b/site/src/pages/OrganizationSettingsPage/OrganizationMembersPage.test.tsx index 1270f78484dc7..f828969238cec 100644 --- a/site/src/pages/OrganizationSettingsPage/OrganizationMembersPage.test.tsx +++ b/site/src/pages/OrganizationSettingsPage/OrganizationMembersPage.test.tsx @@ -38,8 +38,8 @@ beforeEach(() => { const renderPage = async () => { renderWithOrganizationSettingsLayout(, { - route: `/organizations/${MockOrganization.name}/members`, - path: "/organizations/:organization/members", + route: `/organizations/${MockOrganization.name}/paginated-members`, + path: "/organizations/:organization/paginated-members", }); await waitForLoaderToBeRemoved(); }; From 26ae355890c1872cdd0efbc54d044212688f3595 Mon Sep 17 00:00:00 2001 From: Brett Kolodny Date: Wed, 12 Mar 2025 15:24:29 +0000 Subject: [PATCH 06/10] fix: mock paginated response --- site/src/testHelpers/handlers.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/site/src/testHelpers/handlers.ts b/site/src/testHelpers/handlers.ts index 7fbd14147af83..cde141ac90e37 100644 --- a/site/src/testHelpers/handlers.ts +++ b/site/src/testHelpers/handlers.ts @@ -70,6 +70,12 @@ export const handlers = [ M.MockOrganizationMember2, ]); }), + http.get("/api/v2/organizations/:organizationId/paginated-members", () => { + return HttpResponse.json({ + members: [M.MockOrganizationMember, M.MockOrganizationMember2], + count: 2, + }); + }), http.delete( "/api/v2/organizations/:organizationId/members/:userId", async () => { From 4ed07c4651d377b3bd672d6d4c5f7a6124372dfc Mon Sep 17 00:00:00 2001 From: Brett Kolodny Date: Wed, 12 Mar 2025 15:25:57 +0000 Subject: [PATCH 07/10] fix: remove unused mock --- site/src/testHelpers/handlers.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/site/src/testHelpers/handlers.ts b/site/src/testHelpers/handlers.ts index cde141ac90e37..79bc116891bf9 100644 --- a/site/src/testHelpers/handlers.ts +++ b/site/src/testHelpers/handlers.ts @@ -64,12 +64,6 @@ export const handlers = [ M.MockOrganizationAuditorRole, ]); }), - http.get("/api/v2/organizations/:organizationId/members", () => { - return HttpResponse.json([ - M.MockOrganizationMember, - M.MockOrganizationMember2, - ]); - }), http.get("/api/v2/organizations/:organizationId/paginated-members", () => { return HttpResponse.json({ members: [M.MockOrganizationMember, M.MockOrganizationMember2], From f07f92ec722834fc8d29becd808f4eea8f5ecd26 Mon Sep 17 00:00:00 2001 From: Brett Kolodny Date: Thu, 13 Mar 2025 16:39:16 +0000 Subject: [PATCH 08/10] fix: return the correct users in dbmem query --- coderd/database/dbmem/dbmem.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index 63ee1d0bd95e7..1ece2571f4960 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -9596,7 +9596,7 @@ func (q *FakeQuerier) PaginatedOrganizationMembers(_ context.Context, arg databa // All of the members in the organization orgMembers := make([]database.OrganizationMember, 0) for _, mem := range q.organizationMembers { - if arg.OrganizationID != uuid.Nil && mem.OrganizationID != arg.OrganizationID { + if mem.OrganizationID != arg.OrganizationID { continue } @@ -9606,7 +9606,7 @@ func (q *FakeQuerier) PaginatedOrganizationMembers(_ context.Context, arg databa selectedMembers := make([]database.PaginatedOrganizationMembersRow, 0) skippedMembers := 0 - for _, organizationMember := range q.organizationMembers { + for _, organizationMember := range orgMembers { if skippedMembers < int(arg.OffsetOpt) { skippedMembers++ continue From 5a58c1e87a7bbe61b9236a140b8f1176281a8f9a Mon Sep 17 00:00:00 2001 From: Brett Kolodny Date: Thu, 13 Mar 2025 18:13:55 +0000 Subject: [PATCH 09/10] fix: update stories for new pagination related args --- .../OrganizationMembersPageView.stories.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/site/src/pages/OrganizationSettingsPage/OrganizationMembersPageView.stories.tsx b/site/src/pages/OrganizationSettingsPage/OrganizationMembersPageView.stories.tsx index f3427bd58775d..6e096cfbb8fec 100644 --- a/site/src/pages/OrganizationSettingsPage/OrganizationMembersPageView.stories.tsx +++ b/site/src/pages/OrganizationSettingsPage/OrganizationMembersPageView.stories.tsx @@ -5,6 +5,8 @@ import { MockUser, } from "testHelpers/entities"; import { OrganizationMembersPageView } from "./OrganizationMembersPageView"; +import { UsePaginatedQueryResult } from "hooks/usePaginatedQuery"; +import { mockSuccessResult } from "components/PaginationWidget/PaginationContainer.mocks"; const meta: Meta = { title: "pages/OrganizationMembersPageView", @@ -14,11 +16,16 @@ const meta: Meta = { error: undefined, isAddingMember: false, isUpdatingMemberRoles: false, + canViewMembers: true, me: MockUser, members: [ { ...MockOrganizationMember, groups: [] }, { ...MockOrganizationMember2, groups: [] }, ], + membersQuery: { + ...mockSuccessResult, + totalRecords: 2, + } as UsePaginatedQueryResult, addMember: () => Promise.resolve(), removeMember: () => Promise.resolve(), updateMemberRoles: () => Promise.resolve(), From d0a15e56a8dce4f29a4ea2fba4e0abd95b1712bd Mon Sep 17 00:00:00 2001 From: Brett Kolodny Date: Thu, 13 Mar 2025 18:18:44 +0000 Subject: [PATCH 10/10] fix: fmt --- .../OrganizationMembersPageView.stories.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/pages/OrganizationSettingsPage/OrganizationMembersPageView.stories.tsx b/site/src/pages/OrganizationSettingsPage/OrganizationMembersPageView.stories.tsx index 6e096cfbb8fec..1c2f2c6e804a3 100644 --- a/site/src/pages/OrganizationSettingsPage/OrganizationMembersPageView.stories.tsx +++ b/site/src/pages/OrganizationSettingsPage/OrganizationMembersPageView.stories.tsx @@ -1,12 +1,12 @@ import type { Meta, StoryObj } from "@storybook/react"; +import { mockSuccessResult } from "components/PaginationWidget/PaginationContainer.mocks"; +import type { UsePaginatedQueryResult } from "hooks/usePaginatedQuery"; import { MockOrganizationMember, MockOrganizationMember2, MockUser, } from "testHelpers/entities"; import { OrganizationMembersPageView } from "./OrganizationMembersPageView"; -import { UsePaginatedQueryResult } from "hooks/usePaginatedQuery"; -import { mockSuccessResult } from "components/PaginationWidget/PaginationContainer.mocks"; const meta: Meta = { title: "pages/OrganizationMembersPageView", pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy