Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat: add pagination to the organizaton members table#16870

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
13 commits
Select commitHold shift + click to select a range
98b48d6
feat: implement pagination for org members table
brettkolodnyMar 10, 2025
54a9067
fix: use new endpoint in Autocomplete component
brettkolodnyMar 11, 2025
1f6b13c
fix: remove unused code
brettkolodnyMar 11, 2025
aaaa89d
Merge branch 'main' into brett-60111/add-pagination-for-organizations…
brettkolodnyMar 11, 2025
cfd11f6
feat: create un-paginated version of query
brettkolodnyMar 12, 2025
fb15b46
wip
brettkolodnyMar 12, 2025
26ae355
fix: mock paginated response
brettkolodnyMar 12, 2025
4ed07c4
fix: remove unused mock
brettkolodnyMar 12, 2025
83ac597
Merge branch 'main' into brett-60111/add-pagination-for-organizations…
brettkolodnyMar 13, 2025
f07f92e
fix: return the correct users in dbmem query
brettkolodnyMar 13, 2025
5a58c1e
fix: update stories for new pagination related args
brettkolodnyMar 13, 2025
d0a15e5
fix: fmt
brettkolodnyMar 13, 2025
f45e9ea
Merge branch 'main' into brett-60111/add-pagination-for-organizations…
brettkolodnyMar 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletionscoderd/database/dbmem/dbmem.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -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 {
ifarg.OrganizationID != uuid.Nil &&mem.OrganizationID != arg.OrganizationID {
if mem.OrganizationID != arg.OrganizationID {
continue
}

Expand All@@ -9606,7 +9606,7 @@ func (q *FakeQuerier) PaginatedOrganizationMembers(_ context.Context, arg databa
selectedMembers := make([]database.PaginatedOrganizationMembersRow, 0)

skippedMembers := 0
for _, organizationMember := rangeq.organizationMembers {
for _, organizationMember := rangeorgMembers {
if skippedMembers < int(arg.OffsetOpt) {
skippedMembers++
continue
Expand Down
9 changes: 4 additions & 5 deletionscodersdk/organizations.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -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 {
Expand Down
18 changes: 18 additions & 0 deletionssite/src/api/api.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -583,6 +583,24 @@ 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<TypesGen.PaginatedMembersResponse>(url);

return response.data;
};

/**
* @param organization Can be the organization's ID or name
*/
Expand Down
37 changes: 36 additions & 1 deletionsite/src/api/queries/organizations.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,12 @@ import { API } from "api/api";
import type {
CreateOrganizationRequest,
GroupSyncSettings,
PaginatedMembersRequest,
PaginatedMembersResponse,
RoleSyncSettings,
UpdateOrganizationRequest,
} from "api/typesGenerated";
import type { UsePaginatedQueryOptions } from "hooks/usePaginatedQuery";
import {
type OrganizationPermissionName,
type OrganizationPermissions,
Expand DownExpand Up@@ -59,13 +62,45 @@ export const organizationMembersKey = (id: string) => [
"members",
];

/**
* 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.getOrganizationMembers(id),
queryFn: () => API.getOrganizationPaginatedMembers(id, { limit: 0 }),
queryKey: organizationMembersKey(id),
};
};

export const paginatedOrganizationMembers = (
id: string,
searchParams: URLSearchParams,
): UsePaginatedQueryOptions<
PaginatedMembersResponse,
PaginatedMembersRequest
> => {
return {
searchParams,
queryPayload: ({ limit, offset }) => {
return {
limit: limit,
offset: offset,
};
},
queryKey: ({ payload }) => [...organizationMembersKey(id), payload],
queryFn: ({ payload }) => API.getOrganizationPaginatedMembers(id, payload),
};
};

export const addOrganizationMember = (queryClient: QueryClient, id: string) => {
return {
mutationFn: (userId: string) => {
Expand Down
3 changes: 1 addition & 2 deletionssite/src/api/typesGenerated.ts
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,7 +69,6 @@ export const MemberAutocomplete: FC<MemberAutocompleteProps> = ({
}) => {
const [filter, setFilter] = useState<string>();

// Currently this queries all members, as there is no pagination.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

nice catch 😄 so easy to miss comments like this

const membersQuery = useQuery({
...organizationMembers(organizationId),
enabled: filter !== undefined,
Expand All@@ -80,7 +79,7 @@ export const MemberAutocomplete: FC<MemberAutocompleteProps> = ({
error={membersQuery.error}
isFetching={membersQuery.isFetching}
setFilter={setFilter}
users={membersQuery.data}
users={membersQuery.data?.members}
{...props}
/>
);
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,8 +38,8 @@ beforeEach(() => {

const renderPage = async () => {
renderWithOrganizationSettingsLayout(<OrganizationMembersPage />, {
route: `/organizations/${MockOrganization.name}/members`,
path: "/organizations/:organization/members",
route: `/organizations/${MockOrganization.name}/paginated-members`,
path: "/organizations/:organization/paginated-members",
});
await waitForLoaderToBeRemoved();
};
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -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";
Expand All@@ -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";

Expand All@@ -30,17 +31,23 @@ 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 groups = groupsByUserIdQuery.data?.get(member.user_id) ?? [];
return { ...member, groups };
});
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 };
},
);

const addMemberMutation = useMutation(
addOrganizationMember(queryClient, organizationName),
Expand DownExpand Up@@ -95,6 +102,7 @@ const OrganizationMembersPage: FC = () => {
isUpdatingMemberRoles={updateMemberRolesMutation.isLoading}
me={me}
members={members}
membersQuery={membersQuery}
addMember={async (user: User) => {
await addMemberMutation.mutateAsync(user.id);
void membersQuery.refetch();
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
import type { Meta, StoryObj } from "@storybook/react";
import { mockSuccessResult } from "components/PaginationWidget/PaginationContainer.mocks";
import type { UsePaginatedQueryResult } from "hooks/usePaginatedQuery";
import {
MockOrganizationMember,
MockOrganizationMember2,
Expand All@@ -14,11 +16,16 @@ const meta: Meta<typeof OrganizationMembersPageView> = {
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(),
Expand Down
Loading
Loading

[8]ページ先頭

©2009-2025 Movatter.jp