Files
SMATEC-FRONTEND/src/pages/Manager/User/Permission/components/ShareThing.tsx

395 lines
11 KiB
TypeScript

import ThingsFilter from '@/components/shared/ThingFilterModal';
import { DEFAULT_PAGE_SIZE } from '@/constants';
import {
apiDeleteUserThingPolicy,
apiGetThingPolicyByUser,
apiShareThingToUser,
} from '@/services/master/ThingController';
import { DeleteOutlined, ShareAltOutlined } from '@ant-design/icons';
import {
ActionType,
FooterToolbar,
ProList,
ProListMetas,
} from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import {
Button,
Checkbox,
GetProp,
message,
Popconfirm,
Tag,
Typography,
} from 'antd';
import { useRef, useState } from 'react';
const { Paragraph } = Typography;
type PolicyShareDefault = {
read: boolean;
write: boolean;
delete: boolean;
};
type ShareThingProps = {
user: MasterModel.UserResponse | null;
};
const ShareThing = ({ user }: ShareThingProps) => {
const listActionRef = useRef<ActionType>();
const intl = useIntl();
const [messageApi, contextHolder] = message.useMessage();
const [selectedRowsState, setSelectedRows] = useState<
MasterModel.ThingPolicy[]
>([]);
const [thingPolicy, setThingPolicy] = useState<MasterModel.ThingPolicy[]>([]);
const [shareThingModalVisible, setShareThingModalVisible] =
useState<boolean>(false);
const [policyShare, setPolicyShare] = useState<PolicyShareDefault>({
read: true,
write: true,
delete: true,
});
const getPolicyInfo = (
policy: MasterModel.Policy,
): { color: string; text: string } => {
switch (policy) {
case 'read':
return {
color: 'blue',
text: intl.formatMessage({
id: 'master.users.things.relation.read',
defaultMessage: 'Read',
}),
};
case 'write':
return {
color: 'gold',
text: intl.formatMessage({
id: 'master.users.things.relation.write',
defaultMessage: 'Write',
}),
};
case 'delete':
return {
color: 'red',
text: intl.formatMessage({
id: 'master.users.things.relation.delete',
defaultMessage: 'Delete',
}),
};
default:
return { color: 'default', text: policy };
}
};
const columns: ProListMetas<MasterModel.ThingPolicy> = {
title: {
dataIndex: 'name',
render: (_, record: MasterModel.ThingPolicy) => (
<Paragraph copyable>{record?.thing_name}</Paragraph>
),
},
subTitle: {
dataIndex: 'metadata.external_id',
render: (_, record: MasterModel.ThingPolicy) => (
<Paragraph copyable>{record?.external_id}</Paragraph>
),
},
description: {
dataIndex: 'policies',
render: (_, record: MasterModel.ThingPolicy) => {
return record?.policies?.map((policy) => {
const info = getPolicyInfo(policy);
return (
<Tag key={policy} color={info.color}>
{info.text}
</Tag>
);
});
},
},
};
const handleShareThings = async (thingIds: string[]) => {
const thingsFiltered = thingIds.filter((thingId) => {
return !thingPolicy.find((thing) => thing.thing_id === thingId);
});
if (thingsFiltered.length === 0) return;
const key = 'share';
try {
messageApi.open({
type: 'loading',
content: intl.formatMessage({
id: 'master.users.things.sharing',
defaultMessage: 'Sharing devices...',
}),
key,
});
const allShare = thingsFiltered.map(async (thingId) => {
const resp = await apiShareThingToUser(
thingId,
user?.id || '',
Object.keys(policyShare).filter(
(key) => policyShare[key as keyof PolicyShareDefault],
),
);
});
await Promise.all(allShare);
messageApi.open({
type: 'success',
content: intl.formatMessage({
id: 'master.users.thing.share.success',
defaultMessage: 'Share successfully and will refresh soon',
}),
key,
});
await new Promise((resolve) => {
setTimeout(resolve, 500);
});
if (listActionRef.current) {
listActionRef.current.reload();
}
} catch (error) {
console.error('Error when share thing: ', error);
messageApi.open({
type: 'error',
content: intl.formatMessage({
id: 'master.users.thing.share.fail',
defaultMessage: 'Share failed, please try again!',
}),
key,
});
}
};
const onChange: GetProp<typeof Checkbox.Group, 'onChange'> = (
checkedValues,
) => {
setPolicyShare({
read: checkedValues.includes('read'),
write: checkedValues.includes('write'),
delete: checkedValues.includes('delete'),
});
};
const handleUnshare = async (selectedRows: MasterModel.ThingPolicy[]) => {
if (!selectedRows) return true;
const key = 'unshare';
try {
messageApi.open({
type: 'loading',
content: intl.formatMessage({
id: 'master.users.things.unsharing',
defaultMessage: 'Unsharing devices...',
}),
key,
});
const allUnshare = selectedRows.map(async (row) => {
const resp = await apiDeleteUserThingPolicy(
row?.thing_id || '',
user?.id || '',
);
});
await Promise.all(allUnshare);
messageApi.open({
type: 'success',
content: intl.formatMessage({
id: 'master.users.thing.unshare.success',
defaultMessage: 'Unshare successfully and will refresh soon',
}),
key,
});
await new Promise((resolve) => {
setTimeout(resolve, 500);
});
return true;
} catch (error) {
console.error('Error when unshare thing: ', error);
messageApi.open({
type: 'error',
content: intl.formatMessage({
id: 'master.users.thing.unshare.fail',
defaultMessage: 'Unshare failed, please try again!',
}),
key,
});
return false;
}
};
return (
<>
{contextHolder}
<ThingsFilter
isOpen={shareThingModalVisible}
setIsOpen={setShareThingModalVisible}
thingIds={thingPolicy.map((thing) => thing.thing_id!)}
disabled
extra={
<Checkbox.Group
options={[
{
label: intl.formatMessage({
id: 'master.users.things.relation.read',
defaultMessage: 'Read',
}),
value: 'read',
disabled: true,
},
{
label: intl.formatMessage({
id: 'master.users.things.relation.write',
defaultMessage: 'Write',
}),
value: 'write',
},
{
label: intl.formatMessage({
id: 'master.users.things.relation.delete',
defaultMessage: 'Delete',
}),
value: 'delete',
},
]}
value={Object.keys(policyShare).filter(
(key) => policyShare[key as keyof PolicyShareDefault],
)}
onChange={onChange}
/>
}
onSubmit={handleShareThings}
/>
<ProList<MasterModel.ThingPolicy>
headerTitle={intl.formatMessage({
id: 'master.users.things.list',
defaultMessage: 'List things',
})}
actionRef={listActionRef}
toolBarRender={() => [
<Button
type="primary"
key="primary"
icon={<ShareAltOutlined />}
onClick={() => {
setShareThingModalVisible(true);
}}
>
<FormattedMessage
id="master.users.thing.share.title"
defaultMessage="Share"
/>
</Button>,
]}
pagination={{
pageSize: DEFAULT_PAGE_SIZE,
}}
metas={columns}
request={async (params) => {
const { current, pageSize } = params;
const query = {
type: 'sub',
id: user?.id || '',
};
if (user?.id) {
const offset = current === 1 ? 0 : (current! - 1) * pageSize!;
const policyBody: Partial<MasterModel.SearchPaginationBody> = {
offset: offset,
limit: pageSize,
};
const resp = await apiGetThingPolicyByUser(policyBody, user.id);
if (resp.things) {
setThingPolicy(resp.things);
return Promise.resolve({
success: true,
data: resp.things,
total: resp.total,
});
} else {
return {
success: false,
data: [],
total: 0,
};
}
}
return Promise.resolve({
success: false,
data: [],
total: 0,
});
}}
rowKey="external_id"
search={false}
rowSelection={{
selectedRowKeys: selectedRowsState
.map((row) => row.external_id)
.filter((id): id is string => id !== undefined),
onChange: (_, selectedRows: MasterModel.ThingPolicy[]) => {
setSelectedRows(selectedRows);
},
}}
/>
{selectedRowsState?.length > 0 && (
<FooterToolbar
extra={
<div>
<FormattedMessage
id="master.footer.chosen"
defaultMessage="Chosen"
/>{' '}
<a
style={{
fontWeight: 600,
}}
>
{selectedRowsState.length}
</a>{' '}
<FormattedMessage
id="common.paginations.things"
defaultMessage="item"
/>
</div>
}
>
<Popconfirm
title={intl.formatMessage({
id: 'master.users.thing.unshare.confirm',
defaultMessage: 'Are you sure to stop sharing these devices?',
})}
okText={intl.formatMessage({
id: 'common.sure',
defaultMessage: 'Sure',
})}
onConfirm={async () => {
const success = await handleUnshare(selectedRowsState);
if (success) {
setSelectedRows([]);
if (listActionRef.current) {
listActionRef.current.reload();
}
}
}}
>
<Button type="primary" danger icon={<DeleteOutlined />}>
<FormattedMessage
id="master.users.thing.unshare.title"
defaultMessage="Sure"
/>
</Button>
</Popconfirm>
</FooterToolbar>
)}
</>
);
};
export default ShareThing;