fix(admin): user count is out of sync and search results are not cached in account management (#11980)

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **New Features**
  - Improved user management table with dynamic row count updates and enhanced synchronization of memoized user lists.
- **Bug Fixes**
  - User count and displayed data now update immediately after user creation, deletion, or import, ensuring accurate and consistent information.
- **Chores**
  - Enhanced internal state management for better responsiveness and reliability in the accounts section.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
JimmFly
2025-05-22 07:29:08 +00:00
parent 2999497f16
commit 940ab69374
9 changed files with 42 additions and 40 deletions

View File

@@ -4,8 +4,8 @@ import { useQuery } from '@affine/admin/use-query';
import { getUserByEmailQuery } from '@affine/graphql';
import { ExportIcon, ImportIcon, PlusIcon } from '@blocksuite/icons/rc';
import type { Table } from '@tanstack/react-table';
import type { Dispatch, SetStateAction } from 'react';
import {
type SetStateAction,
startTransition,
useCallback,
useEffect,
@@ -22,8 +22,11 @@ import { CreateUserForm } from './user-form';
interface DataTableToolbarProps<TData> {
data: TData[];
setDataTable: (data: TData[]) => void;
usersCount: number;
selectedUsers: UserType[];
setDataTable: (data: TData[]) => void;
setRowCount: (rowCount: number) => void;
setMemoUsers: Dispatch<SetStateAction<UserType[]>>;
table?: Table<TData>;
}
@@ -60,8 +63,11 @@ function useDebouncedValue<T>(value: T, delay: number): T {
export function DataTableToolbar<TData>({
data,
usersCount,
selectedUsers,
setDataTable,
setRowCount,
setMemoUsers,
table,
}: DataTableToolbarProps<TData>) {
const [value, setValue] = useState('');
@@ -90,13 +96,25 @@ export function DataTableToolbar<TData>({
startTransition(() => {
if (!debouncedValue) {
setDataTable(data);
setRowCount(usersCount);
} else if (result) {
setMemoUsers(prev => [...new Set([...prev, result])]);
setDataTable([result as TData]);
setRowCount(1);
} else {
setDataTable([]);
setRowCount(0);
}
});
}, [data, debouncedValue, result, setDataTable]);
}, [
data,
debouncedValue,
result,
setDataTable,
setMemoUsers,
setRowCount,
usersCount,
]);
const onValueChange = useCallback(
(e: { currentTarget: { value: SetStateAction<string> } }) => {

View File

@@ -21,13 +21,14 @@ import { type Dispatch, type SetStateAction, useEffect, useState } from 'react';
import type { UserType } from '../schema';
import { DataTablePagination } from './data-table-pagination';
import { DataTableToolbar } from './data-table-toolbar';
import { useUserCount } from './use-user-management';
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
data: TData[];
pagination: PaginationState;
usersCount: number;
selectedUsers: UserType[];
setMemoUsers: Dispatch<SetStateAction<UserType[]>>;
onPaginationChange: Dispatch<
SetStateAction<{
pageIndex: number;
@@ -40,22 +41,23 @@ export function DataTable<TData extends { id: string }, TValue>({
columns,
data,
pagination,
usersCount,
selectedUsers,
setMemoUsers,
onPaginationChange,
}: DataTableProps<TData, TValue>) {
const usersCount = useUserCount();
const [rowSelection, setRowSelection] = useState({});
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
const [tableData, setTableData] = useState(data);
const [rowCount, setRowCount] = useState(usersCount);
const table = useReactTable({
data: tableData,
columns,
getCoreRowModel: getCoreRowModel(),
getRowId: row => row.id,
manualPagination: true,
rowCount: usersCount,
rowCount: rowCount,
enableFilters: true,
onPaginationChange: onPaginationChange,
enableRowSelection: true,
@@ -72,13 +74,20 @@ export function DataTable<TData extends { id: string }, TValue>({
setTableData(data);
}, [data]);
useEffect(() => {
setRowCount(usersCount);
}, [usersCount]);
return (
<div className="flex flex-col gap-4 py-5 px-6 h-full overflow-auto">
<DataTableToolbar
setDataTable={setTableData}
data={data}
usersCount={usersCount}
table={table}
selectedUsers={selectedUsers}
setRowCount={setRowCount}
setMemoUsers={setMemoUsers}
/>
<div className="rounded-md border h-full flex flex-col overflow-auto">
<Table>

View File

@@ -2,7 +2,6 @@ import {
useMutateQueryResource,
useMutation,
} from '@affine/admin/use-mutation';
import { useQuery } from '@affine/admin/use-query';
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
import {
createChangePasswordUrlMutation,
@@ -10,7 +9,6 @@ import {
deleteUserMutation,
disableUserMutation,
enableUserMutation,
getUsersCountQuery,
type ImportUsersInput,
type ImportUsersMutation,
importUsersMutation,
@@ -225,15 +223,6 @@ export const useDisableUser = () => {
return disableById;
};
export const useUserCount = () => {
const {
data: { usersCount },
} = useQuery({
query: getUsersCountQuery,
});
return usersCount;
};
export const useImportUsers = () => {
const { trigger: importUsers } = useMutation({
mutation: importUsersMutation,

View File

@@ -7,7 +7,7 @@ import type { UserType } from './schema';
import { useUserList } from './use-user-list';
export function AccountPage() {
const { users, pagination, setPagination } = useUserList();
const { users, pagination, setPagination, usersCount } = useUserList();
// Remember the user temporarily, because userList is paginated on the server side,can't get all users at once.
const [memoUsers, setMemoUsers] = useState<UserType[]>([]);
@@ -32,8 +32,10 @@ export function AccountPage() {
data={users}
columns={columns}
pagination={pagination}
usersCount={usersCount}
onPaginationChange={setPagination}
selectedUsers={selectedUsers}
setMemoUsers={setMemoUsers}
/>
</div>
);

View File

@@ -8,7 +8,7 @@ export const useUserList = () => {
pageSize: 10,
});
const {
data: { users },
data: { users, usersCount },
} = useQuery({
query: listUsersQuery,
variables: {
@@ -23,5 +23,6 @@ export const useUserList = () => {
users,
pagination,
setPagination,
usersCount,
};
};