mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 09:33:46 +00:00
feat: add system info card to admin dashboard
Shows cabinet version, bot version, Python version, uptime, users and active subscriptions at the bottom of the dashboard page.
This commit is contained in:
@@ -308,9 +308,23 @@ export interface RecentPaymentsResponse {
|
||||
total_week_kopeks: number;
|
||||
}
|
||||
|
||||
export interface SystemInfo {
|
||||
bot_version: string;
|
||||
python_version: string;
|
||||
uptime_seconds: number;
|
||||
users_total: number;
|
||||
subscriptions_active: number;
|
||||
}
|
||||
|
||||
// ============ Dashboard Stats API ============
|
||||
|
||||
export const statsApi = {
|
||||
// Get system info
|
||||
getSystemInfo: async (): Promise<SystemInfo> => {
|
||||
const response = await apiClient.get('/cabinet/admin/stats/system-info');
|
||||
return response.data;
|
||||
},
|
||||
|
||||
// Get complete dashboard stats
|
||||
getDashboardStats: async (): Promise<DashboardStats> => {
|
||||
const response = await apiClient.get('/cabinet/admin/stats/dashboard');
|
||||
|
||||
@@ -2205,6 +2205,15 @@
|
||||
"purchasedWeek": "Week",
|
||||
"purchasedMonth": "Month",
|
||||
"noTariffs": "No tariffs"
|
||||
},
|
||||
"systemInfo": {
|
||||
"title": "System",
|
||||
"cabinet": "Cabinet",
|
||||
"bot": "Bot",
|
||||
"python": "Python",
|
||||
"uptime": "Uptime",
|
||||
"users": "Users",
|
||||
"activeSubs": "Active Subs"
|
||||
}
|
||||
},
|
||||
"banSystem": {
|
||||
|
||||
@@ -2008,6 +2008,15 @@
|
||||
"amount": "مبلغ",
|
||||
"method": "روش",
|
||||
"date": "تاریخ"
|
||||
},
|
||||
"systemInfo": {
|
||||
"title": "سیستم",
|
||||
"cabinet": "کابینت",
|
||||
"bot": "ربات",
|
||||
"python": "Python",
|
||||
"uptime": "آپتایم",
|
||||
"users": "کاربران",
|
||||
"activeSubs": "اشتراکها"
|
||||
}
|
||||
},
|
||||
"profile": {
|
||||
|
||||
@@ -2756,6 +2756,15 @@
|
||||
"amount": "Сумма",
|
||||
"method": "Метод",
|
||||
"date": "Дата"
|
||||
},
|
||||
"systemInfo": {
|
||||
"title": "Система",
|
||||
"cabinet": "Кабинет",
|
||||
"bot": "Бот",
|
||||
"python": "Python",
|
||||
"uptime": "Аптайм",
|
||||
"users": "Юзеров",
|
||||
"activeSubs": "Подписок"
|
||||
}
|
||||
},
|
||||
"banSystem": {
|
||||
|
||||
@@ -2007,6 +2007,15 @@
|
||||
"amount": "金额",
|
||||
"method": "方法",
|
||||
"date": "日期"
|
||||
},
|
||||
"systemInfo": {
|
||||
"title": "系统",
|
||||
"cabinet": "面板",
|
||||
"bot": "机器人",
|
||||
"python": "Python",
|
||||
"uptime": "运行时间",
|
||||
"users": "用户",
|
||||
"activeSubs": "订阅"
|
||||
}
|
||||
},
|
||||
"profile": {
|
||||
|
||||
@@ -5,10 +5,13 @@ import {
|
||||
statsApi,
|
||||
type DashboardStats,
|
||||
type NodeStatus,
|
||||
type SystemInfo,
|
||||
type TopReferrersResponse,
|
||||
type TopCampaignsResponse,
|
||||
type RecentPaymentsResponse,
|
||||
} from '../api/admin';
|
||||
|
||||
const CABINET_VERSION = __APP_VERSION__;
|
||||
import { useCurrency } from '../hooks/useCurrency';
|
||||
import { usePlatform } from '../platform/hooks/usePlatform';
|
||||
|
||||
@@ -375,6 +378,7 @@ export default function AdminDashboard() {
|
||||
const [campaigns, setCampaigns] = useState<TopCampaignsResponse | null>(null);
|
||||
const [payments, setPayments] = useState<RecentPaymentsResponse | null>(null);
|
||||
const [referrersTab, setReferrersTab] = useState<'earnings' | 'invited'>('earnings');
|
||||
const [systemInfo, setSystemInfo] = useState<SystemInfo | null>(null);
|
||||
|
||||
const fetchStats = useCallback(async () => {
|
||||
try {
|
||||
@@ -392,14 +396,16 @@ export default function AdminDashboard() {
|
||||
|
||||
const fetchExtendedStats = useCallback(async () => {
|
||||
try {
|
||||
const [referrersData, campaignsData, paymentsData] = await Promise.all([
|
||||
const [referrersData, campaignsData, paymentsData, sysInfoData] = await Promise.all([
|
||||
statsApi.getTopReferrers(10),
|
||||
statsApi.getTopCampaigns(10),
|
||||
statsApi.getRecentPayments(20),
|
||||
statsApi.getSystemInfo(),
|
||||
]);
|
||||
setReferrers(referrersData);
|
||||
setCampaigns(campaignsData);
|
||||
setPayments(paymentsData);
|
||||
setSystemInfo(sysInfoData);
|
||||
} catch (err) {
|
||||
console.error('Failed to load extended stats:', err);
|
||||
}
|
||||
@@ -1137,6 +1143,49 @@ export default function AdminDashboard() {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* System Info */}
|
||||
{systemInfo && (
|
||||
<div className="rounded-xl border border-dark-700 bg-dark-800 p-4">
|
||||
<h3 className="mb-3 text-sm font-semibold text-dark-300">
|
||||
{t('adminDashboard.systemInfo.title')}
|
||||
</h3>
|
||||
<div className="flex flex-wrap gap-x-6 gap-y-2 text-sm">
|
||||
<div>
|
||||
<span className="text-dark-500">{t('adminDashboard.systemInfo.cabinet')}: </span>
|
||||
<span className="font-medium text-dark-200">v{CABINET_VERSION}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-dark-500">{t('adminDashboard.systemInfo.bot')}: </span>
|
||||
<span className="font-medium text-dark-200">v{systemInfo.bot_version}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-dark-500">{t('adminDashboard.systemInfo.python')}: </span>
|
||||
<span className="font-medium text-dark-200">{systemInfo.python_version}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-dark-500">{t('adminDashboard.systemInfo.uptime')}: </span>
|
||||
<span className="font-medium text-dark-200">
|
||||
{(() => {
|
||||
const s = systemInfo.uptime_seconds;
|
||||
const d = Math.floor(s / 86400);
|
||||
const h = Math.floor((s % 86400) / 3600);
|
||||
const m = Math.floor((s % 3600) / 60);
|
||||
return [d > 0 && `${d}d`, h > 0 && `${h}h`, `${m}m`].filter(Boolean).join(' ');
|
||||
})()}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-dark-500">{t('adminDashboard.systemInfo.users')}: </span>
|
||||
<span className="font-medium text-dark-200">{systemInfo.users_total}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-dark-500">{t('adminDashboard.systemInfo.activeSubs')}: </span>
|
||||
<span className="font-medium text-dark-200">{systemInfo.subscriptions_active}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
2
src/vite-env.d.ts
vendored
2
src/vite-env.d.ts
vendored
@@ -1,5 +1,7 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare const __APP_VERSION__: string;
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_API_URL: string;
|
||||
readonly VITE_TELEGRAM_BOT_USERNAME?: string;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"resolveJsonModule": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import path from 'path';
|
||||
import packageJson from './package.json';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
define: {
|
||||
__APP_VERSION__: JSON.stringify(packageJson.version),
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src'),
|
||||
|
||||
Reference in New Issue
Block a user