Files
upstream-affine/packages/frontend/admin/src/modules/accounts/components/data-table-toolbar.tsx
upstream-mirror 50f788e799
Some checks failed
Build & Test / Analyze (javascript, blocksuite) (push) Failing after 3s
Build & Test / Rust test filter (push) Failing after 3s
Build & Test / E2E BlockSuite Test (2) (push) Failing after 3s
Build & Test / E2E BlockSuite Test (3) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (4) (push) Failing after 3s
Build & Test / E2E BlockSuite Cross Browser Test (push) Failing after 3s
Build & Test / E2E Mobile Test (1) (push) Failing after 3s
Build & Test / E2E Mobile Test (2) (push) Failing after 3s
Build & Test / Build AFFiNE native (x86_64-unknown-linux-gnu) (push) Failing after 4s
Build & Test / Unit Test (1) (push) Has been skipped
Build & Test / Unit Test (2) (push) Has been skipped
Build & Test / Unit Test (3) (push) Has been skipped
Build & Test / Build Server native (push) Failing after 3s
Build & Test / Check Git Status (push) Has been skipped
Build & Test / Server Test (0, 4) (push) Has been skipped
Build & Test / Server Test (1, 4) (push) Has been skipped
Build & Test / Analyze (javascript, affine) (push) Failing after 3s
Build & Test / Analyze (typescript, affine) (push) Failing after 3s
Build & Test / Analyze (typescript, blocksuite) (push) Failing after 3s
Build & Test / Mobile native build filter (push) Failing after 3s
Build & Test / Check yarn binary (push) Failing after 3s
Build & Test / E2E BlockSuite Test (1) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (5) (push) Failing after 3s
Build & Test / Unit Test (4) (push) Has been skipped
Build & Test / Unit Test (5) (push) Has been skipped
Build & Test / Native Unit Test (push) Has been skipped
Build & Test / Server Test (2, 4) (push) Has been skipped
Build & Test / Server Test (3, 4) (push) Has been skipped
Build & Test / Server Test with Elasticsearch (push) Has been skipped
Build & Test / Server Test with Embedded Search (push) Has been skipped
Build & Test / Server Test with Manticore Search (push) Has been skipped
Build & Test / Server E2E Test (push) Has been skipped
Build & Test / Cloud Desktop E2E Test (push) Has been skipped
Build & Test / Cloud E2E Test 1/2 (push) Has been skipped
Build & Test / Cloud E2E Test 2/2 (push) Has been skipped
Build & Test / Build @affine/electron renderer (push) Failing after 3s
Build & Test / Copilot test filter (push) Failing after 4s
Build & Test / Lint Rust (push) Has been skipped
Build & Test / Build Android app (push) Has been skipped
Build & Test / Test Android app (push) Has been skipped
Build & Test / Run native tests (push) Has been skipped
Build & Test / Server Copilot Api Test (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (3, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (4, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (1, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (2, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (5, 5) (push) Has been skipped
Build & Test / Desktop Test (ubuntu-latest, linux, x64, x86_64-unknown-linux-gnu, true) (push) Has been cancelled
Build & Test / Desktop Test (windows-latest, windows, x64, x86_64-pc-windows-msvc, true) (push) Has been cancelled
Build & Test / 3, 2, 1 Launch (push) Has been cancelled
Build & Test / Lint (push) Has been cancelled
Build & Test / Typecheck (push) Has been cancelled
Build & Test / Build iOS app (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-apple-darwin) (push) Has been cancelled
Build & Test / E2E Test (1) (push) Has been cancelled
Build & Test / E2E Test (2) (push) Has been cancelled
Build & Test / E2E Test (3) (push) Has been cancelled
Build & Test / E2E Test (4) (push) Has been cancelled
Build & Test / E2E Test (5) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-apple-darwin) (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, arm64, aarch64-apple-darwin, true) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, x64, x86_64-apple-darwin, false) (push) Has been cancelled
snapshot of https://github.com/toeverything/AFFiNE.git (canary, 2026-09-16; blobs > 131072B omitted)
2026-09-16 00:31:07 +05:00

199 lines
5.4 KiB
TypeScript

import { Button } from '@affine/admin/components/ui/button';
import { Input } from '@affine/admin/components/ui/input';
import type { FeatureType } from '@affine/graphql';
import { ExportIcon, ImportIcon, PlusIcon } from '@blocksuite/icons/rc';
import type { Table } from '@tanstack/react-table';
import {
type ChangeEvent,
type Dispatch,
type SetStateAction,
useCallback,
useEffect,
useState,
} from 'react';
import { DiscardChanges } from '../../../components/shared/discard-changes';
import { FeatureFilterPopover } from '../../../components/shared/feature-filter-popover';
import { useDebouncedValue } from '../../../hooks/use-debounced-value';
import { useServerConfig } from '../../common';
import { useRightPanel } from '../../panel/context';
import type { UserType } from '../schema';
import { ExportUsersDialog } from './export-users-dialog';
import { ImportUsersDialog } from './import-users';
import { CreateUserForm } from './user-form';
interface DataTableToolbarProps<TData> {
selectedUsers: UserType[];
table?: Table<TData>;
keyword: string;
onKeywordChange: Dispatch<SetStateAction<string>>;
selectedFeatures: FeatureType[];
onFeaturesChange: Dispatch<SetStateAction<FeatureType[]>>;
}
export function DataTableToolbar<TData>({
selectedUsers,
table,
keyword,
onKeywordChange,
selectedFeatures,
onFeaturesChange,
}: DataTableToolbarProps<TData>) {
const [value, setValue] = useState(keyword);
const [dialogOpen, setDialogOpen] = useState(false);
const [exportDialogOpen, setExportDialogOpen] = useState(false);
const [importDialogOpen, setImportDialogOpen] = useState(false);
const debouncedValue = useDebouncedValue(value, 500);
const {
setPanelContent,
openPanel,
closePanel,
isOpen,
hasDirtyChanges,
setHasDirtyChanges,
} = useRightPanel();
const serverConfig = useServerConfig();
const availableFeatures = serverConfig.availableUserFeatures ?? [];
const handleConfirm = useCallback(() => {
setPanelContent(
<CreateUserForm
onComplete={closePanel}
onDirtyChange={setHasDirtyChanges}
/>
);
if (dialogOpen) {
setDialogOpen(false);
}
if (!isOpen) {
openPanel();
}
}, [
setPanelContent,
closePanel,
dialogOpen,
isOpen,
openPanel,
setHasDirtyChanges,
]);
useEffect(() => {
setValue(keyword);
}, [keyword]);
useEffect(() => {
onKeywordChange(debouncedValue.trim());
}, [debouncedValue, onKeywordChange]);
const onValueChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
setValue(e.currentTarget.value);
}, []);
const handleFeatureToggle = useCallback(
(features: FeatureType[]) => {
onFeaturesChange(features);
},
[onFeaturesChange]
);
const handleCancel = useCallback(() => {
setDialogOpen(false);
}, []);
const handleOpenConfirm = useCallback(() => {
if (hasDirtyChanges) {
return setDialogOpen(true);
}
return handleConfirm();
}, [handleConfirm, hasDirtyChanges]);
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">
<FeatureFilterPopover
selectedFeatures={selectedFeatures}
availableFeatures={availableFeatures}
onChange={handleFeatureToggle}
align="end"
/>
<div className="flex">
<Input
placeholder="Search Email / UUID"
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>
);
}