mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-11 05:58:56 +08:00
feat(admin): add import and export users to admin panel (#10810)
This commit is contained in:
@@ -11,6 +11,9 @@ import {
|
||||
disableUserMutation,
|
||||
enableUserMutation,
|
||||
getUsersCountQuery,
|
||||
type ImportUsersInput,
|
||||
type ImportUsersMutation,
|
||||
importUsersMutation,
|
||||
listUsersQuery,
|
||||
updateAccountFeaturesMutation,
|
||||
updateAccountMutation,
|
||||
@@ -18,7 +21,15 @@ import {
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import type { UserInput } from '../schema';
|
||||
import type { UserInput, UserType } from '../schema';
|
||||
|
||||
export interface ExportField {
|
||||
id: string;
|
||||
label: string;
|
||||
checked: boolean;
|
||||
}
|
||||
|
||||
export type UserImportReturnType = ImportUsersMutation['importUsers'];
|
||||
|
||||
export const useCreateUser = () => {
|
||||
const {
|
||||
@@ -221,3 +232,115 @@ export const useUserCount = () => {
|
||||
});
|
||||
return usersCount;
|
||||
};
|
||||
|
||||
export const useImportUsers = () => {
|
||||
const { trigger: importUsers } = useMutation({
|
||||
mutation: importUsersMutation,
|
||||
});
|
||||
const revalidate = useMutateQueryResource();
|
||||
|
||||
const handleImportUsers = useCallback(
|
||||
async (
|
||||
input: ImportUsersInput,
|
||||
callback?: (importUsers: UserImportReturnType) => void
|
||||
) => {
|
||||
await importUsers({ input })
|
||||
.then(async ({ importUsers }) => {
|
||||
await revalidate(listUsersQuery);
|
||||
callback?.(importUsers);
|
||||
})
|
||||
.catch(e => {
|
||||
toast.error('Failed to import users: ' + e.message);
|
||||
});
|
||||
},
|
||||
[importUsers, revalidate]
|
||||
);
|
||||
|
||||
return handleImportUsers;
|
||||
};
|
||||
|
||||
export const useExportUsers = () => {
|
||||
const exportCSV = useCallback(
|
||||
async (users: UserType[], fields: ExportField[], callback?: () => void) => {
|
||||
const selectedFields = fields
|
||||
.filter(field => field.checked)
|
||||
.map(field => field.id);
|
||||
|
||||
if (selectedFields.length === 0) {
|
||||
alert('Please select at least one field to export');
|
||||
return;
|
||||
}
|
||||
|
||||
const headers = selectedFields.map(
|
||||
fieldId => fields.find(field => field.id === fieldId)?.label || fieldId
|
||||
);
|
||||
|
||||
const csvRows = [headers.join(',')];
|
||||
|
||||
users.forEach(user => {
|
||||
const row = selectedFields.map(fieldId => {
|
||||
const value = user[fieldId as keyof UserType];
|
||||
|
||||
return typeof value === 'string'
|
||||
? `"${value.replace(/"/g, '""')}"`
|
||||
: String(value);
|
||||
});
|
||||
csvRows.push(row.join(','));
|
||||
});
|
||||
|
||||
const csvContent = csvRows.join('\n');
|
||||
|
||||
// Add BOM (Byte Order Mark) to force Excel to interpret the file as UTF-8
|
||||
const BOM = '\uFEFF';
|
||||
const csvContentWithBOM = BOM + csvContent;
|
||||
|
||||
const blob = new Blob([csvContentWithBOM], {
|
||||
type: 'text/csv;charset=utf-8;',
|
||||
});
|
||||
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.setAttribute('href', url);
|
||||
link.setAttribute('download', 'exported_users.csv');
|
||||
link.style.visibility = 'hidden';
|
||||
document.body.append(link);
|
||||
link.click();
|
||||
|
||||
setTimeout(() => {
|
||||
link.remove();
|
||||
URL.revokeObjectURL(url);
|
||||
}, 100);
|
||||
|
||||
callback?.();
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const copyToClipboard = useCallback(
|
||||
async (users: UserType[], fields: ExportField[], callback?: () => void) => {
|
||||
const selectedFields = fields
|
||||
.filter(field => field.checked)
|
||||
.map(field => field.id);
|
||||
|
||||
const dataToCopy: {
|
||||
[key: string]: string;
|
||||
}[] = [];
|
||||
users.forEach(user => {
|
||||
const row: { [key: string]: string } = {};
|
||||
selectedFields.forEach(fieldId => {
|
||||
const value = user[fieldId as keyof UserType];
|
||||
row[fieldId] = typeof value === 'string' ? value : String(value);
|
||||
});
|
||||
dataToCopy.push(row);
|
||||
});
|
||||
navigator.clipboard.writeText(JSON.stringify(dataToCopy, null, 2));
|
||||
callback?.();
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return {
|
||||
exportCSV,
|
||||
copyToClipboard,
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user