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