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
199 lines
5.4 KiB
TypeScript
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>
|
|
);
|
|
}
|