From 7426e1e6d14a43f269c976954d33dece7c2ec19e Mon Sep 17 00:00:00 2001 From: c0mrade Date: Tue, 26 May 2026 13:35:39 +0300 Subject: [PATCH] perf(admin-panel): migrate system info + stats poll to React Query MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 60s setInterval becomes refetchInterval, dropping the cancelled-flag ceremony and the manual loading/setState plumbing. Two independent queries — one failure no longer poisons the other (Promise.all previously rejected the whole batch on a single 5xx). --- src/pages/AdminPanel.tsx | 49 ++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/src/pages/AdminPanel.tsx b/src/pages/AdminPanel.tsx index 6f9f917..088869c 100644 --- a/src/pages/AdminPanel.tsx +++ b/src/pages/AdminPanel.tsx @@ -1,6 +1,7 @@ import { useState, useEffect, useRef, useCallback, useMemo, memo } from 'react'; import { Link } from 'react-router'; import { useTranslation } from 'react-i18next'; +import { useQuery } from '@tanstack/react-query'; import { usePermissionStore } from '@/store/permissions'; import { statsApi, type SystemInfo, type DashboardStats } from '@/api/admin'; import { useAnimatedNumber } from '@/hooks/useAnimatedNumber'; @@ -878,38 +879,28 @@ export default function AdminPanel() { const inputRef = useRef(null); const { safeAreaInset, contentSafeAreaInset } = useTelegramSDK(); - const [systemInfo, setSystemInfo] = useState(null); - const [dashboardStats, setDashboardStats] = useState(null); - const [loading, setLoading] = useState(true); - const safeTop = Math.max(safeAreaInset.top, contentSafeAreaInset.top); const safeBottom = Math.max(safeAreaInset.bottom, contentSafeAreaInset.bottom); - // Fetch stats - useEffect(() => { - let cancelled = false; - const fetchData = async () => { - try { - const [sysInfo, stats] = await Promise.all([ - statsApi.getSystemInfo(), - statsApi.getDashboardStats(), - ]); - if (!cancelled) { - setSystemInfo(sysInfo); - setDashboardStats(stats); - setLoading(false); - } - } catch { - if (!cancelled) setLoading(false); - } - }; - fetchData(); - const interval = setInterval(fetchData, 60_000); - return () => { - cancelled = true; - clearInterval(interval); - }; - }, []); + // System info + dashboard stats — polled every 60s via React Query + // (replaces the manual setInterval + useState + cancelled-flag pattern). + const systemInfoQuery = useQuery({ + queryKey: ['admin-panel-system-info'] as const, + queryFn: () => statsApi.getSystemInfo(), + refetchInterval: 60_000, + refetchOnWindowFocus: false, + }); + const dashboardStatsQuery = useQuery({ + queryKey: ['admin-panel-dashboard-stats'] as const, + queryFn: () => statsApi.getDashboardStats(), + refetchInterval: 60_000, + refetchOnWindowFocus: false, + }); + + const systemInfo = systemInfoQuery.data ?? null; + const dashboardStats = dashboardStatsQuery.data ?? null; + // "loading" only counts the very first fetch — once we have any data, render it. + const loading = systemInfoQuery.isLoading || dashboardStatsQuery.isLoading; // Keyboard shortcuts: Cmd+K to focus search, Escape to clear useEffect(() => {