add dashboard statistic

This commit is contained in:
smanylov
2026-05-15 15:31:40 +07:00
parent 80692d6446
commit cce7eb6f96
22 changed files with 972 additions and 176 deletions
@@ -1,18 +1,46 @@
'use client'
import { useProtectedContentGeneralStatistic } from '@/app/hooks/react-query/dashboard/useProtectedContentGeneralStatistic'
import { convertBytes } from '@/app/lib/convertBytes';
import { useEffect } from 'react';
export default function GeneralInfoContent() {
const { data, isLoading, isError } = useProtectedContentGeneralStatistic();
if (isLoading) {
return (
<div>
<h4 className="stats-title">Защищенный контент (месяц/год)</h4>
<div className="stats-grid">
<div className="stat-card">Загрузка...</div>
</div>
</div>
);
}
if (isError) {
return (
<div>
<h4 className="stats-title">Защищенный контент (месяц/год)</h4>
<div className="stats-grid">
<div className="stat-card">Ошибка при загрузке данных</div>
</div>
</div>
);
}
return (
<div>
<h4
className="stats-title"
>
<h4 className="stats-title">
Защищенный контент (месяц/год)
</h4>
<div className="stats-grid">
<div className="stats-grid user-content">
<div className="stat-card">
<div className="stat-card-group">
<span className="stat-card-title">Всего файлов этом месяце</span>
<div className="stat-card-value">0</div>
{/* <span className="stat-card-title">Всего файлов этом месяце</span> */}
<span className="stat-card-title">Всего файлов</span>
<div className="stat-card-value">{data?.total_files || 0}</div>
</div>
<div className="stat-card-group">
<span className="stat-card-title">В прошлом месяце</span>
@@ -22,8 +50,9 @@ export default function GeneralInfoContent() {
<div className="stat-card">
<div className="stat-card-group">
<span className="stat-card-title">Всего занято места в этом месяце</span>
<div className="stat-card-value">0</div>
{/* <span className="stat-card-title">Всего занято места в этом месяце</span> */}
<span className="stat-card-title">Всего занято места</span>
<div className="stat-card-value">{convertBytes(data?.total_size_bytes)}</div>
</div>
<div className="stat-card-group">
<span className="stat-card-title">В прошлом месяце</span>
@@ -34,11 +63,19 @@ export default function GeneralInfoContent() {
<div className="stat-card">
<div className="stat-card-group">
<span className="stat-card-title">Файлов на пользователя</span>
<div className="stat-card-value">0</div>
<div className="stat-card-value">
{data?.files_per_user_percentiles?.p5 || 0}/
{data?.files_per_user_percentiles?.p50 || 0}/
{data?.files_per_user_percentiles?.p95 || 0}
</div>
</div>
<div className="stat-card-group">
<span className="stat-card-title">Места на диске на пользователя</span>
<div className="stat-card-value">0</div>
<div className="stat-card-value">
{convertBytes(data?.size_per_user_percentiles?.p5_bytes)} /
{convertBytes(data?.size_per_user_percentiles?.p50_bytes)} /
{convertBytes(data?.size_per_user_percentiles?.p95_bytes)}
</div>
</div>
</div>
</div>
@@ -1,44 +1,67 @@
'use client'
import { useIncomeGeneralStatistic } from '@/app/hooks/react-query/dashboard/useIncomeGeneralStatistic';
import { useEffect } from 'react';
export default function GeneralInfoIncome() {
const { data, isLoading, isError } = useIncomeGeneralStatistic();
if (isLoading) {
return (
<div>
<h4 className="stats-title">Доход (день/месяц)</h4>
<div className="stats-grid">
<div className="stat-card">Загрузка...</div>
</div>
</div>
);
}
if (isError) {
return (
<div>
<h4 className="stats-title">Доход (день/месяц)</h4>
<div className="stats-grid">
<div className="stat-card">Ошибка при загрузке данных</div>
</div>
</div>
);
}
return (
<div>
<h4
className="stats-title"
>
<h4 className="stats-title">
Доход (день/месяц)
</h4>
<div className="stats-grid">
<div className="stat-card">
<div className="stat-card-group">
<span className="stat-card-title">Всего заработано в этом месяце</span>
<div className="stat-card-value">0</div>
<span className="stat-card-title">Всего заработано</span>
<div className="stat-card-value">{data?.total_income || 0} </div>
</div>
<div className="stat-card-group">
<span className="stat-card-title">В прошлом месяце</span>
<div className="stat-card-value">0</div>
<span className="stat-card-title">Доступно</span>
<div className="stat-card-value">{data?.total_available || 0} </div>
</div>
<div className="stat-card-group">
<span className="stat-card-title">Заморожено</span>
<div className="stat-card-value">{data?.total_hold || 0} </div>
</div>
</div>
<div className="stat-card">
<div className="stat-card-group">
<span className="stat-card-title">Всего заработано на любого пользователя в этом месяце</span>
<div className="stat-card-value">0</div>
<span className="stat-card-title">Всего заработано на пользователя</span>
<div className="stat-card-value">
{data?.income_per_user?.p5 || 0}/
{data?.income_per_user?.p50 || 0}/
{data?.income_per_user?.p95 || 0}
</div>
</div>
<div className="stat-card-group">
<span className="stat-card-title">В прошлом месяце</span>
<div className="stat-card-value">0</div>
</div>
</div>
<div className="stat-card">
<div className="stat-card-group">
<span className="stat-card-title">Всего заработано на платного пользователя в этом месяце</span>
<div className="stat-card-value">0</div>
</div>
<div className="stat-card-group">
<span className="stat-card-title">В прошлом месяце</span>
<div className="stat-card-value">0</div>
<div className="stat-card-value">0/0/0</div>
</div>
</div>
</div>
@@ -1,66 +1,91 @@
'use client'
import { useSubscribeGeneralStatistic } from '@/app/hooks/react-query/dashboard/useSubscribeGeneralStatistic';
export default function GeneralInfoSubscriptions() {
const { data, isLoading, isError } = useSubscribeGeneralStatistic();
if (isLoading) {
return (
<div>
<h4 className="stats-title">Активные подписчики (месяц/год)</h4>
<div className="stats-grid">
<div className="stat-card">Загрузка...</div>
</div>
</div>
);
}
if (isError) {
return (
<div>
<h4 className="stats-title">Активные подписчики (месяц/год)</h4>
<div className="stats-grid">
<div className="stat-card">Ошибка при загрузке данных</div>
</div>
</div>
);
}
return (
<div>
<h4
className="stats-title"
>
Активные подписчики (месяц/год)
</h4>
<h4 className="stats-title">Активные подписчики (месяц/год)</h4>
<div className="stats-grid">
<div className="stat-card">
<div className="stat-card-group">
<span className="stat-card-title">Текущие платные подписки</span>
<div className="stat-card-value">0</div>
<div className="stat-card-value">
{(data?.monthly?.current_active_subscriptions || 0) + (data?.yearly?.current_active_subscriptions || 0)}
</div>
</div>
</div>
<div className="stat-card">
<div className="stat-card-group">
<span className="stat-card-title">оформили впервые в этом месяце</span>
<div className="stat-card-value">0</div>
<div className="stat-card-value">{data?.monthly?.first_time_subscriptions || 0}</div>
</div>
<div className="stat-card-group">
<span className="stat-card-title">в прошлом месяце</span>
<div className="stat-card-value">0</div>
<div className="stat-card-value">{data?.yearly?.first_time_subscriptions || 0}</div>
</div>
</div>
<div className="stat-card">
<div className="stat-card-group">
<span className="stat-card-title">продлили подпискув этом месяце</span>
<div className="stat-card-value">0</div>
<span className="stat-card-title">продлили подписку в этом месяце</span>
<div className="stat-card-value">{data?.monthly?.renewals || 0}</div>
</div>
<div className="stat-card-group">
<span className="stat-card-title">в прошлом месяце</span>
<div className="stat-card-value">0</div>
<div className="stat-card-value">{data?.yearly?.renewals || 0}</div>
</div>
</div>
<div className="stat-card">
<div className="stat-card-group">
<span className="stat-card-title">сделали апгрейдв этом месяце</span>
<div className="stat-card-value">0</div>
<span className="stat-card-title">сделали апгрейд в этом месяце</span>
<div className="stat-card-value">{data?.monthly?.upgrades || 0}</div>
</div>
<div className="stat-card-group">
<span className="stat-card-title">в прошлом месяце</span>
<div className="stat-card-value">0</div>
<div className="stat-card-value">{data?.yearly?.upgrades || 0}</div>
</div>
</div>
<div className="stat-card">
{/* <div className="stat-card">
<div className="stat-card-group">
<span className="stat-card-title">сделали даунгрейдв этом месяце</span>
<div className="stat-card-value">0</div>
<span className="stat-card-title">сделали даунгрейд в этом месяце</span>
<div className="stat-card-value">{data?.monthly?.downgrades || 0}</div>
</div>
<div className="stat-card-group">
<span className="stat-card-title">в прошлом месяце</span>
<div className="stat-card-value">0</div>
<div className="stat-card-value">{data?.yearly?.downgrades || 0}</div>
</div>
</div>
</div> */}
</div>
</div>
)
);
}
@@ -1,18 +1,45 @@
'use client'
import { useTokensGeneralStatistic } from '@/app/hooks/react-query/dashboard/useTokensGeneralStatistic';
import { useEffect } from 'react';
export default function GeneralInfoTokens() {
const { data, isLoading, isError } = useTokensGeneralStatistic();
if (isLoading) {
return (
<div>
<h4 className="stats-title">Токены (день/месяц)</h4>
<div className="stats-grid">
<div className="stat-card">Загрузка...</div>
</div>
</div>
);
}
if (isError) {
return (
<div>
<h4 className="stats-title">Токены (день/месяц)</h4>
<div className="stats-grid">
<div className="stat-card">Ошибка при загрузке данных</div>
</div>
</div>
);
}
return (
<div>
<h4
className="stats-title"
>
<h4 className="stats-title">
Токены (день/месяц)
</h4>
<div className="stats-grid">
<div className="stat-card">
<div className="stat-card-group">
<span className="stat-card-title">Всего купленов этом месяце</span>
<div className="stat-card-value">0</div>
{/* <span className="stat-card-title">Всего куплено в этом месяце</span> */}
<span className="stat-card-title">Всего куплено</span>
<div className="stat-card-value">{data?.total_bought || 0}</div>
</div>
<div className="stat-card-group">
<span className="stat-card-title">В прошлом месяце</span>
@@ -22,8 +49,9 @@ export default function GeneralInfoTokens() {
<div className="stat-card">
<div className="stat-card-group">
<span className="stat-card-title">Всего потраченов этом месяце</span>
<div className="stat-card-value">0</div>
{/* <span className="stat-card-title">Всего потрачено в этом месяце</span> */}
<span className="stat-card-title">Всего потрачено</span>
<div className="stat-card-value">{data?.total_spent || 0}</div>
</div>
<div className="stat-card-group">
<span className="stat-card-title">В прошлом месяце</span>
@@ -34,22 +62,30 @@ export default function GeneralInfoTokens() {
<div className="stat-card">
<div className="stat-card-group">
<span className="stat-card-title">Куплено на пользователя в этом месяце</span>
<div className="stat-card-value">0</div>
<div className="stat-card-value">
{data?.bought_per_user?.p5 || 0}/
{data?.bought_per_user?.p50 || 0}/
{data?.bought_per_user?.p95 || 0}
</div>
</div>
<div className="stat-card-group">
<span className="stat-card-title">В прошлом месяце</span>
<div className="stat-card-value">0</div>
<div className="stat-card-value">0/0/0</div>
</div>
</div>
<div className="stat-card">
<div className="stat-card-group">
<span className="stat-card-title">Потрачено на пользователя в этом месяце</span>
<div className="stat-card-value">0</div>
<div className="stat-card-value">
{data?.spent_per_user?.p5 || 0}/
{data?.spent_per_user?.p50 || 0}/
{data?.spent_per_user?.p95 || 0}
</div>
</div>
<div className="stat-card-group">
<span className="stat-card-title">В прошлом месяце</span>
<div className="stat-card-value">0</div>
<div className="stat-card-value">0/0/0</div>
</div>
</div>
</div>
@@ -1,51 +1,87 @@
'use client'
import { useUserGeneralStatistic } from '@/app/hooks/react-query/dashboard/useUserGeneralStatistic';
export default function GeneralInfoUsers() {
const { data, isLoading, isError } = useUserGeneralStatistic();
if (isLoading) {
return (
<div>
<h4 className="stats-title">Кол-во пользователей и динамика</h4>
<div className="stats-grid">
<div className="stat-card">Загрузка...</div>
</div>
</div>
);
}
if (isError) {
return (
<div>
<h4 className="stats-title">Кол-во пользователей и динамика</h4>
<div className="stats-grid">
<div className="stat-card">Ошибка при загрузке данных</div>
</div>
</div>
);
}
const renderDelta = (percent: number) => {
const isPositive = percent > 0;
const isNegative = percent < 0;
const sign = isPositive ? '+' : '';
const color = isPositive ? '#10b981' : isNegative ? '#ef4444' : '#6b7280';
return (
<div className="stat-card-value" style={{ color }}>
{sign}{percent}%
</div>
);
};
return (
<div>
<h4
className="stats-title"
>
Кол-во пользователей и динамика
</h4>
<h4 className="stats-title">Кол-во пользователей и динамика</h4>
<div className="stats-grid">
<div className="stat-card">
<div className="stat-card-group">
<span className="stat-card-title">Всего пользователей</span>
<div className="stat-card-value">0</div>
<div className="stat-card-value">{data?.total_users || 0}</div>
</div>
</div>
<div className="stat-card">
<div className="stat-card-group">
<span className="stat-card-title">Новых за 30 дней</span>
<div className="stat-card-value">0</div>
<div className="stat-card-value">{data?.new_last_30_days || 0}</div>
</div>
<div className="stat-card-group">
<span className="stat-card-title">Новых за предыдущие 30 дней</span>
<div className="stat-card-value">0</div>
<div className="stat-card-value">{data?.new_previous_30_days || 0}</div>
</div>
<div className="stat-card-group">
<span className="stat-card-title">Дельта</span>
<div className="stat-card-value">0</div>
{renderDelta(data?.growth_percent || 0)}
</div>
</div>
<div className="stat-card">
<div className="stat-card-group">
<span className="stat-card-title">Новых за текущий месяц</span>
<div className="stat-card-value">0</div>
<div className="stat-card-value">{data?.new_current_month || 0}</div>
</div>
<div className="stat-card-group">
<span className="stat-card-title">Новых за прошлый месяц</span>
<div className="stat-card-value">0</div>
<div className="stat-card-value">{data?.new_previous_month || 0}</div>
</div>
<div className="stat-card-group">
<span className="stat-card-title">Дельта</span>
<div className="stat-card-value">0</div>
{renderDelta(data?.month_growth_percent || 0)}
</div>
</div>
</div>
</div>
)
);
}
@@ -1,18 +1,43 @@
'use client'
import { useViolationGeneralStatistic } from '@/app/hooks/react-query/dashboard/useViolationGeneralStatistic';
export default function GeneralInfoViolations() {
const { data, isLoading, isError } = useViolationGeneralStatistic();
if (isLoading) {
return (
<div>
<h4 className="stats-title">Нарушения (день/месяц)</h4>
<div className="stats-grid">
<div className="stat-card">Загрузка...</div>
</div>
</div>
);
}
if (isError) {
return (
<div>
<h4 className="stats-title">Нарушения (день/месяц)</h4>
<div className="stats-grid">
<div className="stat-card">Ошибка при загрузке данных</div>
</div>
</div>
);
}
return (
<div>
<h4
className="stats-title"
>
<h4 className="stats-title">
Нарушения (день/месяц)
</h4>
<div className="stats-grid">
<div className="stat-card">
<div className="stat-card-group">
<span className="stat-card-title">Всего нарушений в этом месяце</span>
<div className="stat-card-value">0</div>
<span className="stat-card-title">Всего совпадений</span>
<div className="stat-card-value">{data?.total_violations || 0}</div>
</div>
<div className="stat-card-group">
<span className="stat-card-title">В прошлом месяце</span>
@@ -22,9 +47,20 @@ export default function GeneralInfoViolations() {
<div className="stat-card">
<div className="stat-card-group">
<span className="stat-card-title">Подано жалоб в этом месяце</span>
<span className="stat-card-title">Всего подано жалоб</span>
<div className="stat-card-value">{data?.total_complaints || 0}</div>
</div>
<div className="stat-card-group">
<span className="stat-card-title">В прошлом месяце</span>
<div className="stat-card-value">0</div>
</div>
</div>
<div className="stat-card">
<div className="stat-card-group">
<span className="stat-card-title">Всего подано претензий</span>
<div className="stat-card-value">{data?.total_law_cases || 0}</div>
</div>
<div className="stat-card-group">
<span className="stat-card-title">В прошлом месяце</span>
<div className="stat-card-value">0</div>
@@ -1,4 +1,38 @@
'use client'
import { useMonitoringStats } from '@/app/hooks/react-query/dashboard/useTariffStatistic';
export default function SubscriptionsStatistic() {
const { data, isLoading, isError } = useMonitoringStats();
if (isLoading) {
return (
<div className="admin-content">
<div className="content-header">
<h3>Статистика подписок</h3>
</div>
<div className="content-body">
<div>Загрузка...</div>
</div>
</div>
);
}
if (isError) {
return (
<div className="admin-content">
<div className="content-header">
<h3>Статистика подписок</h3>
</div>
<div className="content-body">
<div>Ошибка при загрузке данных</div>
</div>
</div>
);
}
const tariffs = data?.tariffs || [];
return (
<div className="admin-content">
<div className="content-header">
@@ -14,36 +48,28 @@ export default function SubscriptionsStatistic() {
</tr>
</thead>
<tbody>
<tr>
<td>
<span className="badge badge-primary">
СТАРТ
</span>
</td>
<td>0</td>
<td>0%</td>
</tr>
<tr>
<td>
<span className="badge badge-primary">
ДЕМО
</span>
</td>
<td>0</td>
<td>0%</td>
</tr>
<tr>
<td>
<span className="badge badge-primary">
ПРОФИ
</span>
</td>
<td>0</td>
<td>0%</td>
</tr>
{tariffs.length > 0 ? (
tariffs.map((tariff, index) => (
<tr key={index}>
<td>
<span className={`badge badge-primary`}>
{tariff.tariff_name}
</span>
</td>
<td>{tariff.user_count}</td>
<td>{tariff.usage_percent.toFixed(2)}%</td>
</tr>
))
) : (
<tr>
<td colSpan={3} style={{ textAlign: 'center' }}>
Нет данных
</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
)
);
}
+54 -47
View File
@@ -1,4 +1,37 @@
'use client'
import { useUserFilesTopStatistic } from '@/app/hooks/react-query/dashboard/useUserFilesTopStatistic';
import { convertBytes } from '@/app/lib/convertBytes';
export default function UsersTop() {
const { data, isLoading, isError } = useUserFilesTopStatistic();
if (isLoading) {
return (
<div className="admin-content users-top">
<div className="content-header">
<h3>Топ пользователей</h3>
</div>
<div className="content-body">
<div>Загрузка...</div>
</div>
</div>
);
}
if (isError) {
return (
<div className="admin-content users-top">
<div className="content-header">
<h3>Топ пользователей</h3>
</div>
<div className="content-body">
<div>Ошибка при загрузке данных</div>
</div>
</div>
);
}
return (
<div className="admin-content users-top">
<div className="content-header">
@@ -14,55 +47,29 @@ export default function UsersTop() {
</tr>
</thead>
<tbody>
<tr>
<td>
<div className="users-top-user">Сидоров Станислав Андреевич</div>
<div className="users-top-mail">Info@pro-ctrl.ru</div>
</td>
<td>0</td>
<td>0 MB</td>
</tr>
<tr>
<td>
<div className="users-top-user">Валерка</div>
<div className="users-top-mail">va@no-copy.ru</div>
</td>
<td>0</td>
<td>0 MB</td>
</tr>
<tr>
<td>
<div className="users-top-user">Игорь Викторович</div>
<div className="users-top-mail">igor@pro-ctrl.ru</div>
</td>
<td>0</td>
<td>0 MB</td>
</tr>
<tr>
<td>
<div className="users-top-user">Синицын Игорь Владимирович</div>
<div className="users-top-mail">admin@no-copy.ru</div>
</td>
<td>0</td>
<td>0 MB</td>
</tr>
<tr>
<td>
<div className="users-top-user">test test</div>
<div className="users-top-mail">test@no-copy.ru</div>
</td>
<td>0</td>
<td>0 MB</td>
</tr>
{Array.isArray(data) && data.length > 0 ? (
data.map((user, index) => (
<tr key={user.user_id || index}>
<td>
<div className="users-top-user">{user.full_name}</div>
<div className="users-top-mail">
ID: {user.user_id}
</div>
</td>
<td>{user.count}</td>
<td>{convertBytes(user.files_size)}</td>
</tr>
))
) : (
<tr>
<td colSpan={3} style={{ textAlign: 'center' }}>
Нет данных
</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
)
);
}