feat(users): add user permissions management and enhance theme switcher
This commit is contained in:
318
src/pages/Manager/User/Permission/components/AssignGroup.tsx
Normal file
318
src/pages/Manager/User/Permission/components/AssignGroup.tsx
Normal file
@@ -0,0 +1,318 @@
|
||||
import TreeSelectedGroup from '@/components/shared/TreeSelectedGroup';
|
||||
import { HTTPSTATUS } from '@/constants';
|
||||
import {
|
||||
apiAssignToGroup,
|
||||
apiQueryMembers,
|
||||
apiUnassignToGroup,
|
||||
} from '@/services/master/GroupController';
|
||||
import { apiChangeRoleUser } from '@/services/master/UserController';
|
||||
import { DeleteOutlined } from '@ant-design/icons';
|
||||
import { ActionType, ProList, ProListMetas } from '@ant-design/pro-components';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import {
|
||||
Button,
|
||||
Flex,
|
||||
message,
|
||||
Modal,
|
||||
Popconfirm,
|
||||
Segmented,
|
||||
Space,
|
||||
Tag,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import { useRef, useState } from 'react';
|
||||
const { Text } = Typography;
|
||||
type AssignGroupProps = {
|
||||
user: MasterModel.ProfileResponse | null;
|
||||
};
|
||||
const AssignGroup = ({ user }: AssignGroupProps) => {
|
||||
const groupActionRef = useRef<ActionType>();
|
||||
const intl = useIntl();
|
||||
const [assignedGroups, setAssignedGroups] = useState<MasterModel.GroupNode[]>(
|
||||
[],
|
||||
);
|
||||
const [messageApi, contextHolder] = message.useMessage();
|
||||
const [userRole, setUserRole] = useState<string>(
|
||||
user?.metadata?.user_type || 'users',
|
||||
);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [confirmLoading, setConfirmLoading] = useState(false);
|
||||
const [groupSelected, setGroupSelected] = useState<string | string[] | null>(
|
||||
null,
|
||||
);
|
||||
const columns: ProListMetas<MasterModel.GroupNode> = {
|
||||
title: {
|
||||
dataIndex: 'name',
|
||||
|
||||
render: (_, item: MasterModel.GroupNode) => (
|
||||
<Typography.Paragraph
|
||||
style={{
|
||||
marginBottom: 0,
|
||||
marginLeft: 8,
|
||||
verticalAlign: 'middle',
|
||||
display: 'inline-block',
|
||||
}}
|
||||
copyable
|
||||
>
|
||||
{item?.name}
|
||||
</Typography.Paragraph>
|
||||
),
|
||||
},
|
||||
subTitle: {
|
||||
render: (_, entity) => {
|
||||
return (
|
||||
<Space>
|
||||
<Tooltip
|
||||
title={intl.formatMessage({
|
||||
id: 'master.groups.code',
|
||||
defaultMessage: 'Mã đơn vị',
|
||||
})}
|
||||
>
|
||||
<Tag color="blue">{entity?.metadata?.code || '-'}</Tag>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
title={intl.formatMessage({
|
||||
id: 'master.groups.short_name',
|
||||
defaultMessage: 'Tên viết tắt',
|
||||
})}
|
||||
>
|
||||
<Tag color="green">{entity?.metadata?.short_name || '-'}</Tag>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
);
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
render: (_, item: MasterModel.GroupNode) => {
|
||||
return (
|
||||
<Popconfirm
|
||||
title={`${intl.formatMessage({
|
||||
id: 'master.users.unassign.content',
|
||||
defaultMessage: 'Are you sure unassign this group',
|
||||
})} ${item?.name}?`}
|
||||
onConfirm={async () => {
|
||||
try {
|
||||
const body: MasterModel.AssignMemberRequest = {
|
||||
group_id: item.id,
|
||||
type: 'users',
|
||||
members: [user?.id || ''],
|
||||
};
|
||||
const success = await apiUnassignToGroup(body);
|
||||
if (success.status === HTTPSTATUS.HTTP_NOCONTENT) {
|
||||
messageApi.success(
|
||||
intl.formatMessage({
|
||||
id: 'master.users.unassign.success',
|
||||
defaultMessage: 'Unassign group successfully',
|
||||
}),
|
||||
);
|
||||
groupActionRef.current?.reload();
|
||||
} else {
|
||||
throw new Error('Unassign group failed');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error when unassign group: ', error);
|
||||
messageApi.error(
|
||||
intl.formatMessage({
|
||||
id: 'master.users.unassign.fail',
|
||||
defaultMessage: 'Unassign group failed',
|
||||
}),
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Button type="primary" danger icon={<DeleteOutlined />} />
|
||||
</Popconfirm>
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const showModal = () => {
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const handleAssignUserToGroup = async () => {
|
||||
setConfirmLoading(true);
|
||||
try {
|
||||
const body: MasterModel.AssignMemberRequest = {
|
||||
group_id: groupSelected as string,
|
||||
type: userRole === 'admin' ? 'admin' : 'users',
|
||||
members: [user?.id || ''],
|
||||
};
|
||||
const resp = await apiAssignToGroup(body);
|
||||
if (resp.status === HTTPSTATUS.HTTP_SUCCESS) {
|
||||
messageApi.success(
|
||||
intl.formatMessage({
|
||||
id: 'master.users.assign.success',
|
||||
defaultMessage: 'Assign group successfully',
|
||||
}),
|
||||
);
|
||||
groupActionRef.current?.reload();
|
||||
setOpen(false);
|
||||
} else {
|
||||
throw new Error('Assign group failed');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error when assign group: ', error);
|
||||
messageApi.error(
|
||||
intl.formatMessage({
|
||||
id: 'master.users.assign.fail',
|
||||
defaultMessage: 'Assign group failed',
|
||||
}),
|
||||
);
|
||||
} finally {
|
||||
setConfirmLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
{contextHolder}
|
||||
<Modal
|
||||
title={intl.formatMessage({
|
||||
id: 'master.users.group_assign.button.title',
|
||||
defaultMessage: 'Assign Groups',
|
||||
})}
|
||||
open={open}
|
||||
onOk={handleAssignUserToGroup}
|
||||
confirmLoading={confirmLoading}
|
||||
onCancel={handleCancel}
|
||||
>
|
||||
<Flex vertical gap={5}>
|
||||
<Text>
|
||||
{intl.formatMessage({
|
||||
id: 'master.users.group_assign.select.title',
|
||||
defaultMessage: 'Select Group',
|
||||
})}
|
||||
:
|
||||
</Text>
|
||||
<TreeSelectedGroup
|
||||
disabled
|
||||
groupIds={assignedGroups.map((group) => group.id)}
|
||||
onSelected={(value) => {
|
||||
setGroupSelected(value);
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
</Modal>
|
||||
<ProList<MasterModel.GroupNode>
|
||||
headerTitle={intl.formatMessage({
|
||||
id: 'master.users.group_assign.title',
|
||||
defaultMessage: 'Assigned list',
|
||||
})}
|
||||
actionRef={groupActionRef}
|
||||
metas={columns}
|
||||
toolBarRender={() => [
|
||||
user?.metadata?.user_type !== 'enduser' && (
|
||||
<Segmented
|
||||
key="role"
|
||||
value={userRole}
|
||||
onChange={(value) => {
|
||||
Modal.confirm({
|
||||
title: intl.formatMessage({
|
||||
id: 'master.users.change_role.confirm.title',
|
||||
defaultMessage: 'Xác nhận thay đổi vai trò',
|
||||
}),
|
||||
content:
|
||||
userRole === 'admin'
|
||||
? intl.formatMessage({
|
||||
id: 'master.users.change_role.user.content',
|
||||
defaultMessage:
|
||||
'Bạn có chắc muốn thay đổi vai trò thành giám sát đơn vị không?',
|
||||
})
|
||||
: intl.formatMessage({
|
||||
id: 'master.users.change_role.admin.content',
|
||||
defaultMessage:
|
||||
'Bạn có chắc muốn thay đổi vai trò thành quản lý đơn vị không?',
|
||||
}),
|
||||
onOk: async () => {
|
||||
try {
|
||||
const resp = await apiChangeRoleUser(
|
||||
user?.id || '',
|
||||
value as 'users' | 'admin',
|
||||
);
|
||||
if (resp.status === HTTPSTATUS.HTTP_SUCCESS) {
|
||||
messageApi.success(
|
||||
intl.formatMessage({
|
||||
id: 'master.users.change_role.user.success',
|
||||
defaultMessage: 'Role change successful',
|
||||
}),
|
||||
);
|
||||
groupActionRef.current?.reload();
|
||||
setUserRole(value as string);
|
||||
} else {
|
||||
throw new Error('Change role failed');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error when change role user: ', error);
|
||||
messageApi.error(
|
||||
intl.formatMessage({
|
||||
id: 'master.users.change_role.user.fail',
|
||||
defaultMessage: 'Role change failed',
|
||||
}),
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
}}
|
||||
options={[
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'master.users.role.user',
|
||||
defaultMessage: 'Giám sát',
|
||||
}),
|
||||
value: 'users',
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'master.users.role.admin',
|
||||
defaultMessage: 'Quản lý',
|
||||
}),
|
||||
value: 'admin',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
),
|
||||
<Button
|
||||
key="assign"
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
showModal();
|
||||
}}
|
||||
>
|
||||
{intl.formatMessage({
|
||||
id: 'master.users.group_assign.button.title',
|
||||
defaultMessage: 'Assign',
|
||||
})}
|
||||
</Button>,
|
||||
]}
|
||||
request={async () => {
|
||||
if (user?.id) {
|
||||
const resp = await apiQueryMembers(user.id);
|
||||
if (resp?.groups) {
|
||||
setAssignedGroups(resp.groups);
|
||||
return Promise.resolve({
|
||||
success: true,
|
||||
data: resp?.groups,
|
||||
total: resp?.groups?.length || 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
return Promise.resolve({
|
||||
success: false,
|
||||
data: [],
|
||||
total: 0,
|
||||
});
|
||||
}}
|
||||
rowKey="id"
|
||||
search={false}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AssignGroup;
|
||||
100
src/pages/Manager/User/Permission/components/ShareThing.tsx
Normal file
100
src/pages/Manager/User/Permission/components/ShareThing.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import { ActionType, ProList } from '@ant-design/pro-components';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { message } from 'antd';
|
||||
import { useRef } from 'react';
|
||||
|
||||
type ShareThingProps = {
|
||||
user: MasterModel.ProfileResponse | null;
|
||||
};
|
||||
const ShareThing = ({ user }: ShareThingProps) => {
|
||||
const listActionRef = useRef<ActionType>();
|
||||
const intl = useIntl();
|
||||
const [messageAPI, contextHolder] = message.useMessage();
|
||||
return (
|
||||
<>
|
||||
{contextHolder}
|
||||
<ProList
|
||||
headerTitle={intl.formatMessage({
|
||||
id: 'pages.users.things.list',
|
||||
defaultMessage: 'List things',
|
||||
})}
|
||||
actionRef={listActionRef}
|
||||
toolBarRender={() => [
|
||||
// <Button
|
||||
// type="primary"
|
||||
// key="primary"
|
||||
// onClick={() => {
|
||||
// handleShareModalVisible(true);
|
||||
// }}
|
||||
// >
|
||||
// <PlusOutlined />{" "}
|
||||
// <FormattedMessage
|
||||
// id="pages.things.share.text"
|
||||
// defaultMessage="Share"
|
||||
// />
|
||||
// </Button>,
|
||||
]}
|
||||
metas={columns}
|
||||
request={async () => {
|
||||
const query = {
|
||||
type: 'sub',
|
||||
id: user?.id || '',
|
||||
};
|
||||
if (user?.id) {
|
||||
const resp = (await apiQueryThingsByPolicy(
|
||||
query,
|
||||
)) as PolicyResponse;
|
||||
const { relations } = resp;
|
||||
if (relations) {
|
||||
const queries = relations.map(async (rel: PolicyRelation) => {
|
||||
const thg = await apiQueryThing(rel.id);
|
||||
return {
|
||||
...thg,
|
||||
relations: rel?.actions,
|
||||
};
|
||||
});
|
||||
const policies = await Promise.all(queries);
|
||||
return Promise.resolve({
|
||||
success: true,
|
||||
data: policies,
|
||||
total: policies.length,
|
||||
});
|
||||
}
|
||||
}
|
||||
return Promise.resolve({
|
||||
success: false,
|
||||
data: [],
|
||||
total: 0,
|
||||
});
|
||||
}}
|
||||
rowKey="id"
|
||||
search={false}
|
||||
// rowSelection={{
|
||||
// selectedRowKeys: selectedRowsState.map((row) => row.id).filter((id): id is string => id !== undefined),
|
||||
// onChange: (_: React.Key[], selectedRows: API.Thing[]) => {
|
||||
// setSelectedRows(selectedRows);
|
||||
// },
|
||||
// }}
|
||||
/>
|
||||
{/* <FormShareVms
|
||||
visible={shareModalVisibale}
|
||||
onVisibleChange={handleShareModalVisible}
|
||||
user={user}
|
||||
onSubmit={async (values: ShareFormValues) => {
|
||||
console.log(values);
|
||||
const success = await handleShare(values);
|
||||
if (success) {
|
||||
handleShareModalVisible(false);
|
||||
onReload();
|
||||
if (actionRef.current) {
|
||||
//await delay(1000);
|
||||
actionRef.current.reload();
|
||||
}
|
||||
}
|
||||
}}
|
||||
/> */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ShareThing;
|
||||
Reference in New Issue
Block a user