feat(project): base smatec's frontend
This commit is contained in:
451
src/pages/Manager/Group/index.tsx
Normal file
451
src/pages/Manager/Group/index.tsx
Normal file
@@ -0,0 +1,451 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user