feat: add Manager Dashboard and Device Terminal features
This commit is contained in:
259
src/pages/Manager/Dashboard/index.tsx
Normal file
259
src/pages/Manager/Dashboard/index.tsx
Normal file
@@ -0,0 +1,259 @@
|
||||
import IconFont from '@/components/IconFont';
|
||||
import { apiQueryGroups } from '@/services/master/GroupController';
|
||||
import { apiQueryLogs } from '@/services/master/LogController';
|
||||
import { apiSearchThings } from '@/services/master/ThingController';
|
||||
import { apiQueryUsers } from '@/services/master/UserController';
|
||||
import { RightOutlined } from '@ant-design/icons';
|
||||
import { PageContainer, ProCard } from '@ant-design/pro-components';
|
||||
import { history, useIntl } from '@umijs/max';
|
||||
import { Button, Col, Divider, Row, Statistic, Typography } from 'antd';
|
||||
import { useEffect, useState } from 'react';
|
||||
import CountUp from 'react-countup';
|
||||
const { Text } = Typography;
|
||||
|
||||
const ManagerDashboard = () => {
|
||||
const intl = useIntl();
|
||||
const [counts, setCounts] = useState({
|
||||
devices: 0,
|
||||
groups: 0,
|
||||
users: 0,
|
||||
logs: 0,
|
||||
});
|
||||
const [deviceMetadata, setDeviceMetadata] =
|
||||
useState<MasterModel.ThingsResponseMetadata | null>(null);
|
||||
|
||||
const formatter = (value: number | string) => (
|
||||
<CountUp end={Number(value)} separator="," duration={2} />
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const [devicesRes, groupsRes, usersRes, logsRes] = await Promise.all([
|
||||
apiSearchThings({ limit: 1 }),
|
||||
apiQueryGroups({}),
|
||||
apiQueryUsers({ limit: 1 }),
|
||||
apiQueryLogs({ limit: 1 }, 'user_logs'),
|
||||
]);
|
||||
|
||||
const devicesTotal = devicesRes?.total || 0;
|
||||
const metadata = (devicesRes as any)?.metadata || null;
|
||||
|
||||
// Group response handling
|
||||
const groupsTotal =
|
||||
(Array.isArray(groupsRes)
|
||||
? groupsRes.length
|
||||
: (groupsRes as any)?.total) || 0;
|
||||
const usersTotal = usersRes?.total || 0;
|
||||
const logsTotal = logsRes?.total || 0;
|
||||
|
||||
setCounts({
|
||||
devices: devicesTotal,
|
||||
groups: groupsTotal,
|
||||
users: usersTotal,
|
||||
logs: logsTotal,
|
||||
});
|
||||
setDeviceMetadata(metadata);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch dashboard counts:', error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<ProCard gutter={[16, 16]} ghost>
|
||||
<ProCard colSpan={{ xs: 24, md: 10 }} ghost gutter={[16, 16]} wrap>
|
||||
<ProCard
|
||||
colSpan={24}
|
||||
title={intl.formatMessage({
|
||||
id: 'menu.manager.devices',
|
||||
defaultMessage: 'Thiết bị',
|
||||
})}
|
||||
extra={
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() => history.push('/manager/devices')}
|
||||
icon={<RightOutlined />}
|
||||
>
|
||||
Xem chi tiết
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Statistic
|
||||
value={counts.devices}
|
||||
formatter={formatter as any}
|
||||
valueStyle={{ color: '#1890ff' }}
|
||||
prefix={
|
||||
<IconFont
|
||||
type="icon-gateway"
|
||||
style={{ fontSize: 24, marginRight: 8 }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</ProCard>
|
||||
<ProCard
|
||||
colSpan={24}
|
||||
title={intl.formatMessage({
|
||||
id: 'menu.manager.groups',
|
||||
defaultMessage: 'Đơn vị',
|
||||
})}
|
||||
extra={
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() => history.push('/manager/groups')}
|
||||
icon={<RightOutlined />}
|
||||
>
|
||||
Xem chi tiết
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Statistic
|
||||
value={counts.groups}
|
||||
formatter={formatter as any}
|
||||
valueStyle={{ color: '#52c41a' }}
|
||||
prefix={
|
||||
<IconFont
|
||||
type="icon-tree"
|
||||
style={{ fontSize: 24, marginRight: 8 }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</ProCard>
|
||||
<ProCard
|
||||
colSpan={24}
|
||||
title={intl.formatMessage({
|
||||
id: 'menu.manager.users',
|
||||
defaultMessage: 'Người dùng',
|
||||
})}
|
||||
extra={
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() => history.push('/manager/users')}
|
||||
icon={<RightOutlined />}
|
||||
>
|
||||
Xem chi tiết
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Statistic
|
||||
value={counts.users}
|
||||
formatter={formatter as any}
|
||||
valueStyle={{ color: '#faad14' }}
|
||||
prefix={
|
||||
<IconFont
|
||||
type="icon-users"
|
||||
style={{ fontSize: 24, marginRight: 8 }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</ProCard>
|
||||
<ProCard
|
||||
colSpan={24}
|
||||
title={intl.formatMessage({
|
||||
id: 'menu.manager.logs',
|
||||
defaultMessage: 'Hoạt động',
|
||||
})}
|
||||
extra={
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() => history.push('/manager/logs')}
|
||||
icon={<RightOutlined />}
|
||||
>
|
||||
Xem chi tiết
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Statistic
|
||||
value={counts.logs}
|
||||
formatter={formatter as any}
|
||||
valueStyle={{ color: '#f5222d' }}
|
||||
prefix={
|
||||
<IconFont
|
||||
type="icon-diary"
|
||||
style={{ fontSize: 24, marginRight: 8 }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</ProCard>
|
||||
</ProCard>
|
||||
<ProCard
|
||||
colSpan={{ xs: 24, md: 8 }}
|
||||
title="Trạng thái thiết bị"
|
||||
headerBordered
|
||||
>
|
||||
{deviceMetadata && (
|
||||
<Row gutter={[0, 16]}>
|
||||
<Col span={24}>
|
||||
<Text type="secondary">Kết nối</Text>
|
||||
<div style={{ fontSize: 16, fontWeight: 'bold' }}>
|
||||
<CountUp end={deviceMetadata.total_connected || 0} /> /{' '}
|
||||
{deviceMetadata.total_thing}
|
||||
</div>
|
||||
</Col>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<Col span={24}>
|
||||
<Text type="secondary">SOS</Text>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
color:
|
||||
(deviceMetadata.total_sos || 0) > 0
|
||||
? '#ff4d4f'
|
||||
: 'inherit',
|
||||
}}
|
||||
>
|
||||
<CountUp end={deviceMetadata.total_sos || 0} />
|
||||
</div>
|
||||
</Col>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<Col span={24}>
|
||||
<Text type="secondary">Bình thường</Text>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
color: '#52c41a',
|
||||
}}
|
||||
>
|
||||
<CountUp end={deviceMetadata.total_state_level_0 || 0} />
|
||||
</div>
|
||||
</Col>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<Col span={24}>
|
||||
<Text type="secondary">Cảnh báo</Text>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
color: '#faad14',
|
||||
}}
|
||||
>
|
||||
<CountUp end={deviceMetadata.total_state_level_1 || 0} />
|
||||
</div>
|
||||
</Col>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<Col span={24}>
|
||||
<Text type="secondary">Nghiêm trọng</Text>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
color: '#f5222d',
|
||||
}}
|
||||
>
|
||||
<CountUp end={deviceMetadata.total_state_level_2 || 0} />
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
</ProCard>
|
||||
</ProCard>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default ManagerDashboard;
|
||||
Reference in New Issue
Block a user