395 lines
11 KiB
TypeScript
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;
|