import React from 'react'; import { Box, Heading, Text, Stack, Button, Stat, StatLabel, StatNumber, StatHelpText, SimpleGrid, useToast, Card, CardHeader, CardBody, Badge, Divider, Modal, ModalOverlay, ModalContent, ModalHeader, ModalCloseButton, ModalBody, ModalFooter, Textarea, HStack, VStack, } from '@chakra-ui/react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { getPrefetchStatus, triggerPrefetch, PrefetchStatus } from '../../services/admin/prefetch'; import AdminLayout from '../../layouts/AdminLayout'; import api, { API_URL } from '../../services/api'; const PrefetchAdminPage: React.FC = () => { const toast = useToast(); const qc = useQueryClient(); // RAW cache viewer state const [rawOpen, setRawOpen] = React.useState(false); const [rawSel, setRawSel] = React.useState(''); const [rawLoading, setRawLoading] = React.useState(false); const [rawError, setRawError] = React.useState(null); const [rawText, setRawText] = React.useState(''); // Load list of available cache files (admin) const { data: rawList, isError: rawListError, error: rawListErrorMsg } = useQuery<{ files: Array<{ label: string; path: string; size_bytes?: number; mod_time?: string }>}>({ queryKey: ['admin', 'cache', 'list'], queryFn: async () => { const res = await api.get('/admin/cache/list'); return res.data; }, staleTime: 30_000, retry: 1, }); const fetchRaw = async (path: string) => { setRawLoading(true); setRawError(null); setRawText(''); try { const res = await api.get(`/admin/cache/file?path=${encodeURIComponent(path)}`, { transformResponse: [(data) => data], // Get raw text response }); const txt = res.data; try { const obj = JSON.parse(txt); setRawText(JSON.stringify(obj, null, 2)); } catch { setRawText(txt); } } catch (e: any) { setRawError(e?.message || 'Nelze načíst data'); } finally { setRawLoading(false); } }; const { data: status, isLoading, isFetching } = useQuery({ queryKey: ['admin', 'prefetch', 'status'], queryFn: getPrefetchStatus, refetchInterval: 30_000, // keep page live }); const trigger = useMutation({ mutationFn: triggerPrefetch, onSuccess: async () => { toast({ title: 'Prefetch spuštěn', status: 'success' }); await qc.invalidateQueries({ queryKey: ['admin', 'prefetch', 'status'] }); }, onError: (err: any) => { toast({ title: 'Spuštění prefetch selhalo', description: String(err?.message || err), status: 'error' }); }, }); const last = status?.lastUpdated ? new Date(status.lastUpdated) : null; const next = status?.nextApproximate ? new Date(status.nextApproximate) : null; return ( Prefetch & Cache Na pozadí běží úloha, která pravidelně stahuje JSON snapshoty z veřejných API pro rychlejší načítání stránek. Zde uvidíte aktuální plán a můžete spustit ruční stažení. Režim Aktuální režim {status?.fastMode ? ( Rychlý (během zápasu) ) : ( Normální )} V době konání zápasů se automaticky přepne do rychlého režimu. Poslední aktualizace Poslední prefetch {last ? last.toLocaleString() : 'Unknown'} {isFetching ? 'Obnovuji…' : 'Aktuální'} Další spuštění Přibližně {next ? next.toLocaleString() : '—'} Interval: {status?.intervalMinutes ?? 30} min Ovládání Ruční spuštění zahájí na pozadí obnovu všech veřejných endpointů a zdrojů FAČR. Nezablokuje uživatelské rozhraní. {/* RAW viewer modal */} setRawOpen(false)} size="6xl" scrollBehavior="inside"> RAW data (prefetch & cache) {rawListError ? ( Chyba při načítání seznamu souborů {String(rawListErrorMsg)} ) : !rawList?.files || rawList.files.length === 0 ? ( Žádné cache soubory nebyly nalezeny Zkuste spustit prefetch stažení nejprve ) : ( {rawList.files.map((f) => ( ))} {rawSel || 'Vyberte soubor'} {rawError && {rawError}}