Files
SMATEC-FRONTEND/src/pages/Alarm/index.tsx

336 lines
11 KiB
TypeScript

import ThingsFilter from '@/components/shared/ThingFilterModal';
import { DATE_TIME_FORMAT, DEFAULT_PAGE_SIZE, HTTPSTATUS } from '@/constants';
import {
apiGetAlarms,
apiUnconfirmAlarm,
} from '@/services/master/AlarmController';
import {
CloseOutlined,
DeleteOutlined,
FilterOutlined,
} from '@ant-design/icons';
import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components';
import { FormattedMessage, useIntl, useModel } from '@umijs/max';
import { Button, Flex, message, Popconfirm, Progress, Tooltip } from 'antd';
import moment from 'moment';
import { useRef, useState } from 'react';
import AlarmDescription from './components/AlarmDescription';
import AlarmFormConfirm from './components/AlarmFormConfirm';
const AlarmPage = () => {
const tableRef = useRef<ActionType>();
const [selectedKey, setSelectedKey] = useState<React.Key>();
const [thingFilterVisible, setThingFilterVisible] = useState<boolean>(false);
const [thingFilterDatas, setThingFilterDatas] = useState<string[]>([]);
const intl = useIntl();
const [messageApi, contextHolder] = message.useMessage();
const { initialState } = useModel('@@initialState');
const { currentUserProfile } = initialState || {};
const columns: ProColumns<MasterModel.Alarm>[] = [
{
title: intl.formatMessage({
id: 'common.name',
defaultMessage: 'Name',
}),
dataIndex: 'name',
key: 'name',
copyable: true,
},
{
title: intl.formatMessage({
id: 'master.alarms.severity',
defaultMessage: 'Severity',
}),
dataIndex: 'level',
key: 'level',
responsive: ['lg', 'md', 'sm'],
filters: true,
onFilter: true,
valueType: 'select',
valueEnum: {
0: {
text: intl.formatMessage({
id: 'common.level.normal',
defaultMessage: 'Normal',
}),
status: 'Success',
},
1: {
text: intl.formatMessage({
id: 'common.level.warning',
defaultMessage: 'Warning',
}),
status: 'Warning',
},
2: {
text: intl.formatMessage({
id: 'common.level.critical',
defaultMessage: 'Critical',
}),
status: 'Error',
},
3: {
text: intl.formatMessage({
id: 'common.level.sos',
defaultMessage: 'SOS',
}),
status: 'Error',
},
},
},
{
title: intl.formatMessage({
id: 'sgw.ship',
defaultMessage: 'Source',
}),
dataIndex: 'thing_name',
key: 'thing_name',
responsive: ['lg', 'md', 'sm'],
ellipsis: true,
copyable: true,
// render: (dom, row) => {
// return (
// <Paragraph copyable>
// <Link
// onClick={() => {
// const t = row?.thing_type;
// const path = `/devices/${row?.thing_id}/${t}`;
// history.push({
// pathname: path,
// });
// }}
// >
// {dom}
// </Link>
// </Paragraph>
// );
// },
// render: (_, item) => {
// return <Paragraph copyable>{item?.thing_name}</Paragraph>
// }
},
{
title: intl.formatMessage({
id: 'master.alarms.occurred_at',
defaultMessage: 'Occured time',
}),
dataIndex: 'time',
key: 'time',
search: false,
render: (_, item) => {
return moment.unix(item.time!).format(DATE_TIME_FORMAT);
},
},
{
title: intl.formatMessage({
id: 'master.alarms.confirmed',
defaultMessage: 'Confirmed',
}),
dataIndex: 'confirmed',
responsive: ['lg', 'md'],
align: 'center',
width: '10%',
search: false,
render: (_, item) => {
return item?.confirmed ? (
<Progress type="circle" percent={100} size={24} />
) : null;
},
},
];
return (
<>
<ThingsFilter
isOpen={thingFilterVisible}
setIsOpen={setThingFilterVisible}
thingIds={thingFilterDatas}
onSubmit={(thingIds) => {
console.log('ThingIDs: ', thingIds);
setThingFilterDatas(thingIds);
tableRef.current?.reload();
}}
/>
{contextHolder}
<ProTable<MasterModel.Alarm>
actionRef={tableRef}
rowKey={(record) => `${record.thing_id}_${record.id}`}
columns={columns}
rowSelection={{
type: 'checkbox',
selectedRowKeys: selectedKey ? [selectedKey] : [],
onChange: (selectedKeys) => {
// Checkbox with max 1 row selection - toggle behavior
if (selectedKeys.length === 0) {
setSelectedKey(undefined);
} else {
// Get the key that's different from current selection (the newly clicked row)
const newKey = selectedKeys.find((key) => key !== selectedKey);
setSelectedKey(newKey ?? selectedKeys[0]);
}
},
}}
size="large"
tableAlertRender={({ selectedRows }) => {
const alarm = selectedRows[0];
return <AlarmDescription alarm={alarm} />;
}}
options={{
search: false,
setting: false,
density: false,
reload: true,
}}
toolbar={{
actions: [
<Tooltip
title={intl.formatMessage({ id: 'master.alarms.filter_things' })}
key="thing-filter-tooltip"
>
<Button
color={thingFilterDatas.length > 0 ? 'red' : 'default'}
icon={<FilterOutlined />}
variant="text"
onClick={() => setThingFilterVisible(true)}
/>
</Tooltip>,
],
}}
tableAlertOptionRender={({ selectedRows, onCleanSelected }) => {
const alarm = selectedRows[0];
return (
<Flex gap={10}>
{alarm?.confirmed || false ? (
<Popconfirm
title={intl.formatMessage({
id: 'master.alarms.unconfirm.body',
defaultMessage:
'Are you sure you want to unconfirm this alarm?',
})}
okText={intl.formatMessage({
id: 'common.yes',
defaultMessage: 'Yes',
})}
cancelText={intl.formatMessage({
id: 'common.no',
defaultMessage: 'No',
})}
onConfirm={async () => {
const body: MasterModel.ConfirmAlarmRequest = {
id: alarm.id,
thing_id: alarm.thing_id,
time: alarm.time,
};
try {
const resp = await apiUnconfirmAlarm(body);
if (resp.status === HTTPSTATUS.HTTP_SUCCESS) {
message.success({
content: intl.formatMessage({
id: 'master.alarms.unconfirm.success',
defaultMessage: 'Confirm alarm successfully',
}),
});
tableRef.current?.reload();
} else if (resp.status === HTTPSTATUS.HTTP_NOTFOUND) {
message.warning({
content: intl.formatMessage({
id: 'master.alarms.not_found',
defaultMessage:
'Alarm has expired or does not exist',
}),
});
tableRef.current?.reload();
} else {
throw new Error('Failed to confirm alarm');
}
} catch (error) {
console.error('Error when unconfirm alarm: ', error);
message.error({
content: intl.formatMessage({
id: 'master.alarms.unconfirm.fail',
defaultMessage: 'Unconfirm alarm failed',
}),
});
}
}}
>
<Button danger icon={<DeleteOutlined />} size="small">
<FormattedMessage id="master.alarms.unconfirm.title" />
</Button>
</Popconfirm>
) : (
<AlarmFormConfirm
alarm={alarm}
message={messageApi}
onFinish={(isReload) => {
if (isReload && tableRef.current) {
tableRef.current.reload();
}
}}
/>
)}
<Button
size="small"
icon={<CloseOutlined />}
onClick={onCleanSelected}
danger
/>
</Flex>
);
}}
pagination={{
defaultPageSize: DEFAULT_PAGE_SIZE * 2,
showSizeChanger: true,
pageSizeOptions: ['5', '10', '15', '20'],
showTotal: (total, range) =>
`${range[0]}-${range[1]}
${intl.formatMessage({
id: 'common.paginations.of',
defaultMessage: 'of',
})}
${total} ${intl.formatMessage({
id: 'master.alarms.table.pagination',
defaultMessage: 'alarms',
})}`,
}}
request={async (params) => {
const { current, pageSize, name, level, thing_name } = params;
const offset = current === 1 ? 0 : (current! - 1) * pageSize!;
const body: MasterModel.SearchAlarmPaginationBody = {
name: name,
order: 'name',
limit: pageSize,
offset: offset,
level: level as number | undefined,
thing_name: thing_name,
dir: 'desc',
};
if (thingFilterDatas.length > 0) {
body.thing_id = Array.isArray(thingFilterDatas)
? thingFilterDatas.join(',')
: thingFilterDatas;
}
try {
const res = await apiGetAlarms(body);
return {
data: res.alarms,
total: res.total,
success: true,
};
} catch (error) {
return {
data: [],
total: 0,
success: false,
};
}
}}
></ProTable>
</>
);
};
export default AlarmPage;