Files
AFFiNE-Mirror/packages/frontend/admin/src/modules/accounts/components/data-table-toolbar.tsx
T
2025-03-26 10:16:18 +00:00

202 lines
5.3 KiB
TypeScript

import { Button } from '@affine/admin/components/ui/button';
import { Input } from '@affine/admin/components/ui/input';
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 SetStateAction,
startTransition,
useCallback,
useEffect,
useMemo,
useState,
} from 'react';
import { useRightPanel } from '../../panel/context';
import type { UserType } from '../schema';
import { DiscardChanges } from './discard-changes';
import { ExportUsersDialog } from './export-users-dialog';
import { ImportUsersDialog } from './import-users-dialog';
import { CreateUserForm } from './user-form';
interface DataTableToolbarProps<TData> {
data: TData[];
setDataTable: (data: TData[]) => void;
selectedUsers: UserType[];
table?: Table<TData>;
}
const useSearch = () => {
const [value, setValue] = useState('');
const { data } = useQuery({
query: getUserByEmailQuery,
variables: { email: value },
});
const result = useMemo(() => data?.userByEmail, [data]);
return {
result,
query: setValue,
};
};
function useDebouncedValue<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}
export function DataTableToolbar<TData>({
data,
selectedUsers,
setDataTable,
table,
}: DataTableToolbarProps<TData>) {
const [value, setValue] = useState('');
const [dialogOpen, setDialogOpen] = useState(false);
const [exportDialogOpen, setExportDialogOpen] = useState(false);
const [importDialogOpen, setImportDialogOpen] = useState(false);
const debouncedValue = useDebouncedValue(value, 1000);
const { setPanelContent, openPanel, closePanel, isOpen } = useRightPanel();
const { result, query } = useSearch();
const handleConfirm = useCallback(() => {
setPanelContent(<CreateUserForm onComplete={closePanel} />);
if (dialogOpen) {
setDialogOpen(false);
}
if (!isOpen) {
openPanel();
}
}, [setPanelContent, closePanel, dialogOpen, isOpen, openPanel]);
useEffect(() => {
query(debouncedValue);
}, [debouncedValue, query]);
useEffect(() => {
startTransition(() => {
if (!debouncedValue) {
setDataTable(data);
} else if (result) {
setDataTable([result as TData]);
} else {
setDataTable([]);
}
});
}, [data, debouncedValue, result, setDataTable]);
const onValueChange = useCallback(
(e: { currentTarget: { value: SetStateAction<string> } }) => {
setValue(e.currentTarget.value);
},
[]
);
const handleCancel = useCallback(() => {
setDialogOpen(false);
}, []);
const handleOpenConfirm = useCallback(() => {
if (isOpen) {
return setDialogOpen(true);
}
return handleConfirm();
}, [handleConfirm, isOpen]);
const handleExportUsers = useCallback(() => {
if (!table) return;
const selectedRows = table.getFilteredSelectedRowModel().rows;
if (selectedRows.length === 0) {
alert('Please select at least one user to export');
return;
}
setExportDialogOpen(true);
}, [table]);
const handleImportUsers = useCallback(() => {
setImportDialogOpen(true);
}, []);
return (
<div className="flex items-center justify-between gap-y-2 gap-x-4">
<div className="flex items-center gap-2 flex-wrap">
<Button
variant="outline"
size="sm"
className="h-8 px-2 lg:px-3"
onClick={handleImportUsers}
>
<ImportIcon fontSize={20} />
<span className="ml-2 hidden md:inline-block">Import</span>
</Button>
<Button
variant="outline"
size="sm"
className="h-8 px-2 lg:px-3"
onClick={handleExportUsers}
disabled={
!table || table.getFilteredSelectedRowModel().rows.length === 0
}
>
<ExportIcon fontSize={20} />
<span className="ml-2 hidden md:inline-block">Export</span>
</Button>
{table && (
<ExportUsersDialog
users={selectedUsers}
open={exportDialogOpen}
onOpenChange={setExportDialogOpen}
/>
)}
<ImportUsersDialog
open={importDialogOpen}
onOpenChange={setImportDialogOpen}
/>
</div>
<div className="flex items-center gap-y-2 flex-wrap justify-end gap-2">
<div className="flex">
<Input
placeholder="Search Email"
value={value}
onChange={onValueChange}
className="h-8 w-[150px] lg:w-[250px]"
/>
</div>
<Button
className="h-8 px-2 lg:px-3 space-x-[6px] text-sm font-medium"
onClick={handleOpenConfirm}
>
<PlusIcon fontSize={20} /> <span>Add User</span>
</Button>
</div>
<DiscardChanges
open={dialogOpen}
onOpenChange={setDialogOpen}
onClose={handleCancel}
onConfirm={handleConfirm}
/>
</div>
);
}