a1bd11afee
Moderator/support видят свою статистику; admin_activity заменён ссылкой на /audit. Co-authored-by: Cursor <cursoragent@cursor.com>
218 lines
9.5 KiB
TypeScript
218 lines
9.5 KiB
TypeScript
import React from 'react';
|
||
import { Card, Col, Row, Statistic, Spin, Alert, Button } from 'antd';
|
||
import {
|
||
UserOutlined,
|
||
CalendarOutlined,
|
||
StarOutlined,
|
||
TeamOutlined,
|
||
WarningOutlined,
|
||
BugOutlined,
|
||
ClockCircleOutlined,
|
||
DollarOutlined,
|
||
} from '@ant-design/icons';
|
||
import { Link, useNavigate } from 'react-router-dom';
|
||
import { useDashboardStats } from '../../hooks/useDashboard';
|
||
import { useAuthStore } from '../../store/authStore';
|
||
import DailyLineChartCard from '../../components/DailyLineChartCard';
|
||
import StatisticSparklineCard from '../../components/StatisticSparklineCard';
|
||
|
||
const DashboardPage: React.FC = () => {
|
||
const { data, isLoading, error } = useDashboardStats();
|
||
const role = useAuthStore((s) => s.user?.role);
|
||
const navigate = useNavigate();
|
||
|
||
if (isLoading) return <Spin size="large" style={{ display: 'block', margin: '100px auto' }} />;
|
||
if (error) return <Alert type="error" message="Ошибка загрузки статистики" />;
|
||
if (!data) return null;
|
||
|
||
if (role === 'moderator') {
|
||
return (
|
||
<div>
|
||
<h2>Модерация</h2>
|
||
<Row gutter={[16, 16]}>
|
||
<Col xs={24} sm={8}>
|
||
<Card><Statistic title="Рассмотрено" value={data.reports_reviewed ?? 0} prefix={<WarningOutlined />} /></Card>
|
||
</Col>
|
||
<Col xs={24} sm={8}>
|
||
<Card><Statistic title="Отклонено" value={data.reports_dismissed ?? 0} /></Card>
|
||
</Col>
|
||
<Col xs={24} sm={8}>
|
||
<Card><Statistic title="Ср. время (ч)" value={data.avg_report_resolution_h ?? 0} precision={1} prefix={<ClockCircleOutlined />} /></Card>
|
||
</Col>
|
||
</Row>
|
||
<Button type="primary" style={{ marginTop: 16 }} onClick={() => navigate('/reports')}>
|
||
Перейти к жалобам
|
||
</Button>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
if (role === 'support') {
|
||
return (
|
||
<div>
|
||
<h2>Поддержка</h2>
|
||
<Row gutter={[16, 16]}>
|
||
<Col xs={24} sm={8}>
|
||
<Card><Statistic title="Назначено открытых" value={data.tickets_assigned_open ?? 0} prefix={<BugOutlined />} /></Card>
|
||
</Col>
|
||
<Col xs={24} sm={8}>
|
||
<Card><Statistic title="Всего назначено" value={data.tickets_assigned_total ?? 0} /></Card>
|
||
</Col>
|
||
<Col xs={24} sm={8}>
|
||
<Card><Statistic title="Ср. решение (ч)" value={data.avg_ticket_resolution_h ?? 0} precision={1} prefix={<ClockCircleOutlined />} /></Card>
|
||
</Col>
|
||
</Row>
|
||
<Button type="primary" style={{ marginTop: 16 }} onClick={() => navigate('/tickets')}>
|
||
Перейти к тикетам
|
||
</Button>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
const registrationsSparkline = data.registrations_by_day?.map((item) => ({
|
||
date: item.date,
|
||
value: item.count,
|
||
})) ?? [];
|
||
|
||
const eventsSparkline = data.events_by_day?.map((item) => ({
|
||
date: item.date,
|
||
value: item.count,
|
||
})) ?? [];
|
||
|
||
return (
|
||
<div>
|
||
<h2>Общая статистика</h2>
|
||
<Row gutter={[16, 16]}>
|
||
<Col xs={24} sm={12} lg={6}>
|
||
<StatisticSparklineCard
|
||
title="Пользователи"
|
||
value={data.users_total}
|
||
prefix={<UserOutlined />}
|
||
data={registrationsSparkline}
|
||
color="#52c41a"
|
||
gradientId="colorRegistrations"
|
||
/>
|
||
</Col>
|
||
|
||
<Col xs={24} sm={12} lg={6}>
|
||
<StatisticSparklineCard
|
||
title="События"
|
||
value={data.events_total}
|
||
prefix={<TeamOutlined />}
|
||
data={eventsSparkline}
|
||
color="#1890ff"
|
||
gradientId="colorEvents"
|
||
/>
|
||
</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>
|
||
|
||
{data.pending_users_total !== undefined && (
|
||
<Col xs={24} sm={12} lg={6}>
|
||
<Card>
|
||
<Statistic title="Неподтверждённые" value={data.pending_users_total} prefix={<UserOutlined />} />
|
||
</Card>
|
||
</Col>
|
||
)}
|
||
{data.reports_pending !== undefined && (
|
||
<Col xs={24} sm={12} lg={6}>
|
||
<Card>
|
||
<Statistic title="Жалобы: pending" value={data.reports_pending} />
|
||
</Card>
|
||
</Col>
|
||
)}
|
||
{data.reports_reviewed !== undefined && (
|
||
<Col xs={24} sm={12} lg={6}>
|
||
<Card>
|
||
<Statistic title="Жалобы: reviewed" value={data.reports_reviewed} />
|
||
</Card>
|
||
</Col>
|
||
)}
|
||
{data.reports_dismissed !== undefined && (
|
||
<Col xs={24} sm={12} lg={6}>
|
||
<Card>
|
||
<Statistic title="Жалобы: dismissed" value={data.reports_dismissed} />
|
||
</Card>
|
||
</Col>
|
||
)}
|
||
{data.subscriptions_total !== undefined && (
|
||
<Col xs={24} sm={12} lg={6}>
|
||
<Card>
|
||
<Statistic title="Подписки" value={data.subscriptions_total} prefix={<DollarOutlined />} />
|
||
</Card>
|
||
</Col>
|
||
)}
|
||
{data.trial_subscriptions !== undefined && (
|
||
<Col xs={24} sm={12} lg={6}>
|
||
<Card>
|
||
<Statistic title="Пробные подписки" value={data.trial_subscriptions} />
|
||
</Card>
|
||
</Col>
|
||
)}
|
||
</Row>
|
||
|
||
<Row gutter={[16, 16]} style={{ marginTop: 32 }}>
|
||
<DailyLineChartCard title="События по дням" data={data.events_by_day} color="#1890ff" />
|
||
<DailyLineChartCard title="Регистрации по дням" data={data.registrations_by_day} color="#52c41a" />
|
||
<DailyLineChartCard title="Отзывы по дням" data={data.reviews_by_day} color="#faad14" />
|
||
<DailyLineChartCard title="Календари по дням" data={data.calendars_by_day} color="#722ed1" />
|
||
<DailyLineChartCard title="Жалобы по дням" data={data.reports_by_day} color="#ff4d4f" />
|
||
<DailyLineChartCard title="Тикеты по дням" data={data.tickets_by_day} color="#13c2c2" />
|
||
<DailyLineChartCard title="Подписки по дням" data={data.subscriptions_by_day} color="#eb2f96" />
|
||
<DailyLineChartCard title="Неподтверждённые по дням" data={data.pending_users_by_day} color="#fa8c16" />
|
||
</Row>
|
||
|
||
{role === 'superadmin' && (
|
||
<Alert
|
||
style={{ marginTop: 32 }}
|
||
type="info"
|
||
showIcon
|
||
message="Активность администраторов"
|
||
description={<>Подробный журнал действий — в разделе <Link to="/audit">Аудит</Link>.</>}
|
||
/>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default DashboardPage;
|