diff --git a/packages/console/src/pages/clusters/containers/ClusterSetting/Members/index.tsx b/packages/console/src/pages/clusters/containers/ClusterSetting/Members/index.tsx index 809cd5f17c5..398d2be83d7 100644 --- a/packages/console/src/pages/clusters/containers/ClusterSetting/Members/index.tsx +++ b/packages/console/src/pages/clusters/containers/ClusterSetting/Members/index.tsx @@ -5,9 +5,8 @@ import React, { useRef, useState } from 'react'; import { useParams } from 'react-router-dom'; +import type { ListPageProps, Column, FormattedUser, FormattedRole } from '@ks-console/shared'; import { - Column, - FormattedUser, formatTime, hasPermission, StatusIndicator, @@ -18,7 +17,6 @@ import { MemberModifyModal, InviteMemberPayload, EditMemberRoleValue, - FormattedRole, useActionMenu, ListPage, useCommonActions, @@ -51,7 +49,7 @@ function Members() { const { isOpen: isCreateOpen, open: openCreate, close: closeCreate } = useDisclosure(false); const { isOpen: isEditOpen, open: openEdit, close: closeEdit } = useDisclosure(false); - const { data: allUserList } = useAllUserListQuery(); + const { data: allUserList, refetch: refetchAllUserList } = useAllUserListQuery(); const { del } = useCommonActions({ store: userStore, @@ -78,14 +76,17 @@ function Members() { action: 'view', }); - const { data: roles = [] } = useQuery(['clusterroles'], async () => { - const res = await fetchList({ - ...params, - limit: -1, - annotation: 'kubesphere.io/creator', - } as any); - return res.data; - }); + const { data: roles = [], refetch: refetchRoles } = useQuery( + ['clusterroles'], + async () => { + const res = await fetchList({ + ...params, + limit: -1, + annotation: 'kubesphere.io/creator', + } as any); + return res.data; + }, + ); const { mutate: mutateEditUser, isLoading: isEditLoading } = useEditUserMutation( { @@ -254,7 +255,7 @@ function Members() { description: t('INVITE_CLUSTER_MEMBER_DESC'), }; - const table = { + const table: { ref: typeof tableRef } & ListPageProps['table'] = { ref: tableRef, columns: columns, tableName: 'members', @@ -264,6 +265,10 @@ function Members() { disableRowSelect: (row: any) => isCurrentUser(row), toolbarRight: renderTableAction({}), serverDataFormat: serverDataFormatter, + onRefresh: () => { + refetchAllUserList(); + refetchRoles(); + }, }; return ( diff --git a/packages/console/src/pages/projects/containers/Members/index.tsx b/packages/console/src/pages/projects/containers/Members/index.tsx index a593aed2edf..4d401a47114 100644 --- a/packages/console/src/pages/projects/containers/Members/index.tsx +++ b/packages/console/src/pages/projects/containers/Members/index.tsx @@ -5,9 +5,8 @@ import React, { useRef, useState } from 'react'; import { useParams } from 'react-router-dom'; +import type { ListPageProps, Column, FormattedUser, FormattedRole } from '@ks-console/shared'; import { - Column, - FormattedUser, formatTime, hasPermission, StatusIndicator, @@ -18,7 +17,6 @@ import { MemberModifyModal, InviteMemberPayload, EditMemberRoleValue, - FormattedRole, useActionMenu, ListPage, useCommonActions, @@ -45,7 +43,7 @@ function Members() { const params: Record = useParams(); const [editUser, setEditUser] = useState(); const [rolesMap, setRolesMap] = useState>({}); - const { data: allUserList } = useAllUserListQuery(); + const { data: allUserList, refetch: refetchAllUserList } = useAllUserListQuery(); const { isOpen: isCreateOpen, open: openCreate, close: closeCreate } = useDisclosure(false); const { isOpen: isEditOpen, open: openEdit, close: closeEdit } = useDisclosure(false); @@ -63,19 +61,22 @@ function Members() { action: 'view', }); - const { data: roles = [] } = useQuery(['memberRoles'], async () => { - const res = await fetchList({ - ...params, - limit: -1, - annotation: 'kubesphere.io/creator', - } as any); - const maps: Record = {}; - res.data.forEach((item: any) => { - maps[item.name] = item.aliasName ? `${item.aliasName}(${item.name})` : item.name; - }); - setRolesMap(maps); - return res.data; - }); + const { data: roles = [], refetch: refetchRoles } = useQuery( + ['memberRoles'], + async () => { + const res = await fetchList({ + ...params, + limit: -1, + annotation: 'kubesphere.io/creator', + } as any); + const maps: Record = {}; + res.data.forEach((item: any) => { + maps[item.name] = item.aliasName ? `${item.aliasName}(${item.name})` : item.name; + }); + setRolesMap(maps); + return res.data; + }, + ); const { mutate: mutateEditUser, isLoading: isEditLoading } = useEditUserMutation( { @@ -239,9 +240,9 @@ function Members() { description: t('PROJECT_MEMBER_DESC'), }; - const table = { - url: getResourceUrl(params), + const table: { ref: typeof tableRef } & ListPageProps['table'] = { ref: tableRef, + url: getResourceUrl(params), columns: columns, tableName: 'members', rowKey: 'name', @@ -250,6 +251,10 @@ function Members() { disableRowSelect: (row: any) => isCurrentUser(row), toolbarRight: renderTableAction({}), serverDataFormat: serverDataFormatter, + onRefresh: () => { + refetchAllUserList(); + refetchRoles(); + }, }; return ( diff --git a/packages/console/src/pages/workspaces/containers/Members/index.tsx b/packages/console/src/pages/workspaces/containers/Members/index.tsx index d7c435e9c54..e795e7a9597 100644 --- a/packages/console/src/pages/workspaces/containers/Members/index.tsx +++ b/packages/console/src/pages/workspaces/containers/Members/index.tsx @@ -5,9 +5,8 @@ import React, { useRef, useState } from 'react'; import { Link, useParams } from 'react-router-dom'; +import type { ListPageProps, Column, FormattedUser, FormattedRole } from '@ks-console/shared'; import { - Column, - FormattedUser, formatTime, hasPermission, StatusIndicator, @@ -18,7 +17,6 @@ import { MemberModifyModal, InviteMemberPayload, EditMemberRoleValue, - FormattedRole, useActionMenu, ListPage, useCommonActions, @@ -45,7 +43,7 @@ function Members() { const params: Record = useParams(); const [editUser, setEditUser] = useState(); const [rolesMap, setRolesMap] = useState>({}); - const { data: allUserList } = useAllUserListQuery(); + const { data: allUserList, refetch: refetchAllUserList } = useAllUserListQuery(); const { isOpen: isCreateOpen, open: openCreate, close: closeCreate } = useDisclosure(false); const { isOpen: isEditOpen, open: openEdit, close: closeEdit } = useDisclosure(false); @@ -63,19 +61,22 @@ function Members() { action: 'view', }); - const { data: roles = [] } = useQuery(['memberRoles'], async () => { - const res = await fetchList({ - ...params, - limit: -1, - annotation: 'kubesphere.io/creator', - } as any); - const maps: Record = {}; - res.data.forEach((item: any) => { - maps[item.name] = item.aliasName ? `${item.aliasName}(${item.name})` : item.name; - }); - setRolesMap(maps); - return res.data; - }); + const { data: roles = [], refetch: refetchRoles } = useQuery( + ['memberRoles'], + async () => { + const res = await fetchList({ + ...params, + limit: -1, + annotation: 'kubesphere.io/creator', + } as any); + const maps: Record = {}; + res.data.forEach((item: any) => { + maps[item.name] = item.aliasName ? `${item.aliasName}(${item.name})` : item.name; + }); + setRolesMap(maps); + return res.data; + }, + ); const { mutate: mutateEditUser, isLoading: isEditLoading } = useEditUserMutation( { @@ -239,9 +240,9 @@ function Members() { description: t('WORKSPACE_MEMBER_DESC'), }; - const table = { - url: getResourceUrl(params), + const table: { ref: typeof tableRef } & ListPageProps['table'] = { ref: tableRef, + url: getResourceUrl(params), columns: columns, tableName: 'members', rowKey: 'name', @@ -250,6 +251,10 @@ function Members() { disableRowSelect: (row: any) => isCurrentUser(row), toolbarRight: renderTableAction({}), serverDataFormat: serverDataFormatter, + onRefresh: () => { + refetchAllUserList(); + refetchRoles(); + }, }; return ( diff --git a/packages/shared/src/components/Roles/AuthorizedUsers/index.tsx b/packages/shared/src/components/Roles/AuthorizedUsers/index.tsx index 670103b37ad..9e83cb24a83 100644 --- a/packages/shared/src/components/Roles/AuthorizedUsers/index.tsx +++ b/packages/shared/src/components/Roles/AuthorizedUsers/index.tsx @@ -3,85 +3,108 @@ * https://github.com/kubesphere/console/blob/master/LICENSE */ -import React from 'react'; +import React, { useState, useMemo } from 'react'; import { useParams } from 'react-router-dom'; -import styled from 'styled-components'; +import type { ColumnDef } from '@tanstack/react-table'; +import { merge } from 'lodash'; +import { Card, DataTable } from '@kubed/components'; import { Human } from '@kubed/icons'; -import { Card, Empty } from '@kubed/components'; -import { Column, DataTable } from '../../DataTable'; -import StatusIndicator from '../../StatusIndicator'; +import type { FormattedUser } from '../../../types'; +import { formatTime, useUrlSearchParamsStatus, tableState2Query } from '../../../utils'; import { userStore } from '../../../stores'; -import { formatTime } from '../../../utils'; -import type { OriginalUser } from '../../../types'; +import StatusIndicator from '../../StatusIndicator'; + +const { useFetchMembersList } = userStore; interface Props { roleKey: string; } -const { mapper: formatUser, getResourceUrl } = userStore; - -const StyledEmpty = styled(Empty)` - padding: 32px; - // margin: -12px; -`; - function AuthorizedUsers({ roleKey }: Props) { const { name, namespace, workspace, cluster } = useParams(); - const url = getResourceUrl({ + + const { state, setState } = useUrlSearchParamsStatus(['']); + const query = tableState2Query(state); + + const { isFetching, totalItems, formattedUsers } = useFetchMembersList({ + roleKey, + name, namespace, workspace, cluster, + ...query, }); - const columns: Column[] = [ + + const columns: ColumnDef[] = [ { - title: t('USERNAME'), - field: 'username', - width: '33%', + accessorKey: 'username', + header: t('USERNAME'), + meta: { + th: { width: '33.33%' }, + }, }, { - title: t('STATUS'), - field: 'status', - width: '33%', - render: status => ( - {t(`USER_${status.toUpperCase()}`)} - ), + accessorKey: 'status', + header: t('STATUS'), + meta: { + th: { width: '33.33%' }, + }, + cell: ({ getValue }) => { + const status = getValue(); + return {t(`USER_${status.toUpperCase()}`)}; + }, }, { - title: t('LAST_LOGIN'), - field: 'lastLoginTime', - width: '33%', - render: time =>

{time ? formatTime(time) : t('NOT_LOGIN_YET')}

, + accessorKey: 'lastLoginTime', + header: t('LAST_LOGIN'), + meta: { + th: { width: '33.33%' }, + }, + cell: ({ getValue }) => { + const time = getValue(); + return

{time ? formatTime(time) : t('NOT_LOGIN_YET')}

; + }, }, ]; - const parameters = { - [roleKey]: name, - namespace, - workspace, - cluster, - }; + + const data = useMemo(() => formattedUsers, [JSON.stringify(formattedUsers)]); + + const [baseConfig] = useState(() => + DataTable.getDefaultTableOptions({ + tableName: 'AuthorizedUsers', + manual: true, + enableToolbar: false, + }), + ); + + const tableOptions: DataTable.TableOptions = merge({}, baseConfig, { + columns, + loading: isFetching, + data, + rowCount: totalItems, + state, + meta: { + getProps: { + table: () => ({ + style: { + margin: '0 12px 12px', + }, + }), + empty: () => ({ + title: t('USER'), + description: {t('NO_AUTHORIZED_USER_DESC')}, + image: , + }), + }, + }, + onParamsChange: setState, + }); + const table = DataTable.useTable(tableOptions); return ( - formatUser(data as OriginalUser)} - showToolbar={false} - emptyOptions={{ - element: ( - {t('NO_AUTHORIZED_USER_DESC')}} - image={} - /> - ), - withoutTable: true, - }} - /> + ); } diff --git a/packages/shared/src/components/index.ts b/packages/shared/src/components/index.ts index 5d2d48c0e92..761f92eaf2b 100644 --- a/packages/shared/src/components/index.ts +++ b/packages/shared/src/components/index.ts @@ -53,6 +53,7 @@ export { default as MetaData } from './Widgets/MetaData'; export { default as Events } from './Widgets/Events'; export { default as NavTitle } from './Layouts/NavTitle'; export * from './Layouts/NavMenu'; +export type { ListPageProps } from './Layouts/ListPage'; export { default as ListPage } from './Layouts/ListPage'; export { default as DetailPage, useDetailPage } from './Layouts/DetailPage'; export type { DetailPageRef } from './Layouts/DetailPage'; diff --git a/packages/shared/src/stores/user.ts b/packages/shared/src/stores/user.ts index 3d3e60526a3..82230398b01 100644 --- a/packages/shared/src/stores/user.ts +++ b/packages/shared/src/stores/user.ts @@ -4,7 +4,7 @@ */ import { set, get, noop, merge, cloneDeep } from 'lodash'; -import { useMutation, useInfiniteQuery, useQueries, useQuery } from 'react-query'; +import { useMutation, useInfiniteQuery, useQuery } from 'react-query'; import { useUrl } from '../hooks'; import { @@ -30,6 +30,7 @@ import type { FetchListParams } from '../utils/formatter'; export const module = 'users'; +// eslint-disable-next-line react-hooks/rules-of-hooks const { getPath } = useUrl({ module }); function getModule(params?: PathParams) { @@ -165,8 +166,7 @@ function useInfiniteUserList(params: FetchListParams & Record) { const res = useInfiniteQuery({ queryKey: ['InfinityUserList', params], queryFn: async ({ pageParam = params }) => { - const result = await fetchList(pageParam); - return result; + return fetchList(pageParam); }, getNextPageParam: (lastPage: any) => { const { data, total, limit, page, ...rest } = lastPage; @@ -268,30 +268,90 @@ function useUsersStatusMutation(options?: { onSuccess?: () => void }) { ); } -function useFetchMembersList(params: PathParams) { - const queryKeys = Object.entries(params).map((key, value) => `${key}-${value}`); - const results = useQueries([ - { - queryKey: ['get', ...queryKeys], - queryFn: () => request.get(getResourceUrl(params)), - }, - { - queryFn: () => request.get(getResourceUrl()), - queryKey: ['getAllUsers'], - }, - ]); - const data = get(results, '0.data'); - const list = get(results, '1.data'); +interface FetchMemberUsersOptions extends FetchListParams { + roleKey: string; +} + +async function fetchMemberUsers({ + roleKey, + name, + cluster, + workspace, + namespace, + devops, + sortBy, + ascending, + limit, + page, +}: FetchMemberUsersOptions) { + const pathParams = { cluster, workspace, namespace, devops }; + const moduleName = getModule(pathParams); + + const fetchListParams = formatFetchListParams(moduleName, { + sortBy, + ascending, + limit, + page, + }); + const filterParams = getFilterParams(fetchListParams); + + const membersUrl = getResourceUrl(pathParams); + const memberList = await request.get(membersUrl, { + params: { ...filterParams, [roleKey]: name, namespace, workspace, cluster }, + }); + const members = memberList?.items ?? []; + + let result: ResponseUser; + + if (members.length === 0 || moduleName === 'users') { + result = memberList; + } else { + const usersUrl = getResourceUrl(); + const names = members.map(member => member?.metadata?.name); + const userList = await request.get(usersUrl, { + params: { names: names.join(','), limit: -1 }, + }); + result = combineUserList(memberList, userList); + } + + const totalItems = result?.totalItems ?? 0; + const originalUsers = result?.items ?? []; + const formattedUsers = originalUsers.map(originalUser => ({ + ...pathParams, + ...originalUser, + ...mapper(originalUser), + })); + + return { totalItems, originalUsers, formattedUsers }; +} + +type UseFetchMembersListOptions = FetchMemberUsersOptions; + +function useFetchMembersList(options: UseFetchMembersListOptions) { + const { sortBy, ascending, limit, page, ...pathParams } = options; + const filterParams = { sortBy, ascending, limit, page }; + const queryKey = ['fetchMembersList', pathParams, filterParams]; + + const { data, ...rest } = useQuery({ + queryKey, + queryFn: () => fetchMemberUsers(options), + }); + const totalItems = data?.totalItems ?? 0; + const originalUsers = data?.originalUsers ?? []; + const formattedUsers = data?.formattedUsers ?? []; return { - data: combineUserList(data, list), - isLoading: results.some(result => result.isLoading), + ...rest, + data: originalUsers, + totalItems, + originalUsers, + formattedUsers, }; } function useAllUserListQuery() { const queryKey = ['getAllUserList']; - return useQuery(queryKey, () => request.get(getResourceUrl())); + return useQuery(queryKey, () => request.get(getResourceUrl(), { params: { limit: -1 } })); } const store = { diff --git a/packages/shared/src/utils/table/hooks/useUrlSearchParamsStatus.ts b/packages/shared/src/utils/table/hooks/useUrlSearchParamsStatus.ts index a80155c82b2..91476db30f1 100644 --- a/packages/shared/src/utils/table/hooks/useUrlSearchParamsStatus.ts +++ b/packages/shared/src/utils/table/hooks/useUrlSearchParamsStatus.ts @@ -127,7 +127,6 @@ export const useUrlSearchParamsStatus = (omitKeys: string[] = []) => { React.useEffect(() => { if (paramsRef.current !== params.toString()) { - console.log(222); setState(urlParams2Status(omitUrlSearchParams(params, omitKeys))); paramsRef.current = params.toString(); }