Разработка админ-панели EventHubFrontAdmin v1.0 #1
This commit is contained in:
@@ -0,0 +1,238 @@
|
||||
import React from 'react';
|
||||
import { Card, Col, Row, Statistic, Spin, Alert, Table, Tag } from 'antd';
|
||||
import {
|
||||
UserOutlined,
|
||||
CalendarOutlined,
|
||||
StarOutlined,
|
||||
TeamOutlined,
|
||||
WarningOutlined,
|
||||
BugOutlined,
|
||||
ClockCircleOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { useDashboardStats } from '../../hooks/useDashboard';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import { AdminActivity } from '../../types/api';
|
||||
import {
|
||||
LineChart,
|
||||
Line,
|
||||
XAxis,
|
||||
YAxis,
|
||||
CartesianGrid,
|
||||
Tooltip,
|
||||
Legend,
|
||||
ResponsiveContainer,
|
||||
AreaChart,
|
||||
Area,
|
||||
} from 'recharts';
|
||||
|
||||
const DashboardPage: React.FC = () => {
|
||||
const { data, isLoading, error } = useDashboardStats();
|
||||
|
||||
if (isLoading) return <Spin size="large" style={{ display: 'block', margin: '100px auto' }} />;
|
||||
if (error) return <Alert type="error" message="Ошибка загрузки статистики" />;
|
||||
if (!data) return null;
|
||||
|
||||
const roleColors: Record<string, string> = {
|
||||
superadmin: 'red',
|
||||
admin: 'blue',
|
||||
moderator: 'purple',
|
||||
support: 'cyan',
|
||||
};
|
||||
|
||||
const adminColumns: ColumnsType<AdminActivity> = [
|
||||
{ title: 'Email', dataIndex: 'email', key: 'email' },
|
||||
{ title: 'Ник', dataIndex: 'nickname', key: 'nickname' },
|
||||
{
|
||||
title: 'Роль',
|
||||
dataIndex: 'role',
|
||||
key: 'role',
|
||||
render: (role: string) => (
|
||||
<Tag color={roleColors[role] || 'default'}>{role}</Tag>
|
||||
),
|
||||
},
|
||||
{ title: 'Действий', dataIndex: 'actions', key: 'actions' },
|
||||
{ title: 'Последний вход', dataIndex: 'last_login', key: 'last_login' },
|
||||
];
|
||||
|
||||
const eventsChartData = data.events_by_day?.map(item => ({
|
||||
date: item.date,
|
||||
events: item.count,
|
||||
})) || [];
|
||||
|
||||
const registrationsChartData = data.registrations_by_day?.map(item => ({
|
||||
date: item.date,
|
||||
registrations: item.count,
|
||||
})) || [];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>Общая статистика</h2>
|
||||
<Row gutter={[16, 16]}>
|
||||
{/* Пользователи с мини-графиком */}
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<Card>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Statistic
|
||||
title="Пользователи"
|
||||
value={data.users_total}
|
||||
prefix={<UserOutlined />}
|
||||
style={{ flex: '0 0 auto', marginRight: 16 }}
|
||||
/>
|
||||
{registrationsChartData.length > 0 && (
|
||||
<AreaChart
|
||||
width={150}
|
||||
height={50}
|
||||
data={registrationsChartData}
|
||||
margin={{ top: 5, right: 0, left: 0, bottom: 0 }}
|
||||
>
|
||||
<defs>
|
||||
<linearGradient id="colorRegistrations" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="5%" stopColor="#52c41a" stopOpacity={0.4} />
|
||||
<stop offset="95%" stopColor="#52c41a" stopOpacity={0.05} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<XAxis dataKey="date" hide />
|
||||
<Tooltip labelFormatter={(label) => `Дата: ${label}`} />
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="registrations"
|
||||
name="Регистрации"
|
||||
stroke="#52c41a"
|
||||
fill="url(#colorRegistrations)"
|
||||
strokeWidth={2}
|
||||
dot={false}
|
||||
activeDot={{ r: 3, strokeWidth: 0 }}
|
||||
/>
|
||||
</AreaChart>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
{/* События с мини-графиком */}
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<Card>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Statistic
|
||||
title="События"
|
||||
value={data.events_total}
|
||||
prefix={<TeamOutlined />}
|
||||
style={{ flex: '0 0 auto', marginRight: 16 }}
|
||||
/>
|
||||
{eventsChartData.length > 0 && (
|
||||
<AreaChart
|
||||
width={150}
|
||||
height={50}
|
||||
data={eventsChartData}
|
||||
margin={{ top: 5, right: 0, left: 0, bottom: 0 }}
|
||||
>
|
||||
<defs>
|
||||
<linearGradient id="colorEvents" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="5%" stopColor="#1890ff" stopOpacity={0.4} />
|
||||
<stop offset="95%" stopColor="#1890ff" stopOpacity={0.05} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<XAxis dataKey="date" hide />
|
||||
<Tooltip labelFormatter={(label) => `Дата: ${label}`} />
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="events"
|
||||
name="События"
|
||||
stroke="#1890ff"
|
||||
fill="url(#colorEvents)"
|
||||
strokeWidth={2}
|
||||
dot={false}
|
||||
activeDot={{ r: 3, strokeWidth: 0 }}
|
||||
/>
|
||||
</AreaChart>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<Card>
|
||||
<Statistic title="Отзывы" value={data.reviews_total} prefix={<StarOutlined />} />
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<Card>
|
||||
<Statistic title="Календари" value={data.calendars_total} prefix={<CalendarOutlined />} />
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<Card>
|
||||
<Statistic title="Жалобы" value={data.reports_total} prefix={<WarningOutlined />} />
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<Card>
|
||||
<Statistic title="Тикеты (всего)" value={data.tickets_total} prefix={<BugOutlined />} />
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<Card>
|
||||
<Statistic
|
||||
title="Открытых тикетов"
|
||||
value={data.tickets_open}
|
||||
prefix={<ClockCircleOutlined />}
|
||||
/>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<Card>
|
||||
<Statistic
|
||||
title="Среднее время решения (ч)"
|
||||
value={data.avg_ticket_resolution_h}
|
||||
precision={1}
|
||||
/>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row gutter={[16, 16]} style={{ marginTop: 32 }}>
|
||||
<Col xs={24} lg={12}>
|
||||
<Card title="События по дням">
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<LineChart data={eventsChartData}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="date" />
|
||||
<YAxis allowDecimals={false} />
|
||||
<Tooltip />
|
||||
<Legend />
|
||||
<Line type="monotone" dataKey="events" stroke="#1890ff" name="События" />
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xs={24} lg={12}>
|
||||
<Card title="Регистрации по дням">
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<LineChart data={registrationsChartData}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="date" />
|
||||
<YAxis allowDecimals={false} />
|
||||
<Tooltip />
|
||||
<Legend />
|
||||
<Line type="monotone" dataKey="registrations" stroke="#52c41a" name="Регистрации" />
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<h2 style={{ marginTop: 32 }}>Активность администраторов</h2>
|
||||
<Table
|
||||
columns={adminColumns}
|
||||
dataSource={data.admin_activity}
|
||||
rowKey="admin_id"
|
||||
pagination={false}
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardPage;
|
||||
Reference in New Issue
Block a user