452 lines
15 KiB
TypeScript
452 lines
15 KiB
TypeScript
import IconFont from '@/components/IconFont';
|
|
import TreeGroup from '@/components/shared/TreeGroup';
|
|
import { HTTPSTATUS } from '@/constants';
|
|
import {
|
|
apiDeleteGroup,
|
|
apiUpdateGroup,
|
|
} from '@/services/master/GroupController';
|
|
import {
|
|
DeleteOutlined,
|
|
EditOutlined,
|
|
ExclamationCircleOutlined,
|
|
PlusOutlined,
|
|
} from '@ant-design/icons';
|
|
import { ProCard, ProDescriptions } from '@ant-design/pro-components';
|
|
import { FormattedMessage, useIntl, useModel } from '@umijs/max';
|
|
import { Button, Grid, message, Modal } from 'antd';
|
|
import { Dropdown, MenuProps, Tooltip } from 'antd/lib';
|
|
import { useState } from 'react';
|
|
import CreateOrUpdateGroup from './components/CreateOrUpdateGroup';
|
|
type MenuItem = Required<MenuProps>['items'][number];
|
|
const ManagerGroupPage = () => {
|
|
const { useBreakpoint } = Grid;
|
|
const { initialState } = useModel('@@initialState');
|
|
const { currentUserProfile } = initialState || {};
|
|
const intl = useIntl();
|
|
const screens = useBreakpoint();
|
|
const [selectedItem, setSelectedItem] = useState<
|
|
MasterModel.GroupNode | undefined
|
|
>();
|
|
const [selectedGroup, setSelectedGroup] = useState<
|
|
MasterModel.GroupNode | undefined
|
|
>();
|
|
|
|
const { groups, getGroups } = useModel('master.useGroups');
|
|
const [handleChildModal, setHandleChildModal] = useState<boolean>(false);
|
|
const [type, setType] = useState<'create-root' | 'create-child' | 'update'>(
|
|
'create-root',
|
|
);
|
|
const [messageApi, contextHolder] = message.useMessage();
|
|
const [modalKey, setModalKey] = useState(0);
|
|
|
|
const findGroupById = (
|
|
groups: MasterModel.GroupNode[],
|
|
id: string,
|
|
): MasterModel.GroupNode | undefined => {
|
|
for (const group of groups) {
|
|
if (group.id === id) return group;
|
|
if (group.children) {
|
|
const found = findGroupById(group.children, id);
|
|
if (found) return found;
|
|
}
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
const handleUpdate = async (group: Partial<MasterModel.GroupNode>) => {
|
|
const key = 'update_group';
|
|
const { name, description, parent_id, code, short_name } = group;
|
|
const editGroup = parent_id
|
|
? {
|
|
...selectedItem,
|
|
name: name,
|
|
parent_id: parent_id,
|
|
metadata: {
|
|
code: code,
|
|
short_name: short_name,
|
|
},
|
|
description: description,
|
|
}
|
|
: {
|
|
...selectedItem,
|
|
name: name,
|
|
metadata: {
|
|
code: code,
|
|
short_name: short_name,
|
|
},
|
|
description: description,
|
|
};
|
|
try {
|
|
messageApi.open({
|
|
type: 'loading',
|
|
content: intl.formatMessage({
|
|
id: 'common.updating',
|
|
defaultMessage: 'updating...',
|
|
}),
|
|
key,
|
|
});
|
|
await apiUpdateGroup({ ...editGroup });
|
|
messageApi.open({
|
|
type: 'success',
|
|
content: intl.formatMessage({
|
|
id: 'master.groups.update.success',
|
|
defaultMessage: 'Updated successfully',
|
|
}),
|
|
key,
|
|
});
|
|
await getGroups();
|
|
return true;
|
|
} catch (error) {
|
|
messageApi.open({
|
|
type: 'error',
|
|
content: intl.formatMessage({
|
|
id: 'master.groups.update.failed',
|
|
defaultMessage: 'Updating failed, please try again!',
|
|
}),
|
|
key,
|
|
});
|
|
return false;
|
|
}
|
|
};
|
|
const showDeleteConfirm = (group: MasterModel.GroupNode) => {
|
|
Modal.confirm({
|
|
title: intl.formatMessage({
|
|
id: 'master.groups.delete.confirm',
|
|
defaultMessage: 'Are you sure delete this group',
|
|
}),
|
|
content: selectedItem?.name,
|
|
icon: <ExclamationCircleOutlined />,
|
|
okText: intl.formatMessage({ id: 'common.yes', defaultMessage: 'Yes' }),
|
|
okType: 'danger',
|
|
cancelText: intl.formatMessage({ id: 'common.no', defaultMessage: 'No' }),
|
|
async onOk() {
|
|
try {
|
|
const resp = await apiDeleteGroup(group.id);
|
|
if (resp.status === HTTPSTATUS.HTTP_NOCONTENT) {
|
|
messageApi.success(
|
|
intl.formatMessage({
|
|
id: 'master.groups.delete.success',
|
|
defaultMessage: 'Deleted group successfully',
|
|
}),
|
|
);
|
|
setSelectedItem(undefined);
|
|
await getGroups();
|
|
} else if (resp.status === HTTPSTATUS.HTTP_SERVERERROR) {
|
|
messageApi.warning(
|
|
intl.formatMessage({
|
|
id: 'master.groups.delete.failed_internal',
|
|
defaultMessage:
|
|
'The group contains devices or users and cannot be deleted',
|
|
}),
|
|
);
|
|
} else {
|
|
throw new Error('Delete group failed');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error when delete group ', error);
|
|
|
|
messageApi.error(
|
|
intl.formatMessage({
|
|
id: 'master.groups.delete.failed',
|
|
defaultMessage: 'Delete group failed',
|
|
}),
|
|
);
|
|
}
|
|
},
|
|
});
|
|
};
|
|
const onItemClick = ({ key }: { key: string }) => {
|
|
const splitIndex = key.indexOf('-');
|
|
const action = key.substring(0, splitIndex);
|
|
const groupId = key.substring(splitIndex + 1);
|
|
|
|
const groupNode = findGroupById(groups || [], groupId);
|
|
console.log('GroupName', groupNode?.name);
|
|
|
|
setSelectedItem(groupNode);
|
|
setSelectedGroup(groupNode);
|
|
if (action === '1') {
|
|
setType('create-child');
|
|
} else if (action === '2') {
|
|
setType('update');
|
|
} else if (action === '3') {
|
|
showDeleteConfirm(groupNode!);
|
|
return;
|
|
}
|
|
|
|
// Increment modalKey to force remount component
|
|
setModalKey((prev) => prev + 1);
|
|
setHandleChildModal(true);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
{contextHolder}
|
|
|
|
<CreateOrUpdateGroup
|
|
type={type}
|
|
key={modalKey}
|
|
isOpen={handleChildModal}
|
|
setIsOpen={setHandleChildModal}
|
|
group={selectedGroup}
|
|
message={messageApi}
|
|
/>
|
|
<ProCard split={screens.md ? 'vertical' : 'horizontal'}>
|
|
<ProCard
|
|
colSpan={{ xs: 24, sm: 24, md: 10, lg: 10, xl: 10 }}
|
|
extra={[
|
|
currentUserProfile?.metadata?.user_type === 'sysadmin' && (
|
|
<Button
|
|
type="primary"
|
|
key="primary"
|
|
icon={<PlusOutlined />}
|
|
onClick={() => {
|
|
setType('create-root');
|
|
setSelectedItem(undefined);
|
|
setModalKey((prev) => prev + 1);
|
|
setHandleChildModal(true);
|
|
}}
|
|
>
|
|
<FormattedMessage
|
|
id="master.groups.root"
|
|
defaultMessage="Add root"
|
|
/>
|
|
</Button>
|
|
),
|
|
]}
|
|
>
|
|
<TreeGroup
|
|
titleRender={(item) => {
|
|
const groupNode = findGroupById(groups || [], item.key as string);
|
|
const menus: MenuItem[] = [
|
|
{
|
|
label: (
|
|
<Tooltip
|
|
title={
|
|
groupNode?.metadata?.has_thing
|
|
? intl.formatMessage({
|
|
id: 'master.groups.cannot-add-group',
|
|
defaultMessage:
|
|
'Cannot add child to a group that has things',
|
|
})
|
|
: undefined
|
|
}
|
|
>
|
|
{intl.formatMessage({
|
|
id: 'master.groups.add',
|
|
defaultMessage: 'Add child',
|
|
})}
|
|
</Tooltip>
|
|
),
|
|
key: `1-${groupNode?.id}`,
|
|
|
|
icon: <IconFont type="icon-leaf" />,
|
|
disabled: groupNode?.metadata?.has_thing === true,
|
|
},
|
|
{
|
|
label: intl.formatMessage({
|
|
id: 'common.edit',
|
|
defaultMessage: 'Update',
|
|
}),
|
|
key: `2-${groupNode?.id}`,
|
|
icon: <EditOutlined />,
|
|
},
|
|
{
|
|
label: (
|
|
<Tooltip
|
|
title={
|
|
groupNode?.metadata?.has_thing
|
|
? intl.formatMessage({
|
|
id: 'master.groups.delete.failed_internal',
|
|
defaultMessage:
|
|
'The group contains devices or users and cannot be deleted',
|
|
})
|
|
: undefined
|
|
}
|
|
>
|
|
{intl.formatMessage({
|
|
id: 'common.delete',
|
|
defaultMessage: 'Delete',
|
|
})}
|
|
</Tooltip>
|
|
),
|
|
key: `3-${groupNode?.id}`,
|
|
icon: <DeleteOutlined />,
|
|
disabled: groupNode?.metadata?.has_thing === true,
|
|
},
|
|
];
|
|
return (
|
|
<Dropdown
|
|
menu={{
|
|
items: menus,
|
|
onClick: onItemClick,
|
|
}}
|
|
trigger={['contextMenu']}
|
|
placement="bottomRight"
|
|
arrow
|
|
>
|
|
<span>
|
|
{typeof item.title === 'function'
|
|
? item.title(item)
|
|
: item.title}
|
|
</span>
|
|
</Dropdown>
|
|
);
|
|
}}
|
|
multiple={false}
|
|
onSelected={(value: string | string[] | null) => {
|
|
if (!groups) {
|
|
setSelectedItem(undefined);
|
|
return;
|
|
}
|
|
// Find the selected group from model
|
|
if (value && !Array.isArray(value)) {
|
|
const found = findGroupById(groups, value);
|
|
setSelectedItem(found);
|
|
} else {
|
|
setSelectedItem(undefined);
|
|
}
|
|
}}
|
|
/>
|
|
</ProCard>
|
|
<ProCard colSpan={{ xs: 24, sm: 24, md: 14, lg: 14, xl: 14 }}>
|
|
{selectedItem?.name && (
|
|
<>
|
|
<ProDescriptions<Partial<MasterModel.GroupNode>>
|
|
column={1}
|
|
bordered
|
|
title={selectedItem.name}
|
|
extra={[
|
|
<Tooltip
|
|
key="add"
|
|
title={
|
|
selectedItem?.metadata?.has_thing
|
|
? intl.formatMessage({
|
|
id: 'master.groups.cannot-add-group',
|
|
defaultMessage:
|
|
'Cannot add child to a group that has things',
|
|
})
|
|
: undefined
|
|
}
|
|
>
|
|
<Button
|
|
key="add"
|
|
type="primary"
|
|
icon={<PlusOutlined />}
|
|
onClick={() => {
|
|
setType('create-child');
|
|
setModalKey((prev) => prev + 1);
|
|
setHandleChildModal(true);
|
|
}}
|
|
disabled={
|
|
selectedItem.metadata?.has_thing === true ? true : false
|
|
}
|
|
>
|
|
<FormattedMessage
|
|
id="master.groups.add"
|
|
defaultMessage="Add Group"
|
|
/>
|
|
</Button>
|
|
</Tooltip>,
|
|
<Tooltip
|
|
key="add-fail"
|
|
title={
|
|
selectedItem?.metadata?.has_thing
|
|
? intl.formatMessage({
|
|
id: 'master.groups.delete.failed_internal',
|
|
defaultMessage:
|
|
'Cannot add child to a group that has things',
|
|
})
|
|
: undefined
|
|
}
|
|
>
|
|
<Button
|
|
danger
|
|
key="delete"
|
|
title={intl.formatMessage({
|
|
id: 'master.groups.delete.confirm',
|
|
defaultMessage: 'Delete this group?',
|
|
})}
|
|
onClick={() => {
|
|
showDeleteConfirm(selectedItem);
|
|
}}
|
|
icon={<DeleteOutlined />}
|
|
disabled={
|
|
selectedItem.metadata?.has_thing === true ? true : false
|
|
}
|
|
>
|
|
<FormattedMessage
|
|
id="common.delete"
|
|
defaultMessage="Delete"
|
|
/>
|
|
</Button>
|
|
,
|
|
</Tooltip>,
|
|
]}
|
|
dataSource={{
|
|
name: selectedItem.name,
|
|
code: selectedItem.metadata?.code || '',
|
|
short_name: selectedItem.metadata?.short_name || '',
|
|
description: selectedItem.description || '',
|
|
}}
|
|
columns={[
|
|
{
|
|
title: intl.formatMessage({
|
|
id: 'common.name',
|
|
defaultMessage: 'Name',
|
|
}),
|
|
dataIndex: 'name',
|
|
},
|
|
{
|
|
title: intl.formatMessage({
|
|
id: 'master.groups.code',
|
|
defaultMessage: 'Code',
|
|
}),
|
|
dataIndex: 'code',
|
|
},
|
|
{
|
|
title: intl.formatMessage({
|
|
id: 'master.groups.short_name',
|
|
defaultMessage: 'Short name',
|
|
}),
|
|
dataIndex: 'short_name',
|
|
},
|
|
{
|
|
title: intl.formatMessage({
|
|
id: 'common.description',
|
|
defaultMessage: 'Description',
|
|
}),
|
|
dataIndex: 'description',
|
|
},
|
|
]}
|
|
editable={{
|
|
onSave: async (_, record) => {
|
|
const updatedData: MasterModel.GroupNode = {
|
|
...selectedItem,
|
|
name: record.name || '',
|
|
description: record.description || '',
|
|
metadata: {
|
|
...selectedItem.metadata,
|
|
code: record.code,
|
|
short_name: record.short_name,
|
|
},
|
|
};
|
|
const success = await handleUpdate(updatedData);
|
|
if (success) {
|
|
setSelectedItem(updatedData);
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
}}
|
|
/>
|
|
</>
|
|
)}
|
|
</ProCard>
|
|
</ProCard>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default ManagerGroupPage;
|