From ab0270ac58565f883722f7b04aa300b644e7973b Mon Sep 17 00:00:00 2001 From: Fringg Date: Sun, 8 Feb 2026 22:52:14 +0300 Subject: [PATCH] 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. --- src/api/admin.ts | 14 ++++++++++ src/locales/en.json | 9 +++++++ src/locales/fa.json | 9 +++++++ src/locales/ru.json | 9 +++++++ src/locales/zh.json | 9 +++++++ src/pages/AdminDashboard.tsx | 51 +++++++++++++++++++++++++++++++++++- src/vite-env.d.ts | 2 ++ tsconfig.node.json | 1 + vite.config.ts | 4 +++ 9 files changed, 107 insertions(+), 1 deletion(-) diff --git a/src/api/admin.ts b/src/api/admin.ts index 843f969..608581d 100644 --- a/src/api/admin.ts +++ b/src/api/admin.ts @@ -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 => { + const response = await apiClient.get('/cabinet/admin/stats/system-info'); + return response.data; + }, + // Get complete dashboard stats getDashboardStats: async (): Promise => { const response = await apiClient.get('/cabinet/admin/stats/dashboard'); diff --git a/src/locales/en.json b/src/locales/en.json index bef4532..afd36c6 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -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": { diff --git a/src/locales/fa.json b/src/locales/fa.json index a010f2c..f5e4d25 100644 --- a/src/locales/fa.json +++ b/src/locales/fa.json @@ -2008,6 +2008,15 @@ "amount": "مبلغ", "method": "روش", "date": "تاریخ" + }, + "systemInfo": { + "title": "سیستم", + "cabinet": "کابینت", + "bot": "ربات", + "python": "Python", + "uptime": "آپتایم", + "users": "کاربران", + "activeSubs": "اشتراک‌ها" } }, "profile": { diff --git a/src/locales/ru.json b/src/locales/ru.json index 490a61b..ce8de31 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -2756,6 +2756,15 @@ "amount": "Сумма", "method": "Метод", "date": "Дата" + }, + "systemInfo": { + "title": "Система", + "cabinet": "Кабинет", + "bot": "Бот", + "python": "Python", + "uptime": "Аптайм", + "users": "Юзеров", + "activeSubs": "Подписок" } }, "banSystem": { diff --git a/src/locales/zh.json b/src/locales/zh.json index 5a33f00..9a56aec 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -2007,6 +2007,15 @@ "amount": "金额", "method": "方法", "date": "日期" + }, + "systemInfo": { + "title": "系统", + "cabinet": "面板", + "bot": "机器人", + "python": "Python", + "uptime": "运行时间", + "users": "用户", + "activeSubs": "订阅" } }, "profile": { diff --git a/src/pages/AdminDashboard.tsx b/src/pages/AdminDashboard.tsx index f9e523a..304edc5 100644 --- a/src/pages/AdminDashboard.tsx +++ b/src/pages/AdminDashboard.tsx @@ -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(null); const [payments, setPayments] = useState(null); const [referrersTab, setReferrersTab] = useState<'earnings' | 'invited'>('earnings'); + const [systemInfo, setSystemInfo] = useState(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() { )} + + {/* System Info */} + {systemInfo && ( +
+

+ {t('adminDashboard.systemInfo.title')} +

+
+
+ {t('adminDashboard.systemInfo.cabinet')}: + v{CABINET_VERSION} +
+
+ {t('adminDashboard.systemInfo.bot')}: + v{systemInfo.bot_version} +
+
+ {t('adminDashboard.systemInfo.python')}: + {systemInfo.python_version} +
+
+ {t('adminDashboard.systemInfo.uptime')}: + + {(() => { + 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(' '); + })()} + +
+
+ {t('adminDashboard.systemInfo.users')}: + {systemInfo.users_total} +
+
+ {t('adminDashboard.systemInfo.activeSubs')}: + {systemInfo.subscriptions_active} +
+
+
+ )} ); } diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 47caca1..a5e4314 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -1,5 +1,7 @@ /// +declare const __APP_VERSION__: string; + interface ImportMetaEnv { readonly VITE_API_URL: string; readonly VITE_TELEGRAM_BOT_USERNAME?: string; diff --git a/tsconfig.node.json b/tsconfig.node.json index b940375..ec78806 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -5,6 +5,7 @@ "module": "ESNext", "moduleResolution": "bundler", "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, "types": ["node"] }, "include": ["vite.config.ts"] diff --git a/vite.config.ts b/vite.config.ts index 97abefa..3c5012f 100644 --- a/vite.config.ts +++ b/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'),