feat(users): add user permissions management and enhance theme switcher
This commit is contained in:
@@ -2,17 +2,24 @@ import IconFont from '@/components/IconFont';
|
||||
import TreeGroup from '@/components/shared/TreeGroup';
|
||||
import { DEFAULT_PAGE_SIZE } from '@/constants';
|
||||
import {
|
||||
ROUTE_MANAGER_USERS,
|
||||
ROUTE_MANAGER_USERS_PERMISSIONS,
|
||||
} from '@/constants/routes';
|
||||
import {
|
||||
apiDeleteUser,
|
||||
apiQueryUsers,
|
||||
apiQueryUsersByGroup,
|
||||
} from '@/services/master/UserController';
|
||||
import { DeleteOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
ActionType,
|
||||
FooterToolbar,
|
||||
ProCard,
|
||||
ProColumns,
|
||||
ProTable,
|
||||
} from '@ant-design/pro-components';
|
||||
import { FormattedMessage, useIntl } from '@umijs/max';
|
||||
import { Button, Grid } from 'antd';
|
||||
import { FormattedMessage, history, useIntl } from '@umijs/max';
|
||||
import { Button, Grid, Popconfirm, theme } from 'antd';
|
||||
import message from 'antd/es/message';
|
||||
import Paragraph from 'antd/lib/typography/Paragraph';
|
||||
import { useRef, useState } from 'react';
|
||||
@@ -22,18 +29,21 @@ const ManagerUserPage = () => {
|
||||
const { useBreakpoint } = Grid;
|
||||
const intl = useIntl();
|
||||
const screens = useBreakpoint();
|
||||
const { token } = theme.useToken();
|
||||
const actionRef = useRef<ActionType | null>(null);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [messageApi, contextHolder] = message.useMessage();
|
||||
const [selectedRowsState, setSelectedRowsState] = useState<
|
||||
MasterModel.ProfileResponse[]
|
||||
>([]);
|
||||
|
||||
const [groupCheckedKeys, setGroupCheckedKeys] = useState<
|
||||
string | string[] | null
|
||||
>(null);
|
||||
|
||||
const handleClickAssign = (user: MasterModel.ProfileResponse) => {
|
||||
console.log('User ', user);
|
||||
const path = `${ROUTE_MANAGER_USERS}/${user.id}/${ROUTE_MANAGER_USERS_PERMISSIONS}`;
|
||||
history.push(path);
|
||||
};
|
||||
|
||||
const columns: ProColumns<MasterModel.ProfileResponse>[] = [
|
||||
@@ -53,6 +63,7 @@ const ManagerUserPage = () => {
|
||||
marginBottom: 0,
|
||||
verticalAlign: 'middle',
|
||||
display: 'inline-block',
|
||||
color: token.colorText,
|
||||
}}
|
||||
copyable
|
||||
>
|
||||
@@ -82,6 +93,7 @@ const ManagerUserPage = () => {
|
||||
marginBottom: 0,
|
||||
verticalAlign: 'middle',
|
||||
display: 'inline-block',
|
||||
color: token.colorText,
|
||||
}}
|
||||
copyable
|
||||
>
|
||||
@@ -124,6 +136,48 @@ const ManagerUserPage = () => {
|
||||
},
|
||||
];
|
||||
|
||||
const handleRemove = async (selectedRows: MasterModel.ProfileResponse[]) => {
|
||||
const key = 'remove_user';
|
||||
if (!selectedRows) return true;
|
||||
|
||||
try {
|
||||
messageApi.open({
|
||||
type: 'loading',
|
||||
content: intl.formatMessage({
|
||||
id: 'common.deleting',
|
||||
defaultMessage: 'deleting...',
|
||||
}),
|
||||
duration: 0,
|
||||
key,
|
||||
});
|
||||
const allDelete = selectedRows.map(
|
||||
async (row: MasterModel.ProfileResponse) => {
|
||||
await apiDeleteUser(row?.id || '');
|
||||
},
|
||||
);
|
||||
await Promise.all(allDelete);
|
||||
messageApi.open({
|
||||
type: 'success',
|
||||
content: intl.formatMessage({
|
||||
id: 'master.users.delete.success',
|
||||
defaultMessage: 'Deleted successfully and will refresh soon',
|
||||
}),
|
||||
key,
|
||||
});
|
||||
return true;
|
||||
} catch (error) {
|
||||
messageApi.open({
|
||||
type: 'error',
|
||||
content: intl.formatMessage({
|
||||
id: 'master.users.delete.fail',
|
||||
defaultMessage: 'Delete failed, please try again!',
|
||||
}),
|
||||
key,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{contextHolder}
|
||||
@@ -182,56 +236,66 @@ const ManagerUserPage = () => {
|
||||
const offset = current === 1 ? 0 : (current - 1) * size;
|
||||
setIsLoading(true);
|
||||
// If groups are checked, use queryUsersByGroup
|
||||
if (groupCheckedKeys && groupCheckedKeys.length > 0) {
|
||||
// Ensure groupCheckedKeys is an array
|
||||
const groupIdsArray = Array.isArray(groupCheckedKeys)
|
||||
? groupCheckedKeys.join(',')
|
||||
: groupCheckedKeys;
|
||||
try {
|
||||
if (groupCheckedKeys && groupCheckedKeys.length > 0) {
|
||||
// Ensure groupCheckedKeys is an array
|
||||
const groupIdsArray = Array.isArray(groupCheckedKeys)
|
||||
? groupCheckedKeys.join(',')
|
||||
: groupCheckedKeys;
|
||||
|
||||
const userByGroupResponses = await apiQueryUsersByGroup(
|
||||
groupIdsArray,
|
||||
);
|
||||
let users = userByGroupResponses.users || [];
|
||||
// Apply filters
|
||||
if (email) {
|
||||
users = users.filter((user: MasterModel.ProfileResponse) =>
|
||||
user.email?.includes(email),
|
||||
const userByGroupResponses = await apiQueryUsersByGroup(
|
||||
groupIdsArray,
|
||||
);
|
||||
}
|
||||
if (phone_number) {
|
||||
users = users.filter((user: MasterModel.ProfileResponse) =>
|
||||
user.metadata?.phone_number?.includes(phone_number),
|
||||
);
|
||||
}
|
||||
let users = userByGroupResponses.users || [];
|
||||
// Apply filters
|
||||
if (email) {
|
||||
users = users.filter((user: MasterModel.ProfileResponse) =>
|
||||
user.email?.includes(email),
|
||||
);
|
||||
}
|
||||
if (phone_number) {
|
||||
users = users.filter((user: MasterModel.ProfileResponse) =>
|
||||
user.metadata?.phone_number?.includes(phone_number),
|
||||
);
|
||||
}
|
||||
|
||||
const total = users.length;
|
||||
const paginatedUsers = users.slice(offset, offset + size);
|
||||
const total = users.length;
|
||||
const paginatedUsers = users.slice(offset, offset + size);
|
||||
setIsLoading(false);
|
||||
return {
|
||||
data: paginatedUsers,
|
||||
success: true,
|
||||
total: total,
|
||||
};
|
||||
} else {
|
||||
// Use regular queryUsers API
|
||||
const metadata: Partial<MasterModel.ProfileMetadata> = {};
|
||||
if (phone_number) metadata.phone_number = phone_number;
|
||||
|
||||
const query: MasterModel.SearchUserPaginationBody = {
|
||||
offset: offset,
|
||||
limit: size,
|
||||
order: 'name',
|
||||
dir: 'asc',
|
||||
};
|
||||
if (email) query.email = email;
|
||||
if (Object.keys(metadata).length > 0)
|
||||
query.metadata = metadata;
|
||||
|
||||
const response = await apiQueryUsers(query);
|
||||
setIsLoading(false);
|
||||
return {
|
||||
data: response.users,
|
||||
success: true,
|
||||
total: response.total,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
setIsLoading(false);
|
||||
return {
|
||||
data: paginatedUsers,
|
||||
success: true,
|
||||
total: total,
|
||||
};
|
||||
} else {
|
||||
// Use regular queryUsers API
|
||||
const metadata: Partial<MasterModel.ProfileMetadata> = {};
|
||||
if (phone_number) metadata.phone_number = phone_number;
|
||||
|
||||
const query: MasterModel.SearchUserPaginationBody = {
|
||||
offset: offset,
|
||||
limit: size,
|
||||
order: 'name',
|
||||
dir: 'asc',
|
||||
};
|
||||
if (email) query.email = email;
|
||||
if (Object.keys(metadata).length > 0) query.metadata = metadata;
|
||||
|
||||
const response = await apiQueryUsers(query);
|
||||
setIsLoading(false);
|
||||
return {
|
||||
data: response.users,
|
||||
success: true,
|
||||
total: response.total,
|
||||
data: [],
|
||||
success: false,
|
||||
total: 0,
|
||||
};
|
||||
}
|
||||
}}
|
||||
@@ -255,6 +319,53 @@ const ManagerUserPage = () => {
|
||||
/>
|
||||
</ProCard>
|
||||
</ProCard>
|
||||
{selectedRowsState?.length > 0 && (
|
||||
<FooterToolbar
|
||||
extra={
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id="master.footer.chosen"
|
||||
defaultMessage="Chosen"
|
||||
/>{' '}
|
||||
<a
|
||||
style={{
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
{selectedRowsState.length}
|
||||
</a>{' '}
|
||||
<FormattedMessage
|
||||
id="master.users.table.pagination"
|
||||
defaultMessage="users"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Popconfirm
|
||||
title={intl.formatMessage({
|
||||
id: 'master.users.deletion.title',
|
||||
defaultMessage: 'Are you sure to delete this selected items',
|
||||
})}
|
||||
okText={intl.formatMessage({
|
||||
id: 'common.sure',
|
||||
defaultMessage: 'Sure',
|
||||
})}
|
||||
onConfirm={async () => {
|
||||
const success = await handleRemove(selectedRowsState);
|
||||
if (success) {
|
||||
setSelectedRowsState([]);
|
||||
if (actionRef.current) {
|
||||
actionRef.current.reload();
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Button icon={<DeleteOutlined />} type="primary" danger>
|
||||
<FormattedMessage id="common.delete" defaultMessage="Delete" />
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</FooterToolbar>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user